18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci// For Philips TEA5767 FM Chip used on some TV Cards like Prolink Pixelview
38c2ecf20Sopenharmony_ci// I2C address is always 0xC0.
48c2ecf20Sopenharmony_ci//
58c2ecf20Sopenharmony_ci// Copyright (c) 2005 Mauro Carvalho Chehab <mchehab@kernel.org>
68c2ecf20Sopenharmony_ci//
78c2ecf20Sopenharmony_ci// tea5767 autodetection thanks to Torsten Seeboth and Atsushi Nakagawa
88c2ecf20Sopenharmony_ci// from their contributions on DScaler.
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/i2c.h>
138c2ecf20Sopenharmony_ci#include <linux/slab.h>
148c2ecf20Sopenharmony_ci#include <linux/delay.h>
158c2ecf20Sopenharmony_ci#include <linux/videodev2.h>
168c2ecf20Sopenharmony_ci#include "tuner-i2c.h"
178c2ecf20Sopenharmony_ci#include "tea5767.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic int debug;
208c2ecf20Sopenharmony_cimodule_param(debug, int, 0644);
218c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "enable verbose debug messages");
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci/*****************************************************************************/
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistruct tea5767_priv {
268c2ecf20Sopenharmony_ci	struct tuner_i2c_props	i2c_props;
278c2ecf20Sopenharmony_ci	u32			frequency;
288c2ecf20Sopenharmony_ci	struct tea5767_ctrl	ctrl;
298c2ecf20Sopenharmony_ci};
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci/*****************************************************************************/
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci/******************************
348c2ecf20Sopenharmony_ci * Write mode register values *
358c2ecf20Sopenharmony_ci ******************************/
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci/* First register */
388c2ecf20Sopenharmony_ci#define TEA5767_MUTE		0x80	/* Mutes output */
398c2ecf20Sopenharmony_ci#define TEA5767_SEARCH		0x40	/* Activates station search */
408c2ecf20Sopenharmony_ci/* Bits 0-5 for divider MSB */
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/* Second register */
438c2ecf20Sopenharmony_ci/* Bits 0-7 for divider LSB */
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci/* Third register */
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/* Station search from botton to up */
488c2ecf20Sopenharmony_ci#define TEA5767_SEARCH_UP	0x80
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci/* Searches with ADC output = 10 */
518c2ecf20Sopenharmony_ci#define TEA5767_SRCH_HIGH_LVL	0x60
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci/* Searches with ADC output = 10 */
548c2ecf20Sopenharmony_ci#define TEA5767_SRCH_MID_LVL	0x40
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci/* Searches with ADC output = 5 */
578c2ecf20Sopenharmony_ci#define TEA5767_SRCH_LOW_LVL	0x20
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci/* if on, div=4*(Frf+Fif)/Fref otherwise, div=4*(Frf-Fif)/Freq) */
608c2ecf20Sopenharmony_ci#define TEA5767_HIGH_LO_INJECT	0x10
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci/* Disable stereo */
638c2ecf20Sopenharmony_ci#define TEA5767_MONO		0x08
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci/* Disable right channel and turns to mono */
668c2ecf20Sopenharmony_ci#define TEA5767_MUTE_RIGHT	0x04
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci/* Disable left channel and turns to mono */
698c2ecf20Sopenharmony_ci#define TEA5767_MUTE_LEFT	0x02
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci#define TEA5767_PORT1_HIGH	0x01
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci/* Fourth register */
748c2ecf20Sopenharmony_ci#define TEA5767_PORT2_HIGH	0x80
758c2ecf20Sopenharmony_ci/* Chips stops working. Only I2C bus remains on */
768c2ecf20Sopenharmony_ci#define TEA5767_STDBY		0x40
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci/* Japan freq (76-108 MHz. If disabled, 87.5-108 MHz */
798c2ecf20Sopenharmony_ci#define TEA5767_JAPAN_BAND	0x20
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci/* Unselected means 32.768 KHz freq as reference. Otherwise Xtal at 13 MHz */
828c2ecf20Sopenharmony_ci#define TEA5767_XTAL_32768	0x10
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci/* Cuts weak signals */
858c2ecf20Sopenharmony_ci#define TEA5767_SOFT_MUTE	0x08
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci/* Activates high cut control */
888c2ecf20Sopenharmony_ci#define TEA5767_HIGH_CUT_CTRL	0x04
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci/* Activates stereo noise control */
918c2ecf20Sopenharmony_ci#define TEA5767_ST_NOISE_CTL	0x02
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci/* If activate PORT 1 indicates SEARCH or else it is used as PORT1 */
948c2ecf20Sopenharmony_ci#define TEA5767_SRCH_IND	0x01
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci/* Fifth register */
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci/* By activating, it will use Xtal at 13 MHz as reference for divider */
998c2ecf20Sopenharmony_ci#define TEA5767_PLLREF_ENABLE	0x80
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci/* By activating, deemphasis=50, or else, deemphasis of 50us */
1028c2ecf20Sopenharmony_ci#define TEA5767_DEEMPH_75	0X40
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci/*****************************
1058c2ecf20Sopenharmony_ci * Read mode register values *
1068c2ecf20Sopenharmony_ci *****************************/
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci/* First register */
1098c2ecf20Sopenharmony_ci#define TEA5767_READY_FLAG_MASK	0x80
1108c2ecf20Sopenharmony_ci#define TEA5767_BAND_LIMIT_MASK	0X40
1118c2ecf20Sopenharmony_ci/* Bits 0-5 for divider MSB after search or preset */
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci/* Second register */
1148c2ecf20Sopenharmony_ci/* Bits 0-7 for divider LSB after search or preset */
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci/* Third register */
1178c2ecf20Sopenharmony_ci#define TEA5767_STEREO_MASK	0x80
1188c2ecf20Sopenharmony_ci#define TEA5767_IF_CNTR_MASK	0x7f
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci/* Fourth register */
1218c2ecf20Sopenharmony_ci#define TEA5767_ADC_LEVEL_MASK	0xf0
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci/* should be 0 */
1248c2ecf20Sopenharmony_ci#define TEA5767_CHIP_ID_MASK	0x0f
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci/* Fifth register */
1278c2ecf20Sopenharmony_ci/* Reserved for future extensions */
1288c2ecf20Sopenharmony_ci#define TEA5767_RESERVED_MASK	0xff
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci/*****************************************************************************/
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic void tea5767_status_dump(struct tea5767_priv *priv,
1338c2ecf20Sopenharmony_ci				unsigned char *buffer)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	unsigned int div, frq;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	if (TEA5767_READY_FLAG_MASK & buffer[0])
1388c2ecf20Sopenharmony_ci		tuner_info("Ready Flag ON\n");
1398c2ecf20Sopenharmony_ci	else
1408c2ecf20Sopenharmony_ci		tuner_info("Ready Flag OFF\n");
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	if (TEA5767_BAND_LIMIT_MASK & buffer[0])
1438c2ecf20Sopenharmony_ci		tuner_info("Tuner at band limit\n");
1448c2ecf20Sopenharmony_ci	else
1458c2ecf20Sopenharmony_ci		tuner_info("Tuner not at band limit\n");
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	div = ((buffer[0] & 0x3f) << 8) | buffer[1];
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	switch (priv->ctrl.xtal_freq) {
1508c2ecf20Sopenharmony_ci	case TEA5767_HIGH_LO_13MHz:
1518c2ecf20Sopenharmony_ci		frq = (div * 50000 - 700000 - 225000) / 4;	/* Freq in KHz */
1528c2ecf20Sopenharmony_ci		break;
1538c2ecf20Sopenharmony_ci	case TEA5767_LOW_LO_13MHz:
1548c2ecf20Sopenharmony_ci		frq = (div * 50000 + 700000 + 225000) / 4;	/* Freq in KHz */
1558c2ecf20Sopenharmony_ci		break;
1568c2ecf20Sopenharmony_ci	case TEA5767_LOW_LO_32768:
1578c2ecf20Sopenharmony_ci		frq = (div * 32768 + 700000 + 225000) / 4;	/* Freq in KHz */
1588c2ecf20Sopenharmony_ci		break;
1598c2ecf20Sopenharmony_ci	case TEA5767_HIGH_LO_32768:
1608c2ecf20Sopenharmony_ci	default:
1618c2ecf20Sopenharmony_ci		frq = (div * 32768 - 700000 - 225000) / 4;	/* Freq in KHz */
1628c2ecf20Sopenharmony_ci		break;
1638c2ecf20Sopenharmony_ci	}
1648c2ecf20Sopenharmony_ci	buffer[0] = (div >> 8) & 0x3f;
1658c2ecf20Sopenharmony_ci	buffer[1] = div & 0xff;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	tuner_info("Frequency %d.%03d KHz (divider = 0x%04x)\n",
1688c2ecf20Sopenharmony_ci		   frq / 1000, frq % 1000, div);
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	if (TEA5767_STEREO_MASK & buffer[2])
1718c2ecf20Sopenharmony_ci		tuner_info("Stereo\n");
1728c2ecf20Sopenharmony_ci	else
1738c2ecf20Sopenharmony_ci		tuner_info("Mono\n");
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	tuner_info("IF Counter = %d\n", buffer[2] & TEA5767_IF_CNTR_MASK);
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	tuner_info("ADC Level = %d\n",
1788c2ecf20Sopenharmony_ci		   (buffer[3] & TEA5767_ADC_LEVEL_MASK) >> 4);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	tuner_info("Chip ID = %d\n", (buffer[3] & TEA5767_CHIP_ID_MASK));
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	tuner_info("Reserved = 0x%02x\n",
1838c2ecf20Sopenharmony_ci		   (buffer[4] & TEA5767_RESERVED_MASK));
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci/* Freq should be specifyed at 62.5 Hz */
1878c2ecf20Sopenharmony_cistatic int set_radio_freq(struct dvb_frontend *fe,
1888c2ecf20Sopenharmony_ci			  struct analog_parameters *params)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	struct tea5767_priv *priv = fe->tuner_priv;
1918c2ecf20Sopenharmony_ci	unsigned int frq = params->frequency;
1928c2ecf20Sopenharmony_ci	unsigned char buffer[5];
1938c2ecf20Sopenharmony_ci	unsigned div;
1948c2ecf20Sopenharmony_ci	int rc;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	tuner_dbg("radio freq = %d.%03d MHz\n", frq/16000,(frq/16)%1000);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	buffer[2] = 0;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	if (priv->ctrl.port1)
2018c2ecf20Sopenharmony_ci		buffer[2] |= TEA5767_PORT1_HIGH;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	if (params->audmode == V4L2_TUNER_MODE_MONO) {
2048c2ecf20Sopenharmony_ci		tuner_dbg("TEA5767 set to mono\n");
2058c2ecf20Sopenharmony_ci		buffer[2] |= TEA5767_MONO;
2068c2ecf20Sopenharmony_ci	} else {
2078c2ecf20Sopenharmony_ci		tuner_dbg("TEA5767 set to stereo\n");
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	buffer[3] = 0;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	if (priv->ctrl.port2)
2148c2ecf20Sopenharmony_ci		buffer[3] |= TEA5767_PORT2_HIGH;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	if (priv->ctrl.high_cut)
2178c2ecf20Sopenharmony_ci		buffer[3] |= TEA5767_HIGH_CUT_CTRL;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	if (priv->ctrl.st_noise)
2208c2ecf20Sopenharmony_ci		buffer[3] |= TEA5767_ST_NOISE_CTL;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	if (priv->ctrl.soft_mute)
2238c2ecf20Sopenharmony_ci		buffer[3] |= TEA5767_SOFT_MUTE;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	if (priv->ctrl.japan_band)
2268c2ecf20Sopenharmony_ci		buffer[3] |= TEA5767_JAPAN_BAND;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	buffer[4] = 0;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	if (priv->ctrl.deemph_75)
2318c2ecf20Sopenharmony_ci		buffer[4] |= TEA5767_DEEMPH_75;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	if (priv->ctrl.pllref)
2348c2ecf20Sopenharmony_ci		buffer[4] |= TEA5767_PLLREF_ENABLE;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	/* Rounds freq to next decimal value - for 62.5 KHz step */
2388c2ecf20Sopenharmony_ci	/* frq = 20*(frq/16)+radio_frq[frq%16]; */
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	switch (priv->ctrl.xtal_freq) {
2418c2ecf20Sopenharmony_ci	case TEA5767_HIGH_LO_13MHz:
2428c2ecf20Sopenharmony_ci		tuner_dbg("radio HIGH LO inject xtal @ 13 MHz\n");
2438c2ecf20Sopenharmony_ci		buffer[2] |= TEA5767_HIGH_LO_INJECT;
2448c2ecf20Sopenharmony_ci		div = (frq * (4000 / 16) + 700000 + 225000 + 25000) / 50000;
2458c2ecf20Sopenharmony_ci		break;
2468c2ecf20Sopenharmony_ci	case TEA5767_LOW_LO_13MHz:
2478c2ecf20Sopenharmony_ci		tuner_dbg("radio LOW LO inject xtal @ 13 MHz\n");
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci		div = (frq * (4000 / 16) - 700000 - 225000 + 25000) / 50000;
2508c2ecf20Sopenharmony_ci		break;
2518c2ecf20Sopenharmony_ci	case TEA5767_LOW_LO_32768:
2528c2ecf20Sopenharmony_ci		tuner_dbg("radio LOW LO inject xtal @ 32,768 MHz\n");
2538c2ecf20Sopenharmony_ci		buffer[3] |= TEA5767_XTAL_32768;
2548c2ecf20Sopenharmony_ci		/* const 700=4000*175 Khz - to adjust freq to right value */
2558c2ecf20Sopenharmony_ci		div = ((frq * (4000 / 16) - 700000 - 225000) + 16384) >> 15;
2568c2ecf20Sopenharmony_ci		break;
2578c2ecf20Sopenharmony_ci	case TEA5767_HIGH_LO_32768:
2588c2ecf20Sopenharmony_ci	default:
2598c2ecf20Sopenharmony_ci		tuner_dbg("radio HIGH LO inject xtal @ 32,768 MHz\n");
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci		buffer[2] |= TEA5767_HIGH_LO_INJECT;
2628c2ecf20Sopenharmony_ci		buffer[3] |= TEA5767_XTAL_32768;
2638c2ecf20Sopenharmony_ci		div = ((frq * (4000 / 16) + 700000 + 225000) + 16384) >> 15;
2648c2ecf20Sopenharmony_ci		break;
2658c2ecf20Sopenharmony_ci	}
2668c2ecf20Sopenharmony_ci	buffer[0] = (div >> 8) & 0x3f;
2678c2ecf20Sopenharmony_ci	buffer[1] = div & 0xff;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	if (5 != (rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 5)))
2708c2ecf20Sopenharmony_ci		tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc);
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	if (debug) {
2738c2ecf20Sopenharmony_ci		if (5 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props, buffer, 5)))
2748c2ecf20Sopenharmony_ci			tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc);
2758c2ecf20Sopenharmony_ci		else
2768c2ecf20Sopenharmony_ci			tea5767_status_dump(priv, buffer);
2778c2ecf20Sopenharmony_ci	}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	priv->frequency = frq * 125 / 2;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	return 0;
2828c2ecf20Sopenharmony_ci}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_cistatic int tea5767_read_status(struct dvb_frontend *fe, char *buffer)
2858c2ecf20Sopenharmony_ci{
2868c2ecf20Sopenharmony_ci	struct tea5767_priv *priv = fe->tuner_priv;
2878c2ecf20Sopenharmony_ci	int rc;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	memset(buffer, 0, 5);
2908c2ecf20Sopenharmony_ci	if (5 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props, buffer, 5))) {
2918c2ecf20Sopenharmony_ci		tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc);
2928c2ecf20Sopenharmony_ci		return -EREMOTEIO;
2938c2ecf20Sopenharmony_ci	}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	return 0;
2968c2ecf20Sopenharmony_ci}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_cistatic inline int tea5767_signal(struct dvb_frontend *fe, const char *buffer)
2998c2ecf20Sopenharmony_ci{
3008c2ecf20Sopenharmony_ci	struct tea5767_priv *priv = fe->tuner_priv;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	int signal = ((buffer[3] & TEA5767_ADC_LEVEL_MASK) << 8);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	tuner_dbg("Signal strength: %d\n", signal);
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	return signal;
3078c2ecf20Sopenharmony_ci}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_cistatic inline int tea5767_stereo(struct dvb_frontend *fe, const char *buffer)
3108c2ecf20Sopenharmony_ci{
3118c2ecf20Sopenharmony_ci	struct tea5767_priv *priv = fe->tuner_priv;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	int stereo = buffer[2] & TEA5767_STEREO_MASK;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	tuner_dbg("Radio ST GET = %02x\n", stereo);
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	return (stereo ? V4L2_TUNER_SUB_STEREO : 0);
3188c2ecf20Sopenharmony_ci}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_cistatic int tea5767_get_status(struct dvb_frontend *fe, u32 *status)
3218c2ecf20Sopenharmony_ci{
3228c2ecf20Sopenharmony_ci	unsigned char buffer[5];
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	*status = 0;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	if (0 == tea5767_read_status(fe, buffer)) {
3278c2ecf20Sopenharmony_ci		if (tea5767_signal(fe, buffer))
3288c2ecf20Sopenharmony_ci			*status = TUNER_STATUS_LOCKED;
3298c2ecf20Sopenharmony_ci		if (tea5767_stereo(fe, buffer))
3308c2ecf20Sopenharmony_ci			*status |= TUNER_STATUS_STEREO;
3318c2ecf20Sopenharmony_ci	}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	return 0;
3348c2ecf20Sopenharmony_ci}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_cistatic int tea5767_get_rf_strength(struct dvb_frontend *fe, u16 *strength)
3378c2ecf20Sopenharmony_ci{
3388c2ecf20Sopenharmony_ci	unsigned char buffer[5];
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	*strength = 0;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	if (0 == tea5767_read_status(fe, buffer))
3438c2ecf20Sopenharmony_ci		*strength = tea5767_signal(fe, buffer);
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	return 0;
3468c2ecf20Sopenharmony_ci}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_cistatic int tea5767_standby(struct dvb_frontend *fe)
3498c2ecf20Sopenharmony_ci{
3508c2ecf20Sopenharmony_ci	unsigned char buffer[5];
3518c2ecf20Sopenharmony_ci	struct tea5767_priv *priv = fe->tuner_priv;
3528c2ecf20Sopenharmony_ci	unsigned div, rc;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	div = (87500 * 4 + 700 + 225 + 25) / 50; /* Set frequency to 87.5 MHz */
3558c2ecf20Sopenharmony_ci	buffer[0] = (div >> 8) & 0x3f;
3568c2ecf20Sopenharmony_ci	buffer[1] = div & 0xff;
3578c2ecf20Sopenharmony_ci	buffer[2] = TEA5767_PORT1_HIGH;
3588c2ecf20Sopenharmony_ci	buffer[3] = TEA5767_PORT2_HIGH | TEA5767_HIGH_CUT_CTRL |
3598c2ecf20Sopenharmony_ci		    TEA5767_ST_NOISE_CTL | TEA5767_JAPAN_BAND | TEA5767_STDBY;
3608c2ecf20Sopenharmony_ci	buffer[4] = 0;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	if (5 != (rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 5)))
3638c2ecf20Sopenharmony_ci		tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc);
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	return 0;
3668c2ecf20Sopenharmony_ci}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ciint tea5767_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	struct tuner_i2c_props i2c = { .adap = i2c_adap, .addr = i2c_addr };
3718c2ecf20Sopenharmony_ci	unsigned char buffer[7] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	int rc;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	if ((rc = tuner_i2c_xfer_recv(&i2c, buffer, 7))< 5) {
3768c2ecf20Sopenharmony_ci		pr_warn("It is not a TEA5767. Received %i bytes.\n", rc);
3778c2ecf20Sopenharmony_ci		return -EINVAL;
3788c2ecf20Sopenharmony_ci	}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	/* If all bytes are the same then it's a TV tuner and not a tea5767 */
3818c2ecf20Sopenharmony_ci	if (buffer[0] == buffer[1] && buffer[0] == buffer[2] &&
3828c2ecf20Sopenharmony_ci	    buffer[0] == buffer[3] && buffer[0] == buffer[4]) {
3838c2ecf20Sopenharmony_ci		pr_warn("All bytes are equal. It is not a TEA5767\n");
3848c2ecf20Sopenharmony_ci		return -EINVAL;
3858c2ecf20Sopenharmony_ci	}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	/*  Status bytes:
3888c2ecf20Sopenharmony_ci	 *  Byte 4: bit 3:1 : CI (Chip Identification) == 0
3898c2ecf20Sopenharmony_ci	 *          bit 0   : internally set to 0
3908c2ecf20Sopenharmony_ci	 *  Byte 5: bit 7:0 : == 0
3918c2ecf20Sopenharmony_ci	 */
3928c2ecf20Sopenharmony_ci	if (((buffer[3] & 0x0f) != 0x00) || (buffer[4] != 0x00)) {
3938c2ecf20Sopenharmony_ci		pr_warn("Chip ID is not zero. It is not a TEA5767\n");
3948c2ecf20Sopenharmony_ci		return -EINVAL;
3958c2ecf20Sopenharmony_ci	}
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	return 0;
3998c2ecf20Sopenharmony_ci}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_cistatic void tea5767_release(struct dvb_frontend *fe)
4028c2ecf20Sopenharmony_ci{
4038c2ecf20Sopenharmony_ci	kfree(fe->tuner_priv);
4048c2ecf20Sopenharmony_ci	fe->tuner_priv = NULL;
4058c2ecf20Sopenharmony_ci}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_cistatic int tea5767_get_frequency(struct dvb_frontend *fe, u32 *frequency)
4088c2ecf20Sopenharmony_ci{
4098c2ecf20Sopenharmony_ci	struct tea5767_priv *priv = fe->tuner_priv;
4108c2ecf20Sopenharmony_ci	*frequency = priv->frequency;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	return 0;
4138c2ecf20Sopenharmony_ci}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_cistatic int tea5767_set_config (struct dvb_frontend *fe, void *priv_cfg)
4168c2ecf20Sopenharmony_ci{
4178c2ecf20Sopenharmony_ci	struct tea5767_priv *priv = fe->tuner_priv;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	memcpy(&priv->ctrl, priv_cfg, sizeof(priv->ctrl));
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	return 0;
4228c2ecf20Sopenharmony_ci}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_cistatic const struct dvb_tuner_ops tea5767_tuner_ops = {
4258c2ecf20Sopenharmony_ci	.info = {
4268c2ecf20Sopenharmony_ci		.name           = "tea5767", // Philips TEA5767HN FM Radio
4278c2ecf20Sopenharmony_ci	},
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	.set_analog_params = set_radio_freq,
4308c2ecf20Sopenharmony_ci	.set_config	   = tea5767_set_config,
4318c2ecf20Sopenharmony_ci	.sleep             = tea5767_standby,
4328c2ecf20Sopenharmony_ci	.release           = tea5767_release,
4338c2ecf20Sopenharmony_ci	.get_frequency     = tea5767_get_frequency,
4348c2ecf20Sopenharmony_ci	.get_status        = tea5767_get_status,
4358c2ecf20Sopenharmony_ci	.get_rf_strength   = tea5767_get_rf_strength,
4368c2ecf20Sopenharmony_ci};
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_cistruct dvb_frontend *tea5767_attach(struct dvb_frontend *fe,
4398c2ecf20Sopenharmony_ci				    struct i2c_adapter* i2c_adap,
4408c2ecf20Sopenharmony_ci				    u8 i2c_addr)
4418c2ecf20Sopenharmony_ci{
4428c2ecf20Sopenharmony_ci	struct tea5767_priv *priv = NULL;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	priv = kzalloc(sizeof(struct tea5767_priv), GFP_KERNEL);
4458c2ecf20Sopenharmony_ci	if (priv == NULL)
4468c2ecf20Sopenharmony_ci		return NULL;
4478c2ecf20Sopenharmony_ci	fe->tuner_priv = priv;
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	priv->i2c_props.addr  = i2c_addr;
4508c2ecf20Sopenharmony_ci	priv->i2c_props.adap  = i2c_adap;
4518c2ecf20Sopenharmony_ci	priv->i2c_props.name  = "tea5767";
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	priv->ctrl.xtal_freq  = TEA5767_HIGH_LO_32768;
4548c2ecf20Sopenharmony_ci	priv->ctrl.port1      = 1;
4558c2ecf20Sopenharmony_ci	priv->ctrl.port2      = 1;
4568c2ecf20Sopenharmony_ci	priv->ctrl.high_cut   = 1;
4578c2ecf20Sopenharmony_ci	priv->ctrl.st_noise   = 1;
4588c2ecf20Sopenharmony_ci	priv->ctrl.japan_band = 1;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	memcpy(&fe->ops.tuner_ops, &tea5767_tuner_ops,
4618c2ecf20Sopenharmony_ci	       sizeof(struct dvb_tuner_ops));
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	tuner_info("type set to %s\n", "Philips TEA5767HN FM Radio");
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	return fe;
4668c2ecf20Sopenharmony_ci}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tea5767_attach);
4698c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tea5767_autodetection);
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Philips TEA5767 FM tuner driver");
4728c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@kernel.org>");
4738c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
474