146a60cfeSFabio Belavenuto /*
246a60cfeSFabio Belavenuto  * driver/media/radio/radio-tea5764.c
346a60cfeSFabio Belavenuto  *
446a60cfeSFabio Belavenuto  * Driver for TEA5764 radio chip for linux 2.6.
546a60cfeSFabio Belavenuto  * This driver is for TEA5764 chip from NXP, used in EZX phones from Motorola.
646a60cfeSFabio Belavenuto  * The I2C protocol is used for communicate with chip.
746a60cfeSFabio Belavenuto  *
846a60cfeSFabio Belavenuto  * Based in radio-tea5761.c Copyright (C) 2005 Nokia Corporation
946a60cfeSFabio Belavenuto  *
1046a60cfeSFabio Belavenuto  *  Copyright (c) 2008 Fabio Belavenuto <belavenuto@gmail.com>
1146a60cfeSFabio Belavenuto  *
1246a60cfeSFabio Belavenuto  * This program is free software; you can redistribute it and/or modify
1346a60cfeSFabio Belavenuto  * it under the terms of the GNU General Public License as published by
1446a60cfeSFabio Belavenuto  * the Free Software Foundation; either version 2 of the License, or
1546a60cfeSFabio Belavenuto  * (at your option) any later version.
1646a60cfeSFabio Belavenuto  *
1746a60cfeSFabio Belavenuto  * This program is distributed in the hope that it will be useful,
1846a60cfeSFabio Belavenuto  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1946a60cfeSFabio Belavenuto  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
2046a60cfeSFabio Belavenuto  * GNU General Public License for more details.
2146a60cfeSFabio Belavenuto  *
2246a60cfeSFabio Belavenuto  * You should have received a copy of the GNU General Public License
2346a60cfeSFabio Belavenuto  * along with this program; if not, write to the Free Software
2446a60cfeSFabio Belavenuto  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2546a60cfeSFabio Belavenuto  *
2646a60cfeSFabio Belavenuto  * History:
2746a60cfeSFabio Belavenuto  * 2008-12-06   Fabio Belavenuto <belavenuto@gmail.com>
2846a60cfeSFabio Belavenuto  *              initial code
2946a60cfeSFabio Belavenuto  *
3046a60cfeSFabio Belavenuto  * TODO:
3146a60cfeSFabio Belavenuto  *  add platform_data support for IRQs platform dependencies
3246a60cfeSFabio Belavenuto  *  add RDS support
3346a60cfeSFabio Belavenuto  */
3446a60cfeSFabio Belavenuto #include <linux/kernel.h>
355a0e3ad6STejun Heo #include <linux/slab.h>
3646a60cfeSFabio Belavenuto #include <linux/module.h>
3746a60cfeSFabio Belavenuto #include <linux/init.h>			/* Initdata			*/
3846a60cfeSFabio Belavenuto #include <linux/videodev2.h>		/* kernel radio structs		*/
3946a60cfeSFabio Belavenuto #include <linux/i2c.h>			/* I2C				*/
4046a60cfeSFabio Belavenuto #include <media/v4l2-common.h>
4146a60cfeSFabio Belavenuto #include <media/v4l2-ioctl.h>
42099f88eeSHans Verkuil #include <media/v4l2-device.h>
4316b37178SHans Verkuil #include <media/v4l2-ctrls.h>
44090fdf6aSHans Verkuil #include <media/v4l2-event.h>
4546a60cfeSFabio Belavenuto 
4629834c1aSMauro Carvalho Chehab #define DRIVER_VERSION	"0.0.2"
4746a60cfeSFabio Belavenuto 
4846a60cfeSFabio Belavenuto #define DRIVER_AUTHOR	"Fabio Belavenuto <belavenuto@gmail.com>"
4946a60cfeSFabio Belavenuto #define DRIVER_DESC	"A driver for the TEA5764 radio chip for EZX Phones."
5046a60cfeSFabio Belavenuto 
5146a60cfeSFabio Belavenuto #define PINFO(format, ...)\
5246a60cfeSFabio Belavenuto 	printk(KERN_INFO KBUILD_MODNAME ": "\
5346a60cfeSFabio Belavenuto 		DRIVER_VERSION ": " format "\n", ## __VA_ARGS__)
5446a60cfeSFabio Belavenuto #define PWARN(format, ...)\
5546a60cfeSFabio Belavenuto 	printk(KERN_WARNING KBUILD_MODNAME ": "\
5646a60cfeSFabio Belavenuto 		DRIVER_VERSION ": " format "\n", ## __VA_ARGS__)
5746a60cfeSFabio Belavenuto # define PDEBUG(format, ...)\
5846a60cfeSFabio Belavenuto 	printk(KERN_DEBUG KBUILD_MODNAME ": "\
5946a60cfeSFabio Belavenuto 		DRIVER_VERSION ": " format "\n", ## __VA_ARGS__)
6046a60cfeSFabio Belavenuto 
6146a60cfeSFabio Belavenuto /* Frequency limits in MHz -- these are European values.  For Japanese
6246a60cfeSFabio Belavenuto devices, that would be 76000 and 91000.  */
63b7300892SHans Verkuil #define FREQ_MIN  87500U
64b7300892SHans Verkuil #define FREQ_MAX 108000U
6546a60cfeSFabio Belavenuto #define FREQ_MUL 16
6646a60cfeSFabio Belavenuto 
6746a60cfeSFabio Belavenuto /* TEA5764 registers */
6846a60cfeSFabio Belavenuto #define TEA5764_MANID		0x002b
6946a60cfeSFabio Belavenuto #define TEA5764_CHIPID		0x5764
7046a60cfeSFabio Belavenuto 
7146a60cfeSFabio Belavenuto #define TEA5764_INTREG_BLMSK	0x0001
7246a60cfeSFabio Belavenuto #define TEA5764_INTREG_FRRMSK	0x0002
7346a60cfeSFabio Belavenuto #define TEA5764_INTREG_LEVMSK	0x0008
7446a60cfeSFabio Belavenuto #define TEA5764_INTREG_IFMSK	0x0010
7546a60cfeSFabio Belavenuto #define TEA5764_INTREG_BLMFLAG	0x0100
7646a60cfeSFabio Belavenuto #define TEA5764_INTREG_FRRFLAG	0x0200
7746a60cfeSFabio Belavenuto #define TEA5764_INTREG_LEVFLAG	0x0800
7846a60cfeSFabio Belavenuto #define TEA5764_INTREG_IFFLAG	0x1000
7946a60cfeSFabio Belavenuto 
8046a60cfeSFabio Belavenuto #define TEA5764_FRQSET_SUD	0x8000
8146a60cfeSFabio Belavenuto #define TEA5764_FRQSET_SM	0x4000
8246a60cfeSFabio Belavenuto 
8346a60cfeSFabio Belavenuto #define TEA5764_TNCTRL_PUPD1	0x8000
8446a60cfeSFabio Belavenuto #define TEA5764_TNCTRL_PUPD0	0x4000
8546a60cfeSFabio Belavenuto #define TEA5764_TNCTRL_BLIM	0x2000
8646a60cfeSFabio Belavenuto #define TEA5764_TNCTRL_SWPM	0x1000
8746a60cfeSFabio Belavenuto #define TEA5764_TNCTRL_IFCTC	0x0800
8846a60cfeSFabio Belavenuto #define TEA5764_TNCTRL_AFM	0x0400
8946a60cfeSFabio Belavenuto #define TEA5764_TNCTRL_SMUTE	0x0200
9046a60cfeSFabio Belavenuto #define TEA5764_TNCTRL_SNC	0x0100
9146a60cfeSFabio Belavenuto #define TEA5764_TNCTRL_MU	0x0080
9246a60cfeSFabio Belavenuto #define TEA5764_TNCTRL_SSL1	0x0040
9346a60cfeSFabio Belavenuto #define TEA5764_TNCTRL_SSL0	0x0020
9446a60cfeSFabio Belavenuto #define TEA5764_TNCTRL_HLSI	0x0010
9546a60cfeSFabio Belavenuto #define TEA5764_TNCTRL_MST	0x0008
9646a60cfeSFabio Belavenuto #define TEA5764_TNCTRL_SWP	0x0004
9746a60cfeSFabio Belavenuto #define TEA5764_TNCTRL_DTC	0x0002
9846a60cfeSFabio Belavenuto #define TEA5764_TNCTRL_AHLSI	0x0001
9946a60cfeSFabio Belavenuto 
10046a60cfeSFabio Belavenuto #define TEA5764_TUNCHK_LEVEL(x)	(((x) & 0x00F0) >> 4)
10146a60cfeSFabio Belavenuto #define TEA5764_TUNCHK_IFCNT(x) (((x) & 0xFE00) >> 9)
10246a60cfeSFabio Belavenuto #define TEA5764_TUNCHK_TUNTO	0x0100
10346a60cfeSFabio Belavenuto #define TEA5764_TUNCHK_LD	0x0008
10446a60cfeSFabio Belavenuto #define TEA5764_TUNCHK_STEREO	0x0004
10546a60cfeSFabio Belavenuto 
10646a60cfeSFabio Belavenuto #define TEA5764_TESTREG_TRIGFR	0x0800
10746a60cfeSFabio Belavenuto 
10846a60cfeSFabio Belavenuto struct tea5764_regs {
10946a60cfeSFabio Belavenuto 	u16 intreg;				/* INTFLAG & INTMSK */
11046a60cfeSFabio Belavenuto 	u16 frqset;				/* FRQSETMSB & FRQSETLSB */
11146a60cfeSFabio Belavenuto 	u16 tnctrl;				/* TNCTRL1 & TNCTRL2 */
11246a60cfeSFabio Belavenuto 	u16 frqchk;				/* FRQCHKMSB & FRQCHKLSB */
11346a60cfeSFabio Belavenuto 	u16 tunchk;				/* IFCHK & LEVCHK */
11446a60cfeSFabio Belavenuto 	u16 testreg;				/* TESTBITS & TESTMODE */
11546a60cfeSFabio Belavenuto 	u16 rdsstat;				/* RDSSTAT1 & RDSSTAT2 */
11646a60cfeSFabio Belavenuto 	u16 rdslb;				/* RDSLBMSB & RDSLBLSB */
11746a60cfeSFabio Belavenuto 	u16 rdspb;				/* RDSPBMSB & RDSPBLSB */
11846a60cfeSFabio Belavenuto 	u16 rdsbc;				/* RDSBBC & RDSGBC */
11946a60cfeSFabio Belavenuto 	u16 rdsctrl;				/* RDSCTRL1 & RDSCTRL2 */
12046a60cfeSFabio Belavenuto 	u16 rdsbbl;				/* PAUSEDET & RDSBBL */
12146a60cfeSFabio Belavenuto 	u16 manid;				/* MANID1 & MANID2 */
12246a60cfeSFabio Belavenuto 	u16 chipid;				/* CHIPID1 & CHIPID2 */
12346a60cfeSFabio Belavenuto } __attribute__ ((packed));
12446a60cfeSFabio Belavenuto 
12546a60cfeSFabio Belavenuto struct tea5764_write_regs {
12646a60cfeSFabio Belavenuto 	u8 intreg;				/* INTMSK */
12746a60cfeSFabio Belavenuto 	u16 frqset;				/* FRQSETMSB & FRQSETLSB */
12846a60cfeSFabio Belavenuto 	u16 tnctrl;				/* TNCTRL1 & TNCTRL2 */
12946a60cfeSFabio Belavenuto 	u16 testreg;				/* TESTBITS & TESTMODE */
13046a60cfeSFabio Belavenuto 	u16 rdsctrl;				/* RDSCTRL1 & RDSCTRL2 */
13146a60cfeSFabio Belavenuto 	u16 rdsbbl;				/* PAUSEDET & RDSBBL */
13246a60cfeSFabio Belavenuto } __attribute__ ((packed));
13346a60cfeSFabio Belavenuto 
1348c4343e5SPaul Bolle #ifdef CONFIG_RADIO_TEA5764_XTAL
13546a60cfeSFabio Belavenuto #define RADIO_TEA5764_XTAL 1
1368c4343e5SPaul Bolle #else
1378c4343e5SPaul Bolle #define RADIO_TEA5764_XTAL 0
13846a60cfeSFabio Belavenuto #endif
13946a60cfeSFabio Belavenuto 
14046a60cfeSFabio Belavenuto static int radio_nr = -1;
14146a60cfeSFabio Belavenuto static int use_xtal = RADIO_TEA5764_XTAL;
14246a60cfeSFabio Belavenuto 
14346a60cfeSFabio Belavenuto struct tea5764_device {
144099f88eeSHans Verkuil 	struct v4l2_device		v4l2_dev;
14516b37178SHans Verkuil 	struct v4l2_ctrl_handler	ctrl_handler;
14646a60cfeSFabio Belavenuto 	struct i2c_client		*i2c_client;
147cf9033f9SHans Verkuil 	struct video_device		vdev;
14846a60cfeSFabio Belavenuto 	struct tea5764_regs		regs;
14946a60cfeSFabio Belavenuto 	struct mutex			mutex;
15046a60cfeSFabio Belavenuto };
15146a60cfeSFabio Belavenuto 
15246a60cfeSFabio Belavenuto /* I2C code related */
1530dec8688SMauro Carvalho Chehab static int tea5764_i2c_read(struct tea5764_device *radio)
15446a60cfeSFabio Belavenuto {
15546a60cfeSFabio Belavenuto 	int i;
15646a60cfeSFabio Belavenuto 	u16 *p = (u16 *) &radio->regs;
15746a60cfeSFabio Belavenuto 
15846a60cfeSFabio Belavenuto 	struct i2c_msg msgs[1] = {
15966ba9590SShubhrajyoti D 		{	.addr = radio->i2c_client->addr,
16066ba9590SShubhrajyoti D 			.flags = I2C_M_RD,
16166ba9590SShubhrajyoti D 			.len = sizeof(radio->regs),
16266ba9590SShubhrajyoti D 			.buf = (void *)&radio->regs
16366ba9590SShubhrajyoti D 		},
16446a60cfeSFabio Belavenuto 	};
16546a60cfeSFabio Belavenuto 	if (i2c_transfer(radio->i2c_client->adapter, msgs, 1) != 1)
16646a60cfeSFabio Belavenuto 		return -EIO;
16746a60cfeSFabio Belavenuto 	for (i = 0; i < sizeof(struct tea5764_regs) / sizeof(u16); i++)
16846a60cfeSFabio Belavenuto 		p[i] = __be16_to_cpu(p[i]);
16946a60cfeSFabio Belavenuto 
17046a60cfeSFabio Belavenuto 	return 0;
17146a60cfeSFabio Belavenuto }
17246a60cfeSFabio Belavenuto 
1730dec8688SMauro Carvalho Chehab static int tea5764_i2c_write(struct tea5764_device *radio)
17446a60cfeSFabio Belavenuto {
17546a60cfeSFabio Belavenuto 	struct tea5764_write_regs wr;
17646a60cfeSFabio Belavenuto 	struct tea5764_regs *r = &radio->regs;
17746a60cfeSFabio Belavenuto 	struct i2c_msg msgs[1] = {
17866ba9590SShubhrajyoti D 		{
17966ba9590SShubhrajyoti D 			.addr = radio->i2c_client->addr,
18066ba9590SShubhrajyoti D 			.len = sizeof(wr),
18166ba9590SShubhrajyoti D 			.buf = (void *)&wr
18266ba9590SShubhrajyoti D 		},
18346a60cfeSFabio Belavenuto 	};
18446a60cfeSFabio Belavenuto 	wr.intreg  = r->intreg & 0xff;
18546a60cfeSFabio Belavenuto 	wr.frqset  = __cpu_to_be16(r->frqset);
18646a60cfeSFabio Belavenuto 	wr.tnctrl  = __cpu_to_be16(r->tnctrl);
18746a60cfeSFabio Belavenuto 	wr.testreg = __cpu_to_be16(r->testreg);
18846a60cfeSFabio Belavenuto 	wr.rdsctrl = __cpu_to_be16(r->rdsctrl);
18946a60cfeSFabio Belavenuto 	wr.rdsbbl  = __cpu_to_be16(r->rdsbbl);
19046a60cfeSFabio Belavenuto 	if (i2c_transfer(radio->i2c_client->adapter, msgs, 1) != 1)
19146a60cfeSFabio Belavenuto 		return -EIO;
19246a60cfeSFabio Belavenuto 	return 0;
19346a60cfeSFabio Belavenuto }
19446a60cfeSFabio Belavenuto 
19546a60cfeSFabio Belavenuto static void tea5764_power_up(struct tea5764_device *radio)
19646a60cfeSFabio Belavenuto {
19746a60cfeSFabio Belavenuto 	struct tea5764_regs *r = &radio->regs;
19846a60cfeSFabio Belavenuto 
19946a60cfeSFabio Belavenuto 	if (!(r->tnctrl & TEA5764_TNCTRL_PUPD0)) {
20046a60cfeSFabio Belavenuto 		r->tnctrl &= ~(TEA5764_TNCTRL_AFM | TEA5764_TNCTRL_MU |
20146a60cfeSFabio Belavenuto 			       TEA5764_TNCTRL_HLSI);
20246a60cfeSFabio Belavenuto 		if (!use_xtal)
20346a60cfeSFabio Belavenuto 			r->testreg |= TEA5764_TESTREG_TRIGFR;
20446a60cfeSFabio Belavenuto 		else
20546a60cfeSFabio Belavenuto 			r->testreg &= ~TEA5764_TESTREG_TRIGFR;
20646a60cfeSFabio Belavenuto 
20746a60cfeSFabio Belavenuto 		r->tnctrl |= TEA5764_TNCTRL_PUPD0;
20846a60cfeSFabio Belavenuto 		tea5764_i2c_write(radio);
20946a60cfeSFabio Belavenuto 	}
21046a60cfeSFabio Belavenuto }
21146a60cfeSFabio Belavenuto 
21246a60cfeSFabio Belavenuto static void tea5764_power_down(struct tea5764_device *radio)
21346a60cfeSFabio Belavenuto {
21446a60cfeSFabio Belavenuto 	struct tea5764_regs *r = &radio->regs;
21546a60cfeSFabio Belavenuto 
21646a60cfeSFabio Belavenuto 	if (r->tnctrl & TEA5764_TNCTRL_PUPD0) {
21746a60cfeSFabio Belavenuto 		r->tnctrl &= ~TEA5764_TNCTRL_PUPD0;
21846a60cfeSFabio Belavenuto 		tea5764_i2c_write(radio);
21946a60cfeSFabio Belavenuto 	}
22046a60cfeSFabio Belavenuto }
22146a60cfeSFabio Belavenuto 
22246a60cfeSFabio Belavenuto static void tea5764_set_freq(struct tea5764_device *radio, int freq)
22346a60cfeSFabio Belavenuto {
22446a60cfeSFabio Belavenuto 	struct tea5764_regs *r = &radio->regs;
22546a60cfeSFabio Belavenuto 
22646a60cfeSFabio Belavenuto 	/* formula: (freq [+ or -] 225000) / 8192 */
22746a60cfeSFabio Belavenuto 	if (r->tnctrl & TEA5764_TNCTRL_HLSI)
22846a60cfeSFabio Belavenuto 		r->frqset = (freq + 225000) / 8192;
22946a60cfeSFabio Belavenuto 	else
23046a60cfeSFabio Belavenuto 		r->frqset = (freq - 225000) / 8192;
23146a60cfeSFabio Belavenuto }
23246a60cfeSFabio Belavenuto 
23346a60cfeSFabio Belavenuto static int tea5764_get_freq(struct tea5764_device *radio)
23446a60cfeSFabio Belavenuto {
23546a60cfeSFabio Belavenuto 	struct tea5764_regs *r = &radio->regs;
23646a60cfeSFabio Belavenuto 
23746a60cfeSFabio Belavenuto 	if (r->tnctrl & TEA5764_TNCTRL_HLSI)
23846a60cfeSFabio Belavenuto 		return (r->frqchk * 8192) - 225000;
23946a60cfeSFabio Belavenuto 	else
24046a60cfeSFabio Belavenuto 		return (r->frqchk * 8192) + 225000;
24146a60cfeSFabio Belavenuto }
24246a60cfeSFabio Belavenuto 
24346a60cfeSFabio Belavenuto /* tune an frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */
24446a60cfeSFabio Belavenuto static void tea5764_tune(struct tea5764_device *radio, int freq)
24546a60cfeSFabio Belavenuto {
24646a60cfeSFabio Belavenuto 	tea5764_set_freq(radio, freq);
24746a60cfeSFabio Belavenuto 	if (tea5764_i2c_write(radio))
24846a60cfeSFabio Belavenuto 		PWARN("Could not set frequency!");
24946a60cfeSFabio Belavenuto }
25046a60cfeSFabio Belavenuto 
25146a60cfeSFabio Belavenuto static void tea5764_set_audout_mode(struct tea5764_device *radio, int audmode)
25246a60cfeSFabio Belavenuto {
25346a60cfeSFabio Belavenuto 	struct tea5764_regs *r = &radio->regs;
25446a60cfeSFabio Belavenuto 	int tnctrl = r->tnctrl;
25546a60cfeSFabio Belavenuto 
25646a60cfeSFabio Belavenuto 	if (audmode == V4L2_TUNER_MODE_MONO)
25746a60cfeSFabio Belavenuto 		r->tnctrl |= TEA5764_TNCTRL_MST;
25846a60cfeSFabio Belavenuto 	else
25946a60cfeSFabio Belavenuto 		r->tnctrl &= ~TEA5764_TNCTRL_MST;
26046a60cfeSFabio Belavenuto 	if (tnctrl != r->tnctrl)
26146a60cfeSFabio Belavenuto 		tea5764_i2c_write(radio);
26246a60cfeSFabio Belavenuto }
26346a60cfeSFabio Belavenuto 
26446a60cfeSFabio Belavenuto static int tea5764_get_audout_mode(struct tea5764_device *radio)
26546a60cfeSFabio Belavenuto {
26646a60cfeSFabio Belavenuto 	struct tea5764_regs *r = &radio->regs;
26746a60cfeSFabio Belavenuto 
26846a60cfeSFabio Belavenuto 	if (r->tnctrl & TEA5764_TNCTRL_MST)
26946a60cfeSFabio Belavenuto 		return V4L2_TUNER_MODE_MONO;
27046a60cfeSFabio Belavenuto 	else
27146a60cfeSFabio Belavenuto 		return V4L2_TUNER_MODE_STEREO;
27246a60cfeSFabio Belavenuto }
27346a60cfeSFabio Belavenuto 
27446a60cfeSFabio Belavenuto static void tea5764_mute(struct tea5764_device *radio, int on)
27546a60cfeSFabio Belavenuto {
27646a60cfeSFabio Belavenuto 	struct tea5764_regs *r = &radio->regs;
27746a60cfeSFabio Belavenuto 	int tnctrl = r->tnctrl;
27846a60cfeSFabio Belavenuto 
27946a60cfeSFabio Belavenuto 	if (on)
28046a60cfeSFabio Belavenuto 		r->tnctrl |= TEA5764_TNCTRL_MU;
28146a60cfeSFabio Belavenuto 	else
28246a60cfeSFabio Belavenuto 		r->tnctrl &= ~TEA5764_TNCTRL_MU;
28346a60cfeSFabio Belavenuto 	if (tnctrl != r->tnctrl)
28446a60cfeSFabio Belavenuto 		tea5764_i2c_write(radio);
28546a60cfeSFabio Belavenuto }
28646a60cfeSFabio Belavenuto 
28746a60cfeSFabio Belavenuto /* V4L2 vidioc */
28846a60cfeSFabio Belavenuto static int vidioc_querycap(struct file *file, void  *priv,
28946a60cfeSFabio Belavenuto 					struct v4l2_capability *v)
29046a60cfeSFabio Belavenuto {
29146a60cfeSFabio Belavenuto 	struct tea5764_device *radio = video_drvdata(file);
292cf9033f9SHans Verkuil 	struct video_device *dev = &radio->vdev;
29346a60cfeSFabio Belavenuto 
29446a60cfeSFabio Belavenuto 	strlcpy(v->driver, dev->dev.driver->name, sizeof(v->driver));
29546a60cfeSFabio Belavenuto 	strlcpy(v->card, dev->name, sizeof(v->card));
296c3ef01ceSKay Sievers 	snprintf(v->bus_info, sizeof(v->bus_info),
297c3ef01ceSKay Sievers 		 "I2C:%s", dev_name(&dev->dev));
298cc9231f8SHans Verkuil 	v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
299cc9231f8SHans Verkuil 	v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS;
30046a60cfeSFabio Belavenuto 	return 0;
30146a60cfeSFabio Belavenuto }
30246a60cfeSFabio Belavenuto 
30346a60cfeSFabio Belavenuto static int vidioc_g_tuner(struct file *file, void *priv,
30446a60cfeSFabio Belavenuto 				struct v4l2_tuner *v)
30546a60cfeSFabio Belavenuto {
30646a60cfeSFabio Belavenuto 	struct tea5764_device *radio = video_drvdata(file);
30746a60cfeSFabio Belavenuto 	struct tea5764_regs *r = &radio->regs;
30846a60cfeSFabio Belavenuto 
30946a60cfeSFabio Belavenuto 	if (v->index > 0)
31046a60cfeSFabio Belavenuto 		return -EINVAL;
31146a60cfeSFabio Belavenuto 
312b7300892SHans Verkuil 	strlcpy(v->name, "FM", sizeof(v->name));
31346a60cfeSFabio Belavenuto 	v->type = V4L2_TUNER_RADIO;
31446a60cfeSFabio Belavenuto 	tea5764_i2c_read(radio);
31546a60cfeSFabio Belavenuto 	v->rangelow   = FREQ_MIN * FREQ_MUL;
31646a60cfeSFabio Belavenuto 	v->rangehigh  = FREQ_MAX * FREQ_MUL;
31746a60cfeSFabio Belavenuto 	v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
31846a60cfeSFabio Belavenuto 	if (r->tunchk & TEA5764_TUNCHK_STEREO)
31946a60cfeSFabio Belavenuto 		v->rxsubchans = V4L2_TUNER_SUB_STEREO;
3205543e2b4SHans Verkuil 	else
3215543e2b4SHans Verkuil 		v->rxsubchans = V4L2_TUNER_SUB_MONO;
32246a60cfeSFabio Belavenuto 	v->audmode = tea5764_get_audout_mode(radio);
32346a60cfeSFabio Belavenuto 	v->signal = TEA5764_TUNCHK_LEVEL(r->tunchk) * 0xffff / 0xf;
32446a60cfeSFabio Belavenuto 	v->afc = TEA5764_TUNCHK_IFCNT(r->tunchk);
32546a60cfeSFabio Belavenuto 
32646a60cfeSFabio Belavenuto 	return 0;
32746a60cfeSFabio Belavenuto }
32846a60cfeSFabio Belavenuto 
32946a60cfeSFabio Belavenuto static int vidioc_s_tuner(struct file *file, void *priv,
3302f73c7c5SHans Verkuil 				const struct v4l2_tuner *v)
33146a60cfeSFabio Belavenuto {
33246a60cfeSFabio Belavenuto 	struct tea5764_device *radio = video_drvdata(file);
33346a60cfeSFabio Belavenuto 
33446a60cfeSFabio Belavenuto 	if (v->index > 0)
33546a60cfeSFabio Belavenuto 		return -EINVAL;
33646a60cfeSFabio Belavenuto 
33746a60cfeSFabio Belavenuto 	tea5764_set_audout_mode(radio, v->audmode);
33846a60cfeSFabio Belavenuto 	return 0;
33946a60cfeSFabio Belavenuto }
34046a60cfeSFabio Belavenuto 
34146a60cfeSFabio Belavenuto static int vidioc_s_frequency(struct file *file, void *priv,
342b530a447SHans Verkuil 				const struct v4l2_frequency *f)
34346a60cfeSFabio Belavenuto {
34446a60cfeSFabio Belavenuto 	struct tea5764_device *radio = video_drvdata(file);
345b7300892SHans Verkuil 	unsigned freq = f->frequency;
34646a60cfeSFabio Belavenuto 
347a3a9e287SHans Verkuil 	if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
34846a60cfeSFabio Belavenuto 		return -EINVAL;
349b7300892SHans Verkuil 	if (freq == 0) {
35046a60cfeSFabio Belavenuto 		/* We special case this as a power down control. */
35146a60cfeSFabio Belavenuto 		tea5764_power_down(radio);
352b7300892SHans Verkuil 		/* Yes, that's what is returned in this case. This
353b7300892SHans Verkuil 		   whole special case is non-compliant and should really
354b7300892SHans Verkuil 		   be replaced with something better, but changing this
355b7300892SHans Verkuil 		   might well break code that depends on this behavior.
356b7300892SHans Verkuil 		   So we keep it as-is. */
357b7300892SHans Verkuil 		return -EINVAL;
35846a60cfeSFabio Belavenuto 	}
3599ba6a91fSHans Verkuil 	freq = clamp(freq, FREQ_MIN * FREQ_MUL, FREQ_MAX * FREQ_MUL);
36046a60cfeSFabio Belavenuto 	tea5764_power_up(radio);
361b7300892SHans Verkuil 	tea5764_tune(radio, (freq * 125) / 2);
36246a60cfeSFabio Belavenuto 	return 0;
36346a60cfeSFabio Belavenuto }
36446a60cfeSFabio Belavenuto 
36546a60cfeSFabio Belavenuto static int vidioc_g_frequency(struct file *file, void *priv,
36646a60cfeSFabio Belavenuto 				struct v4l2_frequency *f)
36746a60cfeSFabio Belavenuto {
36846a60cfeSFabio Belavenuto 	struct tea5764_device *radio = video_drvdata(file);
36946a60cfeSFabio Belavenuto 	struct tea5764_regs *r = &radio->regs;
37046a60cfeSFabio Belavenuto 
371a3a9e287SHans Verkuil 	if (f->tuner != 0)
372a3a9e287SHans Verkuil 		return -EINVAL;
37346a60cfeSFabio Belavenuto 	tea5764_i2c_read(radio);
37446a60cfeSFabio Belavenuto 	f->type = V4L2_TUNER_RADIO;
37546a60cfeSFabio Belavenuto 	if (r->tnctrl & TEA5764_TNCTRL_PUPD0)
37646a60cfeSFabio Belavenuto 		f->frequency = (tea5764_get_freq(radio) * 2) / 125;
37746a60cfeSFabio Belavenuto 	else
37846a60cfeSFabio Belavenuto 		f->frequency = 0;
37946a60cfeSFabio Belavenuto 
38046a60cfeSFabio Belavenuto 	return 0;
38146a60cfeSFabio Belavenuto }
38246a60cfeSFabio Belavenuto 
38316b37178SHans Verkuil static int tea5764_s_ctrl(struct v4l2_ctrl *ctrl)
38446a60cfeSFabio Belavenuto {
38516b37178SHans Verkuil 	struct tea5764_device *radio =
38616b37178SHans Verkuil 		container_of(ctrl->handler, struct tea5764_device, ctrl_handler);
38746a60cfeSFabio Belavenuto 
38846a60cfeSFabio Belavenuto 	switch (ctrl->id) {
38946a60cfeSFabio Belavenuto 	case V4L2_CID_AUDIO_MUTE:
39016b37178SHans Verkuil 		tea5764_mute(radio, ctrl->val);
39146a60cfeSFabio Belavenuto 		return 0;
39246a60cfeSFabio Belavenuto 	}
39346a60cfeSFabio Belavenuto 	return -EINVAL;
39446a60cfeSFabio Belavenuto }
39546a60cfeSFabio Belavenuto 
39616b37178SHans Verkuil static const struct v4l2_ctrl_ops tea5764_ctrl_ops = {
39716b37178SHans Verkuil 	.s_ctrl = tea5764_s_ctrl,
39816b37178SHans Verkuil };
39916b37178SHans Verkuil 
40046a60cfeSFabio Belavenuto /* File system interface */
40146a60cfeSFabio Belavenuto static const struct v4l2_file_operations tea5764_fops = {
40246a60cfeSFabio Belavenuto 	.owner		= THIS_MODULE,
403090fdf6aSHans Verkuil 	.open		= v4l2_fh_open,
404090fdf6aSHans Verkuil 	.release	= v4l2_fh_release,
405090fdf6aSHans Verkuil 	.poll		= v4l2_ctrl_poll,
406ee71e423SHans Verkuil 	.unlocked_ioctl	= video_ioctl2,
40746a60cfeSFabio Belavenuto };
40846a60cfeSFabio Belavenuto 
40946a60cfeSFabio Belavenuto static const struct v4l2_ioctl_ops tea5764_ioctl_ops = {
41046a60cfeSFabio Belavenuto 	.vidioc_querycap    = vidioc_querycap,
41146a60cfeSFabio Belavenuto 	.vidioc_g_tuner     = vidioc_g_tuner,
41246a60cfeSFabio Belavenuto 	.vidioc_s_tuner     = vidioc_s_tuner,
41346a60cfeSFabio Belavenuto 	.vidioc_g_frequency = vidioc_g_frequency,
41446a60cfeSFabio Belavenuto 	.vidioc_s_frequency = vidioc_s_frequency,
415090fdf6aSHans Verkuil 	.vidioc_log_status  = v4l2_ctrl_log_status,
416090fdf6aSHans Verkuil 	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
417090fdf6aSHans Verkuil 	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
41846a60cfeSFabio Belavenuto };
41946a60cfeSFabio Belavenuto 
42046a60cfeSFabio Belavenuto /* V4L2 interface */
42146a60cfeSFabio Belavenuto static struct video_device tea5764_radio_template = {
42246a60cfeSFabio Belavenuto 	.name		= "TEA5764 FM-Radio",
42346a60cfeSFabio Belavenuto 	.fops           = &tea5764_fops,
42446a60cfeSFabio Belavenuto 	.ioctl_ops 	= &tea5764_ioctl_ops,
425cf9033f9SHans Verkuil 	.release	= video_device_release_empty,
42646a60cfeSFabio Belavenuto };
42746a60cfeSFabio Belavenuto 
42846a60cfeSFabio Belavenuto /* I2C probe: check if the device exists and register with v4l if it is */
4294c62e976SGreg Kroah-Hartman static int tea5764_i2c_probe(struct i2c_client *client,
43046a60cfeSFabio Belavenuto 			     const struct i2c_device_id *id)
43146a60cfeSFabio Belavenuto {
43246a60cfeSFabio Belavenuto 	struct tea5764_device *radio;
433099f88eeSHans Verkuil 	struct v4l2_device *v4l2_dev;
43416b37178SHans Verkuil 	struct v4l2_ctrl_handler *hdl;
43546a60cfeSFabio Belavenuto 	struct tea5764_regs *r;
43646a60cfeSFabio Belavenuto 	int ret;
43746a60cfeSFabio Belavenuto 
43846a60cfeSFabio Belavenuto 	PDEBUG("probe");
439ee71e423SHans Verkuil 	radio = kzalloc(sizeof(struct tea5764_device), GFP_KERNEL);
44046a60cfeSFabio Belavenuto 	if (!radio)
44146a60cfeSFabio Belavenuto 		return -ENOMEM;
44246a60cfeSFabio Belavenuto 
443099f88eeSHans Verkuil 	v4l2_dev = &radio->v4l2_dev;
444099f88eeSHans Verkuil 	ret = v4l2_device_register(&client->dev, v4l2_dev);
445099f88eeSHans Verkuil 	if (ret < 0) {
446099f88eeSHans Verkuil 		v4l2_err(v4l2_dev, "could not register v4l2_device\n");
447099f88eeSHans Verkuil 		goto errfr;
448099f88eeSHans Verkuil 	}
44916b37178SHans Verkuil 
45016b37178SHans Verkuil 	hdl = &radio->ctrl_handler;
45116b37178SHans Verkuil 	v4l2_ctrl_handler_init(hdl, 1);
45216b37178SHans Verkuil 	v4l2_ctrl_new_std(hdl, &tea5764_ctrl_ops,
45316b37178SHans Verkuil 			V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
45416b37178SHans Verkuil 	v4l2_dev->ctrl_handler = hdl;
45516b37178SHans Verkuil 	if (hdl->error) {
45616b37178SHans Verkuil 		ret = hdl->error;
45716b37178SHans Verkuil 		v4l2_err(v4l2_dev, "Could not register controls\n");
45816b37178SHans Verkuil 		goto errunreg;
45916b37178SHans Verkuil 	}
46016b37178SHans Verkuil 
46146a60cfeSFabio Belavenuto 	mutex_init(&radio->mutex);
46246a60cfeSFabio Belavenuto 	radio->i2c_client = client;
46346a60cfeSFabio Belavenuto 	ret = tea5764_i2c_read(radio);
46446a60cfeSFabio Belavenuto 	if (ret)
465099f88eeSHans Verkuil 		goto errunreg;
46646a60cfeSFabio Belavenuto 	r = &radio->regs;
46746a60cfeSFabio Belavenuto 	PDEBUG("chipid = %04X, manid = %04X", r->chipid, r->manid);
46846a60cfeSFabio Belavenuto 	if (r->chipid != TEA5764_CHIPID ||
46946a60cfeSFabio Belavenuto 		(r->manid & 0x0fff) != TEA5764_MANID) {
47046a60cfeSFabio Belavenuto 		PWARN("This chip is not a TEA5764!");
47146a60cfeSFabio Belavenuto 		ret = -EINVAL;
472099f88eeSHans Verkuil 		goto errunreg;
47346a60cfeSFabio Belavenuto 	}
47446a60cfeSFabio Belavenuto 
475cf9033f9SHans Verkuil 	radio->vdev = tea5764_radio_template;
47646a60cfeSFabio Belavenuto 
47746a60cfeSFabio Belavenuto 	i2c_set_clientdata(client, radio);
478cf9033f9SHans Verkuil 	video_set_drvdata(&radio->vdev, radio);
479cf9033f9SHans Verkuil 	radio->vdev.lock = &radio->mutex;
480cf9033f9SHans Verkuil 	radio->vdev.v4l2_dev = v4l2_dev;
481090fdf6aSHans Verkuil 	set_bit(V4L2_FL_USE_FH_PRIO, &radio->vdev.flags);
48246a60cfeSFabio Belavenuto 
48346a60cfeSFabio Belavenuto 	/* initialize and power off the chip */
48446a60cfeSFabio Belavenuto 	tea5764_i2c_read(radio);
48546a60cfeSFabio Belavenuto 	tea5764_set_audout_mode(radio, V4L2_TUNER_MODE_STEREO);
48646a60cfeSFabio Belavenuto 	tea5764_mute(radio, 1);
48746a60cfeSFabio Belavenuto 	tea5764_power_down(radio);
48846a60cfeSFabio Belavenuto 
489cf9033f9SHans Verkuil 	ret = video_register_device(&radio->vdev, VFL_TYPE_RADIO, radio_nr);
490ee71e423SHans Verkuil 	if (ret < 0) {
491ee71e423SHans Verkuil 		PWARN("Could not register video device!");
492cf9033f9SHans Verkuil 		goto errunreg;
493ee71e423SHans Verkuil 	}
494ee71e423SHans Verkuil 
49546a60cfeSFabio Belavenuto 	PINFO("registered.");
49646a60cfeSFabio Belavenuto 	return 0;
497099f88eeSHans Verkuil errunreg:
49816b37178SHans Verkuil 	v4l2_ctrl_handler_free(hdl);
499099f88eeSHans Verkuil 	v4l2_device_unregister(v4l2_dev);
50046a60cfeSFabio Belavenuto errfr:
50146a60cfeSFabio Belavenuto 	kfree(radio);
50246a60cfeSFabio Belavenuto 	return ret;
50346a60cfeSFabio Belavenuto }
50446a60cfeSFabio Belavenuto 
5054c62e976SGreg Kroah-Hartman static int tea5764_i2c_remove(struct i2c_client *client)
50646a60cfeSFabio Belavenuto {
50746a60cfeSFabio Belavenuto 	struct tea5764_device *radio = i2c_get_clientdata(client);
50846a60cfeSFabio Belavenuto 
50946a60cfeSFabio Belavenuto 	PDEBUG("remove");
51046a60cfeSFabio Belavenuto 	if (radio) {
51146a60cfeSFabio Belavenuto 		tea5764_power_down(radio);
512cf9033f9SHans Verkuil 		video_unregister_device(&radio->vdev);
51316b37178SHans Verkuil 		v4l2_ctrl_handler_free(&radio->ctrl_handler);
514099f88eeSHans Verkuil 		v4l2_device_unregister(&radio->v4l2_dev);
51546a60cfeSFabio Belavenuto 		kfree(radio);
51646a60cfeSFabio Belavenuto 	}
51746a60cfeSFabio Belavenuto 	return 0;
51846a60cfeSFabio Belavenuto }
51946a60cfeSFabio Belavenuto 
52046a60cfeSFabio Belavenuto /* I2C subsystem interface */
52146a60cfeSFabio Belavenuto static const struct i2c_device_id tea5764_id[] = {
52246a60cfeSFabio Belavenuto 	{ "radio-tea5764", 0 },
52346a60cfeSFabio Belavenuto 	{ }					/* Terminating entry */
52446a60cfeSFabio Belavenuto };
52546a60cfeSFabio Belavenuto MODULE_DEVICE_TABLE(i2c, tea5764_id);
52646a60cfeSFabio Belavenuto 
52746a60cfeSFabio Belavenuto static struct i2c_driver tea5764_i2c_driver = {
52846a60cfeSFabio Belavenuto 	.driver = {
52946a60cfeSFabio Belavenuto 		.name = "radio-tea5764",
53046a60cfeSFabio Belavenuto 		.owner = THIS_MODULE,
53146a60cfeSFabio Belavenuto 	},
53246a60cfeSFabio Belavenuto 	.probe = tea5764_i2c_probe,
5334c62e976SGreg Kroah-Hartman 	.remove = tea5764_i2c_remove,
53446a60cfeSFabio Belavenuto 	.id_table = tea5764_id,
53546a60cfeSFabio Belavenuto };
53646a60cfeSFabio Belavenuto 
537c6e8d86fSAxel Lin module_i2c_driver(tea5764_i2c_driver);
53846a60cfeSFabio Belavenuto 
53946a60cfeSFabio Belavenuto MODULE_AUTHOR(DRIVER_AUTHOR);
54046a60cfeSFabio Belavenuto MODULE_DESCRIPTION(DRIVER_DESC);
54146a60cfeSFabio Belavenuto MODULE_LICENSE("GPL");
54229834c1aSMauro Carvalho Chehab MODULE_VERSION(DRIVER_VERSION);
54346a60cfeSFabio Belavenuto 
544731884baSJean Delvare module_param(use_xtal, int, 0);
54546a60cfeSFabio Belavenuto MODULE_PARM_DESC(use_xtal, "Chip have a xtal connected in board");
54646a60cfeSFabio Belavenuto module_param(radio_nr, int, 0);
54746a60cfeSFabio Belavenuto MODULE_PARM_DESC(radio_nr, "video4linux device number to use");
548