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