aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/radio
diff options
context:
space:
mode:
authorRichard Röjfors <richard.rojfors@mocean-labs.com>2009-09-22 09:14:39 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2009-12-05 15:40:43 -0500
commiteea85b0a629970d462481a80e1d45f4d71fe797f (patch)
tree6b305c5fb254f6afc623094b07a501df2e305805 /drivers/media/radio
parent42752f7a3f4afbabb513d5769c590e9abe2d0cd6 (diff)
V4L/DVB (13177): radio: Add support for TEF6862 tuner
This patch adds support for TEF6862 Car Radio Enhanced Selectivity Tuner. It's implemented as a subdev, supporting checking signal strength and setting and getting frequency. Signed-off-by: Richard Röjfors <richard.rojfors@mocean-labs.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/radio')
-rw-r--r--drivers/media/radio/Kconfig12
-rw-r--r--drivers/media/radio/Makefile1
-rw-r--r--drivers/media/radio/tef6862.c232
3 files changed, 245 insertions, 0 deletions
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig
index a87a477c87f2..931b8b370b50 100644
--- a/drivers/media/radio/Kconfig
+++ b/drivers/media/radio/Kconfig
@@ -401,4 +401,16 @@ config RADIO_TEA5764_XTAL
401 Say Y here if TEA5764 have a 32768 Hz crystal in circuit, say N 401 Say Y here if TEA5764 have a 32768 Hz crystal in circuit, say N
402 here if TEA5764 reference frequency is connected in FREQIN. 402 here if TEA5764 reference frequency is connected in FREQIN.
403 403
404config RADIO_TEF6862
405 tristate "TEF6862 Car Radio Enhanced Selectivity Tuner"
406 depends on I2C && VIDEO_V4L2
407 ---help---
408 Say Y here if you want to use the TEF6862 Car Radio Enhanced
409 Selectivity Tuner, found for instance on the Russellville development
410 board. On the russellville the device is connected to internal
411 timberdale I2C bus.
412
413 To compile this driver as a module, choose M here: the
414 module will be called TEF6862.
415
404endif # RADIO_ADAPTERS 416endif # RADIO_ADAPTERS
diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile
index 2a1be3bf4f7c..a4bdca08a94d 100644
--- a/drivers/media/radio/Makefile
+++ b/drivers/media/radio/Makefile
@@ -22,5 +22,6 @@ obj-$(CONFIG_USB_DSBR) += dsbr100.o
22obj-$(CONFIG_RADIO_SI470X) += si470x/ 22obj-$(CONFIG_RADIO_SI470X) += si470x/
23obj-$(CONFIG_USB_MR800) += radio-mr800.o 23obj-$(CONFIG_USB_MR800) += radio-mr800.o
24obj-$(CONFIG_RADIO_TEA5764) += radio-tea5764.o 24obj-$(CONFIG_RADIO_TEA5764) += radio-tea5764.o
25obj-$(CONFIG_RADIO_TEF6862) += tef6862.o
25 26
26EXTRA_CFLAGS += -Isound 27EXTRA_CFLAGS += -Isound
diff --git a/drivers/media/radio/tef6862.c b/drivers/media/radio/tef6862.c
new file mode 100644
index 000000000000..6e607ff0c169
--- /dev/null
+++ b/drivers/media/radio/tef6862.c
@@ -0,0 +1,232 @@
1/*
2 * tef6862.c Philips TEF6862 Car Radio Enhanced Selectivity Tuner
3 * Copyright (c) 2009 Intel Corporation
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18
19#include <linux/module.h>
20#include <linux/init.h>
21#include <linux/errno.h>
22#include <linux/kernel.h>
23#include <linux/interrupt.h>
24#include <linux/i2c.h>
25#include <linux/i2c-id.h>
26#include <media/v4l2-ioctl.h>
27#include <media/v4l2-device.h>
28#include <media/v4l2-chip-ident.h>
29
30#define DRIVER_NAME "tef6862"
31
32#define FREQ_MUL 16000
33
34#define TEF6862_LO_FREQ (875 * FREQ_MUL / 10)
35#define TEF6862_HI_FREQ (108 * FREQ_MUL)
36
37/* Write mode sub addresses */
38#define WM_SUB_BANDWIDTH 0x0
39#define WM_SUB_PLLM 0x1
40#define WM_SUB_PLLL 0x2
41#define WM_SUB_DAA 0x3
42#define WM_SUB_AGC 0x4
43#define WM_SUB_BAND 0x5
44#define WM_SUB_CONTROL 0x6
45#define WM_SUB_LEVEL 0x7
46#define WM_SUB_IFCF 0x8
47#define WM_SUB_IFCAP 0x9
48#define WM_SUB_ACD 0xA
49#define WM_SUB_TEST 0xF
50
51/* Different modes of the MSA register */
52#define MODE_BUFFER 0x0
53#define MODE_PRESET 0x1
54#define MODE_SEARCH 0x2
55#define MODE_AF_UPDATE 0x3
56#define MODE_JUMP 0x4
57#define MODE_CHECK 0x5
58#define MODE_LOAD 0x6
59#define MODE_END 0x7
60#define MODE_SHIFT 5
61
62struct tef6862_state {
63 struct v4l2_subdev sd;
64 unsigned long freq;
65};
66
67static inline struct tef6862_state *to_state(struct v4l2_subdev *sd)
68{
69 return container_of(sd, struct tef6862_state, sd);
70}
71
72static u16 tef6862_sigstr(struct i2c_client *client)
73{
74 u8 buf[4];
75 int err = i2c_master_recv(client, buf, sizeof(buf));
76 if (err == sizeof(buf))
77 return buf[3] << 8;
78 return 0;
79}
80
81static int tef6862_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *v)
82{
83 if (v->index > 0)
84 return -EINVAL;
85
86 /* only support FM for now */
87 strlcpy(v->name, "FM", sizeof(v->name));
88 v->type = V4L2_TUNER_RADIO;
89 v->rangelow = TEF6862_LO_FREQ;
90 v->rangehigh = TEF6862_HI_FREQ;
91 v->rxsubchans = V4L2_TUNER_SUB_MONO;
92 v->capability = V4L2_TUNER_CAP_LOW;
93 v->audmode = V4L2_TUNER_MODE_STEREO;
94 v->signal = tef6862_sigstr(v4l2_get_subdevdata(sd));
95
96 return 0;
97}
98
99static int tef6862_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *v)
100{
101 return v->index ? -EINVAL : 0;
102}
103
104static int tef6862_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f)
105{
106 struct tef6862_state *state = to_state(sd);
107 struct i2c_client *client = v4l2_get_subdevdata(sd);
108 u16 pll;
109 u8 i2cmsg[3];
110 int err;
111
112 if (f->tuner != 0)
113 return -EINVAL;
114
115 pll = 1964 + ((f->frequency - TEF6862_LO_FREQ) * 20) / FREQ_MUL;
116 i2cmsg[0] = (MODE_PRESET << MODE_SHIFT) | WM_SUB_PLLM;
117 i2cmsg[1] = (pll >> 8) & 0xff;
118 i2cmsg[2] = pll & 0xff;
119
120 err = i2c_master_send(client, i2cmsg, sizeof(i2cmsg));
121 if (!err)
122 state->freq = f->frequency;
123 return err;
124}
125
126static int tef6862_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f)
127{
128 struct tef6862_state *state = to_state(sd);
129
130 if (f->tuner != 0)
131 return -EINVAL;
132 f->type = V4L2_TUNER_RADIO;
133 f->frequency = state->freq;
134 return 0;
135}
136
137static int tef6862_g_chip_ident(struct v4l2_subdev *sd,
138 struct v4l2_dbg_chip_ident *chip)
139{
140 struct i2c_client *client = v4l2_get_subdevdata(sd);
141
142 return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TEF6862, 0);
143}
144
145static const struct v4l2_subdev_tuner_ops tef6862_tuner_ops = {
146 .g_tuner = tef6862_g_tuner,
147 .s_tuner = tef6862_s_tuner,
148 .s_frequency = tef6862_s_frequency,
149 .g_frequency = tef6862_g_frequency,
150};
151
152static const struct v4l2_subdev_core_ops tef6862_core_ops = {
153 .g_chip_ident = tef6862_g_chip_ident,
154};
155
156static const struct v4l2_subdev_ops tef6862_ops = {
157 .core = &tef6862_core_ops,
158 .tuner = &tef6862_tuner_ops,
159};
160
161/*
162 * Generic i2c probe
163 * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
164 */
165
166static int __devinit tef6862_probe(struct i2c_client *client,
167 const struct i2c_device_id *id)
168{
169 struct tef6862_state *state;
170 struct v4l2_subdev *sd;
171
172 /* Check if the adapter supports the needed features */
173 if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
174 return -EIO;
175
176 v4l_info(client, "chip found @ 0x%02x (%s)\n",
177 client->addr << 1, client->adapter->name);
178
179 state = kmalloc(sizeof(struct tef6862_state), GFP_KERNEL);
180 if (state == NULL)
181 return -ENOMEM;
182 state->freq = TEF6862_LO_FREQ;
183
184 sd = &state->sd;
185 v4l2_i2c_subdev_init(sd, client, &tef6862_ops);
186
187 return 0;
188}
189
190static int __devexit tef6862_remove(struct i2c_client *client)
191{
192 struct v4l2_subdev *sd = i2c_get_clientdata(client);
193
194 v4l2_device_unregister_subdev(sd);
195 kfree(to_state(sd));
196 return 0;
197}
198
199static const struct i2c_device_id tef6862_id[] = {
200 {DRIVER_NAME, 0},
201 {},
202};
203
204MODULE_DEVICE_TABLE(i2c, tef6862_id);
205
206static struct i2c_driver tef6862_driver = {
207 .driver = {
208 .owner = THIS_MODULE,
209 .name = DRIVER_NAME,
210 },
211 .probe = tef6862_probe,
212 .remove = tef6862_remove,
213 .id_table = tef6862_id,
214};
215
216static __init int tef6862_init(void)
217{
218 return i2c_add_driver(&tef6862_driver);
219}
220
221static __exit void tef6862_exit(void)
222{
223 i2c_del_driver(&tef6862_driver);
224}
225
226module_init(tef6862_init);
227module_exit(tef6862_exit);
228
229MODULE_DESCRIPTION("TEF6862 Car Radio Enhanced Selectivity Tuner");
230MODULE_AUTHOR("Mocean Laboratories");
231MODULE_LICENSE("GPL v2");
232