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, &reg, 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