Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8d0936ed authored by Michael Krufky's avatar Michael Krufky Committed by Mauro Carvalho Chehab
Browse files

V4L/DVB (6132): tea5767: convert from tuner sub-driver into dvb_frontend module

parent 7ab10bf7
Loading
Loading
Loading
Loading
+97 −52
Original line number Diff line number Diff line
@@ -13,15 +13,19 @@
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/videodev.h>
#include "tuner-driver.h"
#include "tuner-i2c.h"
#include "tea5767.h"

#define PREFIX "TEA5767 "
static int debug = 0;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "enable verbose debug messages");

/* from tuner-core.c */
extern int tuner_debug;
#define PREFIX "tea5767 "

struct tea5767_priv {
	struct tuner_i2c_props i2c_props;

	u32 frequency;
};

/*****************************************************************************/
@@ -133,11 +137,6 @@ enum tea5767_xtal_freq {

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

static void set_tv_freq(struct tuner *t, unsigned int freq)
{
	tuner_warn("This tuner doesn't support TV freq.\n");
}

static void tea5767_status_dump(unsigned char *buffer)
{
	unsigned int div, frq;
@@ -192,14 +191,16 @@ static void tea5767_status_dump(unsigned char *buffer)
}

/* Freq should be specifyed at 62.5 Hz */
static void set_radio_freq(struct tuner *t, unsigned int frq)
static int set_radio_freq(struct dvb_frontend *fe,
			  struct analog_parameters *params)
{
	struct tea5767_priv *priv = t->priv;
	struct tea5767_priv *priv = fe->tuner_priv;
	unsigned int frq = params->frequency;
	unsigned char buffer[5];
	unsigned div;
	int rc;

	tuner_dbg (PREFIX "radio freq = %d.%03d MHz\n", frq/16000,(frq/16)%1000);
	tuner_dbg("radio freq = %d.%03d MHz\n", frq/16000,(frq/16)%1000);

	/* Rounds freq to next decimal value - for 62.5 KHz step */
	/* frq = 20*(frq/16)+radio_frq[frq%16]; */
@@ -209,7 +210,7 @@ static void set_radio_freq(struct tuner *t, unsigned int frq)
		    TEA5767_ST_NOISE_CTL | TEA5767_JAPAN_BAND;
	buffer[4] = 0;

	if (t->audmode == V4L2_TUNER_MODE_MONO) {
	if (params->audmode == V4L2_TUNER_MODE_MONO) {
		tuner_dbg("TEA5767 set to mono\n");
		buffer[2] |= TEA5767_MONO;
	} else {
@@ -219,26 +220,26 @@ static void set_radio_freq(struct tuner *t, unsigned int frq)
	/* Should be replaced */
	switch (TEA5767_HIGH_LO_32768) {
	case TEA5767_HIGH_LO_13MHz:
		tuner_dbg ("TEA5767 radio HIGH LO inject xtal @ 13 MHz\n");
		tuner_dbg("radio HIGH LO inject xtal @ 13 MHz\n");
		buffer[2] |= TEA5767_HIGH_LO_INJECT;
		buffer[4] |= TEA5767_PLLREF_ENABLE;
		div = (frq * (4000 / 16) + 700000 + 225000 + 25000) / 50000;
		break;
	case TEA5767_LOW_LO_13MHz:
		tuner_dbg ("TEA5767 radio LOW LO inject xtal @ 13 MHz\n");
		tuner_dbg("radio LOW LO inject xtal @ 13 MHz\n");

		buffer[4] |= TEA5767_PLLREF_ENABLE;
		div = (frq * (4000 / 16) - 700000 - 225000 + 25000) / 50000;
		break;
	case TEA5767_LOW_LO_32768:
		tuner_dbg ("TEA5767 radio LOW LO inject xtal @ 32,768 MHz\n");
		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 ("TEA5767 radio HIGH LO inject xtal @ 32,768 MHz\n");
		tuner_dbg("radio HIGH LO inject xtal @ 32,768 MHz\n");

		buffer[2] |= TEA5767_HIGH_LO_INJECT;
		buffer[3] |= TEA5767_XTAL_32768;
@@ -251,19 +252,23 @@ static void set_radio_freq(struct tuner *t, unsigned int frq)
	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 (tuner_debug) {
	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(buffer);
	}

	priv->frequency = frq * 125 / 2;

	return 0;
}

static int tea5767_signal(struct tuner *t)
static int tea5767_signal(struct dvb_frontend *fe)
{
	unsigned char buffer[5];
	int rc;
	struct tea5767_priv *priv = t->priv;
	struct tea5767_priv *priv = fe->tuner_priv;

	memset(buffer, 0, sizeof(buffer));
	if (5 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props, buffer, 5)))
@@ -272,11 +277,11 @@ static int tea5767_signal(struct tuner *t)
	return ((buffer[3] & TEA5767_ADC_LEVEL_MASK) << 8);
}

static int tea5767_stereo(struct tuner *t)
static int tea5767_stereo(struct dvb_frontend *fe)
{
	unsigned char buffer[5];
	int rc;
	struct tea5767_priv *priv = t->priv;
	struct tea5767_priv *priv = fe->tuner_priv;

	memset(buffer, 0, sizeof(buffer));
	if (5 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props, buffer, 5)))
@@ -284,15 +289,31 @@ static int tea5767_stereo(struct tuner *t)

	rc = buffer[2] & TEA5767_STEREO_MASK;

	tuner_dbg("TEA5767 radio ST GET = %02x\n", rc);
	tuner_dbg("radio ST GET = %02x\n", rc);

	return ((buffer[2] & TEA5767_STEREO_MASK) ? V4L2_TUNER_SUB_STEREO : 0);
}

static void tea5767_standby(struct tuner *t)
static int tea5767_get_status(struct dvb_frontend *fe, u32 *status)
{
	struct tea5767_priv *priv = fe->tuner_priv;
	int signal = tea5767_signal(fe);
	*status = 0;

	if (signal)
		*status = TUNER_STATUS_LOCKED;
	if (tea5767_stereo(fe))
		*status |= TUNER_STATUS_STEREO;

	tuner_dbg("tea5767: Signal strength: %d\n", signal);

	return 0;
}

static int tea5767_standby(struct dvb_frontend *fe)
{
	unsigned char buffer[5];
	struct tea5767_priv *priv = t->priv;
	struct tea5767_priv *priv = fe->tuner_priv;
	unsigned div, rc;

	div = (87500 * 4 + 700 + 225 + 25) / 50; /* Set frequency to 87.5 MHz */
@@ -305,23 +326,25 @@ static void tea5767_standby(struct tuner *t)

	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;
}

int tea5767_autodetection(struct tuner *t)
int tea5767_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr)
{
	struct tuner_i2c_props i2c = { .adap = t->i2c.adapter, .addr = t->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_send(&i2c, buffer, 7))< 5) {
		tuner_warn("It is not a TEA5767. Received %i bytes.\n", 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]) {
		tuner_warn("All bytes are equal. It is not a TEA5767\n");
		printk(KERN_WARNING "All bytes are equal. It is not a TEA5767\n");
		return EINVAL;
	}

@@ -331,51 +354,73 @@ int tea5767_autodetection(struct tuner *t)
	 *  Byte 5: bit 7:0 : == 0
	 */
	if (((buffer[3] & 0x0f) != 0x00) || (buffer[4] != 0x00)) {
		tuner_warn("Chip ID is not zero. It is not a TEA5767\n");
		printk(KERN_WARNING "Chip ID is not zero. It is not a TEA5767\n");
		return EINVAL;
	}

	/* It seems that tea5767 returns 0xff after the 5th byte */
	if ((buffer[5] != 0xff) || (buffer[6] != 0xff)) {
		tuner_warn("Returned more than 5 bytes. It is not a TEA5767\n");
		printk(KERN_WARNING "Returned more than 5 bytes. It is not a TEA5767\n");
		return EINVAL;
	}

	tuner_warn("TEA5767 detected.\n");
	printk(KERN_WARNING "TEA5767 detected.\n");
	return 0;
}

static void tea5767_release(struct tuner *t)
static int tea5767_release(struct dvb_frontend *fe)
{
	kfree(t->priv);
	t->priv = NULL;
	kfree(fe->tuner_priv);
	fe->tuner_priv = NULL;

	return 0;
}

static struct tuner_operations tea5767_tuner_ops = {
	.set_tv_freq    = set_tv_freq,
	.set_radio_freq = set_radio_freq,
	.has_signal     = tea5767_signal,
	.is_stereo      = tea5767_stereo,
	.standby        = tea5767_standby,
static int tea5767_get_frequency(struct dvb_frontend *fe, u32 *frequency)
{
	struct tea5767_priv *priv = fe->tuner_priv;
	*frequency = priv->frequency;
	return 0;
}

static struct dvb_tuner_ops tea5767_tuner_ops = {
	.info = {
		.name           = "tea5767", // Philips TEA5767HN FM Radio
	},

	.set_analog_params = set_radio_freq,
	.sleep             = tea5767_standby,
	.release           = tea5767_release,
	.get_frequency     = tea5767_get_frequency,
	.get_status        = tea5767_get_status,
};

int tea5767_tuner_init(struct tuner *t)
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 -ENOMEM;
	t->priv = priv;
		return NULL;
	fe->tuner_priv = priv;

	priv->i2c_props.addr = t->i2c.addr;
	priv->i2c_props.adap = t->i2c.adapter;
	priv->i2c_props.addr = i2c_addr;
	priv->i2c_props.adap = i2c_adap;

	tuner_info("type set to %d (%s)\n", t->type, "Philips TEA5767HN FM Radio");
	strlcpy(t->i2c.name, "tea5767", sizeof(t->i2c.name));
	memcpy(&fe->ops.tuner_ops, &tea5767_tuner_ops,
	       sizeof(struct dvb_tuner_ops));

	memcpy(&t->ops, &tea5767_tuner_ops, sizeof(struct tuner_operations));
	tuner_info("type set to %s\n", "Philips TEA5767HN FM Radio");

	return (0);
	return fe;
}


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");
+29 −0
Original line number Diff line number Diff line
/*
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#ifndef __TEA5767_H__
#define __TEA5767_H__

#include <linux/i2c.h>
#include "dvb_frontend.h"

extern int tea5767_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr);

extern struct dvb_frontend *tea5767_attach(struct dvb_frontend *fe,
					   struct i2c_adapter* i2c_adap,
					   u8 i2c_addr);

#endif /* __TEA5767_H__ */
+3 −2
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@
#include "mt20xx.h"
#include "tda8290.h"
#include "tea5761.h"
#include "tea5767.h"

#define UNSET (-1U)

@@ -262,7 +263,7 @@ static void set_type(struct i2c_client *c, unsigned int type,
		break;
	}
	case TUNER_TEA5767:
		if (tea5767_tuner_init(t) == EINVAL) {
		if (tea5767_attach(&t->fe, t->i2c.adapter, t->i2c.addr) == NULL) {
			t->type = TUNER_ABSENT;
			t->mode_mask = T_UNINITIALIZED;
			return;
@@ -600,7 +601,7 @@ static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
			}
			break;
		case 0x60:
			if (tea5767_autodetection(t) != EINVAL) {
			if (tea5767_autodetection(t->i2c.adapter, t->i2c.addr) != EINVAL) {
				t->type = TUNER_TEA5767;
				t->mode_mask = T_RADIO;
				t->mode = T_STANDBY;
+0 −3
Original line number Diff line number Diff line
@@ -76,9 +76,6 @@ extern int default_tuner_init(struct tuner *t);

extern int tda9887_tuner_init(struct tuner *t);

extern int tea5767_autodetection(struct tuner *t);
extern int tea5767_tuner_init(struct tuner *t);

/* ------------------------------------------------------------------------ */

#define tuner_warn(fmt, arg...) do {\