diff options
author | Dinesh Ram <Dinesh.Ram@cern.ch> | 2013-10-15 11:24:42 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <m.chehab@samsung.com> | 2013-12-18 03:37:57 -0500 |
commit | b874b39fcd2fbfec59b78c1c4f958a659d63579e (patch) | |
tree | 20f07419f5201ee49b5b28e1bf3fbd2b67f0941a /drivers/media/radio | |
parent | adc232592337d3ac4c5473ba8bdaf7c202bf215d (diff) |
[media] si4713: Added the USB driver for Si4713
This is the USB driver for the Silicon Labs development board.
It contains the Si4713 FM transmitter chip.
Signed-off-by: Dinesh Ram <dinesh.ram@cern.ch>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Tested-by: Eduardo Valentin <edubezval@gmail.com>
Acked-by: Eduardo Valentin <edubezval@gmail.com>
Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
Diffstat (limited to 'drivers/media/radio')
-rw-r--r-- | drivers/media/radio/si4713/Kconfig | 15 | ||||
-rw-r--r-- | drivers/media/radio/si4713/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/radio/si4713/radio-usb-si4713.c | 540 |
3 files changed, 556 insertions, 0 deletions
diff --git a/drivers/media/radio/si4713/Kconfig b/drivers/media/radio/si4713/Kconfig index ec640b86994b..a7c3ba85d12b 100644 --- a/drivers/media/radio/si4713/Kconfig +++ b/drivers/media/radio/si4713/Kconfig | |||
@@ -1,3 +1,18 @@ | |||
1 | config USB_SI4713 | ||
2 | tristate "Silicon Labs Si4713 FM Radio Transmitter support with USB" | ||
3 | depends on USB && RADIO_SI4713 | ||
4 | select SI4713 | ||
5 | ---help--- | ||
6 | This is a driver for USB devices with the Silicon Labs SI4713 | ||
7 | chip. Currently these devices are known to work. | ||
8 | - 10c4:8244: Silicon Labs FM Transmitter USB device. | ||
9 | |||
10 | Say Y here if you want to connect this type of radio to your | ||
11 | computer's USB port. | ||
12 | |||
13 | To compile this driver as a module, choose M here: the | ||
14 | module will be called radio-usb-si4713. | ||
15 | |||
1 | config PLATFORM_SI4713 | 16 | config PLATFORM_SI4713 |
2 | tristate "Silicon Labs Si4713 FM Radio Transmitter support with I2C" | 17 | tristate "Silicon Labs Si4713 FM Radio Transmitter support with I2C" |
3 | depends on I2C && RADIO_SI4713 | 18 | depends on I2C && RADIO_SI4713 |
diff --git a/drivers/media/radio/si4713/Makefile b/drivers/media/radio/si4713/Makefile index a8c1194dac73..ddaaf925e883 100644 --- a/drivers/media/radio/si4713/Makefile +++ b/drivers/media/radio/si4713/Makefile | |||
@@ -3,4 +3,5 @@ | |||
3 | # | 3 | # |
4 | 4 | ||
5 | obj-$(CONFIG_I2C_SI4713) += si4713.o | 5 | obj-$(CONFIG_I2C_SI4713) += si4713.o |
6 | obj-$(CONFIG_USB_SI4713) += radio-usb-si4713.o | ||
6 | obj-$(CONFIG_PLATFORM_SI4713) += radio-platform-si4713.o | 7 | obj-$(CONFIG_PLATFORM_SI4713) += radio-platform-si4713.o |
diff --git a/drivers/media/radio/si4713/radio-usb-si4713.c b/drivers/media/radio/si4713/radio-usb-si4713.c new file mode 100644 index 000000000000..d97884494d04 --- /dev/null +++ b/drivers/media/radio/si4713/radio-usb-si4713.c | |||
@@ -0,0 +1,540 @@ | |||
1 | /* | ||
2 | * Copyright 2013 Cisco Systems, Inc. and/or its affiliates. | ||
3 | * All rights reserved. | ||
4 | * | ||
5 | * This program is free software; you may redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation; version 2 of the License. | ||
8 | * | ||
9 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
10 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
11 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
12 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | ||
13 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | ||
14 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
15 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
16 | * SOFTWARE. | ||
17 | */ | ||
18 | |||
19 | /* kernel includes */ | ||
20 | #include <linux/kernel.h> | ||
21 | #include <linux/module.h> | ||
22 | #include <linux/usb.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/slab.h> | ||
25 | #include <linux/input.h> | ||
26 | #include <linux/mutex.h> | ||
27 | #include <linux/i2c.h> | ||
28 | /* V4l includes */ | ||
29 | #include <linux/videodev2.h> | ||
30 | #include <media/v4l2-common.h> | ||
31 | #include <media/v4l2-device.h> | ||
32 | #include <media/v4l2-ioctl.h> | ||
33 | #include <media/v4l2-event.h> | ||
34 | #include <media/si4713.h> | ||
35 | |||
36 | #include "si4713.h" | ||
37 | |||
38 | /* driver and module definitions */ | ||
39 | MODULE_AUTHOR("Dinesh Ram <dinesh.ram@cern.ch>"); | ||
40 | MODULE_DESCRIPTION("Si4713 FM Transmitter USB driver"); | ||
41 | MODULE_LICENSE("GPL v2"); | ||
42 | |||
43 | /* The Device announces itself as Cygnal Integrated Products, Inc. */ | ||
44 | #define USB_SI4713_VENDOR 0x10c4 | ||
45 | #define USB_SI4713_PRODUCT 0x8244 | ||
46 | |||
47 | #define BUFFER_LENGTH 64 | ||
48 | #define USB_TIMEOUT 1000 | ||
49 | #define USB_RESP_TIMEOUT 50000 | ||
50 | |||
51 | /* USB Device ID List */ | ||
52 | static struct usb_device_id usb_si4713_usb_device_table[] = { | ||
53 | {USB_DEVICE_AND_INTERFACE_INFO(USB_SI4713_VENDOR, USB_SI4713_PRODUCT, | ||
54 | USB_CLASS_HID, 0, 0) }, | ||
55 | { } /* Terminating entry */ | ||
56 | }; | ||
57 | |||
58 | MODULE_DEVICE_TABLE(usb, usb_si4713_usb_device_table); | ||
59 | |||
60 | struct si4713_usb_device { | ||
61 | struct usb_device *usbdev; | ||
62 | struct usb_interface *intf; | ||
63 | struct video_device vdev; | ||
64 | struct v4l2_device v4l2_dev; | ||
65 | struct v4l2_subdev *v4l2_subdev; | ||
66 | struct mutex lock; | ||
67 | struct i2c_adapter i2c_adapter; | ||
68 | |||
69 | u8 *buffer; | ||
70 | }; | ||
71 | |||
72 | static inline struct si4713_usb_device *to_si4713_dev(struct v4l2_device *v4l2_dev) | ||
73 | { | ||
74 | return container_of(v4l2_dev, struct si4713_usb_device, v4l2_dev); | ||
75 | } | ||
76 | |||
77 | static int vidioc_querycap(struct file *file, void *priv, | ||
78 | struct v4l2_capability *v) | ||
79 | { | ||
80 | struct si4713_usb_device *radio = video_drvdata(file); | ||
81 | |||
82 | strlcpy(v->driver, "radio-usb-si4713", sizeof(v->driver)); | ||
83 | strlcpy(v->card, "Si4713 FM Transmitter", sizeof(v->card)); | ||
84 | usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info)); | ||
85 | v->device_caps = V4L2_CAP_MODULATOR | V4L2_CAP_RDS_OUTPUT; | ||
86 | v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS; | ||
87 | |||
88 | return 0; | ||
89 | } | ||
90 | |||
91 | static int vidioc_g_modulator(struct file *file, void *priv, | ||
92 | struct v4l2_modulator *vm) | ||
93 | { | ||
94 | struct si4713_usb_device *radio = video_drvdata(file); | ||
95 | |||
96 | return v4l2_subdev_call(radio->v4l2_subdev, tuner, g_modulator, vm); | ||
97 | } | ||
98 | |||
99 | static int vidioc_s_modulator(struct file *file, void *priv, | ||
100 | const struct v4l2_modulator *vm) | ||
101 | { | ||
102 | struct si4713_usb_device *radio = video_drvdata(file); | ||
103 | |||
104 | return v4l2_subdev_call(radio->v4l2_subdev, tuner, s_modulator, vm); | ||
105 | } | ||
106 | |||
107 | static int vidioc_s_frequency(struct file *file, void *priv, | ||
108 | const struct v4l2_frequency *vf) | ||
109 | { | ||
110 | struct si4713_usb_device *radio = video_drvdata(file); | ||
111 | |||
112 | return v4l2_subdev_call(radio->v4l2_subdev, tuner, s_frequency, vf); | ||
113 | } | ||
114 | |||
115 | static int vidioc_g_frequency(struct file *file, void *priv, | ||
116 | struct v4l2_frequency *vf) | ||
117 | { | ||
118 | struct si4713_usb_device *radio = video_drvdata(file); | ||
119 | |||
120 | return v4l2_subdev_call(radio->v4l2_subdev, tuner, g_frequency, vf); | ||
121 | } | ||
122 | |||
123 | static const struct v4l2_ioctl_ops usb_si4713_ioctl_ops = { | ||
124 | .vidioc_querycap = vidioc_querycap, | ||
125 | .vidioc_g_modulator = vidioc_g_modulator, | ||
126 | .vidioc_s_modulator = vidioc_s_modulator, | ||
127 | .vidioc_g_frequency = vidioc_g_frequency, | ||
128 | .vidioc_s_frequency = vidioc_s_frequency, | ||
129 | .vidioc_log_status = v4l2_ctrl_log_status, | ||
130 | .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, | ||
131 | .vidioc_unsubscribe_event = v4l2_event_unsubscribe, | ||
132 | }; | ||
133 | |||
134 | /* File system interface */ | ||
135 | static const struct v4l2_file_operations usb_si4713_fops = { | ||
136 | .owner = THIS_MODULE, | ||
137 | .open = v4l2_fh_open, | ||
138 | .release = v4l2_fh_release, | ||
139 | .poll = v4l2_ctrl_poll, | ||
140 | .unlocked_ioctl = video_ioctl2, | ||
141 | }; | ||
142 | |||
143 | static void usb_si4713_video_device_release(struct v4l2_device *v4l2_dev) | ||
144 | { | ||
145 | struct si4713_usb_device *radio = to_si4713_dev(v4l2_dev); | ||
146 | struct i2c_adapter *adapter = &radio->i2c_adapter; | ||
147 | |||
148 | i2c_del_adapter(adapter); | ||
149 | v4l2_device_unregister(&radio->v4l2_dev); | ||
150 | kfree(radio->buffer); | ||
151 | kfree(radio); | ||
152 | } | ||
153 | |||
154 | /* | ||
155 | * This command sequence emulates the behaviour of the Windows driver. | ||
156 | * The structure of these commands was determined by sniffing the | ||
157 | * usb traffic of the device during startup. | ||
158 | * Most likely, these commands make some queries to the device. | ||
159 | * Commands are sent to enquire parameters like the bus mode, | ||
160 | * component revision, boot mode, the device serial number etc. | ||
161 | * | ||
162 | * These commands are necessary to be sent in this order during startup. | ||
163 | * The device fails to powerup if these commands are not sent. | ||
164 | * | ||
165 | * The complete list of startup commands is given in the start_seq table below. | ||
166 | */ | ||
167 | static int si4713_send_startup_command(struct si4713_usb_device *radio) | ||
168 | { | ||
169 | unsigned long until_jiffies = jiffies + usecs_to_jiffies(USB_RESP_TIMEOUT) + 1; | ||
170 | u8 *buffer = radio->buffer; | ||
171 | int retval; | ||
172 | |||
173 | /* send the command */ | ||
174 | retval = usb_control_msg(radio->usbdev, usb_sndctrlpipe(radio->usbdev, 0), | ||
175 | 0x09, 0x21, 0x033f, 0, radio->buffer, | ||
176 | BUFFER_LENGTH, USB_TIMEOUT); | ||
177 | if (retval < 0) | ||
178 | return retval; | ||
179 | |||
180 | for (;;) { | ||
181 | /* receive the response */ | ||
182 | retval = usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), | ||
183 | 0x01, 0xa1, 0x033f, 0, radio->buffer, | ||
184 | BUFFER_LENGTH, USB_TIMEOUT); | ||
185 | if (retval < 0) | ||
186 | return retval; | ||
187 | if (!radio->buffer[1]) { | ||
188 | /* USB traffic sniffing showed that some commands require | ||
189 | * additional checks. */ | ||
190 | switch (buffer[1]) { | ||
191 | case 0x32: | ||
192 | if (radio->buffer[2] == 0) | ||
193 | return 0; | ||
194 | break; | ||
195 | case 0x14: | ||
196 | case 0x12: | ||
197 | if (radio->buffer[2] & SI4713_CTS) | ||
198 | return 0; | ||
199 | break; | ||
200 | case 0x06: | ||
201 | if ((radio->buffer[2] & SI4713_CTS) && radio->buffer[9] == 0x08) | ||
202 | return 0; | ||
203 | break; | ||
204 | default: | ||
205 | return 0; | ||
206 | } | ||
207 | } | ||
208 | if (time_is_before_jiffies(until_jiffies)) | ||
209 | return -EIO; | ||
210 | msleep(3); | ||
211 | } | ||
212 | |||
213 | return retval; | ||
214 | } | ||
215 | |||
216 | struct si4713_start_seq_table { | ||
217 | int len; | ||
218 | u8 payload[8]; | ||
219 | }; | ||
220 | |||
221 | /* | ||
222 | * Some of the startup commands that could be recognized are : | ||
223 | * (0x03): Get serial number of the board (Response : CB000-00-00) | ||
224 | * (0x06, 0x03, 0x03, 0x08, 0x01, 0x0f) : Get Component revision | ||
225 | */ | ||
226 | struct si4713_start_seq_table start_seq[] = { | ||
227 | |||
228 | { 1, { 0x03 } }, | ||
229 | { 2, { 0x32, 0x7f } }, | ||
230 | { 6, { 0x06, 0x03, 0x03, 0x08, 0x01, 0x0f } }, | ||
231 | { 2, { 0x14, 0x02 } }, | ||
232 | { 2, { 0x09, 0x90 } }, | ||
233 | { 3, { 0x08, 0x90, 0xfa } }, | ||
234 | { 2, { 0x36, 0x01 } }, | ||
235 | { 2, { 0x05, 0x03 } }, | ||
236 | { 7, { 0x06, 0x00, 0x06, 0x0e, 0x01, 0x0f, 0x05 } }, | ||
237 | { 1, { 0x12 } }, | ||
238 | /* Commands that are sent after pressing the 'Initialize' | ||
239 | button in the windows application */ | ||
240 | { 1, { 0x03 } }, | ||
241 | { 1, { 0x01 } }, | ||
242 | { 2, { 0x09, 0x90 } }, | ||
243 | { 3, { 0x08, 0x90, 0xfa } }, | ||
244 | { 1, { 0x34 } }, | ||
245 | { 2, { 0x35, 0x01 } }, | ||
246 | { 2, { 0x36, 0x01 } }, | ||
247 | { 2, { 0x30, 0x09 } }, | ||
248 | { 4, { 0x30, 0x06, 0x00, 0xe2 } }, | ||
249 | { 3, { 0x31, 0x01, 0x30 } }, | ||
250 | { 3, { 0x31, 0x04, 0x09 } }, | ||
251 | { 2, { 0x05, 0x02 } }, | ||
252 | { 6, { 0x06, 0x03, 0x03, 0x08, 0x01, 0x0f } }, | ||
253 | }; | ||
254 | |||
255 | static int si4713_start_seq(struct si4713_usb_device *radio) | ||
256 | { | ||
257 | int retval = 0; | ||
258 | int i; | ||
259 | |||
260 | radio->buffer[0] = 0x3f; | ||
261 | |||
262 | for (i = 0; i < ARRAY_SIZE(start_seq); i++) { | ||
263 | int len = start_seq[i].len; | ||
264 | u8 *payload = start_seq[i].payload; | ||
265 | |||
266 | memcpy(radio->buffer + 1, payload, len); | ||
267 | memset(radio->buffer + len + 1, 0, BUFFER_LENGTH - 1 - len); | ||
268 | retval = si4713_send_startup_command(radio); | ||
269 | } | ||
270 | |||
271 | return retval; | ||
272 | } | ||
273 | |||
274 | static struct i2c_board_info si4713_board_info = { | ||
275 | I2C_BOARD_INFO("si4713", SI4713_I2C_ADDR_BUSEN_HIGH), | ||
276 | }; | ||
277 | |||
278 | struct si4713_command_table { | ||
279 | int command_id; | ||
280 | u8 payload[8]; | ||
281 | }; | ||
282 | |||
283 | /* | ||
284 | * Structure of a command : | ||
285 | * Byte 1 : 0x3f (always) | ||
286 | * Byte 2 : 0x06 (send a command) | ||
287 | * Byte 3 : Unknown | ||
288 | * Byte 4 : Number of arguments + 1 (for the command byte) | ||
289 | * Byte 5 : Number of response bytes | ||
290 | */ | ||
291 | struct si4713_command_table command_table[] = { | ||
292 | |||
293 | { SI4713_CMD_POWER_UP, { 0x00, SI4713_PWUP_NARGS + 1, SI4713_PWUP_NRESP} }, | ||
294 | { SI4713_CMD_GET_REV, { 0x03, 0x01, SI4713_GETREV_NRESP } }, | ||
295 | { SI4713_CMD_POWER_DOWN, { 0x00, 0x01, SI4713_PWDN_NRESP} }, | ||
296 | { SI4713_CMD_SET_PROPERTY, { 0x00, SI4713_SET_PROP_NARGS + 1, SI4713_SET_PROP_NRESP } }, | ||
297 | { SI4713_CMD_GET_PROPERTY, { 0x00, SI4713_GET_PROP_NARGS + 1, SI4713_GET_PROP_NRESP } }, | ||
298 | { SI4713_CMD_TX_TUNE_FREQ, { 0x03, SI4713_TXFREQ_NARGS + 1, SI4713_TXFREQ_NRESP } }, | ||
299 | { SI4713_CMD_TX_TUNE_POWER, { 0x03, SI4713_TXPWR_NARGS + 1, SI4713_TXPWR_NRESP } }, | ||
300 | { SI4713_CMD_TX_TUNE_MEASURE, { 0x03, SI4713_TXMEA_NARGS + 1, SI4713_TXMEA_NRESP } }, | ||
301 | { SI4713_CMD_TX_TUNE_STATUS, { 0x00, SI4713_TXSTATUS_NARGS + 1, SI4713_TXSTATUS_NRESP } }, | ||
302 | { SI4713_CMD_TX_ASQ_STATUS, { 0x03, SI4713_ASQSTATUS_NARGS + 1, SI4713_ASQSTATUS_NRESP } }, | ||
303 | { SI4713_CMD_GET_INT_STATUS, { 0x03, 0x01, SI4713_GET_STATUS_NRESP } }, | ||
304 | { SI4713_CMD_TX_RDS_BUFF, { 0x03, SI4713_RDSBUFF_NARGS + 1, SI4713_RDSBUFF_NRESP } }, | ||
305 | { SI4713_CMD_TX_RDS_PS, { 0x00, SI4713_RDSPS_NARGS + 1, SI4713_RDSPS_NRESP } }, | ||
306 | }; | ||
307 | |||
308 | static int send_command(struct si4713_usb_device *radio, u8 *payload, char *data, int len) | ||
309 | { | ||
310 | int retval; | ||
311 | |||
312 | radio->buffer[0] = 0x3f; | ||
313 | radio->buffer[1] = 0x06; | ||
314 | |||
315 | memcpy(radio->buffer + 2, payload, 3); | ||
316 | memcpy(radio->buffer + 5, data, len); | ||
317 | memset(radio->buffer + 5 + len, 0, BUFFER_LENGTH - 5 - len); | ||
318 | |||
319 | /* send the command */ | ||
320 | retval = usb_control_msg(radio->usbdev, usb_sndctrlpipe(radio->usbdev, 0), | ||
321 | 0x09, 0x21, 0x033f, 0, radio->buffer, | ||
322 | BUFFER_LENGTH, USB_TIMEOUT); | ||
323 | |||
324 | return retval < 0 ? retval : 0; | ||
325 | } | ||
326 | |||
327 | static int si4713_i2c_read(struct si4713_usb_device *radio, char *data, int len) | ||
328 | { | ||
329 | unsigned long until_jiffies = jiffies + usecs_to_jiffies(USB_RESP_TIMEOUT) + 1; | ||
330 | int retval; | ||
331 | |||
332 | /* receive the response */ | ||
333 | for (;;) { | ||
334 | retval = usb_control_msg(radio->usbdev, | ||
335 | usb_rcvctrlpipe(radio->usbdev, 0), | ||
336 | 0x01, 0xa1, 0x033f, 0, radio->buffer, | ||
337 | BUFFER_LENGTH, USB_TIMEOUT); | ||
338 | if (retval < 0) | ||
339 | return retval; | ||
340 | |||
341 | /* | ||
342 | * Check that we get a valid reply back (buffer[1] == 0) and | ||
343 | * that CTS is set before returning, otherwise we wait and try | ||
344 | * again. The i2c driver also does the CTS check, but the timeouts | ||
345 | * used there are much too small for this USB driver, so we wait | ||
346 | * for it here. | ||
347 | */ | ||
348 | if (radio->buffer[1] == 0 && (radio->buffer[2] & SI4713_CTS)) { | ||
349 | memcpy(data, radio->buffer + 2, len); | ||
350 | return 0; | ||
351 | } | ||
352 | if (time_is_before_jiffies(until_jiffies)) { | ||
353 | /* Zero the status value, ensuring CTS isn't set */ | ||
354 | data[0] = 0; | ||
355 | return 0; | ||
356 | } | ||
357 | msleep(3); | ||
358 | } | ||
359 | } | ||
360 | |||
361 | static int si4713_i2c_write(struct si4713_usb_device *radio, char *data, int len) | ||
362 | { | ||
363 | int retval = -EINVAL; | ||
364 | int i; | ||
365 | |||
366 | if (len > BUFFER_LENGTH - 5) | ||
367 | return -EINVAL; | ||
368 | |||
369 | for (i = 0; i < ARRAY_SIZE(command_table); i++) { | ||
370 | if (data[0] == command_table[i].command_id) | ||
371 | retval = send_command(radio, command_table[i].payload, | ||
372 | data, len); | ||
373 | } | ||
374 | |||
375 | return retval < 0 ? retval : 0; | ||
376 | } | ||
377 | |||
378 | static int si4713_transfer(struct i2c_adapter *i2c_adapter, | ||
379 | struct i2c_msg *msgs, int num) | ||
380 | { | ||
381 | struct si4713_usb_device *radio = i2c_get_adapdata(i2c_adapter); | ||
382 | int retval = -EINVAL; | ||
383 | int i; | ||
384 | |||
385 | if (num <= 0) | ||
386 | return 0; | ||
387 | |||
388 | for (i = 0; i < num; i++) { | ||
389 | if (msgs[i].flags & I2C_M_RD) | ||
390 | retval = si4713_i2c_read(radio, msgs[i].buf, msgs[i].len); | ||
391 | else | ||
392 | retval = si4713_i2c_write(radio, msgs[i].buf, msgs[i].len); | ||
393 | if (retval) | ||
394 | break; | ||
395 | } | ||
396 | |||
397 | return retval ? retval : num; | ||
398 | } | ||
399 | |||
400 | static u32 si4713_functionality(struct i2c_adapter *adapter) | ||
401 | { | ||
402 | return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; | ||
403 | } | ||
404 | |||
405 | static struct i2c_algorithm si4713_algo = { | ||
406 | .master_xfer = si4713_transfer, | ||
407 | .functionality = si4713_functionality, | ||
408 | }; | ||
409 | |||
410 | /* This name value shows up in the sysfs filename associated | ||
411 | with this I2C adapter */ | ||
412 | static struct i2c_adapter si4713_i2c_adapter_template = { | ||
413 | .name = "si4713-i2c", | ||
414 | .owner = THIS_MODULE, | ||
415 | .algo = &si4713_algo, | ||
416 | }; | ||
417 | |||
418 | int si4713_register_i2c_adapter(struct si4713_usb_device *radio) | ||
419 | { | ||
420 | radio->i2c_adapter = si4713_i2c_adapter_template; | ||
421 | /* set up sysfs linkage to our parent device */ | ||
422 | radio->i2c_adapter.dev.parent = &radio->usbdev->dev; | ||
423 | i2c_set_adapdata(&radio->i2c_adapter, radio); | ||
424 | |||
425 | return i2c_add_adapter(&radio->i2c_adapter); | ||
426 | } | ||
427 | |||
428 | /* check if the device is present and register with v4l and usb if it is */ | ||
429 | static int usb_si4713_probe(struct usb_interface *intf, | ||
430 | const struct usb_device_id *id) | ||
431 | { | ||
432 | struct si4713_usb_device *radio; | ||
433 | struct i2c_adapter *adapter; | ||
434 | struct v4l2_subdev *sd; | ||
435 | int retval = -ENOMEM; | ||
436 | |||
437 | dev_info(&intf->dev, "Si4713 development board discovered: (%04X:%04X)\n", | ||
438 | id->idVendor, id->idProduct); | ||
439 | |||
440 | /* Initialize local device structure */ | ||
441 | radio = kzalloc(sizeof(struct si4713_usb_device), GFP_KERNEL); | ||
442 | if (radio) | ||
443 | radio->buffer = kmalloc(BUFFER_LENGTH, GFP_KERNEL); | ||
444 | |||
445 | if (!radio || !radio->buffer) { | ||
446 | dev_err(&intf->dev, "kmalloc for si4713_usb_device failed\n"); | ||
447 | kfree(radio); | ||
448 | return -ENOMEM; | ||
449 | } | ||
450 | |||
451 | mutex_init(&radio->lock); | ||
452 | |||
453 | radio->usbdev = interface_to_usbdev(intf); | ||
454 | radio->intf = intf; | ||
455 | usb_set_intfdata(intf, &radio->v4l2_dev); | ||
456 | |||
457 | retval = si4713_start_seq(radio); | ||
458 | if (retval < 0) | ||
459 | goto err_v4l2; | ||
460 | |||
461 | retval = v4l2_device_register(&intf->dev, &radio->v4l2_dev); | ||
462 | if (retval < 0) { | ||
463 | dev_err(&intf->dev, "couldn't register v4l2_device\n"); | ||
464 | goto err_v4l2; | ||
465 | } | ||
466 | |||
467 | retval = si4713_register_i2c_adapter(radio); | ||
468 | if (retval < 0) { | ||
469 | dev_err(&intf->dev, "could not register i2c device\n"); | ||
470 | goto err_i2cdev; | ||
471 | } | ||
472 | |||
473 | adapter = &radio->i2c_adapter; | ||
474 | sd = v4l2_i2c_new_subdev_board(&radio->v4l2_dev, adapter, | ||
475 | &si4713_board_info, NULL); | ||
476 | radio->v4l2_subdev = sd; | ||
477 | if (!sd) { | ||
478 | dev_err(&intf->dev, "cannot get v4l2 subdevice\n"); | ||
479 | retval = -ENODEV; | ||
480 | goto del_adapter; | ||
481 | } | ||
482 | |||
483 | radio->vdev.ctrl_handler = sd->ctrl_handler; | ||
484 | radio->v4l2_dev.release = usb_si4713_video_device_release; | ||
485 | strlcpy(radio->vdev.name, radio->v4l2_dev.name, | ||
486 | sizeof(radio->vdev.name)); | ||
487 | radio->vdev.v4l2_dev = &radio->v4l2_dev; | ||
488 | radio->vdev.fops = &usb_si4713_fops; | ||
489 | radio->vdev.ioctl_ops = &usb_si4713_ioctl_ops; | ||
490 | radio->vdev.lock = &radio->lock; | ||
491 | radio->vdev.release = video_device_release_empty; | ||
492 | radio->vdev.vfl_dir = VFL_DIR_TX; | ||
493 | |||
494 | video_set_drvdata(&radio->vdev, radio); | ||
495 | set_bit(V4L2_FL_USE_FH_PRIO, &radio->vdev.flags); | ||
496 | |||
497 | retval = video_register_device(&radio->vdev, VFL_TYPE_RADIO, -1); | ||
498 | if (retval < 0) { | ||
499 | dev_err(&intf->dev, "could not register video device\n"); | ||
500 | goto del_adapter; | ||
501 | } | ||
502 | |||
503 | dev_info(&intf->dev, "V4L2 device registered as %s\n", | ||
504 | video_device_node_name(&radio->vdev)); | ||
505 | |||
506 | return 0; | ||
507 | |||
508 | del_adapter: | ||
509 | i2c_del_adapter(adapter); | ||
510 | err_i2cdev: | ||
511 | v4l2_device_unregister(&radio->v4l2_dev); | ||
512 | err_v4l2: | ||
513 | kfree(radio->buffer); | ||
514 | kfree(radio); | ||
515 | return retval; | ||
516 | } | ||
517 | |||
518 | static void usb_si4713_disconnect(struct usb_interface *intf) | ||
519 | { | ||
520 | struct si4713_usb_device *radio = to_si4713_dev(usb_get_intfdata(intf)); | ||
521 | |||
522 | dev_info(&intf->dev, "Si4713 development board now disconnected\n"); | ||
523 | |||
524 | mutex_lock(&radio->lock); | ||
525 | usb_set_intfdata(intf, NULL); | ||
526 | video_unregister_device(&radio->vdev); | ||
527 | v4l2_device_disconnect(&radio->v4l2_dev); | ||
528 | mutex_unlock(&radio->lock); | ||
529 | v4l2_device_put(&radio->v4l2_dev); | ||
530 | } | ||
531 | |||
532 | /* USB subsystem interface */ | ||
533 | static struct usb_driver usb_si4713_driver = { | ||
534 | .name = "radio-usb-si4713", | ||
535 | .probe = usb_si4713_probe, | ||
536 | .disconnect = usb_si4713_disconnect, | ||
537 | .id_table = usb_si4713_usb_device_table, | ||
538 | }; | ||
539 | |||
540 | module_usb_driver(usb_si4713_driver); | ||