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

Commit f7ce3cc6 authored by Mauro Carvalho Chehab's avatar Mauro Carvalho Chehab Committed by Linus Torvalds
Browse files

[PATCH] v4l: I2C Tuner



- Fixed a trouble on tuner-core that generates erros on computers with more
  than one TV card.
- Rename tuner structures fields.
- Tail spaces removed.
- I2C cleanups and converged to a basic reference structure.
- Removed unused structures.
- Fix setting frequency on tda8290.
- Added code for TEA5767 autodetection.
- Standby mode support implemented. It is used to disable
  a non used tuner. Currenlty implemented on tea5767.
- New macro: set_type disables other tuner when changing mode.
- Some cleanups.
- Use 50 kHz step when tunning radio for most tuners to improve precision.

Signed-off-by: default avatarFabien Perrot <perrot1983@yahoo.fr>
Signed-off-by: default avatarMichael Krufky <mkrufky@m1k.net>
Signed-off-By: default avatarNickolay V. Shmyrev <nshmyrev@yandex.ru>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@brturbo.com.br>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent ebe4c6fa
Loading
Loading
Loading
Loading
+0 −16
Original line number Diff line number Diff line
@@ -511,22 +511,6 @@ int microtune_init(struct i2c_client *c)
	tuner_info("microtune: companycode=%04x part=%02x rev=%02x\n",
		   company_code,buf[0x13],buf[0x14]);

#if 0
	/* seems to cause more problems than it solves ... */
	switch (company_code) {
	case 0x30bf:
	case 0x3cbf:
	case 0x3dbf:
	case 0x4d54:
	case 0x8e81:
	case 0x8e91:
		/* ok (?) */
		break;
	default:
		tuner_warn("tuner: microtune: unknown companycode\n");
		return 0;
	}
#endif

	if (buf[0x13] < ARRAY_SIZE(microtune_part) &&
	    NULL != microtune_part[buf[0x13]])
+5 −8
Original line number Diff line number Diff line
/*
 * $Id: tda8290.c,v 1.11 2005/06/18 06:09:06 nsh Exp $
 * $Id: tda8290.c,v 1.15 2005/07/08 20:21:33 mchehab Exp $
 *
 * i2c tv tuner chip device driver
 * controls the philips tda8290+75 tuner chip combo.
@@ -136,15 +136,12 @@ static int tda8290_tune(struct i2c_client *c)
	return 0;
}

static void set_frequency(struct tuner *t, u16 ifc)
static void set_frequency(struct tuner *t, u16 ifc, unsigned int freq)
{
	u32 freq;
	u32 N;

	if (t->mode == V4L2_TUNER_RADIO)
		freq = t->freq / 1000;
	else
		freq = t->freq;
		freq = freq / 1000;

	N = (((freq<<3)+ifc)&0x3fffc);

@@ -187,14 +184,14 @@ static void set_tv_freq(struct i2c_client *c, unsigned int freq)
	struct tuner *t = i2c_get_clientdata(c);

	set_audio(t);
	set_frequency(t, 864);
	set_frequency(t, 864, freq);
	tda8290_tune(c);
}

static void set_radio_freq(struct i2c_client *c, unsigned int freq)
{
	struct tuner *t = i2c_get_clientdata(c);
	set_frequency(t, 704);
	set_frequency(t, 704, freq);
	tda8290_tune(c);
}

+0 −9
Original line number Diff line number Diff line
@@ -569,15 +569,6 @@ static int tda9887_configure(struct tda9887 *t)
	tda9887_set_config(t,buf);
	tda9887_set_insmod(t,buf);

#if 0
	/* This as-is breaks some cards, must be fixed in a
	 * card-specific way, probably using TDA9887_SET_CONFIG to
	 * turn on/off port2 */
	if (t->std & V4L2_STD_SECAM_L) {
		/* secam fixup (FIXME: move this to tvnorms array?) */
		buf[1] &= ~cOutputPort2Inactive;
	}
#endif

	dprintk(PREFIX "writing: b=0x%02x c=0x%02x e=0x%02x\n",
		buf[1],buf[2],buf[3]);
+81 −77
Original line number Diff line number Diff line
@@ -2,7 +2,7 @@
 * For Philips TEA5767 FM Chip used on some TV Cards like Prolink Pixelview
 * I2C address is allways 0xC0.
 *
 * $Id: tea5767.c,v 1.11 2005/06/21 15:40:33 mchehab Exp $
 * $Id: tea5767.c,v 1.18 2005/07/07 03:02:55 mchehab Exp $
 *
 * Copyright (c) 2005 Mauro Carvalho Chehab (mchehab@brturbo.com.br)
 * This code is placed under the terms of the GNU General Public License
@@ -11,23 +11,11 @@
 * from their contributions on DScaler.
 */

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/videodev.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>

#include <linux/videodev.h>
#include <linux/delay.h>
#include <media/tuner.h>
#include <media/tuner.h>

/* Declared at tuner-core.c */
extern unsigned int tuner_debug;

#define PREFIX "TEA5767 "

@@ -130,6 +118,14 @@ extern unsigned int tuner_debug;
/* Reserved for future extensions */
#define TEA5767_RESERVED_MASK	0xff

enum tea5767_xtal_freq {
        TEA5767_LOW_LO_32768    = 0,
        TEA5767_HIGH_LO_32768   = 1,
        TEA5767_LOW_LO_13MHz    = 2,
        TEA5767_HIGH_LO_13MHz   = 3,
};


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

static void set_tv_freq(struct i2c_client *c, unsigned int freq)
@@ -183,11 +179,13 @@ static void tea5767_status_dump(unsigned char *buffer)

	printk(PREFIX "IF Counter = %d\n", buffer[2] & TEA5767_IF_CNTR_MASK);

	printk(PREFIX "ADC Level = %d\n",(buffer[3] & TEA5767_ADC_LEVEL_MASK)>>4);
	printk(PREFIX "ADC Level = %d\n",
	       (buffer[3] & TEA5767_ADC_LEVEL_MASK) >> 4);

	printk(PREFIX "Chip ID = %d\n", (buffer[3] & TEA5767_CHIP_ID_MASK));

	printk(PREFIX "Reserved = 0x%02x\n",(buffer[4] & TEA5767_RESERVED_MASK));
	printk(PREFIX "Reserved = 0x%02x\n",
	       (buffer[4] & TEA5767_RESERVED_MASK));
}

/* Freq should be specifyed at 62.5 Hz */
@@ -198,23 +196,30 @@ static void set_radio_freq(struct i2c_client *c, unsigned int frq)
	unsigned div;
	int rc;

	if ( tuner_debug )
		printk(PREFIX "radio freq counter %d\n",frq);
	tuner_dbg (PREFIX "radio freq counter %d\n", frq);

	/* Rounds freq to next decimal value - for 62.5 KHz step */
	/* frq = 20*(frq/16)+radio_frq[frq%16]; */

	buffer[2] = TEA5767_PORT1_HIGH;
	buffer[3] = TEA5767_PORT2_HIGH | TEA5767_HIGH_CUT_CTRL | TEA5767_ST_NOISE_CTL | TEA5767_JAPAN_BAND;
	buffer[3] = TEA5767_PORT2_HIGH | TEA5767_HIGH_CUT_CTRL |
		    TEA5767_ST_NOISE_CTL | TEA5767_JAPAN_BAND;
	buffer[4] = 0;

	if (t->mode == T_STANDBY) {
		tuner_dbg("TEA5767 set to standby mode\n");
		buffer[3] |= TEA5767_STDBY;
	}

	if (t->audmode == V4L2_TUNER_MODE_MONO) {
		tuner_dbg("TEA5767 set to mono\n");
		buffer[2] |= TEA5767_MONO;
	} else
	} else {
		tuner_dbg("TEA5767 set to stereo\n");
	}

	switch (t->type) {
	/* Should be replaced */
	switch (TEA5767_HIGH_LO_32768) {
	case TEA5767_HIGH_LO_13MHz:
		tuner_dbg ("TEA5767 radio HIGH LO inject xtal @ 13 MHz\n");
		buffer[2] |= TEA5767_HIGH_LO_INJECT;
@@ -277,13 +282,12 @@ static int tea5767_stereo(struct i2c_client *c)

	rc = buffer[2] & TEA5767_STEREO_MASK;

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

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

int tea_detection(struct i2c_client *c)
int tea5767_autodetection(struct i2c_client *c)
{
	unsigned char buffer[5] = { 0xff, 0xff, 0xff, 0xff, 0xff };
	int rc;
@@ -319,11 +323,11 @@ int tea5767_tuner_init(struct i2c_client *c)
{
	struct tuner *t = i2c_get_clientdata(c);

	if (tea_detection(c)==EINVAL) return EINVAL;
	if (tea5767_autodetection(c) == EINVAL)
		return EINVAL;

        tuner_info("type set to %d (%s)\n",
                   t->type, TEA5767_TUNER_NAME);
        strlcpy(c->name, TEA5767_TUNER_NAME, sizeof(c->name));
	tuner_info("type set to %d (%s)\n", t->type, "Philips TEA5767HN FM Radio");
	strlcpy(c->name, "tea5767", sizeof(c->name));

	t->tv_freq = set_tv_freq;
	t->radio_freq = set_radio_freq;
+402 −305
Original line number Diff line number Diff line
/*
 * $Id: tuner-core.c,v 1.29 2005/06/21 15:40:33 mchehab Exp $
 * $Id: tuner-core.c,v 1.55 2005/07/08 13:20:33 mchehab Exp $
 *
 * i2c tv tuner chip device driver
 * core core, i.e. kernel interfaces, registering and so on
@@ -23,10 +23,6 @@
#include <media/tuner.h>
#include <media/audiochip.h>

/*
 * comment line bellow to return to old behavor, where only one I2C device is supported
 */

#define UNSET (-1U)

/* standard i2c insmod options */
@@ -36,6 +32,7 @@ static unsigned short normal_i2c[] = {
	0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
	I2C_CLIENT_END
};

I2C_CLIENT_INSMOD;

/* insmod options used at init time => read/only */
@@ -56,9 +53,6 @@ MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners");
MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
MODULE_LICENSE("GPL");

static int this_adap;
static unsigned short first_tuner, tv_tuner, radio_tuner;

static struct i2c_driver driver;
static struct i2c_client client_template;

@@ -70,16 +64,17 @@ static void set_tv_freq(struct i2c_client *c, unsigned int freq)
	struct tuner *t = i2c_get_clientdata(c);

	if (t->type == UNSET) {
		tuner_info("tuner type not set\n");
		tuner_warn ("tuner type not set\n");
		return;
	}
	if (NULL == t->tv_freq) {
		tuner_info("Huh? tv_set is NULL?\n");
		tuner_warn ("Tuner has no way to set tv freq\n");
		return;
	}
	if (freq < tv_range[0] * 16 || freq > tv_range[1] * 16) {
			tuner_info("TV freq (%d.%02d) out of range (%d-%d)\n",
				   freq/16,freq%16*100/16,tv_range[0],tv_range[1]);
		tuner_dbg ("TV freq (%d.%02d) out of range (%d-%d)\n",
			   freq / 16, freq % 16 * 100 / 16, tv_range[0],
			   tv_range[1]);
	}
	t->tv_freq(c, freq);
}
@@ -89,24 +84,20 @@ static void set_radio_freq(struct i2c_client *c, unsigned int freq)
	struct tuner *t = i2c_get_clientdata(c);

	if (t->type == UNSET) {
		tuner_info("tuner type not set\n");
		tuner_warn ("tuner type not set\n");
		return;
	}
	if (NULL == t->radio_freq) {
		tuner_info("no radio tuning for this one, sorry.\n");
		tuner_warn ("tuner has no way to set radio frequency\n");
		return;
	}
	if (freq >= radio_range[0]*16000 && freq <= radio_range[1]*16000) {
		if (tuner_debug)
			tuner_info("radio freq step 62.5Hz (%d.%06d)\n",
			 		    freq/16000,freq%16000*1000/16);
		t->radio_freq(c,freq);
        } else {
		tuner_info("radio freq (%d.%02d) out of range (%d-%d)\n",
			    freq/16,freq%16*100/16,
	if (freq <= radio_range[0] * 16000 || freq >= radio_range[1] * 16000) {
		tuner_dbg ("radio freq (%d.%02d) out of range (%d-%d)\n",
			   freq / 16000, freq % 16000 * 100 / 16000,
			   radio_range[0], radio_range[1]);
	}

	t->radio_freq(c, freq);
	return;
}

@@ -117,7 +108,7 @@ static void set_freq(struct i2c_client *c, unsigned long freq)
	switch (t->mode) {
	case V4L2_TUNER_RADIO:
		tuner_dbg("radio freq set to %lu.%02lu\n",
			  freq/16,freq%16*100/16);
			  freq / 16000, freq % 16000 * 100 / 16000);
		set_radio_freq(c, freq);
		break;
	case V4L2_TUNER_ANALOG_TV:
@@ -130,29 +121,32 @@ static void set_freq(struct i2c_client *c, unsigned long freq)
	t->freq = freq;
}

static void set_type(struct i2c_client *c, unsigned int type)
static void set_type(struct i2c_client *c, unsigned int type,
		     unsigned int new_mode_mask)
{
	struct tuner *t = i2c_get_clientdata(c);
	unsigned char buffer[4];

	/* sanity check */
	if (type == UNSET || type == TUNER_ABSENT)
	if (type == UNSET || type == TUNER_ABSENT) {
		tuner_dbg ("tuner 0x%02x: Tuner type absent\n",c->addr);
		return;
	if (type >= tuner_count)
	}

	if (type >= tuner_count) {
		tuner_warn ("tuner 0x%02x: Tuner count greater than %d\n",c->addr,tuner_count);
		return;
	}

	/* This code detects calls by card attach_inform */
	if (NULL == t->i2c.dev.driver) {
		/* not registered yet */
		tuner_dbg ("tuner 0x%02x: called during i2c_client register by adapter's attach_inform\n", c->addr);

		t->type=type;
		return;
	}
	if ((t->initialized) && (t->type == type))
		/* run only once except type change  Hac 04/05*/
		return;

	t->initialized = 1;

	t->type = type;

	switch (t->type) {
	case TUNER_MT2032:
		microtune_init(c);
@@ -161,7 +155,12 @@ static void set_type(struct i2c_client *c, unsigned int type)
		tda8290_init(c);
		break;
	case TUNER_TEA5767:
		if (tea5767_tuner_init(c)==EINVAL) t->type=TUNER_ABSENT;
		if (tea5767_tuner_init(c) == EINVAL) {
			t->type = TUNER_ABSENT;
			t->mode_mask = T_UNINITIALIZED;
			return;
		}
		t->mode_mask = T_RADIO;
		break;
	case TUNER_PHILIPS_FMD1216ME_MK3:
		buffer[0] = 0x0b;
@@ -176,64 +175,67 @@ static void set_type(struct i2c_client *c, unsigned int type)
		default_tuner_init(c);
		break;
	default:
		/* TEA5767 autodetection code */
			if (tea5767_tuner_init(c)!=EINVAL) {
				t->type = TUNER_TEA5767;
				if (first_tuner == 0x60)
					first_tuner++;
				break;
			}

		default_tuner_init(c);
		break;
	}
	tuner_dbg ("I2C addr 0x%02x with type %d\n",c->addr<<1,type);

	if (t->mode_mask == T_UNINITIALIZED)
		t->mode_mask = new_mode_mask;

	set_freq(c, t->freq);
	tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n",
		  c->adapter->name, c->driver->name, c->addr << 1, type,
		  t->mode_mask);
}

#define CHECK_ADDR(tp,cmd,tun)	if (client->addr!=tp) { \
			  return 0; } else if (tuner_debug) \
			  tuner_info ("Cmd %s accepted to "tun"\n",cmd);
#define CHECK_MODE(cmd)	if (t->mode == V4L2_TUNER_RADIO) { \
		 	CHECK_ADDR(radio_tuner,cmd,"radio") } else \
			{ CHECK_ADDR(tv_tuner,cmd,"TV"); }
/*
 * This function apply tuner config to tuner specified
 * by tun_setup structure. I addr is unset, then admin status
 * and tun addr status is more precise then current status,
 * it's applied. Otherwise status and type are applied only to
 * tuner with exactly the same addr.
*/

static void set_addr(struct i2c_client *c, struct tuner_addr *tun_addr)
static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup)
{
	/* ADDR_UNSET defaults to first available tuner */
	if ( tun_addr->addr == ADDR_UNSET ) {
		if (first_tuner != c->addr)
			return;
		switch (tun_addr->v4l2_tuner) {
	struct tuner *t = i2c_get_clientdata(c);

	if (tun_setup->addr == ADDR_UNSET) {
		if (t->mode_mask & tun_setup->mode_mask)
			set_type(c, tun_setup->type, tun_setup->mode_mask);
	} else if (tun_setup->addr == c->addr) {
		set_type(c, tun_setup->type, tun_setup->mode_mask);
	}
}

static inline int check_mode(struct tuner *t, char *cmd)
{
	if (1 << t->mode & t->mode_mask) {
		switch (t->mode) {
		case V4L2_TUNER_RADIO:
	 		radio_tuner=c->addr;
			tuner_dbg("Cmd %s accepted for radio\n", cmd);
			break;
		default:
			tv_tuner=c->addr;
		case V4L2_TUNER_ANALOG_TV:
			tuner_dbg("Cmd %s accepted for analog TV\n", cmd);
			break;
		case V4L2_TUNER_DIGITAL_TV:
			tuner_dbg("Cmd %s accepted for digital TV\n", cmd);
			break;
		}
	} else {
		/* Sets tuner to its configured value */
		switch (tun_addr->v4l2_tuner) {
		case V4L2_TUNER_RADIO:
 			radio_tuner=tun_addr->addr;
			if ( tun_addr->addr == c->addr ) set_type(c,tun_addr->type);
			return;
		default:
			tv_tuner=tun_addr->addr;
			if ( tun_addr->addr == c->addr ) set_type(c,tun_addr->type);
			return;
		}
		return 0;
	}
	set_type(c,tun_addr->type);
	return EINVAL;
}

static char pal[] = "-";
module_param_string(pal, pal, sizeof(pal), 0644);
static char secam[] = "-";
module_param_string(secam, secam, sizeof(secam), 0644);

/* get more precise norm info from insmod option */
static int tuner_fixup_std(struct tuner *t)
{
	if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
		/* get more precise norm info from insmod option */
		switch (pal[0]) {
		case 'b':
		case 'B':
@@ -254,25 +256,50 @@ static int tuner_fixup_std(struct tuner *t)
			tuner_dbg ("insmod fixup: PAL => PAL-DK\n");
			t->std = V4L2_STD_PAL_DK;
			break;
		case 'M':
		case 'm':
			tuner_dbg ("insmod fixup: PAL => PAL-M\n");
			t->std = V4L2_STD_PAL_M;
			break;
		case 'N':
		case 'n':
			tuner_dbg ("insmod fixup: PAL => PAL-N\n");
			t->std = V4L2_STD_PAL_N;
			break;
		}
	}
	if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
		switch (secam[0]) {
		case 'd':
		case 'D':
		case 'k':
		case 'K':
			tuner_dbg ("insmod fixup: SECAM => SECAM-DK\n");
			t->std = V4L2_STD_SECAM_DK;
			break;
		case 'l':
		case 'L':
			tuner_dbg ("insmod fixup: SECAM => SECAM-L\n");
			t->std = V4L2_STD_SECAM_L;
			break;
		}
	}

	return 0;
}

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

/* static var Used only in tuner_attach and tuner_probe */
static unsigned default_mode_mask;

/* During client attach, set_type is called by adapter's attach_inform callback.
   set_type must then be completed by tuner_attach.
 */
static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
{
	struct tuner *t;

	/* by default, first I2C card is both tv and radio tuner */
	if (this_adap == 0) {
		first_tuner = addr;
		tv_tuner = addr;
		radio_tuner = addr;
	}
	this_adap++;

	client_template.adapter = adap;
	client_template.addr = addr;

@@ -285,12 +312,37 @@ static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
	t->type = UNSET;
	t->radio_if2 = 10700 * 1000;	/* 10.7MHz - FM radio */
	t->audmode = V4L2_TUNER_MODE_STEREO;
	t->mode_mask = T_UNINITIALIZED;


	tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name);

	/* TEA5767 autodetection code - only for addr = 0xc0 */
	if (addr == 0x60) {
		if (tea5767_autodetection(&t->i2c) != EINVAL) {
			t->type = TUNER_TEA5767;
			t->mode_mask = T_RADIO;
			t->mode = T_STANDBY;
			t->freq = 87.5 * 16; /* Sets freq to FM range */
			default_mode_mask &= ~T_RADIO;

			i2c_attach_client (&t->i2c);
	tuner_info("chip found @ 0x%x (%s)\n",
		   addr << 1, adap->name);
			set_type(&t->i2c,t->type, t->mode_mask);
			return 0;
		}
	}

	set_type(&t->i2c, t->type);
	/* Initializes only the first adapter found */
	if (default_mode_mask != T_UNINITIALIZED) {
		tuner_dbg ("Setting mode_mask to 0x%02x\n", default_mode_mask);
		t->mode_mask = default_mode_mask;
		t->freq = 400 * 16; /* Sets freq to VHF High */
		default_mode_mask = T_UNINITIALIZED;
	}

	/* Should be just before return */
	i2c_attach_client (&t->i2c);
	set_type (&t->i2c,t->type, t->mode_mask);
	return 0;
}

@@ -300,11 +352,8 @@ static int tuner_probe(struct i2c_adapter *adap)
		normal_i2c[0] = addr;
		normal_i2c[1] = I2C_CLIENT_END;
	}
	this_adap = 0;

	first_tuner = 0;
	tv_tuner = 0;
	radio_tuner = 0;
	default_mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;

	if (adap->class & I2C_CLASS_TV_ANALOG)
		return i2c_probe(adap, &addr_data, tuner_attach);
@@ -318,7 +367,8 @@ static int tuner_detach(struct i2c_client *client)

	err = i2c_detach_client(&t->i2c);
	if (err) {
		tuner_warn ("Client deregistration failed, client not detached.\n");
		tuner_warn
		    ("Client deregistration failed, client not detached.\n");
		return err;
	}

@@ -326,37 +376,65 @@ static int tuner_detach(struct i2c_client *client)
	return 0;
}

#define SWITCH_V4L2	if (!t->using_v4l2 && tuner_debug) \
		          tuner_info("switching to v4l2\n"); \
/*
 * Switch tuner to other mode. If tuner support both tv and radio,
 * set another frequency to some value (This is needed for some pal
 * tuners to avoid locking). Otherwise, just put second tuner in
 * standby mode.
 */

static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd)
{
	if (mode != t->mode) {

		t->mode = mode;
		if (check_mode(t, cmd) == EINVAL) {
			t->mode = T_STANDBY;
			if (V4L2_TUNER_RADIO == mode) {
				set_tv_freq(client, 400 * 16);
			} else {
				set_radio_freq(client, 87.5 * 16000);
			}
			return EINVAL;
		}
	}
	return 0;
}

#define switch_v4l2()	if (!t->using_v4l2) \
		            tuner_dbg("switching to v4l2\n"); \
	                t->using_v4l2 = 1;
#define CHECK_V4L2	if (t->using_v4l2) { if (tuner_debug) \
			  tuner_info("ignore v4l1 call\n"); \
		          return 0; }

static int
tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
static inline int check_v4l2(struct tuner *t)
{
	if (t->using_v4l2) {
		tuner_dbg ("ignore v4l1 call\n");
		return EINVAL;
	}
	return 0;
}

static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
{
	struct tuner *t = i2c_get_clientdata(client);
	unsigned int *iarg = (int *)arg;

	switch (cmd) {
	/* --- configuration --- */
	case TUNER_SET_TYPE:
		set_type(client,*iarg);
		break;
	case TUNER_SET_TYPE_ADDR:
		set_addr(client,(struct tuner_addr *)arg);
		tuner_dbg ("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x\n",
				((struct tuner_setup *)arg)->type,
				((struct tuner_setup *)arg)->addr,
				((struct tuner_setup *)arg)->mode_mask);

		set_addr(client, (struct tuner_setup *)arg);
		break;
	case AUDC_SET_RADIO:
		t->mode = V4L2_TUNER_RADIO;
		CHECK_ADDR(tv_tuner,"AUDC_SET_RADIO","TV");

		if (V4L2_TUNER_RADIO != t->mode) {
			set_tv_freq(client,400 * 16);
		}
		set_mode(client,t,V4L2_TUNER_RADIO, "AUDC_SET_RADIO");
		break;
	case AUDC_CONFIG_PINNACLE:
		CHECK_ADDR(tv_tuner,"AUDC_CONFIG_PINNACLE","TV");
		if (check_mode(t, "AUDC_CONFIG_PINNACLE") == EINVAL)
			return 0;
		switch (*iarg) {
		case 2:
			tuner_dbg("pinnacle pal\n");
@@ -368,6 +446,8 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
			break;
		}
		break;
	case TDA9887_SET_CONFIG:
		break;
	/* --- v4l ioctls --- */
	/* take care: bttv does userspace copying, we'll get a
	   kernel pointer here... */
@@ -383,9 +463,11 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
			};
			struct video_channel *vc = arg;

		CHECK_V4L2;
		t->mode = V4L2_TUNER_ANALOG_TV;
		CHECK_ADDR(tv_tuner,"VIDIOCSCHAN","TV");
			if (check_v4l2(t) == EINVAL)
				return 0;

			if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==EINVAL)
				return 0;

			if (vc->norm < ARRAY_SIZE(map))
				t->std = map[vc->norm];
@@ -398,8 +480,11 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
		{
			unsigned long *v = arg;

		CHECK_MODE("VIDIOCSFREQ");
		CHECK_V4L2;
			if (check_mode(t, "VIDIOCSFREQ") == EINVAL)
				return 0;
			if (check_v4l2(t) == EINVAL)
				return 0;

			set_freq(client, *v);
			return 0;
		}
@@ -407,18 +492,23 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
		{
			struct video_tuner *vt = arg;

		CHECK_ADDR(radio_tuner,"VIDIOCGTUNER","radio");
		CHECK_V4L2;
			if (check_mode(t, "VIDIOCGTUNER") == EINVAL)
				return 0;
			if (check_v4l2(t) == EINVAL)
				return 0;

			if (V4L2_TUNER_RADIO == t->mode) {
				if (t->has_signal)
					vt->signal = t->has_signal(client);
				if (t->is_stereo) {
					if (t->is_stereo(client))
					vt->flags |= VIDEO_TUNER_STEREO_ON;
						vt->flags |=
						    VIDEO_TUNER_STEREO_ON;
					else
					vt->flags &= ~VIDEO_TUNER_STEREO_ON;
						vt->flags &=
						    ~VIDEO_TUNER_STEREO_ON;
				}
			vt->flags |= V4L2_TUNER_CAP_LOW; /* Allow freqs at 62.5 Hz */
				vt->flags |= VIDEO_TUNER_LOW;	/* Allow freqs at 62.5 Hz */

				vt->rangelow = radio_range[0] * 16000;
				vt->rangehigh = radio_range[1] * 16000;
@@ -434,12 +524,14 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
		{
			struct video_audio *va = arg;

		CHECK_ADDR(radio_tuner,"VIDIOCGAUDIO","radio");
		CHECK_V4L2;
			if (check_mode(t, "VIDIOCGAUDIO") == EINVAL)
				return 0;
			if (check_v4l2(t) == EINVAL)
				return 0;

			if (V4L2_TUNER_RADIO == t->mode && t->is_stereo)
				va->mode = t->is_stereo(client)
				? VIDEO_SOUND_STEREO
				: VIDEO_SOUND_MONO;
				    ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
			return 0;
		}

@@ -447,9 +539,11 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
		{
			v4l2_std_id *id = arg;

		SWITCH_V4L2;
		t->mode = V4L2_TUNER_ANALOG_TV;
		CHECK_ADDR(tv_tuner,"VIDIOC_S_STD","TV");
			if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
					== EINVAL)
				return 0;

			switch_v4l2();

			t->std = *id;
			tuner_fixup_std(t);
@@ -461,21 +555,25 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
		{
			struct v4l2_frequency *f = arg;

		CHECK_MODE("VIDIOC_S_FREQUENCY");
		SWITCH_V4L2;
			t->freq = f->frequency;
			switch_v4l2();
			if (V4L2_TUNER_RADIO == f->type &&
		    V4L2_TUNER_RADIO != t->mode)
			set_tv_freq(client,400*16);
		t->mode  = f->type;
		set_freq(client,f->frequency);
			    V4L2_TUNER_RADIO != t->mode) {
			        if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
					    == EINVAL)
					return 0;
			}
			set_freq(client,t->freq);

			break;
		}
	case VIDIOC_G_FREQUENCY:
		{
			struct v4l2_frequency *f = arg;

		CHECK_MODE("VIDIOC_G_FREQUENCY");
		SWITCH_V4L2;
			if (check_mode(t, "VIDIOC_G_FREQUENCY") == EINVAL)
				return 0;
			switch_v4l2();
			f->type = t->mode;
			f->frequency = t->freq;
			break;
@@ -484,19 +582,29 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
		{
			struct v4l2_tuner *tuner = arg;

		CHECK_MODE("VIDIOC_G_TUNER");
		SWITCH_V4L2;
			if (check_mode(t, "VIDIOC_G_TUNER") == EINVAL)
				return 0;
			switch_v4l2();

			if (V4L2_TUNER_RADIO == t->mode) {

				if (t->has_signal)
					tuner->signal = t->has_signal(client);

				if (t->is_stereo) {
					if (t->is_stereo(client)) {
					tuner -> rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO;
						tuner->rxsubchans =
						    V4L2_TUNER_SUB_STEREO |
						    V4L2_TUNER_SUB_MONO;
					} else {
					tuner -> rxsubchans = V4L2_TUNER_SUB_MONO;
						tuner->rxsubchans =
						    V4L2_TUNER_SUB_MONO;
					}
				}
			tuner->capability |= V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;

				tuner->capability |=
				    V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;

				tuner->audmode = t->audmode;

				tuner->rangelow = radio_range[0] * 16000;
@@ -507,33 +615,23 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
			}
			break;
		}
	case VIDIOC_S_TUNER: /* Allow changing radio range and audio mode */
	case VIDIOC_S_TUNER:
		{
			struct v4l2_tuner *tuner = arg;

		CHECK_ADDR(radio_tuner,"VIDIOC_S_TUNER","radio");
		SWITCH_V4L2;

		/* To switch the audio mode, applications initialize the
		   index and audmode fields and the reserved array and
		   call the VIDIOC_S_TUNER ioctl. */
		/* rxsubchannels: V4L2_TUNER_MODE_MONO, V4L2_TUNER_MODE_STEREO,
		   V4L2_TUNER_MODE_LANG1, V4L2_TUNER_MODE_LANG2,
		   V4L2_TUNER_MODE_SAP */
			if (check_mode(t, "VIDIOC_S_TUNER") == EINVAL)
				return 0;

		if (tuner->audmode == V4L2_TUNER_MODE_MONO)
			t->audmode = V4L2_TUNER_MODE_MONO;
		else
			t->audmode = V4L2_TUNER_MODE_STEREO;
			switch_v4l2();

			if (V4L2_TUNER_RADIO == t->mode) {
				t->audmode = tuner->audmode;
				set_radio_freq(client, t->freq);
		break;
			}
	case TDA9887_SET_CONFIG: /* Nothing to do on tuner-core */
			break;
		}
	default:
		tuner_dbg("Unimplemented IOCTL 0x%08x called to tuner.\n", cmd);
		/* nothing */
		break;
	}

@@ -576,8 +674,7 @@ static struct i2c_driver driver = {
		   .resume = tuner_resume,
		   },
};
static struct i2c_client client_template =
{
static struct i2c_client client_template = {
	I2C_DEVNAME("(tuner unset)"),
	.flags = I2C_CLIENT_ALLOW_USE,
	.driver = &driver,
Loading