Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/media/radio/wl128x/fmdrv_v4l2.c
15112 views
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 <[email protected]>
14
* Author: Manjunatha Halli <[email protected]>
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->cur.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
}
581
582