aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/radio
diff options
context:
space:
mode:
authorJoonyoung Shim <jy0922.shim@samsung.com>2009-08-09 13:23:35 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2009-09-12 11:18:43 -0400
commitcc35bbddfe10f77d949f0190764b252cd2b70c3c (patch)
tree4dbf834bb4d0fec001592e177c6786c1e6c701ae /drivers/media/radio
parent3c7cc8d52c0bd60d214aca22772aa3602a2b3198 (diff)
V4L/DVB (12416): radio-si470x: add i2c driver for si470x
This patch supports i2c interface of si470x. The i2c specific part exists in radio-si470x-i2c.c file and the common part uses radio-si470x-common.c file. The '#if defined' is inserted inevitably because of parts used only si470x usb in the common file. The current driver version doesn't support the RDS. Signed-off-by: Joonyoung Shim <jy0922.shim@samsung.com> Signed-off-by: Tobias Lorenz <tobias.lorenz@gmx.net> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/radio')
-rw-r--r--drivers/media/radio/si470x/Kconfig13
-rw-r--r--drivers/media/radio/si470x/Makefile2
-rw-r--r--drivers/media/radio/si470x/radio-si470x-common.c6
-rw-r--r--drivers/media/radio/si470x/radio-si470x-i2c.c254
-rw-r--r--drivers/media/radio/si470x/radio-si470x.h6
5 files changed, 281 insertions, 0 deletions
diff --git a/drivers/media/radio/si470x/Kconfig b/drivers/media/radio/si470x/Kconfig
index 20d05c04fa27..a466654ee5c9 100644
--- a/drivers/media/radio/si470x/Kconfig
+++ b/drivers/media/radio/si470x/Kconfig
@@ -22,3 +22,16 @@ config USB_SI470X
22 22
23 To compile this driver as a module, choose M here: the 23 To compile this driver as a module, choose M here: the
24 module will be called radio-usb-si470x. 24 module will be called radio-usb-si470x.
25
26config I2C_SI470X
27 tristate "Silicon Labs Si470x FM Radio Receiver support with I2C"
28 depends on I2C && RADIO_SI470X && !USB_SI470X
29 ---help---
30 This is a driver for I2C devices with the Silicon Labs SI470x
31 chip.
32
33 Say Y here if you want to connect this type of radio to your
34 computer's I2C port.
35
36 To compile this driver as a module, choose M here: the
37 module will be called radio-i2c-si470x.
diff --git a/drivers/media/radio/si470x/Makefile b/drivers/media/radio/si470x/Makefile
index 3cb777fe36bf..06964816cfd6 100644
--- a/drivers/media/radio/si470x/Makefile
+++ b/drivers/media/radio/si470x/Makefile
@@ -3,5 +3,7 @@
3# 3#
4 4
5radio-usb-si470x-objs := radio-si470x-usb.o radio-si470x-common.o 5radio-usb-si470x-objs := radio-si470x-usb.o radio-si470x-common.o
6radio-i2c-si470x-objs := radio-si470x-i2c.o radio-si470x-common.o
6 7
7obj-$(CONFIG_USB_SI470X) += radio-usb-si470x.o 8obj-$(CONFIG_USB_SI470X) += radio-usb-si470x.o
9obj-$(CONFIG_I2C_SI470X) += radio-i2c-si470x.o
diff --git a/drivers/media/radio/si470x/radio-si470x-common.c b/drivers/media/radio/si470x/radio-si470x-common.c
index 9f9a417caf93..f33315f2c543 100644
--- a/drivers/media/radio/si470x/radio-si470x-common.c
+++ b/drivers/media/radio/si470x/radio-si470x-common.c
@@ -581,8 +581,12 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv,
581 /* driver constants */ 581 /* driver constants */
582 strcpy(tuner->name, "FM"); 582 strcpy(tuner->name, "FM");
583 tuner->type = V4L2_TUNER_RADIO; 583 tuner->type = V4L2_TUNER_RADIO;
584#if defined(CONFIG_USB_SI470X) || defined(CONFIG_USB_SI470X_MODULE)
584 tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO | 585 tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
585 V4L2_TUNER_CAP_RDS; 586 V4L2_TUNER_CAP_RDS;
587#else
588 tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
589#endif
586 590
587 /* range limits */ 591 /* range limits */
588 switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) { 592 switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {
@@ -608,10 +612,12 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv,
608 tuner->rxsubchans = V4L2_TUNER_SUB_MONO; 612 tuner->rxsubchans = V4L2_TUNER_SUB_MONO;
609 else 613 else
610 tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; 614 tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
615#if defined(CONFIG_USB_SI470X) || defined(CONFIG_USB_SI470X_MODULE)
611 /* If there is a reliable method of detecting an RDS channel, 616 /* If there is a reliable method of detecting an RDS channel,
612 then this code should check for that before setting this 617 then this code should check for that before setting this
613 RDS subchannel. */ 618 RDS subchannel. */
614 tuner->rxsubchans |= V4L2_TUNER_SUB_RDS; 619 tuner->rxsubchans |= V4L2_TUNER_SUB_RDS;
620#endif
615 621
616 /* mono/stereo selector */ 622 /* mono/stereo selector */
617 if ((radio->registers[POWERCFG] & POWERCFG_MONO) == 0) 623 if ((radio->registers[POWERCFG] & POWERCFG_MONO) == 0)
diff --git a/drivers/media/radio/si470x/radio-si470x-i2c.c b/drivers/media/radio/si470x/radio-si470x-i2c.c
new file mode 100644
index 000000000000..218102184702
--- /dev/null
+++ b/drivers/media/radio/si470x/radio-si470x-i2c.c
@@ -0,0 +1,254 @@
1/*
2 * drivers/media/radio/si470x/radio-si470x-i2c.c
3 *
4 * I2C driver for radios with Silicon Labs Si470x FM Radio Receivers
5 *
6 * Copyright (C) 2009 Samsung Electronics Co.Ltd
7 * Author: Joonyoung Shim <jy0922.shim@samsung.com>
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation; either version 2 of the License, or (at your
12 * option) any later version.
13 *
14 *
15 * TODO:
16 * - RDS support
17 *
18 */
19
20#include <linux/module.h>
21#include <linux/init.h>
22#include <linux/i2c.h>
23#include <linux/delay.h>
24
25#include "radio-si470x.h"
26
27#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 0)
28#define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver"
29#define DRIVER_VERSION "1.0.0"
30
31/* starting with the upper byte of register 0x0a */
32#define READ_REG_NUM RADIO_REGISTER_NUM
33#define READ_INDEX(i) ((i + RADIO_REGISTER_NUM - 0x0a) % READ_REG_NUM)
34
35static int si470x_get_all_registers(struct si470x_device *radio)
36{
37 int i;
38 u16 buf[READ_REG_NUM];
39 struct i2c_msg msgs[1] = {
40 { radio->client->addr, I2C_M_RD, sizeof(u16) * READ_REG_NUM,
41 (void *)buf },
42 };
43
44 if (i2c_transfer(radio->client->adapter, msgs, 1) != 1)
45 return -EIO;
46
47 for (i = 0; i < READ_REG_NUM; i++)
48 radio->registers[i] = __be16_to_cpu(buf[READ_INDEX(i)]);
49
50 return 0;
51}
52
53int si470x_get_register(struct si470x_device *radio, int regnr)
54{
55 u16 buf[READ_REG_NUM];
56 struct i2c_msg msgs[1] = {
57 { radio->client->addr, I2C_M_RD, sizeof(u16) * READ_REG_NUM,
58 (void *)buf },
59 };
60
61 if (i2c_transfer(radio->client->adapter, msgs, 1) != 1)
62 return -EIO;
63
64 radio->registers[regnr] = __be16_to_cpu(buf[READ_INDEX(regnr)]);
65
66 return 0;
67}
68
69/* starting with the upper byte of register 0x02h */
70#define WRITE_REG_NUM 8
71#define WRITE_INDEX(i) (i + 0x02)
72
73int si470x_set_register(struct si470x_device *radio, int regnr)
74{
75 int i;
76 u16 buf[WRITE_REG_NUM];
77 struct i2c_msg msgs[1] = {
78 { radio->client->addr, 0, sizeof(u16) * WRITE_REG_NUM,
79 (void *)buf },
80 };
81
82 for (i = 0; i < WRITE_REG_NUM; i++)
83 buf[i] = __cpu_to_be16(radio->registers[WRITE_INDEX(i)]);
84
85 if (i2c_transfer(radio->client->adapter, msgs, 1) != 1)
86 return -EIO;
87
88 return 0;
89}
90
91int si470x_disconnect_check(struct si470x_device *radio)
92{
93 return 0;
94}
95
96static int si470x_fops_open(struct file *file)
97{
98 struct si470x_device *radio = video_drvdata(file);
99 int retval = 0;
100
101 mutex_lock(&radio->lock);
102 radio->users++;
103
104 if (radio->users == 1)
105 /* start radio */
106 retval = si470x_start(radio);
107 mutex_unlock(&radio->lock);
108
109 return retval;
110}
111
112static int si470x_fops_release(struct file *file)
113{
114 struct si470x_device *radio = video_drvdata(file);
115 int retval = 0;
116
117 /* safety check */
118 if (!radio)
119 return -ENODEV;
120
121 mutex_lock(&radio->lock);
122 radio->users--;
123 if (radio->users == 0)
124 /* stop radio */
125 retval = si470x_stop(radio);
126 mutex_unlock(&radio->lock);
127
128 return retval;
129}
130
131const struct v4l2_file_operations si470x_fops = {
132 .owner = THIS_MODULE,
133 .ioctl = video_ioctl2,
134 .open = si470x_fops_open,
135 .release = si470x_fops_release,
136};
137
138int si470x_vidioc_querycap(struct file *file, void *priv,
139 struct v4l2_capability *capability)
140{
141 strlcpy(capability->driver, DRIVER_NAME, sizeof(capability->driver));
142 strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card));
143 capability->version = DRIVER_KERNEL_VERSION;
144 capability->capabilities = V4L2_CAP_HW_FREQ_SEEK |
145 V4L2_CAP_TUNER | V4L2_CAP_RADIO;
146
147 return 0;
148}
149
150static int __devinit si470x_i2c_probe(struct i2c_client *client,
151 const struct i2c_device_id *id)
152{
153 struct si470x_device *radio;
154 int retval = 0;
155
156 /* private data allocation and initialization */
157 radio = kzalloc(sizeof(struct si470x_device), GFP_KERNEL);
158 if (!radio) {
159 retval = -ENOMEM;
160 goto err_initial;
161 }
162 radio->client = client;
163 radio->users = 0;
164 mutex_init(&radio->lock);
165
166 /* video device allocation and initialization */
167 radio->videodev = video_device_alloc();
168 if (!radio->videodev) {
169 retval = -ENOMEM;
170 goto err_radio;
171 }
172 memcpy(radio->videodev, &si470x_viddev_template,
173 sizeof(si470x_viddev_template));
174 video_set_drvdata(radio->videodev, radio);
175
176 /* power up : need 110ms */
177 radio->registers[POWERCFG] = POWERCFG_ENABLE;
178 if (si470x_set_register(radio, POWERCFG) < 0) {
179 retval = -EIO;
180 goto err_all;
181 }
182 msleep(110);
183
184 /* show some infos about the specific si470x device */
185 if (si470x_get_all_registers(radio) < 0) {
186 retval = -EIO;
187 goto err_radio;
188 }
189 dev_info(&client->dev, "DeviceID=0x%4.4hx ChipID=0x%4.4hx\n",
190 radio->registers[DEVICEID], radio->registers[CHIPID]);
191
192 /* set initial frequency */
193 si470x_set_freq(radio, 87.5 * FREQ_MUL); /* available in all regions */
194
195 /* register video device */
196 retval = video_register_device(radio->videodev, VFL_TYPE_RADIO, -1);
197 if (retval) {
198 dev_warn(&client->dev, "Could not register video device\n");
199 goto err_all;
200 }
201
202 i2c_set_clientdata(client, radio);
203
204 return 0;
205err_all:
206 video_device_release(radio->videodev);
207err_radio:
208 kfree(radio);
209err_initial:
210 return retval;
211}
212
213static __devexit int si470x_i2c_remove(struct i2c_client *client)
214{
215 struct si470x_device *radio = i2c_get_clientdata(client);
216
217 video_unregister_device(radio->videodev);
218 kfree(radio);
219 i2c_set_clientdata(client, NULL);
220
221 return 0;
222}
223
224static const struct i2c_device_id si470x_i2c_id[] = {
225 { "si470x", 0 },
226 { }
227};
228MODULE_DEVICE_TABLE(i2c, si470x_i2c_id);
229
230static struct i2c_driver si470x_i2c_driver = {
231 .driver = {
232 .name = "si470x",
233 .owner = THIS_MODULE,
234 },
235 .probe = si470x_i2c_probe,
236 .remove = __devexit_p(si470x_i2c_remove),
237 .id_table = si470x_i2c_id,
238};
239
240static int __init si470x_i2c_init(void)
241{
242 return i2c_add_driver(&si470x_i2c_driver);
243}
244module_init(si470x_i2c_init);
245
246static void __exit si470x_i2c_exit(void)
247{
248 i2c_del_driver(&si470x_i2c_driver);
249}
250module_exit(si470x_i2c_exit);
251
252MODULE_DESCRIPTION("i2c radio driver for si470x fm radio receivers");
253MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
254MODULE_LICENSE("GPL");
diff --git a/drivers/media/radio/si470x/radio-si470x.h b/drivers/media/radio/si470x/radio-si470x.h
index d24829b04eb2..794112c759b8 100644
--- a/drivers/media/radio/si470x/radio-si470x.h
+++ b/drivers/media/radio/si470x/radio-si470x.h
@@ -143,6 +143,11 @@
143struct si470x_device { 143struct si470x_device {
144 struct video_device *videodev; 144 struct video_device *videodev;
145 145
146#if defined(CONFIG_I2C_SI470X) || defined(CONFIG_I2C_SI470X_MODULE)
147 struct i2c_client *client;
148#endif
149
150#if defined(CONFIG_USB_SI470X) || defined(CONFIG_USB_SI470X_MODULE)
146 /* reference to USB and video device */ 151 /* reference to USB and video device */
147 struct usb_device *usbdev; 152 struct usb_device *usbdev;
148 struct usb_interface *intf; 153 struct usb_interface *intf;
@@ -160,6 +165,7 @@ struct si470x_device {
160 /* driver management */ 165 /* driver management */
161 unsigned char disconnected; 166 unsigned char disconnected;
162 struct mutex disconnect_lock; 167 struct mutex disconnect_lock;
168#endif
163 unsigned int users; 169 unsigned int users;
164 170
165 /* Silabs internal registers (0..15) */ 171 /* Silabs internal registers (0..15) */