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> 4246a60cfeSFabio Belavenuto #include <linux/version.h> /* for KERNEL_VERSION MACRO */ 4346a60cfeSFabio Belavenuto 4446a60cfeSFabio Belavenuto #define DRIVER_VERSION "v0.01" 4546a60cfeSFabio Belavenuto #define RADIO_VERSION KERNEL_VERSION(0, 0, 1) 4646a60cfeSFabio Belavenuto 4746a60cfeSFabio Belavenuto #define DRIVER_AUTHOR "Fabio Belavenuto <belavenuto@gmail.com>" 4846a60cfeSFabio Belavenuto #define DRIVER_DESC "A driver for the TEA5764 radio chip for EZX Phones." 4946a60cfeSFabio Belavenuto 5046a60cfeSFabio Belavenuto #define PINFO(format, ...)\ 5146a60cfeSFabio Belavenuto printk(KERN_INFO KBUILD_MODNAME ": "\ 5246a60cfeSFabio Belavenuto DRIVER_VERSION ": " format "\n", ## __VA_ARGS__) 5346a60cfeSFabio Belavenuto #define PWARN(format, ...)\ 5446a60cfeSFabio Belavenuto printk(KERN_WARNING KBUILD_MODNAME ": "\ 5546a60cfeSFabio Belavenuto DRIVER_VERSION ": " format "\n", ## __VA_ARGS__) 5646a60cfeSFabio Belavenuto # define PDEBUG(format, ...)\ 5746a60cfeSFabio Belavenuto printk(KERN_DEBUG KBUILD_MODNAME ": "\ 5846a60cfeSFabio Belavenuto DRIVER_VERSION ": " format "\n", ## __VA_ARGS__) 5946a60cfeSFabio Belavenuto 6046a60cfeSFabio Belavenuto /* Frequency limits in MHz -- these are European values. For Japanese 6146a60cfeSFabio Belavenuto devices, that would be 76000 and 91000. */ 6246a60cfeSFabio Belavenuto #define FREQ_MIN 87500 6346a60cfeSFabio Belavenuto #define FREQ_MAX 108000 6446a60cfeSFabio Belavenuto #define FREQ_MUL 16 6546a60cfeSFabio Belavenuto 6646a60cfeSFabio Belavenuto /* TEA5764 registers */ 6746a60cfeSFabio Belavenuto #define TEA5764_MANID 0x002b 6846a60cfeSFabio Belavenuto #define TEA5764_CHIPID 0x5764 6946a60cfeSFabio Belavenuto 7046a60cfeSFabio Belavenuto #define TEA5764_INTREG_BLMSK 0x0001 7146a60cfeSFabio Belavenuto #define TEA5764_INTREG_FRRMSK 0x0002 7246a60cfeSFabio Belavenuto #define TEA5764_INTREG_LEVMSK 0x0008 7346a60cfeSFabio Belavenuto #define TEA5764_INTREG_IFMSK 0x0010 7446a60cfeSFabio Belavenuto #define TEA5764_INTREG_BLMFLAG 0x0100 7546a60cfeSFabio Belavenuto #define TEA5764_INTREG_FRRFLAG 0x0200 7646a60cfeSFabio Belavenuto #define TEA5764_INTREG_LEVFLAG 0x0800 7746a60cfeSFabio Belavenuto #define TEA5764_INTREG_IFFLAG 0x1000 7846a60cfeSFabio Belavenuto 7946a60cfeSFabio Belavenuto #define TEA5764_FRQSET_SUD 0x8000 8046a60cfeSFabio Belavenuto #define TEA5764_FRQSET_SM 0x4000 8146a60cfeSFabio Belavenuto 8246a60cfeSFabio Belavenuto #define TEA5764_TNCTRL_PUPD1 0x8000 8346a60cfeSFabio Belavenuto #define TEA5764_TNCTRL_PUPD0 0x4000 8446a60cfeSFabio Belavenuto #define TEA5764_TNCTRL_BLIM 0x2000 8546a60cfeSFabio Belavenuto #define TEA5764_TNCTRL_SWPM 0x1000 8646a60cfeSFabio Belavenuto #define TEA5764_TNCTRL_IFCTC 0x0800 8746a60cfeSFabio Belavenuto #define TEA5764_TNCTRL_AFM 0x0400 8846a60cfeSFabio Belavenuto #define TEA5764_TNCTRL_SMUTE 0x0200 8946a60cfeSFabio Belavenuto #define TEA5764_TNCTRL_SNC 0x0100 9046a60cfeSFabio Belavenuto #define TEA5764_TNCTRL_MU 0x0080 9146a60cfeSFabio Belavenuto #define TEA5764_TNCTRL_SSL1 0x0040 9246a60cfeSFabio Belavenuto #define TEA5764_TNCTRL_SSL0 0x0020 9346a60cfeSFabio Belavenuto #define TEA5764_TNCTRL_HLSI 0x0010 9446a60cfeSFabio Belavenuto #define TEA5764_TNCTRL_MST 0x0008 9546a60cfeSFabio Belavenuto #define TEA5764_TNCTRL_SWP 0x0004 9646a60cfeSFabio Belavenuto #define TEA5764_TNCTRL_DTC 0x0002 9746a60cfeSFabio Belavenuto #define TEA5764_TNCTRL_AHLSI 0x0001 9846a60cfeSFabio Belavenuto 9946a60cfeSFabio Belavenuto #define TEA5764_TUNCHK_LEVEL(x) (((x) & 0x00F0) >> 4) 10046a60cfeSFabio Belavenuto #define TEA5764_TUNCHK_IFCNT(x) (((x) & 0xFE00) >> 9) 10146a60cfeSFabio Belavenuto #define TEA5764_TUNCHK_TUNTO 0x0100 10246a60cfeSFabio Belavenuto #define TEA5764_TUNCHK_LD 0x0008 10346a60cfeSFabio Belavenuto #define TEA5764_TUNCHK_STEREO 0x0004 10446a60cfeSFabio Belavenuto 10546a60cfeSFabio Belavenuto #define TEA5764_TESTREG_TRIGFR 0x0800 10646a60cfeSFabio Belavenuto 10746a60cfeSFabio Belavenuto struct tea5764_regs { 10846a60cfeSFabio Belavenuto u16 intreg; /* INTFLAG & INTMSK */ 10946a60cfeSFabio Belavenuto u16 frqset; /* FRQSETMSB & FRQSETLSB */ 11046a60cfeSFabio Belavenuto u16 tnctrl; /* TNCTRL1 & TNCTRL2 */ 11146a60cfeSFabio Belavenuto u16 frqchk; /* FRQCHKMSB & FRQCHKLSB */ 11246a60cfeSFabio Belavenuto u16 tunchk; /* IFCHK & LEVCHK */ 11346a60cfeSFabio Belavenuto u16 testreg; /* TESTBITS & TESTMODE */ 11446a60cfeSFabio Belavenuto u16 rdsstat; /* RDSSTAT1 & RDSSTAT2 */ 11546a60cfeSFabio Belavenuto u16 rdslb; /* RDSLBMSB & RDSLBLSB */ 11646a60cfeSFabio Belavenuto u16 rdspb; /* RDSPBMSB & RDSPBLSB */ 11746a60cfeSFabio Belavenuto u16 rdsbc; /* RDSBBC & RDSGBC */ 11846a60cfeSFabio Belavenuto u16 rdsctrl; /* RDSCTRL1 & RDSCTRL2 */ 11946a60cfeSFabio Belavenuto u16 rdsbbl; /* PAUSEDET & RDSBBL */ 12046a60cfeSFabio Belavenuto u16 manid; /* MANID1 & MANID2 */ 12146a60cfeSFabio Belavenuto u16 chipid; /* CHIPID1 & CHIPID2 */ 12246a60cfeSFabio Belavenuto } __attribute__ ((packed)); 12346a60cfeSFabio Belavenuto 12446a60cfeSFabio Belavenuto struct tea5764_write_regs { 12546a60cfeSFabio Belavenuto u8 intreg; /* INTMSK */ 12646a60cfeSFabio Belavenuto u16 frqset; /* FRQSETMSB & FRQSETLSB */ 12746a60cfeSFabio Belavenuto u16 tnctrl; /* TNCTRL1 & TNCTRL2 */ 12846a60cfeSFabio Belavenuto u16 testreg; /* TESTBITS & TESTMODE */ 12946a60cfeSFabio Belavenuto u16 rdsctrl; /* RDSCTRL1 & RDSCTRL2 */ 13046a60cfeSFabio Belavenuto u16 rdsbbl; /* PAUSEDET & RDSBBL */ 13146a60cfeSFabio Belavenuto } __attribute__ ((packed)); 13246a60cfeSFabio Belavenuto 13346a60cfeSFabio Belavenuto #ifndef RADIO_TEA5764_XTAL 13446a60cfeSFabio Belavenuto #define RADIO_TEA5764_XTAL 1 13546a60cfeSFabio Belavenuto #endif 13646a60cfeSFabio Belavenuto 13746a60cfeSFabio Belavenuto static int radio_nr = -1; 13846a60cfeSFabio Belavenuto static int use_xtal = RADIO_TEA5764_XTAL; 13946a60cfeSFabio Belavenuto 14046a60cfeSFabio Belavenuto struct tea5764_device { 14146a60cfeSFabio Belavenuto struct i2c_client *i2c_client; 14246a60cfeSFabio Belavenuto struct video_device *videodev; 14346a60cfeSFabio Belavenuto struct tea5764_regs regs; 14446a60cfeSFabio Belavenuto struct mutex mutex; 14546a60cfeSFabio Belavenuto int users; 14646a60cfeSFabio Belavenuto }; 14746a60cfeSFabio Belavenuto 14846a60cfeSFabio Belavenuto /* I2C code related */ 14946a60cfeSFabio Belavenuto int tea5764_i2c_read(struct tea5764_device *radio) 15046a60cfeSFabio Belavenuto { 15146a60cfeSFabio Belavenuto int i; 15246a60cfeSFabio Belavenuto u16 *p = (u16 *) &radio->regs; 15346a60cfeSFabio Belavenuto 15446a60cfeSFabio Belavenuto struct i2c_msg msgs[1] = { 15546a60cfeSFabio Belavenuto { radio->i2c_client->addr, I2C_M_RD, sizeof(radio->regs), 15646a60cfeSFabio Belavenuto (void *)&radio->regs }, 15746a60cfeSFabio Belavenuto }; 15846a60cfeSFabio Belavenuto if (i2c_transfer(radio->i2c_client->adapter, msgs, 1) != 1) 15946a60cfeSFabio Belavenuto return -EIO; 16046a60cfeSFabio Belavenuto for (i = 0; i < sizeof(struct tea5764_regs) / sizeof(u16); i++) 16146a60cfeSFabio Belavenuto p[i] = __be16_to_cpu(p[i]); 16246a60cfeSFabio Belavenuto 16346a60cfeSFabio Belavenuto return 0; 16446a60cfeSFabio Belavenuto } 16546a60cfeSFabio Belavenuto 16646a60cfeSFabio Belavenuto int tea5764_i2c_write(struct tea5764_device *radio) 16746a60cfeSFabio Belavenuto { 16846a60cfeSFabio Belavenuto struct tea5764_write_regs wr; 16946a60cfeSFabio Belavenuto struct tea5764_regs *r = &radio->regs; 17046a60cfeSFabio Belavenuto struct i2c_msg msgs[1] = { 17146a60cfeSFabio Belavenuto { radio->i2c_client->addr, 0, sizeof(wr), (void *) &wr }, 17246a60cfeSFabio Belavenuto }; 17346a60cfeSFabio Belavenuto wr.intreg = r->intreg & 0xff; 17446a60cfeSFabio Belavenuto wr.frqset = __cpu_to_be16(r->frqset); 17546a60cfeSFabio Belavenuto wr.tnctrl = __cpu_to_be16(r->tnctrl); 17646a60cfeSFabio Belavenuto wr.testreg = __cpu_to_be16(r->testreg); 17746a60cfeSFabio Belavenuto wr.rdsctrl = __cpu_to_be16(r->rdsctrl); 17846a60cfeSFabio Belavenuto wr.rdsbbl = __cpu_to_be16(r->rdsbbl); 17946a60cfeSFabio Belavenuto if (i2c_transfer(radio->i2c_client->adapter, msgs, 1) != 1) 18046a60cfeSFabio Belavenuto return -EIO; 18146a60cfeSFabio Belavenuto return 0; 18246a60cfeSFabio Belavenuto } 18346a60cfeSFabio Belavenuto 18446a60cfeSFabio Belavenuto /* V4L2 code related */ 18546a60cfeSFabio Belavenuto static struct v4l2_queryctrl radio_qctrl[] = { 18646a60cfeSFabio Belavenuto { 18746a60cfeSFabio Belavenuto .id = V4L2_CID_AUDIO_MUTE, 18846a60cfeSFabio Belavenuto .name = "Mute", 18946a60cfeSFabio Belavenuto .minimum = 0, 19046a60cfeSFabio Belavenuto .maximum = 1, 19146a60cfeSFabio Belavenuto .default_value = 1, 19246a60cfeSFabio Belavenuto .type = V4L2_CTRL_TYPE_BOOLEAN, 19346a60cfeSFabio Belavenuto } 19446a60cfeSFabio Belavenuto }; 19546a60cfeSFabio Belavenuto 19646a60cfeSFabio Belavenuto static void tea5764_power_up(struct tea5764_device *radio) 19746a60cfeSFabio Belavenuto { 19846a60cfeSFabio Belavenuto struct tea5764_regs *r = &radio->regs; 19946a60cfeSFabio Belavenuto 20046a60cfeSFabio Belavenuto if (!(r->tnctrl & TEA5764_TNCTRL_PUPD0)) { 20146a60cfeSFabio Belavenuto r->tnctrl &= ~(TEA5764_TNCTRL_AFM | TEA5764_TNCTRL_MU | 20246a60cfeSFabio Belavenuto TEA5764_TNCTRL_HLSI); 20346a60cfeSFabio Belavenuto if (!use_xtal) 20446a60cfeSFabio Belavenuto r->testreg |= TEA5764_TESTREG_TRIGFR; 20546a60cfeSFabio Belavenuto else 20646a60cfeSFabio Belavenuto r->testreg &= ~TEA5764_TESTREG_TRIGFR; 20746a60cfeSFabio Belavenuto 20846a60cfeSFabio Belavenuto r->tnctrl |= TEA5764_TNCTRL_PUPD0; 20946a60cfeSFabio Belavenuto tea5764_i2c_write(radio); 21046a60cfeSFabio Belavenuto } 21146a60cfeSFabio Belavenuto } 21246a60cfeSFabio Belavenuto 21346a60cfeSFabio Belavenuto static void tea5764_power_down(struct tea5764_device *radio) 21446a60cfeSFabio Belavenuto { 21546a60cfeSFabio Belavenuto struct tea5764_regs *r = &radio->regs; 21646a60cfeSFabio Belavenuto 21746a60cfeSFabio Belavenuto if (r->tnctrl & TEA5764_TNCTRL_PUPD0) { 21846a60cfeSFabio Belavenuto r->tnctrl &= ~TEA5764_TNCTRL_PUPD0; 21946a60cfeSFabio Belavenuto tea5764_i2c_write(radio); 22046a60cfeSFabio Belavenuto } 22146a60cfeSFabio Belavenuto } 22246a60cfeSFabio Belavenuto 22346a60cfeSFabio Belavenuto static void tea5764_set_freq(struct tea5764_device *radio, int freq) 22446a60cfeSFabio Belavenuto { 22546a60cfeSFabio Belavenuto struct tea5764_regs *r = &radio->regs; 22646a60cfeSFabio Belavenuto 22746a60cfeSFabio Belavenuto /* formula: (freq [+ or -] 225000) / 8192 */ 22846a60cfeSFabio Belavenuto if (r->tnctrl & TEA5764_TNCTRL_HLSI) 22946a60cfeSFabio Belavenuto r->frqset = (freq + 225000) / 8192; 23046a60cfeSFabio Belavenuto else 23146a60cfeSFabio Belavenuto r->frqset = (freq - 225000) / 8192; 23246a60cfeSFabio Belavenuto } 23346a60cfeSFabio Belavenuto 23446a60cfeSFabio Belavenuto static int tea5764_get_freq(struct tea5764_device *radio) 23546a60cfeSFabio Belavenuto { 23646a60cfeSFabio Belavenuto struct tea5764_regs *r = &radio->regs; 23746a60cfeSFabio Belavenuto 23846a60cfeSFabio Belavenuto if (r->tnctrl & TEA5764_TNCTRL_HLSI) 23946a60cfeSFabio Belavenuto return (r->frqchk * 8192) - 225000; 24046a60cfeSFabio Belavenuto else 24146a60cfeSFabio Belavenuto return (r->frqchk * 8192) + 225000; 24246a60cfeSFabio Belavenuto } 24346a60cfeSFabio Belavenuto 24446a60cfeSFabio Belavenuto /* tune an frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */ 24546a60cfeSFabio Belavenuto static void tea5764_tune(struct tea5764_device *radio, int freq) 24646a60cfeSFabio Belavenuto { 24746a60cfeSFabio Belavenuto tea5764_set_freq(radio, freq); 24846a60cfeSFabio Belavenuto if (tea5764_i2c_write(radio)) 24946a60cfeSFabio Belavenuto PWARN("Could not set frequency!"); 25046a60cfeSFabio Belavenuto } 25146a60cfeSFabio Belavenuto 25246a60cfeSFabio Belavenuto static void tea5764_set_audout_mode(struct tea5764_device *radio, int audmode) 25346a60cfeSFabio Belavenuto { 25446a60cfeSFabio Belavenuto struct tea5764_regs *r = &radio->regs; 25546a60cfeSFabio Belavenuto int tnctrl = r->tnctrl; 25646a60cfeSFabio Belavenuto 25746a60cfeSFabio Belavenuto if (audmode == V4L2_TUNER_MODE_MONO) 25846a60cfeSFabio Belavenuto r->tnctrl |= TEA5764_TNCTRL_MST; 25946a60cfeSFabio Belavenuto else 26046a60cfeSFabio Belavenuto r->tnctrl &= ~TEA5764_TNCTRL_MST; 26146a60cfeSFabio Belavenuto if (tnctrl != r->tnctrl) 26246a60cfeSFabio Belavenuto tea5764_i2c_write(radio); 26346a60cfeSFabio Belavenuto } 26446a60cfeSFabio Belavenuto 26546a60cfeSFabio Belavenuto static int tea5764_get_audout_mode(struct tea5764_device *radio) 26646a60cfeSFabio Belavenuto { 26746a60cfeSFabio Belavenuto struct tea5764_regs *r = &radio->regs; 26846a60cfeSFabio Belavenuto 26946a60cfeSFabio Belavenuto if (r->tnctrl & TEA5764_TNCTRL_MST) 27046a60cfeSFabio Belavenuto return V4L2_TUNER_MODE_MONO; 27146a60cfeSFabio Belavenuto else 27246a60cfeSFabio Belavenuto return V4L2_TUNER_MODE_STEREO; 27346a60cfeSFabio Belavenuto } 27446a60cfeSFabio Belavenuto 27546a60cfeSFabio Belavenuto static void tea5764_mute(struct tea5764_device *radio, int on) 27646a60cfeSFabio Belavenuto { 27746a60cfeSFabio Belavenuto struct tea5764_regs *r = &radio->regs; 27846a60cfeSFabio Belavenuto int tnctrl = r->tnctrl; 27946a60cfeSFabio Belavenuto 28046a60cfeSFabio Belavenuto if (on) 28146a60cfeSFabio Belavenuto r->tnctrl |= TEA5764_TNCTRL_MU; 28246a60cfeSFabio Belavenuto else 28346a60cfeSFabio Belavenuto r->tnctrl &= ~TEA5764_TNCTRL_MU; 28446a60cfeSFabio Belavenuto if (tnctrl != r->tnctrl) 28546a60cfeSFabio Belavenuto tea5764_i2c_write(radio); 28646a60cfeSFabio Belavenuto } 28746a60cfeSFabio Belavenuto 28846a60cfeSFabio Belavenuto static int tea5764_is_muted(struct tea5764_device *radio) 28946a60cfeSFabio Belavenuto { 29046a60cfeSFabio Belavenuto return radio->regs.tnctrl & TEA5764_TNCTRL_MU; 29146a60cfeSFabio Belavenuto } 29246a60cfeSFabio Belavenuto 29346a60cfeSFabio Belavenuto /* V4L2 vidioc */ 29446a60cfeSFabio Belavenuto static int vidioc_querycap(struct file *file, void *priv, 29546a60cfeSFabio Belavenuto struct v4l2_capability *v) 29646a60cfeSFabio Belavenuto { 29746a60cfeSFabio Belavenuto struct tea5764_device *radio = video_drvdata(file); 29846a60cfeSFabio Belavenuto struct video_device *dev = radio->videodev; 29946a60cfeSFabio Belavenuto 30046a60cfeSFabio Belavenuto strlcpy(v->driver, dev->dev.driver->name, sizeof(v->driver)); 30146a60cfeSFabio Belavenuto strlcpy(v->card, dev->name, sizeof(v->card)); 302c3ef01ceSKay Sievers snprintf(v->bus_info, sizeof(v->bus_info), 303c3ef01ceSKay Sievers "I2C:%s", dev_name(&dev->dev)); 30446a60cfeSFabio Belavenuto v->version = RADIO_VERSION; 30546a60cfeSFabio Belavenuto v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; 30646a60cfeSFabio Belavenuto return 0; 30746a60cfeSFabio Belavenuto } 30846a60cfeSFabio Belavenuto 30946a60cfeSFabio Belavenuto static int vidioc_g_tuner(struct file *file, void *priv, 31046a60cfeSFabio Belavenuto struct v4l2_tuner *v) 31146a60cfeSFabio Belavenuto { 31246a60cfeSFabio Belavenuto struct tea5764_device *radio = video_drvdata(file); 31346a60cfeSFabio Belavenuto struct tea5764_regs *r = &radio->regs; 31446a60cfeSFabio Belavenuto 31546a60cfeSFabio Belavenuto if (v->index > 0) 31646a60cfeSFabio Belavenuto return -EINVAL; 31746a60cfeSFabio Belavenuto 318909d15a7SJulia Lawall memset(v, 0, sizeof(*v)); 31946a60cfeSFabio Belavenuto strcpy(v->name, "FM"); 32046a60cfeSFabio Belavenuto v->type = V4L2_TUNER_RADIO; 32146a60cfeSFabio Belavenuto tea5764_i2c_read(radio); 32246a60cfeSFabio Belavenuto v->rangelow = FREQ_MIN * FREQ_MUL; 32346a60cfeSFabio Belavenuto v->rangehigh = FREQ_MAX * FREQ_MUL; 32446a60cfeSFabio Belavenuto v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; 32546a60cfeSFabio Belavenuto if (r->tunchk & TEA5764_TUNCHK_STEREO) 32646a60cfeSFabio Belavenuto v->rxsubchans = V4L2_TUNER_SUB_STEREO; 3275543e2b4SHans Verkuil else 3285543e2b4SHans Verkuil v->rxsubchans = V4L2_TUNER_SUB_MONO; 32946a60cfeSFabio Belavenuto v->audmode = tea5764_get_audout_mode(radio); 33046a60cfeSFabio Belavenuto v->signal = TEA5764_TUNCHK_LEVEL(r->tunchk) * 0xffff / 0xf; 33146a60cfeSFabio Belavenuto v->afc = TEA5764_TUNCHK_IFCNT(r->tunchk); 33246a60cfeSFabio Belavenuto 33346a60cfeSFabio Belavenuto return 0; 33446a60cfeSFabio Belavenuto } 33546a60cfeSFabio Belavenuto 33646a60cfeSFabio Belavenuto static int vidioc_s_tuner(struct file *file, void *priv, 33746a60cfeSFabio Belavenuto struct v4l2_tuner *v) 33846a60cfeSFabio Belavenuto { 33946a60cfeSFabio Belavenuto struct tea5764_device *radio = video_drvdata(file); 34046a60cfeSFabio Belavenuto 34146a60cfeSFabio Belavenuto if (v->index > 0) 34246a60cfeSFabio Belavenuto return -EINVAL; 34346a60cfeSFabio Belavenuto 34446a60cfeSFabio Belavenuto tea5764_set_audout_mode(radio, v->audmode); 34546a60cfeSFabio Belavenuto return 0; 34646a60cfeSFabio Belavenuto } 34746a60cfeSFabio Belavenuto 34846a60cfeSFabio Belavenuto static int vidioc_s_frequency(struct file *file, void *priv, 34946a60cfeSFabio Belavenuto struct v4l2_frequency *f) 35046a60cfeSFabio Belavenuto { 35146a60cfeSFabio Belavenuto struct tea5764_device *radio = video_drvdata(file); 35246a60cfeSFabio Belavenuto 353a3a9e287SHans Verkuil if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) 35446a60cfeSFabio Belavenuto return -EINVAL; 35546a60cfeSFabio Belavenuto if (f->frequency == 0) { 35646a60cfeSFabio Belavenuto /* We special case this as a power down control. */ 35746a60cfeSFabio Belavenuto tea5764_power_down(radio); 35846a60cfeSFabio Belavenuto } 35946a60cfeSFabio Belavenuto if (f->frequency < (FREQ_MIN * FREQ_MUL)) 36046a60cfeSFabio Belavenuto return -EINVAL; 36146a60cfeSFabio Belavenuto if (f->frequency > (FREQ_MAX * FREQ_MUL)) 36246a60cfeSFabio Belavenuto return -EINVAL; 36346a60cfeSFabio Belavenuto tea5764_power_up(radio); 36446a60cfeSFabio Belavenuto tea5764_tune(radio, (f->frequency * 125) / 2); 36546a60cfeSFabio Belavenuto return 0; 36646a60cfeSFabio Belavenuto } 36746a60cfeSFabio Belavenuto 36846a60cfeSFabio Belavenuto static int vidioc_g_frequency(struct file *file, void *priv, 36946a60cfeSFabio Belavenuto struct v4l2_frequency *f) 37046a60cfeSFabio Belavenuto { 37146a60cfeSFabio Belavenuto struct tea5764_device *radio = video_drvdata(file); 37246a60cfeSFabio Belavenuto struct tea5764_regs *r = &radio->regs; 37346a60cfeSFabio Belavenuto 374a3a9e287SHans Verkuil if (f->tuner != 0) 375a3a9e287SHans Verkuil return -EINVAL; 37646a60cfeSFabio Belavenuto tea5764_i2c_read(radio); 377909d15a7SJulia Lawall memset(f, 0, sizeof(*f)); 37846a60cfeSFabio Belavenuto f->type = V4L2_TUNER_RADIO; 37946a60cfeSFabio Belavenuto if (r->tnctrl & TEA5764_TNCTRL_PUPD0) 38046a60cfeSFabio Belavenuto f->frequency = (tea5764_get_freq(radio) * 2) / 125; 38146a60cfeSFabio Belavenuto else 38246a60cfeSFabio Belavenuto f->frequency = 0; 38346a60cfeSFabio Belavenuto 38446a60cfeSFabio Belavenuto return 0; 38546a60cfeSFabio Belavenuto } 38646a60cfeSFabio Belavenuto 38746a60cfeSFabio Belavenuto static int vidioc_queryctrl(struct file *file, void *priv, 38846a60cfeSFabio Belavenuto struct v4l2_queryctrl *qc) 38946a60cfeSFabio Belavenuto { 39046a60cfeSFabio Belavenuto int i; 39146a60cfeSFabio Belavenuto 39246a60cfeSFabio Belavenuto for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { 39346a60cfeSFabio Belavenuto if (qc->id && qc->id == radio_qctrl[i].id) { 39446a60cfeSFabio Belavenuto memcpy(qc, &(radio_qctrl[i]), sizeof(*qc)); 39546a60cfeSFabio Belavenuto return 0; 39646a60cfeSFabio Belavenuto } 39746a60cfeSFabio Belavenuto } 39846a60cfeSFabio Belavenuto return -EINVAL; 39946a60cfeSFabio Belavenuto } 40046a60cfeSFabio Belavenuto 40146a60cfeSFabio Belavenuto static int vidioc_g_ctrl(struct file *file, void *priv, 40246a60cfeSFabio Belavenuto struct v4l2_control *ctrl) 40346a60cfeSFabio Belavenuto { 40446a60cfeSFabio Belavenuto struct tea5764_device *radio = video_drvdata(file); 40546a60cfeSFabio Belavenuto 40646a60cfeSFabio Belavenuto switch (ctrl->id) { 40746a60cfeSFabio Belavenuto case V4L2_CID_AUDIO_MUTE: 40846a60cfeSFabio Belavenuto tea5764_i2c_read(radio); 40946a60cfeSFabio Belavenuto ctrl->value = tea5764_is_muted(radio) ? 1 : 0; 41046a60cfeSFabio Belavenuto return 0; 41146a60cfeSFabio Belavenuto } 41246a60cfeSFabio Belavenuto return -EINVAL; 41346a60cfeSFabio Belavenuto } 41446a60cfeSFabio Belavenuto 41546a60cfeSFabio Belavenuto static int vidioc_s_ctrl(struct file *file, void *priv, 41646a60cfeSFabio Belavenuto struct v4l2_control *ctrl) 41746a60cfeSFabio Belavenuto { 41846a60cfeSFabio Belavenuto struct tea5764_device *radio = video_drvdata(file); 41946a60cfeSFabio Belavenuto 42046a60cfeSFabio Belavenuto switch (ctrl->id) { 42146a60cfeSFabio Belavenuto case V4L2_CID_AUDIO_MUTE: 42246a60cfeSFabio Belavenuto tea5764_mute(radio, ctrl->value); 42346a60cfeSFabio Belavenuto return 0; 42446a60cfeSFabio Belavenuto } 42546a60cfeSFabio Belavenuto return -EINVAL; 42646a60cfeSFabio Belavenuto } 42746a60cfeSFabio Belavenuto 42846a60cfeSFabio Belavenuto static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) 42946a60cfeSFabio Belavenuto { 43046a60cfeSFabio Belavenuto *i = 0; 43146a60cfeSFabio Belavenuto return 0; 43246a60cfeSFabio Belavenuto } 43346a60cfeSFabio Belavenuto 43446a60cfeSFabio Belavenuto static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) 43546a60cfeSFabio Belavenuto { 43646a60cfeSFabio Belavenuto if (i != 0) 43746a60cfeSFabio Belavenuto return -EINVAL; 43846a60cfeSFabio Belavenuto return 0; 43946a60cfeSFabio Belavenuto } 44046a60cfeSFabio Belavenuto 44146a60cfeSFabio Belavenuto static int vidioc_g_audio(struct file *file, void *priv, 44246a60cfeSFabio Belavenuto struct v4l2_audio *a) 44346a60cfeSFabio Belavenuto { 44446a60cfeSFabio Belavenuto if (a->index > 1) 44546a60cfeSFabio Belavenuto return -EINVAL; 44646a60cfeSFabio Belavenuto 44746a60cfeSFabio Belavenuto strcpy(a->name, "Radio"); 44846a60cfeSFabio Belavenuto a->capability = V4L2_AUDCAP_STEREO; 44946a60cfeSFabio Belavenuto return 0; 45046a60cfeSFabio Belavenuto } 45146a60cfeSFabio Belavenuto 45246a60cfeSFabio Belavenuto static int vidioc_s_audio(struct file *file, void *priv, 45346a60cfeSFabio Belavenuto struct v4l2_audio *a) 45446a60cfeSFabio Belavenuto { 45546a60cfeSFabio Belavenuto if (a->index != 0) 45646a60cfeSFabio Belavenuto return -EINVAL; 45746a60cfeSFabio Belavenuto 45846a60cfeSFabio Belavenuto return 0; 45946a60cfeSFabio Belavenuto } 46046a60cfeSFabio Belavenuto 46146a60cfeSFabio Belavenuto static int tea5764_open(struct file *file) 46246a60cfeSFabio Belavenuto { 46346a60cfeSFabio Belavenuto /* Currently we support only one device */ 46446a60cfeSFabio Belavenuto struct tea5764_device *radio = video_drvdata(file); 46546a60cfeSFabio Belavenuto 46646a60cfeSFabio Belavenuto mutex_lock(&radio->mutex); 46746a60cfeSFabio Belavenuto /* Only exclusive access */ 46846a60cfeSFabio Belavenuto if (radio->users) { 46946a60cfeSFabio Belavenuto mutex_unlock(&radio->mutex); 47046a60cfeSFabio Belavenuto return -EBUSY; 47146a60cfeSFabio Belavenuto } 47246a60cfeSFabio Belavenuto radio->users++; 47346a60cfeSFabio Belavenuto mutex_unlock(&radio->mutex); 47446a60cfeSFabio Belavenuto file->private_data = radio; 47546a60cfeSFabio Belavenuto return 0; 47646a60cfeSFabio Belavenuto } 47746a60cfeSFabio Belavenuto 47846a60cfeSFabio Belavenuto static int tea5764_close(struct file *file) 47946a60cfeSFabio Belavenuto { 48046a60cfeSFabio Belavenuto struct tea5764_device *radio = video_drvdata(file); 48146a60cfeSFabio Belavenuto 48246a60cfeSFabio Belavenuto if (!radio) 48346a60cfeSFabio Belavenuto return -ENODEV; 48446a60cfeSFabio Belavenuto mutex_lock(&radio->mutex); 48546a60cfeSFabio Belavenuto radio->users--; 48646a60cfeSFabio Belavenuto mutex_unlock(&radio->mutex); 48746a60cfeSFabio Belavenuto return 0; 48846a60cfeSFabio Belavenuto } 48946a60cfeSFabio Belavenuto 49046a60cfeSFabio Belavenuto /* File system interface */ 49146a60cfeSFabio Belavenuto static const struct v4l2_file_operations tea5764_fops = { 49246a60cfeSFabio Belavenuto .owner = THIS_MODULE, 49346a60cfeSFabio Belavenuto .open = tea5764_open, 49446a60cfeSFabio Belavenuto .release = tea5764_close, 49546a60cfeSFabio Belavenuto .ioctl = video_ioctl2, 49646a60cfeSFabio Belavenuto }; 49746a60cfeSFabio Belavenuto 49846a60cfeSFabio Belavenuto static const struct v4l2_ioctl_ops tea5764_ioctl_ops = { 49946a60cfeSFabio Belavenuto .vidioc_querycap = vidioc_querycap, 50046a60cfeSFabio Belavenuto .vidioc_g_tuner = vidioc_g_tuner, 50146a60cfeSFabio Belavenuto .vidioc_s_tuner = vidioc_s_tuner, 50246a60cfeSFabio Belavenuto .vidioc_g_audio = vidioc_g_audio, 50346a60cfeSFabio Belavenuto .vidioc_s_audio = vidioc_s_audio, 50446a60cfeSFabio Belavenuto .vidioc_g_input = vidioc_g_input, 50546a60cfeSFabio Belavenuto .vidioc_s_input = vidioc_s_input, 50646a60cfeSFabio Belavenuto .vidioc_g_frequency = vidioc_g_frequency, 50746a60cfeSFabio Belavenuto .vidioc_s_frequency = vidioc_s_frequency, 50846a60cfeSFabio Belavenuto .vidioc_queryctrl = vidioc_queryctrl, 50946a60cfeSFabio Belavenuto .vidioc_g_ctrl = vidioc_g_ctrl, 51046a60cfeSFabio Belavenuto .vidioc_s_ctrl = vidioc_s_ctrl, 51146a60cfeSFabio Belavenuto }; 51246a60cfeSFabio Belavenuto 51346a60cfeSFabio Belavenuto /* V4L2 interface */ 51446a60cfeSFabio Belavenuto static struct video_device tea5764_radio_template = { 51546a60cfeSFabio Belavenuto .name = "TEA5764 FM-Radio", 51646a60cfeSFabio Belavenuto .fops = &tea5764_fops, 51746a60cfeSFabio Belavenuto .ioctl_ops = &tea5764_ioctl_ops, 51846a60cfeSFabio Belavenuto .release = video_device_release, 51946a60cfeSFabio Belavenuto }; 52046a60cfeSFabio Belavenuto 52146a60cfeSFabio Belavenuto /* I2C probe: check if the device exists and register with v4l if it is */ 52246a60cfeSFabio Belavenuto static int __devinit tea5764_i2c_probe(struct i2c_client *client, 52346a60cfeSFabio Belavenuto const struct i2c_device_id *id) 52446a60cfeSFabio Belavenuto { 52546a60cfeSFabio Belavenuto struct tea5764_device *radio; 52646a60cfeSFabio Belavenuto struct tea5764_regs *r; 52746a60cfeSFabio Belavenuto int ret; 52846a60cfeSFabio Belavenuto 52946a60cfeSFabio Belavenuto PDEBUG("probe"); 53046a60cfeSFabio Belavenuto radio = kmalloc(sizeof(struct tea5764_device), GFP_KERNEL); 53146a60cfeSFabio Belavenuto if (!radio) 53246a60cfeSFabio Belavenuto return -ENOMEM; 53346a60cfeSFabio Belavenuto 53446a60cfeSFabio Belavenuto mutex_init(&radio->mutex); 53546a60cfeSFabio Belavenuto radio->i2c_client = client; 53646a60cfeSFabio Belavenuto ret = tea5764_i2c_read(radio); 53746a60cfeSFabio Belavenuto if (ret) 53846a60cfeSFabio Belavenuto goto errfr; 53946a60cfeSFabio Belavenuto r = &radio->regs; 54046a60cfeSFabio Belavenuto PDEBUG("chipid = %04X, manid = %04X", r->chipid, r->manid); 54146a60cfeSFabio Belavenuto if (r->chipid != TEA5764_CHIPID || 54246a60cfeSFabio Belavenuto (r->manid & 0x0fff) != TEA5764_MANID) { 54346a60cfeSFabio Belavenuto PWARN("This chip is not a TEA5764!"); 54446a60cfeSFabio Belavenuto ret = -EINVAL; 54546a60cfeSFabio Belavenuto goto errfr; 54646a60cfeSFabio Belavenuto } 54746a60cfeSFabio Belavenuto 54846a60cfeSFabio Belavenuto radio->videodev = video_device_alloc(); 54946a60cfeSFabio Belavenuto if (!(radio->videodev)) { 55046a60cfeSFabio Belavenuto ret = -ENOMEM; 55146a60cfeSFabio Belavenuto goto errfr; 55246a60cfeSFabio Belavenuto } 55346a60cfeSFabio Belavenuto memcpy(radio->videodev, &tea5764_radio_template, 55446a60cfeSFabio Belavenuto sizeof(tea5764_radio_template)); 55546a60cfeSFabio Belavenuto 55646a60cfeSFabio Belavenuto i2c_set_clientdata(client, radio); 55746a60cfeSFabio Belavenuto video_set_drvdata(radio->videodev, radio); 55846a60cfeSFabio Belavenuto 55946a60cfeSFabio Belavenuto ret = video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr); 56046a60cfeSFabio Belavenuto if (ret < 0) { 56146a60cfeSFabio Belavenuto PWARN("Could not register video device!"); 56246a60cfeSFabio Belavenuto goto errrel; 56346a60cfeSFabio Belavenuto } 56446a60cfeSFabio Belavenuto 56546a60cfeSFabio Belavenuto /* initialize and power off the chip */ 56646a60cfeSFabio Belavenuto tea5764_i2c_read(radio); 56746a60cfeSFabio Belavenuto tea5764_set_audout_mode(radio, V4L2_TUNER_MODE_STEREO); 56846a60cfeSFabio Belavenuto tea5764_mute(radio, 1); 56946a60cfeSFabio Belavenuto tea5764_power_down(radio); 57046a60cfeSFabio Belavenuto 57146a60cfeSFabio Belavenuto PINFO("registered."); 57246a60cfeSFabio Belavenuto return 0; 57346a60cfeSFabio Belavenuto errrel: 57446a60cfeSFabio Belavenuto video_device_release(radio->videodev); 57546a60cfeSFabio Belavenuto errfr: 57646a60cfeSFabio Belavenuto kfree(radio); 57746a60cfeSFabio Belavenuto return ret; 57846a60cfeSFabio Belavenuto } 57946a60cfeSFabio Belavenuto 58046a60cfeSFabio Belavenuto static int __devexit tea5764_i2c_remove(struct i2c_client *client) 58146a60cfeSFabio Belavenuto { 58246a60cfeSFabio Belavenuto struct tea5764_device *radio = i2c_get_clientdata(client); 58346a60cfeSFabio Belavenuto 58446a60cfeSFabio Belavenuto PDEBUG("remove"); 58546a60cfeSFabio Belavenuto if (radio) { 58646a60cfeSFabio Belavenuto tea5764_power_down(radio); 58746a60cfeSFabio Belavenuto video_unregister_device(radio->videodev); 58846a60cfeSFabio Belavenuto kfree(radio); 58946a60cfeSFabio Belavenuto } 59046a60cfeSFabio Belavenuto return 0; 59146a60cfeSFabio Belavenuto } 59246a60cfeSFabio Belavenuto 59346a60cfeSFabio Belavenuto /* I2C subsystem interface */ 59446a60cfeSFabio Belavenuto static const struct i2c_device_id tea5764_id[] = { 59546a60cfeSFabio Belavenuto { "radio-tea5764", 0 }, 59646a60cfeSFabio Belavenuto { } /* Terminating entry */ 59746a60cfeSFabio Belavenuto }; 59846a60cfeSFabio Belavenuto MODULE_DEVICE_TABLE(i2c, tea5764_id); 59946a60cfeSFabio Belavenuto 60046a60cfeSFabio Belavenuto static struct i2c_driver tea5764_i2c_driver = { 60146a60cfeSFabio Belavenuto .driver = { 60246a60cfeSFabio Belavenuto .name = "radio-tea5764", 60346a60cfeSFabio Belavenuto .owner = THIS_MODULE, 60446a60cfeSFabio Belavenuto }, 60546a60cfeSFabio Belavenuto .probe = tea5764_i2c_probe, 60646a60cfeSFabio Belavenuto .remove = __devexit_p(tea5764_i2c_remove), 60746a60cfeSFabio Belavenuto .id_table = tea5764_id, 60846a60cfeSFabio Belavenuto }; 60946a60cfeSFabio Belavenuto 61046a60cfeSFabio Belavenuto /* init the driver */ 61146a60cfeSFabio Belavenuto static int __init tea5764_init(void) 61246a60cfeSFabio Belavenuto { 61346a60cfeSFabio Belavenuto int ret = i2c_add_driver(&tea5764_i2c_driver); 61446a60cfeSFabio Belavenuto 61546a60cfeSFabio Belavenuto printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ": " 61646a60cfeSFabio Belavenuto DRIVER_DESC "\n"); 61746a60cfeSFabio Belavenuto return ret; 61846a60cfeSFabio Belavenuto } 61946a60cfeSFabio Belavenuto 62046a60cfeSFabio Belavenuto /* cleanup the driver */ 62146a60cfeSFabio Belavenuto static void __exit tea5764_exit(void) 62246a60cfeSFabio Belavenuto { 62346a60cfeSFabio Belavenuto i2c_del_driver(&tea5764_i2c_driver); 62446a60cfeSFabio Belavenuto } 62546a60cfeSFabio Belavenuto 62646a60cfeSFabio Belavenuto MODULE_AUTHOR(DRIVER_AUTHOR); 62746a60cfeSFabio Belavenuto MODULE_DESCRIPTION(DRIVER_DESC); 62846a60cfeSFabio Belavenuto MODULE_LICENSE("GPL"); 62946a60cfeSFabio Belavenuto 63046a60cfeSFabio Belavenuto module_param(use_xtal, int, 1); 63146a60cfeSFabio Belavenuto MODULE_PARM_DESC(use_xtal, "Chip have a xtal connected in board"); 63246a60cfeSFabio Belavenuto module_param(radio_nr, int, 0); 63346a60cfeSFabio Belavenuto MODULE_PARM_DESC(radio_nr, "video4linux device number to use"); 63446a60cfeSFabio Belavenuto 63546a60cfeSFabio Belavenuto module_init(tea5764_init); 63646a60cfeSFabio Belavenuto module_exit(tea5764_exit); 637