cregit-Linux how code gets into the kernel

Release 4.7 drivers/media/tuners/tea5767.c

/*
 * For Philips TEA5767 FM Chip used on some TV Cards like Prolink Pixelview
 * I2C address is allways 0xC0.
 *
 *
 * Copyright (c) 2005 Mauro Carvalho Chehab (mchehab@infradead.org)
 * This code is placed under the terms of the GNU General Public License
 *
 * tea5767 autodetection thanks to Torsten Seeboth and Atsushi Nakagawa
 * from their contributions on DScaler.
 */

#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/videodev2.h>
#include "tuner-i2c.h"
#include "tea5767.h"


static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "enable verbose debug messages");

/*****************************************************************************/


struct tea5767_priv {
	
struct tuner_i2c_props	i2c_props;
	
u32			frequency;
	
struct tea5767_ctrl	ctrl;
};

/*****************************************************************************/

/******************************
 * Write mode register values *
 ******************************/

/* First register */

#define TEA5767_MUTE		0x80	
/* Mutes output */

#define TEA5767_SEARCH		0x40	
/* Activates station search */
/* Bits 0-5 for divider MSB */

/* Second register */
/* Bits 0-7 for divider LSB */

/* Third register */

/* Station search from botton to up */

#define TEA5767_SEARCH_UP	0x80

/* Searches with ADC output = 10 */

#define TEA5767_SRCH_HIGH_LVL	0x60

/* Searches with ADC output = 10 */

#define TEA5767_SRCH_MID_LVL	0x40

/* Searches with ADC output = 5 */

#define TEA5767_SRCH_LOW_LVL	0x20

/* if on, div=4*(Frf+Fif)/Fref otherwise, div=4*(Frf-Fif)/Freq) */

#define TEA5767_HIGH_LO_INJECT	0x10

/* Disable stereo */

#define TEA5767_MONO		0x08

/* Disable right channel and turns to mono */

#define TEA5767_MUTE_RIGHT	0x04

/* Disable left channel and turns to mono */

#define TEA5767_MUTE_LEFT	0x02


#define TEA5767_PORT1_HIGH	0x01

/* Fourth register */

#define TEA5767_PORT2_HIGH	0x80
/* Chips stops working. Only I2C bus remains on */

#define TEA5767_STDBY		0x40

/* Japan freq (76-108 MHz. If disabled, 87.5-108 MHz */

#define TEA5767_JAPAN_BAND	0x20

/* Unselected means 32.768 KHz freq as reference. Otherwise Xtal at 13 MHz */

#define TEA5767_XTAL_32768	0x10

/* Cuts weak signals */

#define TEA5767_SOFT_MUTE	0x08

/* Activates high cut control */

#define TEA5767_HIGH_CUT_CTRL	0x04

/* Activates stereo noise control */

#define TEA5767_ST_NOISE_CTL	0x02

/* If activate PORT 1 indicates SEARCH or else it is used as PORT1 */

#define TEA5767_SRCH_IND	0x01

/* Fifth register */

/* By activating, it will use Xtal at 13 MHz as reference for divider */

#define TEA5767_PLLREF_ENABLE	0x80

/* By activating, deemphasis=50, or else, deemphasis of 50us */

#define TEA5767_DEEMPH_75	0X40

/*****************************
 * Read mode register values *
 *****************************/

/* First register */

#define TEA5767_READY_FLAG_MASK	0x80

#define TEA5767_BAND_LIMIT_MASK	0X40
/* Bits 0-5 for divider MSB after search or preset */

/* Second register */
/* Bits 0-7 for divider LSB after search or preset */

/* Third register */

#define TEA5767_STEREO_MASK	0x80

#define TEA5767_IF_CNTR_MASK	0x7f

/* Fourth register */

#define TEA5767_ADC_LEVEL_MASK	0xf0

/* should be 0 */

#define TEA5767_CHIP_ID_MASK	0x0f

/* Fifth register */
/* Reserved for future extensions */

#define TEA5767_RESERVED_MASK	0xff

/*****************************************************************************/


static void tea5767_status_dump(struct tea5767_priv *priv, unsigned char *buffer) { unsigned int div, frq; if (TEA5767_READY_FLAG_MASK & buffer[0]) tuner_info("Ready Flag ON\n"); else tuner_info("Ready Flag OFF\n"); if (TEA5767_BAND_LIMIT_MASK & buffer[0]) tuner_info("Tuner at band limit\n"); else tuner_info("Tuner not at band limit\n"); div = ((buffer[0] & 0x3f) << 8) | buffer[1]; switch (priv->ctrl.xtal_freq) { case TEA5767_HIGH_LO_13MHz: frq = (div * 50000 - 700000 - 225000) / 4; /* Freq in KHz */ break; case TEA5767_LOW_LO_13MHz: frq = (div * 50000 + 700000 + 225000) / 4; /* Freq in KHz */ break; case TEA5767_LOW_LO_32768: frq = (div * 32768 + 700000 + 225000) / 4; /* Freq in KHz */ break; case TEA5767_HIGH_LO_32768: default: frq = (div * 32768 - 700000 - 225000) / 4; /* Freq in KHz */ break; } buffer[0] = (div >> 8) & 0x3f; buffer[1] = div & 0xff; tuner_info("Frequency %d.%03d KHz (divider = 0x%04x)\n", frq / 1000, frq % 1000, div); if (TEA5767_STEREO_MASK & buffer[2]) tuner_info("Stereo\n"); else tuner_info("Mono\n"); tuner_info("IF Counter = %d\n", buffer[2] & TEA5767_IF_CNTR_MASK); tuner_info("ADC Level = %d\n", (buffer[3] & TEA5767_ADC_LEVEL_MASK) >> 4); tuner_info("Chip ID = %d\n", (buffer[3] & TEA5767_CHIP_ID_MASK)); tuner_info("Reserved = 0x%02x\n", (buffer[4] & TEA5767_RESERVED_MASK)); }

Contributors

PersonTokensPropCommitsCommitProp
mauro carvalho chehabmauro carvalho chehab27196.10%375.00%
michael krufkymichael krufky113.90%125.00%
Total282100.00%4100.00%

/* Freq should be specifyed at 62.5 Hz */
static int set_radio_freq(struct dvb_frontend *fe, struct analog_parameters *params) { struct tea5767_priv *priv = fe->tuner_priv; unsigned int frq = params->frequency; unsigned char buffer[5]; unsigned div; int rc; tuner_dbg("radio freq = %d.%03d MHz\n", frq/16000,(frq/16)%1000); buffer[2] = 0; if (priv->ctrl.port1) buffer[2] |= TEA5767_PORT1_HIGH; if (params->audmode == V4L2_TUNER_MODE_MONO) { tuner_dbg("TEA5767 set to mono\n"); buffer[2] |= TEA5767_MONO; } else { tuner_dbg("TEA5767 set to stereo\n"); } buffer[3] = 0; if (priv->ctrl.port2) buffer[3] |= TEA5767_PORT2_HIGH; if (priv->ctrl.high_cut) buffer[3] |= TEA5767_HIGH_CUT_CTRL; if (priv->ctrl.st_noise) buffer[3] |= TEA5767_ST_NOISE_CTL; if (priv->ctrl.soft_mute) buffer[3] |= TEA5767_SOFT_MUTE; if (priv->ctrl.japan_band) buffer[3] |= TEA5767_JAPAN_BAND; buffer[4] = 0; if (priv->ctrl.deemph_75) buffer[4] |= TEA5767_DEEMPH_75; if (priv->ctrl.pllref) buffer[4] |= TEA5767_PLLREF_ENABLE; /* Rounds freq to next decimal value - for 62.5 KHz step */ /* frq = 20*(frq/16)+radio_frq[frq%16]; */ switch (priv->ctrl.xtal_freq) { case TEA5767_HIGH_LO_13MHz: tuner_dbg("radio HIGH LO inject xtal @ 13 MHz\n"); buffer[2] |= TEA5767_HIGH_LO_INJECT; div = (frq * (4000 / 16) + 700000 + 225000 + 25000) / 50000; break; case TEA5767_LOW_LO_13MHz: tuner_dbg("radio LOW LO inject xtal @ 13 MHz\n"); div = (frq * (4000 / 16) - 700000 - 225000 + 25000) / 50000; break; case TEA5767_LOW_LO_32768: tuner_dbg("radio LOW LO inject xtal @ 32,768 MHz\n"); buffer[3] |= TEA5767_XTAL_32768; /* const 700=4000*175 Khz - to adjust freq to right value */ div = ((frq * (4000 / 16) - 700000 - 225000) + 16384) >> 15; break; case TEA5767_HIGH_LO_32768: default: tuner_dbg("radio HIGH LO inject xtal @ 32,768 MHz\n"); buffer[2] |= TEA5767_HIGH_LO_INJECT; buffer[3] |= TEA5767_XTAL_32768; div = ((frq * (4000 / 16) + 700000 + 225000) + 16384) >> 15; break; } buffer[0] = (div >> 8) & 0x3f; buffer[1] = div & 0xff; if (5 != (rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 5))) tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc); if (debug) { if (5 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props, buffer, 5))) tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc); else tea5767_status_dump(priv, buffer); } priv->frequency = frq * 125 / 2; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
mauro carvalho chehabmauro carvalho chehab44488.98%457.14%
michael krufkymichael krufky499.82%228.57%
hans verkuilhans verkuil61.20%114.29%
Total499100.00%7100.00%


static int tea5767_read_status(struct dvb_frontend *fe, char *buffer) { struct tea5767_priv *priv = fe->tuner_priv; int rc; memset(buffer, 0, 5); if (5 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props, buffer, 5))) { tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc); return -EREMOTEIO; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
mauro carvalho chehabmauro carvalho chehab4258.33%125.00%
michael krufkymichael krufky3041.67%375.00%
Total72100.00%4100.00%


static inline int tea5767_signal(struct dvb_frontend *fe, const char *buffer) { struct tea5767_priv *priv = fe->tuner_priv; int signal = ((buffer[3] & TEA5767_ADC_LEVEL_MASK) << 8); tuner_dbg("Signal strength: %d\n", signal); return signal; }

Contributors

PersonTokensPropCommitsCommitProp
michael krufkymichael krufky3873.08%133.33%
mauro carvalho chehabmauro carvalho chehab1325.00%133.33%
hans verkuilhans verkuil11.92%133.33%
Total52100.00%3100.00%


static inline int tea5767_stereo(struct dvb_frontend *fe, const char *buffer) { struct tea5767_priv *priv = fe->tuner_priv; int stereo = buffer[2] & TEA5767_STEREO_MASK; tuner_dbg("Radio ST GET = %02x\n", stereo); return (stereo ? V4L2_TUNER_SUB_STEREO : 0); }

Contributors

PersonTokensPropCommitsCommitProp
mauro carvalho chehabmauro carvalho chehab3261.54%125.00%
michael krufkymichael krufky2038.46%375.00%
Total52100.00%4100.00%


static int tea5767_get_status(struct dvb_frontend *fe, u32 *status) { unsigned char buffer[5]; *status = 0; if (0 == tea5767_read_status(fe, buffer)) { if (tea5767_signal(fe, buffer)) *status = TUNER_STATUS_LOCKED; if (tea5767_stereo(fe, buffer)) *status |= TUNER_STATUS_STEREO; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
michael krufkymichael krufky71100.00%2100.00%
Total71100.00%2100.00%


static int tea5767_get_rf_strength(struct dvb_frontend *fe, u16 *strength) { unsigned char buffer[5]; *strength = 0; if (0 == tea5767_read_status(fe, buffer)) *strength = tea5767_signal(fe, buffer); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
michael krufkymichael krufky51100.00%2100.00%
Total51100.00%2100.00%


static int tea5767_standby(struct dvb_frontend *fe) { unsigned char buffer[5]; struct tea5767_priv *priv = fe->tuner_priv; unsigned div, rc; div = (87500 * 4 + 700 + 225 + 25) / 50; /* Set frequency to 87.5 MHz */ buffer[0] = (div >> 8) & 0x3f; buffer[1] = div & 0xff; buffer[2] = TEA5767_PORT1_HIGH; buffer[3] = TEA5767_PORT2_HIGH | TEA5767_HIGH_CUT_CTRL | TEA5767_ST_NOISE_CTL | TEA5767_JAPAN_BAND | TEA5767_STDBY; buffer[4] = 0; if (5 != (rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 5))) tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
mauro carvalho chehabmauro carvalho chehab11487.69%133.33%
michael krufkymichael krufky1612.31%266.67%
Total130100.00%3100.00%


int tea5767_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr) { struct tuner_i2c_props i2c = { .adap = i2c_adap, .addr = i2c_addr }; unsigned char buffer[7] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; int rc; if ((rc = tuner_i2c_xfer_recv(&i2c, buffer, 7))< 5) { printk(KERN_WARNING "It is not a TEA5767. Received %i bytes.\n", rc); return -EINVAL; } /* If all bytes are the same then it's a TV tuner and not a tea5767 */ if (buffer[0] == buffer[1] && buffer[0] == buffer[2] && buffer[0] == buffer[3] && buffer[0] == buffer[4]) { printk(KERN_WARNING "All bytes are equal. It is not a TEA5767\n"); return -EINVAL; } /* Status bytes: * Byte 4: bit 3:1 : CI (Chip Identification) == 0 * bit 0 : internally set to 0 * Byte 5: bit 7:0 : == 0 */ if (((buffer[3] & 0x0f) != 0x00) || (buffer[4] != 0x00)) { printk(KERN_WARNING "Chip ID is not zero. It is not a TEA5767\n"); return -EINVAL; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
mauro carvalho chehabmauro carvalho chehab15283.52%777.78%
michael krufkymichael krufky3016.48%222.22%
Total182100.00%9100.00%


static int tea5767_release(struct dvb_frontend *fe) { kfree(fe->tuner_priv); fe->tuner_priv = NULL; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
michael krufkymichael krufky27100.00%2100.00%
Total27100.00%2100.00%


static int tea5767_get_frequency(struct dvb_frontend *fe, u32 *frequency) { struct tea5767_priv *priv = fe->tuner_priv; *frequency = priv->frequency; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
michael krufkymichael krufky34100.00%2100.00%
Total34100.00%2100.00%


static int tea5767_set_config (struct dvb_frontend *fe, void *priv_cfg) { struct tea5767_priv *priv = fe->tuner_priv; memcpy(&priv->ctrl, priv_cfg, sizeof(priv->ctrl)); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
mauro carvalho chehabmauro carvalho chehab44100.00%1100.00%
Total44100.00%1100.00%

static struct dvb_tuner_ops tea5767_tuner_ops = { .info = { .name = "tea5767", // Philips TEA5767HN FM Radio }, .set_analog_params = set_radio_freq, .set_config = tea5767_set_config, .sleep = tea5767_standby, .release = tea5767_release, .get_frequency = tea5767_get_frequency, .get_status = tea5767_get_status, .get_rf_strength = tea5767_get_rf_strength, };
struct dvb_frontend *tea5767_attach(struct dvb_frontend *fe, struct i2c_adapter* i2c_adap, u8 i2c_addr) { struct tea5767_priv *priv = NULL; priv = kzalloc(sizeof(struct tea5767_priv), GFP_KERNEL); if (priv == NULL) return NULL; fe->tuner_priv = priv; priv->i2c_props.addr = i2c_addr; priv->i2c_props.adap = i2c_adap; priv->i2c_props.name = "tea5767"; priv->ctrl.xtal_freq = TEA5767_HIGH_LO_32768; priv->ctrl.port1 = 1; priv->ctrl.port2 = 1; priv->ctrl.high_cut = 1; priv->ctrl.st_noise = 1; priv->ctrl.japan_band = 1; memcpy(&fe->ops.tuner_ops, &tea5767_tuner_ops, sizeof(struct dvb_tuner_ops)); tuner_info("type set to %s\n", "Philips TEA5767HN FM Radio"); return fe; }

Contributors

PersonTokensPropCommitsCommitProp
michael krufkymichael krufky9258.97%562.50%
mauro carvalho chehabmauro carvalho chehab6441.03%337.50%
Total156100.00%8100.00%

EXPORT_SYMBOL_GPL(tea5767_attach); EXPORT_SYMBOL_GPL(tea5767_autodetection); MODULE_DESCRIPTION("Philips TEA5767 FM tuner driver"); MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>"); MODULE_LICENSE("GPL");

Overall Contributors

PersonTokensPropCommitsCommitProp
mauro carvalho chehabmauro carvalho chehab134469.31%945.00%
michael krufkymichael krufky58029.91%735.00%
hans verkuilhans verkuil120.62%315.00%
tejun heotejun heo30.15%15.00%
Total1939100.00%20100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}