18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci#include <linux/module.h> 38c2ecf20Sopenharmony_ci#include <linux/kernel.h> 48c2ecf20Sopenharmony_ci#include <linux/i2c.h> 58c2ecf20Sopenharmony_ci#include <linux/types.h> 68c2ecf20Sopenharmony_ci#include <linux/init.h> 78c2ecf20Sopenharmony_ci#include <linux/errno.h> 88c2ecf20Sopenharmony_ci#include <linux/delay.h> 98c2ecf20Sopenharmony_ci#include <linux/videodev2.h> 108c2ecf20Sopenharmony_ci#include <media/v4l2-common.h> 118c2ecf20Sopenharmony_ci#include <media/tuner.h> 128c2ecf20Sopenharmony_ci#include "tuner-i2c.h" 138c2ecf20Sopenharmony_ci#include "tda9887.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci/* Chips: 178c2ecf20Sopenharmony_ci TDA9885 (PAL, NTSC) 188c2ecf20Sopenharmony_ci TDA9886 (PAL, SECAM, NTSC) 198c2ecf20Sopenharmony_ci TDA9887 (PAL, SECAM, NTSC, FM Radio) 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci Used as part of several tuners 228c2ecf20Sopenharmony_ci*/ 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic int debug; 258c2ecf20Sopenharmony_cimodule_param(debug, int, 0644); 268c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "enable verbose debug messages"); 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(tda9887_list_mutex); 298c2ecf20Sopenharmony_cistatic LIST_HEAD(hybrid_tuner_instance_list); 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistruct tda9887_priv { 328c2ecf20Sopenharmony_ci struct tuner_i2c_props i2c_props; 338c2ecf20Sopenharmony_ci struct list_head hybrid_tuner_instance_list; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci unsigned char data[4]; 368c2ecf20Sopenharmony_ci unsigned int config; 378c2ecf20Sopenharmony_ci unsigned int mode; 388c2ecf20Sopenharmony_ci unsigned int audmode; 398c2ecf20Sopenharmony_ci v4l2_std_id std; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci bool standby; 428c2ecf20Sopenharmony_ci}; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* ---------------------------------------------------------------------- */ 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define UNSET (-1U) 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistruct tvnorm { 498c2ecf20Sopenharmony_ci v4l2_std_id std; 508c2ecf20Sopenharmony_ci char *name; 518c2ecf20Sopenharmony_ci unsigned char b; 528c2ecf20Sopenharmony_ci unsigned char c; 538c2ecf20Sopenharmony_ci unsigned char e; 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/* ---------------------------------------------------------------------- */ 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci// 598c2ecf20Sopenharmony_ci// TDA defines 608c2ecf20Sopenharmony_ci// 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci//// first reg (b) 638c2ecf20Sopenharmony_ci#define cVideoTrapBypassOFF 0x00 // bit b0 648c2ecf20Sopenharmony_ci#define cVideoTrapBypassON 0x01 // bit b0 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci#define cAutoMuteFmInactive 0x00 // bit b1 678c2ecf20Sopenharmony_ci#define cAutoMuteFmActive 0x02 // bit b1 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#define cIntercarrier 0x00 // bit b2 708c2ecf20Sopenharmony_ci#define cQSS 0x04 // bit b2 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci#define cPositiveAmTV 0x00 // bit b3:4 738c2ecf20Sopenharmony_ci#define cFmRadio 0x08 // bit b3:4 748c2ecf20Sopenharmony_ci#define cNegativeFmTV 0x10 // bit b3:4 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci#define cForcedMuteAudioON 0x20 // bit b5 788c2ecf20Sopenharmony_ci#define cForcedMuteAudioOFF 0x00 // bit b5 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci#define cOutputPort1Active 0x00 // bit b6 818c2ecf20Sopenharmony_ci#define cOutputPort1Inactive 0x40 // bit b6 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci#define cOutputPort2Active 0x00 // bit b7 848c2ecf20Sopenharmony_ci#define cOutputPort2Inactive 0x80 // bit b7 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci//// second reg (c) 888c2ecf20Sopenharmony_ci#define cDeemphasisOFF 0x00 // bit c5 898c2ecf20Sopenharmony_ci#define cDeemphasisON 0x20 // bit c5 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci#define cDeemphasis75 0x00 // bit c6 928c2ecf20Sopenharmony_ci#define cDeemphasis50 0x40 // bit c6 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci#define cAudioGain0 0x00 // bit c7 958c2ecf20Sopenharmony_ci#define cAudioGain6 0x80 // bit c7 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci#define cTopMask 0x1f // bit c0:4 988c2ecf20Sopenharmony_ci#define cTopDefault 0x10 // bit c0:4 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci//// third reg (e) 1018c2ecf20Sopenharmony_ci#define cAudioIF_4_5 0x00 // bit e0:1 1028c2ecf20Sopenharmony_ci#define cAudioIF_5_5 0x01 // bit e0:1 1038c2ecf20Sopenharmony_ci#define cAudioIF_6_0 0x02 // bit e0:1 1048c2ecf20Sopenharmony_ci#define cAudioIF_6_5 0x03 // bit e0:1 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci#define cVideoIFMask 0x1c // bit e2:4 1088c2ecf20Sopenharmony_ci/* Video IF selection in TV Mode (bit B3=0) */ 1098c2ecf20Sopenharmony_ci#define cVideoIF_58_75 0x00 // bit e2:4 1108c2ecf20Sopenharmony_ci#define cVideoIF_45_75 0x04 // bit e2:4 1118c2ecf20Sopenharmony_ci#define cVideoIF_38_90 0x08 // bit e2:4 1128c2ecf20Sopenharmony_ci#define cVideoIF_38_00 0x0C // bit e2:4 1138c2ecf20Sopenharmony_ci#define cVideoIF_33_90 0x10 // bit e2:4 1148c2ecf20Sopenharmony_ci#define cVideoIF_33_40 0x14 // bit e2:4 1158c2ecf20Sopenharmony_ci#define cRadioIF_45_75 0x18 // bit e2:4 1168c2ecf20Sopenharmony_ci#define cRadioIF_38_90 0x1C // bit e2:4 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci/* IF1 selection in Radio Mode (bit B3=1) */ 1198c2ecf20Sopenharmony_ci#define cRadioIF_33_30 0x00 // bit e2,4 (also 0x10,0x14) 1208c2ecf20Sopenharmony_ci#define cRadioIF_41_30 0x04 // bit e2,4 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci/* Output of AFC pin in radio mode when bit E7=1 */ 1238c2ecf20Sopenharmony_ci#define cRadioAGC_SIF 0x00 // bit e3 1248c2ecf20Sopenharmony_ci#define cRadioAGC_FM 0x08 // bit e3 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci#define cTunerGainNormal 0x00 // bit e5 1278c2ecf20Sopenharmony_ci#define cTunerGainLow 0x20 // bit e5 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci#define cGating_18 0x00 // bit e6 1308c2ecf20Sopenharmony_ci#define cGating_36 0x40 // bit e6 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci#define cAgcOutON 0x80 // bit e7 1338c2ecf20Sopenharmony_ci#define cAgcOutOFF 0x00 // bit e7 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci/* ---------------------------------------------------------------------- */ 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic struct tvnorm tvnorms[] = { 1388c2ecf20Sopenharmony_ci { 1398c2ecf20Sopenharmony_ci .std = V4L2_STD_PAL_BG | V4L2_STD_PAL_H | V4L2_STD_PAL_N, 1408c2ecf20Sopenharmony_ci .name = "PAL-BGHN", 1418c2ecf20Sopenharmony_ci .b = ( cNegativeFmTV | 1428c2ecf20Sopenharmony_ci cQSS ), 1438c2ecf20Sopenharmony_ci .c = ( cDeemphasisON | 1448c2ecf20Sopenharmony_ci cDeemphasis50 | 1458c2ecf20Sopenharmony_ci cTopDefault), 1468c2ecf20Sopenharmony_ci .e = ( cGating_36 | 1478c2ecf20Sopenharmony_ci cAudioIF_5_5 | 1488c2ecf20Sopenharmony_ci cVideoIF_38_90 ), 1498c2ecf20Sopenharmony_ci },{ 1508c2ecf20Sopenharmony_ci .std = V4L2_STD_PAL_I, 1518c2ecf20Sopenharmony_ci .name = "PAL-I", 1528c2ecf20Sopenharmony_ci .b = ( cNegativeFmTV | 1538c2ecf20Sopenharmony_ci cQSS ), 1548c2ecf20Sopenharmony_ci .c = ( cDeemphasisON | 1558c2ecf20Sopenharmony_ci cDeemphasis50 | 1568c2ecf20Sopenharmony_ci cTopDefault), 1578c2ecf20Sopenharmony_ci .e = ( cGating_36 | 1588c2ecf20Sopenharmony_ci cAudioIF_6_0 | 1598c2ecf20Sopenharmony_ci cVideoIF_38_90 ), 1608c2ecf20Sopenharmony_ci },{ 1618c2ecf20Sopenharmony_ci .std = V4L2_STD_PAL_DK, 1628c2ecf20Sopenharmony_ci .name = "PAL-DK", 1638c2ecf20Sopenharmony_ci .b = ( cNegativeFmTV | 1648c2ecf20Sopenharmony_ci cQSS ), 1658c2ecf20Sopenharmony_ci .c = ( cDeemphasisON | 1668c2ecf20Sopenharmony_ci cDeemphasis50 | 1678c2ecf20Sopenharmony_ci cTopDefault), 1688c2ecf20Sopenharmony_ci .e = ( cGating_36 | 1698c2ecf20Sopenharmony_ci cAudioIF_6_5 | 1708c2ecf20Sopenharmony_ci cVideoIF_38_90 ), 1718c2ecf20Sopenharmony_ci },{ 1728c2ecf20Sopenharmony_ci .std = V4L2_STD_PAL_M | V4L2_STD_PAL_Nc, 1738c2ecf20Sopenharmony_ci .name = "PAL-M/Nc", 1748c2ecf20Sopenharmony_ci .b = ( cNegativeFmTV | 1758c2ecf20Sopenharmony_ci cQSS ), 1768c2ecf20Sopenharmony_ci .c = ( cDeemphasisON | 1778c2ecf20Sopenharmony_ci cDeemphasis75 | 1788c2ecf20Sopenharmony_ci cTopDefault), 1798c2ecf20Sopenharmony_ci .e = ( cGating_36 | 1808c2ecf20Sopenharmony_ci cAudioIF_4_5 | 1818c2ecf20Sopenharmony_ci cVideoIF_45_75 ), 1828c2ecf20Sopenharmony_ci },{ 1838c2ecf20Sopenharmony_ci .std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H, 1848c2ecf20Sopenharmony_ci .name = "SECAM-BGH", 1858c2ecf20Sopenharmony_ci .b = ( cNegativeFmTV | 1868c2ecf20Sopenharmony_ci cQSS ), 1878c2ecf20Sopenharmony_ci .c = ( cTopDefault), 1888c2ecf20Sopenharmony_ci .e = ( cAudioIF_5_5 | 1898c2ecf20Sopenharmony_ci cVideoIF_38_90 ), 1908c2ecf20Sopenharmony_ci },{ 1918c2ecf20Sopenharmony_ci .std = V4L2_STD_SECAM_L, 1928c2ecf20Sopenharmony_ci .name = "SECAM-L", 1938c2ecf20Sopenharmony_ci .b = ( cPositiveAmTV | 1948c2ecf20Sopenharmony_ci cQSS ), 1958c2ecf20Sopenharmony_ci .c = ( cTopDefault), 1968c2ecf20Sopenharmony_ci .e = ( cGating_36 | 1978c2ecf20Sopenharmony_ci cAudioIF_6_5 | 1988c2ecf20Sopenharmony_ci cVideoIF_38_90 ), 1998c2ecf20Sopenharmony_ci },{ 2008c2ecf20Sopenharmony_ci .std = V4L2_STD_SECAM_LC, 2018c2ecf20Sopenharmony_ci .name = "SECAM-L'", 2028c2ecf20Sopenharmony_ci .b = ( cOutputPort2Inactive | 2038c2ecf20Sopenharmony_ci cPositiveAmTV | 2048c2ecf20Sopenharmony_ci cQSS ), 2058c2ecf20Sopenharmony_ci .c = ( cTopDefault), 2068c2ecf20Sopenharmony_ci .e = ( cGating_36 | 2078c2ecf20Sopenharmony_ci cAudioIF_6_5 | 2088c2ecf20Sopenharmony_ci cVideoIF_33_90 ), 2098c2ecf20Sopenharmony_ci },{ 2108c2ecf20Sopenharmony_ci .std = V4L2_STD_SECAM_DK, 2118c2ecf20Sopenharmony_ci .name = "SECAM-DK", 2128c2ecf20Sopenharmony_ci .b = ( cNegativeFmTV | 2138c2ecf20Sopenharmony_ci cQSS ), 2148c2ecf20Sopenharmony_ci .c = ( cDeemphasisON | 2158c2ecf20Sopenharmony_ci cDeemphasis50 | 2168c2ecf20Sopenharmony_ci cTopDefault), 2178c2ecf20Sopenharmony_ci .e = ( cGating_36 | 2188c2ecf20Sopenharmony_ci cAudioIF_6_5 | 2198c2ecf20Sopenharmony_ci cVideoIF_38_90 ), 2208c2ecf20Sopenharmony_ci },{ 2218c2ecf20Sopenharmony_ci .std = V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_KR, 2228c2ecf20Sopenharmony_ci .name = "NTSC-M", 2238c2ecf20Sopenharmony_ci .b = ( cNegativeFmTV | 2248c2ecf20Sopenharmony_ci cQSS ), 2258c2ecf20Sopenharmony_ci .c = ( cDeemphasisON | 2268c2ecf20Sopenharmony_ci cDeemphasis75 | 2278c2ecf20Sopenharmony_ci cTopDefault), 2288c2ecf20Sopenharmony_ci .e = ( cGating_36 | 2298c2ecf20Sopenharmony_ci cAudioIF_4_5 | 2308c2ecf20Sopenharmony_ci cVideoIF_45_75 ), 2318c2ecf20Sopenharmony_ci },{ 2328c2ecf20Sopenharmony_ci .std = V4L2_STD_NTSC_M_JP, 2338c2ecf20Sopenharmony_ci .name = "NTSC-M-JP", 2348c2ecf20Sopenharmony_ci .b = ( cNegativeFmTV | 2358c2ecf20Sopenharmony_ci cQSS ), 2368c2ecf20Sopenharmony_ci .c = ( cDeemphasisON | 2378c2ecf20Sopenharmony_ci cDeemphasis50 | 2388c2ecf20Sopenharmony_ci cTopDefault), 2398c2ecf20Sopenharmony_ci .e = ( cGating_36 | 2408c2ecf20Sopenharmony_ci cAudioIF_4_5 | 2418c2ecf20Sopenharmony_ci cVideoIF_58_75 ), 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci}; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic struct tvnorm radio_stereo = { 2468c2ecf20Sopenharmony_ci .name = "Radio Stereo", 2478c2ecf20Sopenharmony_ci .b = ( cFmRadio | 2488c2ecf20Sopenharmony_ci cQSS ), 2498c2ecf20Sopenharmony_ci .c = ( cDeemphasisOFF | 2508c2ecf20Sopenharmony_ci cAudioGain6 | 2518c2ecf20Sopenharmony_ci cTopDefault), 2528c2ecf20Sopenharmony_ci .e = ( cTunerGainLow | 2538c2ecf20Sopenharmony_ci cAudioIF_5_5 | 2548c2ecf20Sopenharmony_ci cRadioIF_38_90 ), 2558c2ecf20Sopenharmony_ci}; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic struct tvnorm radio_mono = { 2588c2ecf20Sopenharmony_ci .name = "Radio Mono", 2598c2ecf20Sopenharmony_ci .b = ( cFmRadio | 2608c2ecf20Sopenharmony_ci cQSS ), 2618c2ecf20Sopenharmony_ci .c = ( cDeemphasisON | 2628c2ecf20Sopenharmony_ci cDeemphasis75 | 2638c2ecf20Sopenharmony_ci cTopDefault), 2648c2ecf20Sopenharmony_ci .e = ( cTunerGainLow | 2658c2ecf20Sopenharmony_ci cAudioIF_5_5 | 2668c2ecf20Sopenharmony_ci cRadioIF_38_90 ), 2678c2ecf20Sopenharmony_ci}; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci/* ---------------------------------------------------------------------- */ 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic void dump_read_message(struct dvb_frontend *fe, unsigned char *buf) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci struct tda9887_priv *priv = fe->analog_demod_priv; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci static char *afc[16] = { 2768c2ecf20Sopenharmony_ci "- 12.5 kHz", 2778c2ecf20Sopenharmony_ci "- 37.5 kHz", 2788c2ecf20Sopenharmony_ci "- 62.5 kHz", 2798c2ecf20Sopenharmony_ci "- 87.5 kHz", 2808c2ecf20Sopenharmony_ci "-112.5 kHz", 2818c2ecf20Sopenharmony_ci "-137.5 kHz", 2828c2ecf20Sopenharmony_ci "-162.5 kHz", 2838c2ecf20Sopenharmony_ci "-187.5 kHz [min]", 2848c2ecf20Sopenharmony_ci "+187.5 kHz [max]", 2858c2ecf20Sopenharmony_ci "+162.5 kHz", 2868c2ecf20Sopenharmony_ci "+137.5 kHz", 2878c2ecf20Sopenharmony_ci "+112.5 kHz", 2888c2ecf20Sopenharmony_ci "+ 87.5 kHz", 2898c2ecf20Sopenharmony_ci "+ 62.5 kHz", 2908c2ecf20Sopenharmony_ci "+ 37.5 kHz", 2918c2ecf20Sopenharmony_ci "+ 12.5 kHz", 2928c2ecf20Sopenharmony_ci }; 2938c2ecf20Sopenharmony_ci tuner_info("read: 0x%2x\n", buf[0]); 2948c2ecf20Sopenharmony_ci tuner_info(" after power on : %s\n", (buf[0] & 0x01) ? "yes" : "no"); 2958c2ecf20Sopenharmony_ci tuner_info(" afc : %s\n", afc[(buf[0] >> 1) & 0x0f]); 2968c2ecf20Sopenharmony_ci tuner_info(" fmif level : %s\n", (buf[0] & 0x20) ? "high" : "low"); 2978c2ecf20Sopenharmony_ci tuner_info(" afc window : %s\n", (buf[0] & 0x40) ? "in" : "out"); 2988c2ecf20Sopenharmony_ci tuner_info(" vfi level : %s\n", (buf[0] & 0x80) ? "high" : "low"); 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistatic void dump_write_message(struct dvb_frontend *fe, unsigned char *buf) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci struct tda9887_priv *priv = fe->analog_demod_priv; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci static char *sound[4] = { 3068c2ecf20Sopenharmony_ci "AM/TV", 3078c2ecf20Sopenharmony_ci "FM/radio", 3088c2ecf20Sopenharmony_ci "FM/TV", 3098c2ecf20Sopenharmony_ci "FM/radio" 3108c2ecf20Sopenharmony_ci }; 3118c2ecf20Sopenharmony_ci static char *adjust[32] = { 3128c2ecf20Sopenharmony_ci "-16", "-15", "-14", "-13", "-12", "-11", "-10", "-9", 3138c2ecf20Sopenharmony_ci "-8", "-7", "-6", "-5", "-4", "-3", "-2", "-1", 3148c2ecf20Sopenharmony_ci "0", "+1", "+2", "+3", "+4", "+5", "+6", "+7", 3158c2ecf20Sopenharmony_ci "+8", "+9", "+10", "+11", "+12", "+13", "+14", "+15" 3168c2ecf20Sopenharmony_ci }; 3178c2ecf20Sopenharmony_ci static char *deemph[4] = { 3188c2ecf20Sopenharmony_ci "no", "no", "75", "50" 3198c2ecf20Sopenharmony_ci }; 3208c2ecf20Sopenharmony_ci static char *carrier[4] = { 3218c2ecf20Sopenharmony_ci "4.5 MHz", 3228c2ecf20Sopenharmony_ci "5.5 MHz", 3238c2ecf20Sopenharmony_ci "6.0 MHz", 3248c2ecf20Sopenharmony_ci "6.5 MHz / AM" 3258c2ecf20Sopenharmony_ci }; 3268c2ecf20Sopenharmony_ci static char *vif[8] = { 3278c2ecf20Sopenharmony_ci "58.75 MHz", 3288c2ecf20Sopenharmony_ci "45.75 MHz", 3298c2ecf20Sopenharmony_ci "38.9 MHz", 3308c2ecf20Sopenharmony_ci "38.0 MHz", 3318c2ecf20Sopenharmony_ci "33.9 MHz", 3328c2ecf20Sopenharmony_ci "33.4 MHz", 3338c2ecf20Sopenharmony_ci "45.75 MHz + pin13", 3348c2ecf20Sopenharmony_ci "38.9 MHz + pin13", 3358c2ecf20Sopenharmony_ci }; 3368c2ecf20Sopenharmony_ci static char *rif[4] = { 3378c2ecf20Sopenharmony_ci "44 MHz", 3388c2ecf20Sopenharmony_ci "52 MHz", 3398c2ecf20Sopenharmony_ci "52 MHz", 3408c2ecf20Sopenharmony_ci "44 MHz", 3418c2ecf20Sopenharmony_ci }; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci tuner_info("write: byte B 0x%02x\n", buf[1]); 3448c2ecf20Sopenharmony_ci tuner_info(" B0 video mode : %s\n", 3458c2ecf20Sopenharmony_ci (buf[1] & 0x01) ? "video trap" : "sound trap"); 3468c2ecf20Sopenharmony_ci tuner_info(" B1 auto mute fm : %s\n", 3478c2ecf20Sopenharmony_ci (buf[1] & 0x02) ? "yes" : "no"); 3488c2ecf20Sopenharmony_ci tuner_info(" B2 carrier mode : %s\n", 3498c2ecf20Sopenharmony_ci (buf[1] & 0x04) ? "QSS" : "Intercarrier"); 3508c2ecf20Sopenharmony_ci tuner_info(" B3-4 tv sound/radio : %s\n", 3518c2ecf20Sopenharmony_ci sound[(buf[1] & 0x18) >> 3]); 3528c2ecf20Sopenharmony_ci tuner_info(" B5 force mute audio: %s\n", 3538c2ecf20Sopenharmony_ci (buf[1] & 0x20) ? "yes" : "no"); 3548c2ecf20Sopenharmony_ci tuner_info(" B6 output port 1 : %s\n", 3558c2ecf20Sopenharmony_ci (buf[1] & 0x40) ? "high (inactive)" : "low (active)"); 3568c2ecf20Sopenharmony_ci tuner_info(" B7 output port 2 : %s\n", 3578c2ecf20Sopenharmony_ci (buf[1] & 0x80) ? "high (inactive)" : "low (active)"); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci tuner_info("write: byte C 0x%02x\n", buf[2]); 3608c2ecf20Sopenharmony_ci tuner_info(" C0-4 top adjustment : %s dB\n", 3618c2ecf20Sopenharmony_ci adjust[buf[2] & 0x1f]); 3628c2ecf20Sopenharmony_ci tuner_info(" C5-6 de-emphasis : %s\n", 3638c2ecf20Sopenharmony_ci deemph[(buf[2] & 0x60) >> 5]); 3648c2ecf20Sopenharmony_ci tuner_info(" C7 audio gain : %s\n", 3658c2ecf20Sopenharmony_ci (buf[2] & 0x80) ? "-6" : "0"); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci tuner_info("write: byte E 0x%02x\n", buf[3]); 3688c2ecf20Sopenharmony_ci tuner_info(" E0-1 sound carrier : %s\n", 3698c2ecf20Sopenharmony_ci carrier[(buf[3] & 0x03)]); 3708c2ecf20Sopenharmony_ci tuner_info(" E6 l pll gating : %s\n", 3718c2ecf20Sopenharmony_ci (buf[3] & 0x40) ? "36" : "13"); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci if (buf[1] & 0x08) { 3748c2ecf20Sopenharmony_ci /* radio */ 3758c2ecf20Sopenharmony_ci tuner_info(" E2-4 video if : %s\n", 3768c2ecf20Sopenharmony_ci rif[(buf[3] & 0x0c) >> 2]); 3778c2ecf20Sopenharmony_ci tuner_info(" E7 vif agc output : %s\n", 3788c2ecf20Sopenharmony_ci (buf[3] & 0x80) 3798c2ecf20Sopenharmony_ci ? ((buf[3] & 0x10) ? "fm-agc radio" : 3808c2ecf20Sopenharmony_ci "sif-agc radio") 3818c2ecf20Sopenharmony_ci : "fm radio carrier afc"); 3828c2ecf20Sopenharmony_ci } else { 3838c2ecf20Sopenharmony_ci /* video */ 3848c2ecf20Sopenharmony_ci tuner_info(" E2-4 video if : %s\n", 3858c2ecf20Sopenharmony_ci vif[(buf[3] & 0x1c) >> 2]); 3868c2ecf20Sopenharmony_ci tuner_info(" E5 tuner gain : %s\n", 3878c2ecf20Sopenharmony_ci (buf[3] & 0x80) 3888c2ecf20Sopenharmony_ci ? ((buf[3] & 0x20) ? "external" : "normal") 3898c2ecf20Sopenharmony_ci : ((buf[3] & 0x20) ? "minimum" : "normal")); 3908c2ecf20Sopenharmony_ci tuner_info(" E7 vif agc output : %s\n", 3918c2ecf20Sopenharmony_ci (buf[3] & 0x80) ? ((buf[3] & 0x20) 3928c2ecf20Sopenharmony_ci ? "pin3 port, pin22 vif agc out" 3938c2ecf20Sopenharmony_ci : "pin22 port, pin3 vif acg ext in") 3948c2ecf20Sopenharmony_ci : "pin3+pin22 port"); 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci tuner_info("--\n"); 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci/* ---------------------------------------------------------------------- */ 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cistatic int tda9887_set_tvnorm(struct dvb_frontend *fe) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci struct tda9887_priv *priv = fe->analog_demod_priv; 4048c2ecf20Sopenharmony_ci struct tvnorm *norm = NULL; 4058c2ecf20Sopenharmony_ci char *buf = priv->data; 4068c2ecf20Sopenharmony_ci int i; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci if (priv->mode == V4L2_TUNER_RADIO) { 4098c2ecf20Sopenharmony_ci if (priv->audmode == V4L2_TUNER_MODE_MONO) 4108c2ecf20Sopenharmony_ci norm = &radio_mono; 4118c2ecf20Sopenharmony_ci else 4128c2ecf20Sopenharmony_ci norm = &radio_stereo; 4138c2ecf20Sopenharmony_ci } else { 4148c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(tvnorms); i++) { 4158c2ecf20Sopenharmony_ci if (tvnorms[i].std & priv->std) { 4168c2ecf20Sopenharmony_ci norm = tvnorms+i; 4178c2ecf20Sopenharmony_ci break; 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci } 4218c2ecf20Sopenharmony_ci if (NULL == norm) { 4228c2ecf20Sopenharmony_ci tuner_dbg("Unsupported tvnorm entry - audio muted\n"); 4238c2ecf20Sopenharmony_ci return -1; 4248c2ecf20Sopenharmony_ci } 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci tuner_dbg("configure for: %s\n", norm->name); 4278c2ecf20Sopenharmony_ci buf[1] = norm->b; 4288c2ecf20Sopenharmony_ci buf[2] = norm->c; 4298c2ecf20Sopenharmony_ci buf[3] = norm->e; 4308c2ecf20Sopenharmony_ci return 0; 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cistatic unsigned int port1 = UNSET; 4348c2ecf20Sopenharmony_cistatic unsigned int port2 = UNSET; 4358c2ecf20Sopenharmony_cistatic unsigned int qss = UNSET; 4368c2ecf20Sopenharmony_cistatic unsigned int adjust = UNSET; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_cimodule_param(port1, int, 0644); 4398c2ecf20Sopenharmony_cimodule_param(port2, int, 0644); 4408c2ecf20Sopenharmony_cimodule_param(qss, int, 0644); 4418c2ecf20Sopenharmony_cimodule_param(adjust, int, 0644); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_cistatic int tda9887_set_insmod(struct dvb_frontend *fe) 4448c2ecf20Sopenharmony_ci{ 4458c2ecf20Sopenharmony_ci struct tda9887_priv *priv = fe->analog_demod_priv; 4468c2ecf20Sopenharmony_ci char *buf = priv->data; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci if (UNSET != port1) { 4498c2ecf20Sopenharmony_ci if (port1) 4508c2ecf20Sopenharmony_ci buf[1] |= cOutputPort1Inactive; 4518c2ecf20Sopenharmony_ci else 4528c2ecf20Sopenharmony_ci buf[1] &= ~cOutputPort1Inactive; 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci if (UNSET != port2) { 4558c2ecf20Sopenharmony_ci if (port2) 4568c2ecf20Sopenharmony_ci buf[1] |= cOutputPort2Inactive; 4578c2ecf20Sopenharmony_ci else 4588c2ecf20Sopenharmony_ci buf[1] &= ~cOutputPort2Inactive; 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci if (UNSET != qss) { 4628c2ecf20Sopenharmony_ci if (qss) 4638c2ecf20Sopenharmony_ci buf[1] |= cQSS; 4648c2ecf20Sopenharmony_ci else 4658c2ecf20Sopenharmony_ci buf[1] &= ~cQSS; 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci if (adjust < 0x20) { 4698c2ecf20Sopenharmony_ci buf[2] &= ~cTopMask; 4708c2ecf20Sopenharmony_ci buf[2] |= adjust; 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci return 0; 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_cistatic int tda9887_do_config(struct dvb_frontend *fe) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci struct tda9887_priv *priv = fe->analog_demod_priv; 4788c2ecf20Sopenharmony_ci char *buf = priv->data; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci if (priv->config & TDA9887_PORT1_ACTIVE) 4818c2ecf20Sopenharmony_ci buf[1] &= ~cOutputPort1Inactive; 4828c2ecf20Sopenharmony_ci if (priv->config & TDA9887_PORT1_INACTIVE) 4838c2ecf20Sopenharmony_ci buf[1] |= cOutputPort1Inactive; 4848c2ecf20Sopenharmony_ci if (priv->config & TDA9887_PORT2_ACTIVE) 4858c2ecf20Sopenharmony_ci buf[1] &= ~cOutputPort2Inactive; 4868c2ecf20Sopenharmony_ci if (priv->config & TDA9887_PORT2_INACTIVE) 4878c2ecf20Sopenharmony_ci buf[1] |= cOutputPort2Inactive; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci if (priv->config & TDA9887_QSS) 4908c2ecf20Sopenharmony_ci buf[1] |= cQSS; 4918c2ecf20Sopenharmony_ci if (priv->config & TDA9887_INTERCARRIER) 4928c2ecf20Sopenharmony_ci buf[1] &= ~cQSS; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci if (priv->config & TDA9887_AUTOMUTE) 4958c2ecf20Sopenharmony_ci buf[1] |= cAutoMuteFmActive; 4968c2ecf20Sopenharmony_ci if (priv->config & TDA9887_DEEMPHASIS_MASK) { 4978c2ecf20Sopenharmony_ci buf[2] &= ~0x60; 4988c2ecf20Sopenharmony_ci switch (priv->config & TDA9887_DEEMPHASIS_MASK) { 4998c2ecf20Sopenharmony_ci case TDA9887_DEEMPHASIS_NONE: 5008c2ecf20Sopenharmony_ci buf[2] |= cDeemphasisOFF; 5018c2ecf20Sopenharmony_ci break; 5028c2ecf20Sopenharmony_ci case TDA9887_DEEMPHASIS_50: 5038c2ecf20Sopenharmony_ci buf[2] |= cDeemphasisON | cDeemphasis50; 5048c2ecf20Sopenharmony_ci break; 5058c2ecf20Sopenharmony_ci case TDA9887_DEEMPHASIS_75: 5068c2ecf20Sopenharmony_ci buf[2] |= cDeemphasisON | cDeemphasis75; 5078c2ecf20Sopenharmony_ci break; 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci if (priv->config & TDA9887_TOP_SET) { 5118c2ecf20Sopenharmony_ci buf[2] &= ~cTopMask; 5128c2ecf20Sopenharmony_ci buf[2] |= (priv->config >> 8) & cTopMask; 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci if ((priv->config & TDA9887_INTERCARRIER_NTSC) && 5158c2ecf20Sopenharmony_ci (priv->std & V4L2_STD_NTSC)) 5168c2ecf20Sopenharmony_ci buf[1] &= ~cQSS; 5178c2ecf20Sopenharmony_ci if (priv->config & TDA9887_GATING_18) 5188c2ecf20Sopenharmony_ci buf[3] &= ~cGating_36; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci if (priv->mode == V4L2_TUNER_RADIO) { 5218c2ecf20Sopenharmony_ci if (priv->config & TDA9887_RIF_41_3) { 5228c2ecf20Sopenharmony_ci buf[3] &= ~cVideoIFMask; 5238c2ecf20Sopenharmony_ci buf[3] |= cRadioIF_41_30; 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci if (priv->config & TDA9887_GAIN_NORMAL) 5268c2ecf20Sopenharmony_ci buf[3] &= ~cTunerGainLow; 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci return 0; 5308c2ecf20Sopenharmony_ci} 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci/* ---------------------------------------------------------------------- */ 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cistatic int tda9887_status(struct dvb_frontend *fe) 5358c2ecf20Sopenharmony_ci{ 5368c2ecf20Sopenharmony_ci struct tda9887_priv *priv = fe->analog_demod_priv; 5378c2ecf20Sopenharmony_ci unsigned char buf[1]; 5388c2ecf20Sopenharmony_ci int rc; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci rc = tuner_i2c_xfer_recv(&priv->i2c_props, buf, 1); 5418c2ecf20Sopenharmony_ci if (rc != 1) 5428c2ecf20Sopenharmony_ci tuner_info("i2c i/o error: rc == %d (should be 1)\n", rc); 5438c2ecf20Sopenharmony_ci dump_read_message(fe, buf); 5448c2ecf20Sopenharmony_ci return 0; 5458c2ecf20Sopenharmony_ci} 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_cistatic void tda9887_configure(struct dvb_frontend *fe) 5488c2ecf20Sopenharmony_ci{ 5498c2ecf20Sopenharmony_ci struct tda9887_priv *priv = fe->analog_demod_priv; 5508c2ecf20Sopenharmony_ci int rc; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci memset(priv->data,0,sizeof(priv->data)); 5538c2ecf20Sopenharmony_ci tda9887_set_tvnorm(fe); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci /* A note on the port settings: 5568c2ecf20Sopenharmony_ci These settings tend to depend on the specifics of the board. 5578c2ecf20Sopenharmony_ci By default they are set to inactive (bit value 1) by this driver, 5588c2ecf20Sopenharmony_ci overwriting any changes made by the tvnorm. This means that it 5598c2ecf20Sopenharmony_ci is the responsibility of the module using the tda9887 to set 5608c2ecf20Sopenharmony_ci these values in case of changes in the tvnorm. 5618c2ecf20Sopenharmony_ci In many cases port 2 should be made active (0) when selecting 5628c2ecf20Sopenharmony_ci SECAM-L, and port 2 should remain inactive (1) for SECAM-L'. 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci For the other standards the tda9887 application note says that 5658c2ecf20Sopenharmony_ci the ports should be set to active (0), but, again, that may 5668c2ecf20Sopenharmony_ci differ depending on the precise hardware configuration. 5678c2ecf20Sopenharmony_ci */ 5688c2ecf20Sopenharmony_ci priv->data[1] |= cOutputPort1Inactive; 5698c2ecf20Sopenharmony_ci priv->data[1] |= cOutputPort2Inactive; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci tda9887_do_config(fe); 5728c2ecf20Sopenharmony_ci tda9887_set_insmod(fe); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci if (priv->standby) 5758c2ecf20Sopenharmony_ci priv->data[1] |= cForcedMuteAudioON; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci tuner_dbg("writing: b=0x%02x c=0x%02x e=0x%02x\n", 5788c2ecf20Sopenharmony_ci priv->data[1], priv->data[2], priv->data[3]); 5798c2ecf20Sopenharmony_ci if (debug > 1) 5808c2ecf20Sopenharmony_ci dump_write_message(fe, priv->data); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci if (4 != (rc = tuner_i2c_xfer_send(&priv->i2c_props,priv->data,4))) 5838c2ecf20Sopenharmony_ci tuner_info("i2c i/o error: rc == %d (should be 4)\n", rc); 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci if (debug > 2) { 5868c2ecf20Sopenharmony_ci msleep_interruptible(1000); 5878c2ecf20Sopenharmony_ci tda9887_status(fe); 5888c2ecf20Sopenharmony_ci } 5898c2ecf20Sopenharmony_ci} 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci/* ---------------------------------------------------------------------- */ 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_cistatic void tda9887_tuner_status(struct dvb_frontend *fe) 5948c2ecf20Sopenharmony_ci{ 5958c2ecf20Sopenharmony_ci struct tda9887_priv *priv = fe->analog_demod_priv; 5968c2ecf20Sopenharmony_ci tuner_info("Data bytes: b=0x%02x c=0x%02x e=0x%02x\n", 5978c2ecf20Sopenharmony_ci priv->data[1], priv->data[2], priv->data[3]); 5988c2ecf20Sopenharmony_ci} 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_cistatic int tda9887_get_afc(struct dvb_frontend *fe, s32 *afc) 6018c2ecf20Sopenharmony_ci{ 6028c2ecf20Sopenharmony_ci struct tda9887_priv *priv = fe->analog_demod_priv; 6038c2ecf20Sopenharmony_ci static const int AFC_BITS_2_kHz[] = { 6048c2ecf20Sopenharmony_ci -12500, -37500, -62500, -97500, 6058c2ecf20Sopenharmony_ci -112500, -137500, -162500, -187500, 6068c2ecf20Sopenharmony_ci 187500, 162500, 137500, 112500, 6078c2ecf20Sopenharmony_ci 97500 , 62500, 37500 , 12500 6088c2ecf20Sopenharmony_ci }; 6098c2ecf20Sopenharmony_ci __u8 reg = 0; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci if (priv->mode != V4L2_TUNER_RADIO) 6128c2ecf20Sopenharmony_ci return 0; 6138c2ecf20Sopenharmony_ci if (1 == tuner_i2c_xfer_recv(&priv->i2c_props, ®, 1)) 6148c2ecf20Sopenharmony_ci *afc = AFC_BITS_2_kHz[(reg >> 1) & 0x0f]; 6158c2ecf20Sopenharmony_ci return 0; 6168c2ecf20Sopenharmony_ci} 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_cistatic void tda9887_standby(struct dvb_frontend *fe) 6198c2ecf20Sopenharmony_ci{ 6208c2ecf20Sopenharmony_ci struct tda9887_priv *priv = fe->analog_demod_priv; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci priv->standby = true; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci tda9887_configure(fe); 6258c2ecf20Sopenharmony_ci} 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_cistatic void tda9887_set_params(struct dvb_frontend *fe, 6288c2ecf20Sopenharmony_ci struct analog_parameters *params) 6298c2ecf20Sopenharmony_ci{ 6308c2ecf20Sopenharmony_ci struct tda9887_priv *priv = fe->analog_demod_priv; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci priv->standby = false; 6338c2ecf20Sopenharmony_ci priv->mode = params->mode; 6348c2ecf20Sopenharmony_ci priv->audmode = params->audmode; 6358c2ecf20Sopenharmony_ci priv->std = params->std; 6368c2ecf20Sopenharmony_ci tda9887_configure(fe); 6378c2ecf20Sopenharmony_ci} 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_cistatic int tda9887_set_config(struct dvb_frontend *fe, void *priv_cfg) 6408c2ecf20Sopenharmony_ci{ 6418c2ecf20Sopenharmony_ci struct tda9887_priv *priv = fe->analog_demod_priv; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci priv->config = *(unsigned int *)priv_cfg; 6448c2ecf20Sopenharmony_ci tda9887_configure(fe); 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci return 0; 6478c2ecf20Sopenharmony_ci} 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_cistatic void tda9887_release(struct dvb_frontend *fe) 6508c2ecf20Sopenharmony_ci{ 6518c2ecf20Sopenharmony_ci struct tda9887_priv *priv = fe->analog_demod_priv; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci mutex_lock(&tda9887_list_mutex); 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci if (priv) 6568c2ecf20Sopenharmony_ci hybrid_tuner_release_state(priv); 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci mutex_unlock(&tda9887_list_mutex); 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci fe->analog_demod_priv = NULL; 6618c2ecf20Sopenharmony_ci} 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_cistatic const struct analog_demod_ops tda9887_ops = { 6648c2ecf20Sopenharmony_ci .info = { 6658c2ecf20Sopenharmony_ci .name = "tda9887", 6668c2ecf20Sopenharmony_ci }, 6678c2ecf20Sopenharmony_ci .set_params = tda9887_set_params, 6688c2ecf20Sopenharmony_ci .standby = tda9887_standby, 6698c2ecf20Sopenharmony_ci .tuner_status = tda9887_tuner_status, 6708c2ecf20Sopenharmony_ci .get_afc = tda9887_get_afc, 6718c2ecf20Sopenharmony_ci .release = tda9887_release, 6728c2ecf20Sopenharmony_ci .set_config = tda9887_set_config, 6738c2ecf20Sopenharmony_ci}; 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_cistruct dvb_frontend *tda9887_attach(struct dvb_frontend *fe, 6768c2ecf20Sopenharmony_ci struct i2c_adapter *i2c_adap, 6778c2ecf20Sopenharmony_ci u8 i2c_addr) 6788c2ecf20Sopenharmony_ci{ 6798c2ecf20Sopenharmony_ci struct tda9887_priv *priv = NULL; 6808c2ecf20Sopenharmony_ci int instance; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci mutex_lock(&tda9887_list_mutex); 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci instance = hybrid_tuner_request_state(struct tda9887_priv, priv, 6858c2ecf20Sopenharmony_ci hybrid_tuner_instance_list, 6868c2ecf20Sopenharmony_ci i2c_adap, i2c_addr, "tda9887"); 6878c2ecf20Sopenharmony_ci switch (instance) { 6888c2ecf20Sopenharmony_ci case 0: 6898c2ecf20Sopenharmony_ci mutex_unlock(&tda9887_list_mutex); 6908c2ecf20Sopenharmony_ci return NULL; 6918c2ecf20Sopenharmony_ci case 1: 6928c2ecf20Sopenharmony_ci fe->analog_demod_priv = priv; 6938c2ecf20Sopenharmony_ci priv->standby = true; 6948c2ecf20Sopenharmony_ci tuner_info("tda988[5/6/7] found\n"); 6958c2ecf20Sopenharmony_ci break; 6968c2ecf20Sopenharmony_ci default: 6978c2ecf20Sopenharmony_ci fe->analog_demod_priv = priv; 6988c2ecf20Sopenharmony_ci break; 6998c2ecf20Sopenharmony_ci } 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci mutex_unlock(&tda9887_list_mutex); 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci memcpy(&fe->ops.analog_ops, &tda9887_ops, 7048c2ecf20Sopenharmony_ci sizeof(struct analog_demod_ops)); 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci return fe; 7078c2ecf20Sopenharmony_ci} 7088c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tda9887_attach); 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 711