diff options
author | Joonyoung Shim <jy0922.shim@samsung.com> | 2009-08-09 13:23:35 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2009-09-12 11:18:43 -0400 |
commit | cc35bbddfe10f77d949f0190764b252cd2b70c3c (patch) | |
tree | 4dbf834bb4d0fec001592e177c6786c1e6c701ae /drivers/media/radio | |
parent | 3c7cc8d52c0bd60d214aca22772aa3602a2b3198 (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/Kconfig | 13 | ||||
-rw-r--r-- | drivers/media/radio/si470x/Makefile | 2 | ||||
-rw-r--r-- | drivers/media/radio/si470x/radio-si470x-common.c | 6 | ||||
-rw-r--r-- | drivers/media/radio/si470x/radio-si470x-i2c.c | 254 | ||||
-rw-r--r-- | drivers/media/radio/si470x/radio-si470x.h | 6 |
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 | |||
26 | config 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 | ||
5 | radio-usb-si470x-objs := radio-si470x-usb.o radio-si470x-common.o | 5 | radio-usb-si470x-objs := radio-si470x-usb.o radio-si470x-common.o |
6 | radio-i2c-si470x-objs := radio-si470x-i2c.o radio-si470x-common.o | ||
6 | 7 | ||
7 | obj-$(CONFIG_USB_SI470X) += radio-usb-si470x.o | 8 | obj-$(CONFIG_USB_SI470X) += radio-usb-si470x.o |
9 | obj-$(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 | |||
35 | static 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 | |||
53 | int 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 | |||
73 | int 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 | |||
91 | int si470x_disconnect_check(struct si470x_device *radio) | ||
92 | { | ||
93 | return 0; | ||
94 | } | ||
95 | |||
96 | static 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 | |||
112 | static 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 | |||
131 | const 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 | |||
138 | int 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 | |||
150 | static 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; | ||
205 | err_all: | ||
206 | video_device_release(radio->videodev); | ||
207 | err_radio: | ||
208 | kfree(radio); | ||
209 | err_initial: | ||
210 | return retval; | ||
211 | } | ||
212 | |||
213 | static __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 | |||
224 | static const struct i2c_device_id si470x_i2c_id[] = { | ||
225 | { "si470x", 0 }, | ||
226 | { } | ||
227 | }; | ||
228 | MODULE_DEVICE_TABLE(i2c, si470x_i2c_id); | ||
229 | |||
230 | static 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 | |||
240 | static int __init si470x_i2c_init(void) | ||
241 | { | ||
242 | return i2c_add_driver(&si470x_i2c_driver); | ||
243 | } | ||
244 | module_init(si470x_i2c_init); | ||
245 | |||
246 | static void __exit si470x_i2c_exit(void) | ||
247 | { | ||
248 | i2c_del_driver(&si470x_i2c_driver); | ||
249 | } | ||
250 | module_exit(si470x_i2c_exit); | ||
251 | |||
252 | MODULE_DESCRIPTION("i2c radio driver for si470x fm radio receivers"); | ||
253 | MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); | ||
254 | MODULE_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 @@ | |||
143 | struct si470x_device { | 143 | struct 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) */ |