diff options
author | Manjunatha Halli <manjunatha_halli@ti.com> | 2011-01-11 04:34:26 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2011-03-21 19:32:35 -0400 |
commit | 7e38849631c2f570fe66b52b5b54697fe338f194 (patch) | |
tree | e7bc1565f54352f9466d674e8f36c6a307e3022b | |
parent | 8d19824ba2ef37ff989bee5941c87b45c4c6f468 (diff) |
[media] drivers:media:radio: wl128x: FM Driver V4L2 sources
This module interfaces V4L2 subsystem and FM common module.
It registers itself with V4L2 as Radio module.
Signed-off-by: Manjunatha Halli <manjunatha_halli@ti.com>
Reviewed-by: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r-- | drivers/media/radio/wl128x/fmdrv_v4l2.c | 580 | ||||
-rw-r--r-- | drivers/media/radio/wl128x/fmdrv_v4l2.h | 33 |
2 files changed, 613 insertions, 0 deletions
diff --git a/drivers/media/radio/wl128x/fmdrv_v4l2.c b/drivers/media/radio/wl128x/fmdrv_v4l2.c new file mode 100644 index 000000000000..d50e5ac75ab6 --- /dev/null +++ b/drivers/media/radio/wl128x/fmdrv_v4l2.c | |||
@@ -0,0 +1,580 @@ | |||
1 | /* | ||
2 | * FM Driver for Connectivity chip of Texas Instruments. | ||
3 | * This file provides interfaces to V4L2 subsystem. | ||
4 | * | ||
5 | * This module registers with V4L2 subsystem as Radio | ||
6 | * data system interface (/dev/radio). During the registration, | ||
7 | * it will expose two set of function pointers. | ||
8 | * | ||
9 | * 1) File operation related API (open, close, read, write, poll...etc). | ||
10 | * 2) Set of V4L2 IOCTL complaint API. | ||
11 | * | ||
12 | * Copyright (C) 2011 Texas Instruments | ||
13 | * Author: Raja Mani <raja_mani@ti.com> | ||
14 | * Author: Manjunatha Halli <manjunatha_halli@ti.com> | ||
15 | * | ||
16 | * This program is free software; you can redistribute it and/or modify | ||
17 | * it under the terms of the GNU General Public License version 2 as | ||
18 | * published by the Free Software Foundation. | ||
19 | * | ||
20 | * This program is distributed in the hope that it will be useful, | ||
21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
23 | * GNU General Public License for more details. | ||
24 | * | ||
25 | * You should have received a copy of the GNU General Public License | ||
26 | * along with this program; if not, write to the Free Software | ||
27 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
28 | * | ||
29 | */ | ||
30 | |||
31 | #include "fmdrv.h" | ||
32 | #include "fmdrv_v4l2.h" | ||
33 | #include "fmdrv_common.h" | ||
34 | #include "fmdrv_rx.h" | ||
35 | #include "fmdrv_tx.h" | ||
36 | |||
37 | static struct video_device *gradio_dev; | ||
38 | static u8 radio_disconnected; | ||
39 | |||
40 | /* -- V4L2 RADIO (/dev/radioX) device file operation interfaces --- */ | ||
41 | |||
42 | /* Read RX RDS data */ | ||
43 | static ssize_t fm_v4l2_fops_read(struct file *file, char __user * buf, | ||
44 | size_t count, loff_t *ppos) | ||
45 | { | ||
46 | u8 rds_mode; | ||
47 | int ret; | ||
48 | struct fmdev *fmdev; | ||
49 | |||
50 | fmdev = video_drvdata(file); | ||
51 | |||
52 | if (!radio_disconnected) { | ||
53 | fmerr("FM device is already disconnected\n"); | ||
54 | return -EIO; | ||
55 | } | ||
56 | |||
57 | /* Turn on RDS mode , if it is disabled */ | ||
58 | ret = fm_rx_get_rds_mode(fmdev, &rds_mode); | ||
59 | if (ret < 0) { | ||
60 | fmerr("Unable to read current rds mode\n"); | ||
61 | return ret; | ||
62 | } | ||
63 | |||
64 | if (rds_mode == FM_RDS_DISABLE) { | ||
65 | ret = fmc_set_rds_mode(fmdev, FM_RDS_ENABLE); | ||
66 | if (ret < 0) { | ||
67 | fmerr("Failed to enable rds mode\n"); | ||
68 | return ret; | ||
69 | } | ||
70 | } | ||
71 | |||
72 | /* Copy RDS data from internal buffer to user buffer */ | ||
73 | return fmc_transfer_rds_from_internal_buff(fmdev, file, buf, count); | ||
74 | } | ||
75 | |||
76 | /* Write TX RDS data */ | ||
77 | static ssize_t fm_v4l2_fops_write(struct file *file, const char __user * buf, | ||
78 | size_t count, loff_t *ppos) | ||
79 | { | ||
80 | struct tx_rds rds; | ||
81 | int ret; | ||
82 | struct fmdev *fmdev; | ||
83 | |||
84 | ret = copy_from_user(&rds, buf, sizeof(rds)); | ||
85 | fmdbg("(%d)type: %d, text %s, af %d\n", | ||
86 | ret, rds.text_type, rds.text, rds.af_freq); | ||
87 | |||
88 | fmdev = video_drvdata(file); | ||
89 | fm_tx_set_radio_text(fmdev, rds.text, rds.text_type); | ||
90 | fm_tx_set_af(fmdev, rds.af_freq); | ||
91 | |||
92 | return 0; | ||
93 | } | ||
94 | |||
95 | static u32 fm_v4l2_fops_poll(struct file *file, struct poll_table_struct *pts) | ||
96 | { | ||
97 | int ret; | ||
98 | struct fmdev *fmdev; | ||
99 | |||
100 | fmdev = video_drvdata(file); | ||
101 | ret = fmc_is_rds_data_available(fmdev, file, pts); | ||
102 | if (ret < 0) | ||
103 | return POLLIN | POLLRDNORM; | ||
104 | |||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | /* | ||
109 | * Handle open request for "/dev/radioX" device. | ||
110 | * Start with FM RX mode as default. | ||
111 | */ | ||
112 | static int fm_v4l2_fops_open(struct file *file) | ||
113 | { | ||
114 | int ret; | ||
115 | struct fmdev *fmdev = NULL; | ||
116 | |||
117 | /* Don't allow multiple open */ | ||
118 | if (radio_disconnected) { | ||
119 | fmerr("FM device is already opened\n"); | ||
120 | return -EBUSY; | ||
121 | } | ||
122 | |||
123 | fmdev = video_drvdata(file); | ||
124 | |||
125 | ret = fmc_prepare(fmdev); | ||
126 | if (ret < 0) { | ||
127 | fmerr("Unable to prepare FM CORE\n"); | ||
128 | return ret; | ||
129 | } | ||
130 | |||
131 | fmdbg("Load FM RX firmware..\n"); | ||
132 | |||
133 | ret = fmc_set_mode(fmdev, FM_MODE_RX); | ||
134 | if (ret < 0) { | ||
135 | fmerr("Unable to load FM RX firmware\n"); | ||
136 | return ret; | ||
137 | } | ||
138 | radio_disconnected = 1; | ||
139 | |||
140 | return ret; | ||
141 | } | ||
142 | |||
143 | static int fm_v4l2_fops_release(struct file *file) | ||
144 | { | ||
145 | int ret; | ||
146 | struct fmdev *fmdev; | ||
147 | |||
148 | fmdev = video_drvdata(file); | ||
149 | if (!radio_disconnected) { | ||
150 | fmdbg("FM device is already closed\n"); | ||
151 | return 0; | ||
152 | } | ||
153 | |||
154 | ret = fmc_set_mode(fmdev, FM_MODE_OFF); | ||
155 | if (ret < 0) { | ||
156 | fmerr("Unable to turn off the chip\n"); | ||
157 | return ret; | ||
158 | } | ||
159 | |||
160 | ret = fmc_release(fmdev); | ||
161 | if (ret < 0) { | ||
162 | fmerr("FM CORE release failed\n"); | ||
163 | return ret; | ||
164 | } | ||
165 | radio_disconnected = 0; | ||
166 | |||
167 | return ret; | ||
168 | } | ||
169 | |||
170 | /* V4L2 RADIO (/dev/radioX) device IOCTL interfaces */ | ||
171 | static int fm_v4l2_vidioc_querycap(struct file *file, void *priv, | ||
172 | struct v4l2_capability *capability) | ||
173 | { | ||
174 | strlcpy(capability->driver, FM_DRV_NAME, sizeof(capability->driver)); | ||
175 | strlcpy(capability->card, FM_DRV_CARD_SHORT_NAME, | ||
176 | sizeof(capability->card)); | ||
177 | sprintf(capability->bus_info, "UART"); | ||
178 | capability->version = FM_DRV_RADIO_VERSION; | ||
179 | capability->capabilities = V4L2_CAP_HW_FREQ_SEEK | V4L2_CAP_TUNER | | ||
180 | V4L2_CAP_RADIO | V4L2_CAP_MODULATOR | | ||
181 | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | | ||
182 | V4L2_CAP_RDS_CAPTURE; | ||
183 | |||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | static int fm_g_volatile_ctrl(struct v4l2_ctrl *ctrl) | ||
188 | { | ||
189 | struct fmdev *fmdev = container_of(ctrl->handler, | ||
190 | struct fmdev, ctrl_handler); | ||
191 | |||
192 | switch (ctrl->id) { | ||
193 | case V4L2_CID_TUNE_ANTENNA_CAPACITOR: | ||
194 | ctrl->val = fm_tx_get_tune_cap_val(fmdev); | ||
195 | break; | ||
196 | default: | ||
197 | fmwarn("%s: Unknown IOCTL: %d\n", __func__, ctrl->id); | ||
198 | break; | ||
199 | } | ||
200 | |||
201 | return 0; | ||
202 | } | ||
203 | |||
204 | static int fm_v4l2_s_ctrl(struct v4l2_ctrl *ctrl) | ||
205 | { | ||
206 | struct fmdev *fmdev = container_of(ctrl->handler, | ||
207 | struct fmdev, ctrl_handler); | ||
208 | |||
209 | switch (ctrl->id) { | ||
210 | case V4L2_CID_AUDIO_VOLUME: /* set volume */ | ||
211 | return fm_rx_set_volume(fmdev, (u16)ctrl->val); | ||
212 | |||
213 | case V4L2_CID_AUDIO_MUTE: /* set mute */ | ||
214 | return fmc_set_mute_mode(fmdev, (u8)ctrl->val); | ||
215 | |||
216 | case V4L2_CID_TUNE_POWER_LEVEL: | ||
217 | /* set TX power level - ext control */ | ||
218 | return fm_tx_set_pwr_lvl(fmdev, (u8)ctrl->val); | ||
219 | |||
220 | case V4L2_CID_TUNE_PREEMPHASIS: | ||
221 | return fm_tx_set_preemph_filter(fmdev, (u8) ctrl->val); | ||
222 | |||
223 | default: | ||
224 | return -EINVAL; | ||
225 | } | ||
226 | } | ||
227 | |||
228 | static int fm_v4l2_vidioc_g_audio(struct file *file, void *priv, | ||
229 | struct v4l2_audio *audio) | ||
230 | { | ||
231 | memset(audio, 0, sizeof(*audio)); | ||
232 | strcpy(audio->name, "Radio"); | ||
233 | audio->capability = V4L2_AUDCAP_STEREO; | ||
234 | |||
235 | return 0; | ||
236 | } | ||
237 | |||
238 | static int fm_v4l2_vidioc_s_audio(struct file *file, void *priv, | ||
239 | struct v4l2_audio *audio) | ||
240 | { | ||
241 | if (audio->index != 0) | ||
242 | return -EINVAL; | ||
243 | |||
244 | return 0; | ||
245 | } | ||
246 | |||
247 | /* Get tuner attributes. If current mode is NOT RX, return error */ | ||
248 | static int fm_v4l2_vidioc_g_tuner(struct file *file, void *priv, | ||
249 | struct v4l2_tuner *tuner) | ||
250 | { | ||
251 | struct fmdev *fmdev = video_drvdata(file); | ||
252 | u32 bottom_freq; | ||
253 | u32 top_freq; | ||
254 | u16 stereo_mono_mode; | ||
255 | u16 rssilvl; | ||
256 | int ret; | ||
257 | |||
258 | if (tuner->index != 0) | ||
259 | return -EINVAL; | ||
260 | |||
261 | if (fmdev->curr_fmmode != FM_MODE_RX) | ||
262 | return -EPERM; | ||
263 | |||
264 | ret = fm_rx_get_band_freq_range(fmdev, &bottom_freq, &top_freq); | ||
265 | if (ret != 0) | ||
266 | return ret; | ||
267 | |||
268 | ret = fm_rx_get_stereo_mono(fmdev, &stereo_mono_mode); | ||
269 | if (ret != 0) | ||
270 | return ret; | ||
271 | |||
272 | ret = fm_rx_get_rssi_level(fmdev, &rssilvl); | ||
273 | if (ret != 0) | ||
274 | return ret; | ||
275 | |||
276 | strcpy(tuner->name, "FM"); | ||
277 | tuner->type = V4L2_TUNER_RADIO; | ||
278 | /* Store rangelow and rangehigh freq in unit of 62.5 Hz */ | ||
279 | tuner->rangelow = bottom_freq * 16; | ||
280 | tuner->rangehigh = top_freq * 16; | ||
281 | tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO | | ||
282 | ((fmdev->rx.rds.flag == FM_RDS_ENABLE) ? V4L2_TUNER_SUB_RDS : 0); | ||
283 | tuner->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS | | ||
284 | V4L2_TUNER_CAP_LOW; | ||
285 | tuner->audmode = (stereo_mono_mode ? | ||
286 | V4L2_TUNER_MODE_MONO : V4L2_TUNER_MODE_STEREO); | ||
287 | |||
288 | /* | ||
289 | * Actual rssi value lies in between -128 to +127. | ||
290 | * Convert this range from 0 to 255 by adding +128 | ||
291 | */ | ||
292 | rssilvl += 128; | ||
293 | |||
294 | /* | ||
295 | * Return signal strength value should be within 0 to 65535. | ||
296 | * Find out correct signal radio by multiplying (65535/255) = 257 | ||
297 | */ | ||
298 | tuner->signal = rssilvl * 257; | ||
299 | tuner->afc = 0; | ||
300 | |||
301 | return ret; | ||
302 | } | ||
303 | |||
304 | /* | ||
305 | * Set tuner attributes. If current mode is NOT RX, set to RX. | ||
306 | * Currently, we set only audio mode (mono/stereo) and RDS state (on/off). | ||
307 | * Should we set other tuner attributes, too? | ||
308 | */ | ||
309 | static int fm_v4l2_vidioc_s_tuner(struct file *file, void *priv, | ||
310 | struct v4l2_tuner *tuner) | ||
311 | { | ||
312 | struct fmdev *fmdev = video_drvdata(file); | ||
313 | u16 aud_mode; | ||
314 | u8 rds_mode; | ||
315 | int ret; | ||
316 | |||
317 | if (tuner->index != 0) | ||
318 | return -EINVAL; | ||
319 | |||
320 | aud_mode = (tuner->audmode == V4L2_TUNER_MODE_STEREO) ? | ||
321 | FM_STEREO_MODE : FM_MONO_MODE; | ||
322 | rds_mode = (tuner->rxsubchans & V4L2_TUNER_SUB_RDS) ? | ||
323 | FM_RDS_ENABLE : FM_RDS_DISABLE; | ||
324 | |||
325 | if (fmdev->curr_fmmode != FM_MODE_RX) { | ||
326 | ret = fmc_set_mode(fmdev, FM_MODE_RX); | ||
327 | if (ret < 0) { | ||
328 | fmerr("Failed to set RX mode\n"); | ||
329 | return ret; | ||
330 | } | ||
331 | } | ||
332 | |||
333 | ret = fmc_set_stereo_mono(fmdev, aud_mode); | ||
334 | if (ret < 0) { | ||
335 | fmerr("Failed to set RX stereo/mono mode\n"); | ||
336 | return ret; | ||
337 | } | ||
338 | |||
339 | ret = fmc_set_rds_mode(fmdev, rds_mode); | ||
340 | if (ret < 0) | ||
341 | fmerr("Failed to set RX RDS mode\n"); | ||
342 | |||
343 | return ret; | ||
344 | } | ||
345 | |||
346 | /* Get tuner or modulator radio frequency */ | ||
347 | static int fm_v4l2_vidioc_g_freq(struct file *file, void *priv, | ||
348 | struct v4l2_frequency *freq) | ||
349 | { | ||
350 | struct fmdev *fmdev = video_drvdata(file); | ||
351 | int ret; | ||
352 | |||
353 | ret = fmc_get_freq(fmdev, &freq->frequency); | ||
354 | if (ret < 0) { | ||
355 | fmerr("Failed to get frequency\n"); | ||
356 | return ret; | ||
357 | } | ||
358 | |||
359 | /* Frequency unit of 62.5 Hz*/ | ||
360 | freq->frequency = (u32) freq->frequency * 16; | ||
361 | |||
362 | return 0; | ||
363 | } | ||
364 | |||
365 | /* Set tuner or modulator radio frequency */ | ||
366 | static int fm_v4l2_vidioc_s_freq(struct file *file, void *priv, | ||
367 | struct v4l2_frequency *freq) | ||
368 | { | ||
369 | struct fmdev *fmdev = video_drvdata(file); | ||
370 | |||
371 | /* | ||
372 | * As V4L2_TUNER_CAP_LOW is set 1 user sends the frequency | ||
373 | * in units of 62.5 Hz. | ||
374 | */ | ||
375 | freq->frequency = (u32)(freq->frequency / 16); | ||
376 | |||
377 | return fmc_set_freq(fmdev, freq->frequency); | ||
378 | } | ||
379 | |||
380 | /* Set hardware frequency seek. If current mode is NOT RX, set it RX. */ | ||
381 | static int fm_v4l2_vidioc_s_hw_freq_seek(struct file *file, void *priv, | ||
382 | struct v4l2_hw_freq_seek *seek) | ||
383 | { | ||
384 | struct fmdev *fmdev = video_drvdata(file); | ||
385 | int ret; | ||
386 | |||
387 | if (fmdev->curr_fmmode != FM_MODE_RX) { | ||
388 | ret = fmc_set_mode(fmdev, FM_MODE_RX); | ||
389 | if (ret != 0) { | ||
390 | fmerr("Failed to set RX mode\n"); | ||
391 | return ret; | ||
392 | } | ||
393 | } | ||
394 | |||
395 | ret = fm_rx_seek(fmdev, seek->seek_upward, seek->wrap_around, | ||
396 | seek->spacing); | ||
397 | if (ret < 0) | ||
398 | fmerr("RX seek failed - %d\n", ret); | ||
399 | |||
400 | return ret; | ||
401 | } | ||
402 | /* Get modulator attributes. If mode is not TX, return no attributes. */ | ||
403 | static int fm_v4l2_vidioc_g_modulator(struct file *file, void *priv, | ||
404 | struct v4l2_modulator *mod) | ||
405 | { | ||
406 | struct fmdev *fmdev = video_drvdata(file);; | ||
407 | |||
408 | if (mod->index != 0) | ||
409 | return -EINVAL; | ||
410 | |||
411 | if (fmdev->curr_fmmode != FM_MODE_TX) | ||
412 | return -EPERM; | ||
413 | |||
414 | mod->txsubchans = ((fmdev->tx_data.aud_mode == FM_STEREO_MODE) ? | ||
415 | V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO) | | ||
416 | ((fmdev->tx_data.rds.flag == FM_RDS_ENABLE) ? | ||
417 | V4L2_TUNER_SUB_RDS : 0); | ||
418 | |||
419 | mod->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS | | ||
420 | V4L2_TUNER_CAP_LOW; | ||
421 | |||
422 | return 0; | ||
423 | } | ||
424 | |||
425 | /* Set modulator attributes. If mode is not TX, set to TX. */ | ||
426 | static int fm_v4l2_vidioc_s_modulator(struct file *file, void *priv, | ||
427 | struct v4l2_modulator *mod) | ||
428 | { | ||
429 | struct fmdev *fmdev = video_drvdata(file); | ||
430 | u8 rds_mode; | ||
431 | u16 aud_mode; | ||
432 | int ret; | ||
433 | |||
434 | if (mod->index != 0) | ||
435 | return -EINVAL; | ||
436 | |||
437 | if (fmdev->curr_fmmode != FM_MODE_TX) { | ||
438 | ret = fmc_set_mode(fmdev, FM_MODE_TX); | ||
439 | if (ret != 0) { | ||
440 | fmerr("Failed to set TX mode\n"); | ||
441 | return ret; | ||
442 | } | ||
443 | } | ||
444 | |||
445 | aud_mode = (mod->txsubchans & V4L2_TUNER_SUB_STEREO) ? | ||
446 | FM_STEREO_MODE : FM_MONO_MODE; | ||
447 | rds_mode = (mod->txsubchans & V4L2_TUNER_SUB_RDS) ? | ||
448 | FM_RDS_ENABLE : FM_RDS_DISABLE; | ||
449 | ret = fm_tx_set_stereo_mono(fmdev, aud_mode); | ||
450 | if (ret < 0) { | ||
451 | fmerr("Failed to set mono/stereo mode for TX\n"); | ||
452 | return ret; | ||
453 | } | ||
454 | ret = fm_tx_set_rds_mode(fmdev, rds_mode); | ||
455 | if (ret < 0) | ||
456 | fmerr("Failed to set rds mode for TX\n"); | ||
457 | |||
458 | return ret; | ||
459 | } | ||
460 | |||
461 | static const struct v4l2_file_operations fm_drv_fops = { | ||
462 | .owner = THIS_MODULE, | ||
463 | .read = fm_v4l2_fops_read, | ||
464 | .write = fm_v4l2_fops_write, | ||
465 | .poll = fm_v4l2_fops_poll, | ||
466 | .unlocked_ioctl = video_ioctl2, | ||
467 | .open = fm_v4l2_fops_open, | ||
468 | .release = fm_v4l2_fops_release, | ||
469 | }; | ||
470 | |||
471 | static const struct v4l2_ctrl_ops fm_ctrl_ops = { | ||
472 | .s_ctrl = fm_v4l2_s_ctrl, | ||
473 | .g_volatile_ctrl = fm_g_volatile_ctrl, | ||
474 | }; | ||
475 | static const struct v4l2_ioctl_ops fm_drv_ioctl_ops = { | ||
476 | .vidioc_querycap = fm_v4l2_vidioc_querycap, | ||
477 | .vidioc_g_audio = fm_v4l2_vidioc_g_audio, | ||
478 | .vidioc_s_audio = fm_v4l2_vidioc_s_audio, | ||
479 | .vidioc_g_tuner = fm_v4l2_vidioc_g_tuner, | ||
480 | .vidioc_s_tuner = fm_v4l2_vidioc_s_tuner, | ||
481 | .vidioc_g_frequency = fm_v4l2_vidioc_g_freq, | ||
482 | .vidioc_s_frequency = fm_v4l2_vidioc_s_freq, | ||
483 | .vidioc_s_hw_freq_seek = fm_v4l2_vidioc_s_hw_freq_seek, | ||
484 | .vidioc_g_modulator = fm_v4l2_vidioc_g_modulator, | ||
485 | .vidioc_s_modulator = fm_v4l2_vidioc_s_modulator | ||
486 | }; | ||
487 | |||
488 | /* V4L2 RADIO device parent structure */ | ||
489 | static struct video_device fm_viddev_template = { | ||
490 | .fops = &fm_drv_fops, | ||
491 | .ioctl_ops = &fm_drv_ioctl_ops, | ||
492 | .name = FM_DRV_NAME, | ||
493 | .release = video_device_release, | ||
494 | }; | ||
495 | |||
496 | int fm_v4l2_init_video_device(struct fmdev *fmdev, int radio_nr) | ||
497 | { | ||
498 | struct v4l2_ctrl *ctrl; | ||
499 | int ret; | ||
500 | |||
501 | /* Init mutex for core locking */ | ||
502 | mutex_init(&fmdev->mutex); | ||
503 | |||
504 | /* Allocate new video device */ | ||
505 | gradio_dev = video_device_alloc(); | ||
506 | if (NULL == gradio_dev) { | ||
507 | fmerr("Can't allocate video device\n"); | ||
508 | return -ENOMEM; | ||
509 | } | ||
510 | |||
511 | /* Setup FM driver's V4L2 properties */ | ||
512 | memcpy(gradio_dev, &fm_viddev_template, sizeof(fm_viddev_template)); | ||
513 | |||
514 | video_set_drvdata(gradio_dev, fmdev); | ||
515 | |||
516 | gradio_dev->lock = &fmdev->mutex; | ||
517 | |||
518 | /* Register with V4L2 subsystem as RADIO device */ | ||
519 | if (video_register_device(gradio_dev, VFL_TYPE_RADIO, radio_nr)) { | ||
520 | video_device_release(gradio_dev); | ||
521 | fmerr("Could not register video device\n"); | ||
522 | return -ENOMEM; | ||
523 | } | ||
524 | |||
525 | fmdev->radio_dev = gradio_dev; | ||
526 | |||
527 | /* Register to v4l2 ctrl handler framework */ | ||
528 | fmdev->radio_dev->ctrl_handler = &fmdev->ctrl_handler; | ||
529 | |||
530 | ret = v4l2_ctrl_handler_init(&fmdev->ctrl_handler, 5); | ||
531 | if (ret < 0) { | ||
532 | fmerr("(fmdev): Can't init ctrl handler\n"); | ||
533 | v4l2_ctrl_handler_free(&fmdev->ctrl_handler); | ||
534 | return -EBUSY; | ||
535 | } | ||
536 | |||
537 | /* | ||
538 | * Following controls are handled by V4L2 control framework. | ||
539 | * Added in ascending ID order. | ||
540 | */ | ||
541 | v4l2_ctrl_new_std(&fmdev->ctrl_handler, &fm_ctrl_ops, | ||
542 | V4L2_CID_AUDIO_VOLUME, FM_RX_VOLUME_MIN, | ||
543 | FM_RX_VOLUME_MAX, 1, FM_RX_VOLUME_MAX); | ||
544 | |||
545 | v4l2_ctrl_new_std(&fmdev->ctrl_handler, &fm_ctrl_ops, | ||
546 | V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); | ||
547 | |||
548 | v4l2_ctrl_new_std_menu(&fmdev->ctrl_handler, &fm_ctrl_ops, | ||
549 | V4L2_CID_TUNE_PREEMPHASIS, V4L2_PREEMPHASIS_75_uS, | ||
550 | 0, V4L2_PREEMPHASIS_75_uS); | ||
551 | |||
552 | v4l2_ctrl_new_std(&fmdev->ctrl_handler, &fm_ctrl_ops, | ||
553 | V4L2_CID_TUNE_POWER_LEVEL, FM_PWR_LVL_LOW, | ||
554 | FM_PWR_LVL_HIGH, 1, FM_PWR_LVL_HIGH); | ||
555 | |||
556 | ctrl = v4l2_ctrl_new_std(&fmdev->ctrl_handler, &fm_ctrl_ops, | ||
557 | V4L2_CID_TUNE_ANTENNA_CAPACITOR, 0, | ||
558 | 255, 1, 255); | ||
559 | |||
560 | if (ctrl) | ||
561 | ctrl->is_volatile = 1; | ||
562 | |||
563 | return 0; | ||
564 | } | ||
565 | |||
566 | void *fm_v4l2_deinit_video_device(void) | ||
567 | { | ||
568 | struct fmdev *fmdev; | ||
569 | |||
570 | |||
571 | fmdev = video_get_drvdata(gradio_dev); | ||
572 | |||
573 | /* Unregister to v4l2 ctrl handler framework*/ | ||
574 | v4l2_ctrl_handler_free(&fmdev->ctrl_handler); | ||
575 | |||
576 | /* Unregister RADIO device from V4L2 subsystem */ | ||
577 | video_unregister_device(gradio_dev); | ||
578 | |||
579 | return fmdev; | ||
580 | } | ||
diff --git a/drivers/media/radio/wl128x/fmdrv_v4l2.h b/drivers/media/radio/wl128x/fmdrv_v4l2.h new file mode 100644 index 000000000000..0ba79d745e2f --- /dev/null +++ b/drivers/media/radio/wl128x/fmdrv_v4l2.h | |||
@@ -0,0 +1,33 @@ | |||
1 | /* | ||
2 | * FM Driver for Connectivity chip of Texas Instruments. | ||
3 | * | ||
4 | * FM V4L2 module header. | ||
5 | * | ||
6 | * Copyright (C) 2011 Texas Instruments | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | * | ||
21 | */ | ||
22 | |||
23 | #ifndef _FMDRV_V4L2_H | ||
24 | #define _FMDRV_V4L2_H | ||
25 | |||
26 | #include <media/v4l2-ioctl.h> | ||
27 | #include <media/v4l2-common.h> | ||
28 | #include <media/v4l2-ctrls.h> | ||
29 | |||
30 | int fm_v4l2_init_video_device(struct fmdev *, int); | ||
31 | void *fm_v4l2_deinit_video_device(void); | ||
32 | |||
33 | #endif | ||