~ubuntu-branches/ubuntu/precise/linux-lowlatency/precise

« back to all changes in this revision

Viewing changes to drivers/media/radio/si470x/radio-si470x-common.c

  • Committer: Package Import Robot
  • Author(s): Alessio Igor Bogani
  • Date: 2011-10-26 11:13:05 UTC
  • Revision ID: package-import@ubuntu.com-20111026111305-tz023xykf0i6eosh
Tags: upstream-3.2.0
Import upstream version 3.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *  drivers/media/radio/si470x/radio-si470x-common.c
 
3
 *
 
4
 *  Driver for radios with Silicon Labs Si470x FM Radio Receivers
 
5
 *
 
6
 *  Copyright (c) 2009 Tobias Lorenz <tobias.lorenz@gmx.net>
 
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 as published by
 
10
 * the Free Software Foundation; either version 2 of the License, or
 
11
 * (at your option) any later version.
 
12
 *
 
13
 * This program is distributed in the hope that it will be useful,
 
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
16
 * GNU General Public License for more details.
 
17
 *
 
18
 * You should have received a copy of the GNU General Public License
 
19
 * along with this program; if not, write to the Free Software
 
20
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 
21
 */
 
22
 
 
23
 
 
24
/*
 
25
 * History:
 
26
 * 2008-01-12   Tobias Lorenz <tobias.lorenz@gmx.net>
 
27
 *              Version 1.0.0
 
28
 *              - First working version
 
29
 * 2008-01-13   Tobias Lorenz <tobias.lorenz@gmx.net>
 
30
 *              Version 1.0.1
 
31
 *              - Improved error handling, every function now returns errno
 
32
 *              - Improved multi user access (start/mute/stop)
 
33
 *              - Channel doesn't get lost anymore after start/mute/stop
 
34
 *              - RDS support added (polling mode via interrupt EP 1)
 
35
 *              - marked default module parameters with *value*
 
36
 *              - switched from bit structs to bit masks
 
37
 *              - header file cleaned and integrated
 
38
 * 2008-01-14   Tobias Lorenz <tobias.lorenz@gmx.net>
 
39
 *              Version 1.0.2
 
40
 *              - hex values are now lower case
 
41
 *              - commented USB ID for ADS/Tech moved on todo list
 
42
 *              - blacklisted si470x in hid-quirks.c
 
43
 *              - rds buffer handling functions integrated into *_work, *_read
 
44
 *              - rds_command in si470x_poll exchanged against simple retval
 
45
 *              - check for firmware version 15
 
46
 *              - code order and prototypes still remain the same
 
47
 *              - spacing and bottom of band codes remain the same
 
48
 * 2008-01-16   Tobias Lorenz <tobias.lorenz@gmx.net>
 
49
 *              Version 1.0.3
 
50
 *              - code reordered to avoid function prototypes
 
51
 *              - switch/case defaults are now more user-friendly
 
52
 *              - unified comment style
 
53
 *              - applied all checkpatch.pl v1.12 suggestions
 
54
 *                except the warning about the too long lines with bit comments
 
55
 *              - renamed FMRADIO to RADIO to cut line length (checkpatch.pl)
 
56
 * 2008-01-22   Tobias Lorenz <tobias.lorenz@gmx.net>
 
57
 *              Version 1.0.4
 
58
 *              - avoid poss. locking when doing copy_to_user which may sleep
 
59
 *              - RDS is automatically activated on read now
 
60
 *              - code cleaned of unnecessary rds_commands
 
61
 *              - USB Vendor/Product ID for ADS/Tech FM Radio Receiver verified
 
62
 *                (thanks to Guillaume RAMOUSSE)
 
63
 * 2008-01-27   Tobias Lorenz <tobias.lorenz@gmx.net>
 
64
 *              Version 1.0.5
 
65
 *              - number of seek_retries changed to tune_timeout
 
66
 *              - fixed problem with incomplete tune operations by own buffers
 
67
 *              - optimization of variables and printf types
 
68
 *              - improved error logging
 
69
 * 2008-01-31   Tobias Lorenz <tobias.lorenz@gmx.net>
 
70
 *              Oliver Neukum <oliver@neukum.org>
 
71
 *              Version 1.0.6
 
72
 *              - fixed coverity checker warnings in *_usb_driver_disconnect
 
73
 *              - probe()/open() race by correct ordering in probe()
 
74
 *              - DMA coherency rules by separate allocation of all buffers
 
75
 *              - use of endianness macros
 
76
 *              - abuse of spinlock, replaced by mutex
 
77
 *              - racy handling of timer in disconnect,
 
78
 *                replaced by delayed_work
 
79
 *              - racy interruptible_sleep_on(),
 
80
 *                replaced with wait_event_interruptible()
 
81
 *              - handle signals in read()
 
82
 * 2008-02-08   Tobias Lorenz <tobias.lorenz@gmx.net>
 
83
 *              Oliver Neukum <oliver@neukum.org>
 
84
 *              Version 1.0.7
 
85
 *              - usb autosuspend support
 
86
 *              - unplugging fixed
 
87
 * 2008-05-07   Tobias Lorenz <tobias.lorenz@gmx.net>
 
88
 *              Version 1.0.8
 
89
 *              - hardware frequency seek support
 
90
 *              - afc indication
 
91
 *              - more safety checks, let si470x_get_freq return errno
 
92
 *              - vidioc behavior corrected according to v4l2 spec
 
93
 * 2008-10-20   Alexey Klimov <klimov.linux@gmail.com>
 
94
 *              - add support for KWorld USB FM Radio FM700
 
95
 *              - blacklisted KWorld radio in hid-core.c and hid-ids.h
 
96
 * 2008-12-03   Mark Lord <mlord@pobox.com>
 
97
 *              - add support for DealExtreme USB Radio
 
98
 * 2009-01-31   Bob Ross <pigiron@gmx.com>
 
99
 *              - correction of stereo detection/setting
 
100
 *              - correction of signal strength indicator scaling
 
101
 * 2009-01-31   Rick Bronson <rick@efn.org>
 
102
 *              Tobias Lorenz <tobias.lorenz@gmx.net>
 
103
 *              - add LED status output
 
104
 *              - get HW/SW version from scratchpad
 
105
 * 2009-06-16   Edouard Lafargue <edouard@lafargue.name>
 
106
 *              Version 1.0.10
 
107
 *              - add support for interrupt mode for RDS endpoint,
 
108
 *                instead of polling.
 
109
 *                Improves RDS reception significantly
 
110
 */
 
111
 
 
112
 
 
113
/* kernel includes */
 
114
#include "radio-si470x.h"
 
115
 
 
116
 
 
117
 
 
118
/**************************************************************************
 
119
 * Module Parameters
 
120
 **************************************************************************/
 
121
 
 
122
/* Spacing (kHz) */
 
123
/* 0: 200 kHz (USA, Australia) */
 
124
/* 1: 100 kHz (Europe, Japan) */
 
125
/* 2:  50 kHz */
 
126
static unsigned short space = 2;
 
127
module_param(space, ushort, 0444);
 
128
MODULE_PARM_DESC(space, "Spacing: 0=200kHz 1=100kHz *2=50kHz*");
 
129
 
 
130
/* Bottom of Band (MHz) */
 
131
/* 0: 87.5 - 108 MHz (USA, Europe)*/
 
132
/* 1: 76   - 108 MHz (Japan wide band) */
 
133
/* 2: 76   -  90 MHz (Japan) */
 
134
static unsigned short band = 1;
 
135
module_param(band, ushort, 0444);
 
136
MODULE_PARM_DESC(band, "Band: 0=87.5..108MHz *1=76..108MHz* 2=76..90MHz");
 
137
 
 
138
/* De-emphasis */
 
139
/* 0: 75 us (USA) */
 
140
/* 1: 50 us (Europe, Australia, Japan) */
 
141
static unsigned short de = 1;
 
142
module_param(de, ushort, 0444);
 
143
MODULE_PARM_DESC(de, "De-emphasis: 0=75us *1=50us*");
 
144
 
 
145
/* Tune timeout */
 
146
static unsigned int tune_timeout = 3000;
 
147
module_param(tune_timeout, uint, 0644);
 
148
MODULE_PARM_DESC(tune_timeout, "Tune timeout: *3000*");
 
149
 
 
150
/* Seek timeout */
 
151
static unsigned int seek_timeout = 5000;
 
152
module_param(seek_timeout, uint, 0644);
 
153
MODULE_PARM_DESC(seek_timeout, "Seek timeout: *5000*");
 
154
 
 
155
 
 
156
 
 
157
/**************************************************************************
 
158
 * Generic Functions
 
159
 **************************************************************************/
 
160
 
 
161
/*
 
162
 * si470x_set_chan - set the channel
 
163
 */
 
164
static int si470x_set_chan(struct si470x_device *radio, unsigned short chan)
 
165
{
 
166
        int retval;
 
167
        unsigned long timeout;
 
168
        bool timed_out = 0;
 
169
 
 
170
        /* start tuning */
 
171
        radio->registers[CHANNEL] &= ~CHANNEL_CHAN;
 
172
        radio->registers[CHANNEL] |= CHANNEL_TUNE | chan;
 
173
        retval = si470x_set_register(radio, CHANNEL);
 
174
        if (retval < 0)
 
175
                goto done;
 
176
 
 
177
        /* currently I2C driver only uses interrupt way to tune */
 
178
        if (radio->stci_enabled) {
 
179
                INIT_COMPLETION(radio->completion);
 
180
 
 
181
                /* wait till tune operation has completed */
 
182
                retval = wait_for_completion_timeout(&radio->completion,
 
183
                                msecs_to_jiffies(tune_timeout));
 
184
                if (!retval)
 
185
                        timed_out = true;
 
186
        } else {
 
187
                /* wait till tune operation has completed */
 
188
                timeout = jiffies + msecs_to_jiffies(tune_timeout);
 
189
                do {
 
190
                        retval = si470x_get_register(radio, STATUSRSSI);
 
191
                        if (retval < 0)
 
192
                                goto stop;
 
193
                        timed_out = time_after(jiffies, timeout);
 
194
                } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
 
195
                                && (!timed_out));
 
196
        }
 
197
 
 
198
        if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
 
199
                dev_warn(&radio->videodev->dev, "tune does not complete\n");
 
200
        if (timed_out)
 
201
                dev_warn(&radio->videodev->dev,
 
202
                        "tune timed out after %u ms\n", tune_timeout);
 
203
 
 
204
stop:
 
205
        /* stop tuning */
 
206
        radio->registers[CHANNEL] &= ~CHANNEL_TUNE;
 
207
        retval = si470x_set_register(radio, CHANNEL);
 
208
 
 
209
done:
 
210
        return retval;
 
211
}
 
212
 
 
213
 
 
214
/*
 
215
 * si470x_get_freq - get the frequency
 
216
 */
 
217
static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq)
 
218
{
 
219
        unsigned int spacing, band_bottom;
 
220
        unsigned short chan;
 
221
        int retval;
 
222
 
 
223
        /* Spacing (kHz) */
 
224
        switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) {
 
225
        /* 0: 200 kHz (USA, Australia) */
 
226
        case 0:
 
227
                spacing = 0.200 * FREQ_MUL; break;
 
228
        /* 1: 100 kHz (Europe, Japan) */
 
229
        case 1:
 
230
                spacing = 0.100 * FREQ_MUL; break;
 
231
        /* 2:  50 kHz */
 
232
        default:
 
233
                spacing = 0.050 * FREQ_MUL; break;
 
234
        };
 
235
 
 
236
        /* Bottom of Band (MHz) */
 
237
        switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {
 
238
        /* 0: 87.5 - 108 MHz (USA, Europe) */
 
239
        case 0:
 
240
                band_bottom = 87.5 * FREQ_MUL; break;
 
241
        /* 1: 76   - 108 MHz (Japan wide band) */
 
242
        default:
 
243
                band_bottom = 76   * FREQ_MUL; break;
 
244
        /* 2: 76   -  90 MHz (Japan) */
 
245
        case 2:
 
246
                band_bottom = 76   * FREQ_MUL; break;
 
247
        };
 
248
 
 
249
        /* read channel */
 
250
        retval = si470x_get_register(radio, READCHAN);
 
251
        chan = radio->registers[READCHAN] & READCHAN_READCHAN;
 
252
 
 
253
        /* Frequency (MHz) = Spacing (kHz) x Channel + Bottom of Band (MHz) */
 
254
        *freq = chan * spacing + band_bottom;
 
255
 
 
256
        return retval;
 
257
}
 
258
 
 
259
 
 
260
/*
 
261
 * si470x_set_freq - set the frequency
 
262
 */
 
263
int si470x_set_freq(struct si470x_device *radio, unsigned int freq)
 
264
{
 
265
        unsigned int spacing, band_bottom;
 
266
        unsigned short chan;
 
267
 
 
268
        /* Spacing (kHz) */
 
269
        switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) {
 
270
        /* 0: 200 kHz (USA, Australia) */
 
271
        case 0:
 
272
                spacing = 0.200 * FREQ_MUL; break;
 
273
        /* 1: 100 kHz (Europe, Japan) */
 
274
        case 1:
 
275
                spacing = 0.100 * FREQ_MUL; break;
 
276
        /* 2:  50 kHz */
 
277
        default:
 
278
                spacing = 0.050 * FREQ_MUL; break;
 
279
        };
 
280
 
 
281
        /* Bottom of Band (MHz) */
 
282
        switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {
 
283
        /* 0: 87.5 - 108 MHz (USA, Europe) */
 
284
        case 0:
 
285
                band_bottom = 87.5 * FREQ_MUL; break;
 
286
        /* 1: 76   - 108 MHz (Japan wide band) */
 
287
        default:
 
288
                band_bottom = 76   * FREQ_MUL; break;
 
289
        /* 2: 76   -  90 MHz (Japan) */
 
290
        case 2:
 
291
                band_bottom = 76   * FREQ_MUL; break;
 
292
        };
 
293
 
 
294
        /* Chan = [ Freq (Mhz) - Bottom of Band (MHz) ] / Spacing (kHz) */
 
295
        chan = (freq - band_bottom) / spacing;
 
296
 
 
297
        return si470x_set_chan(radio, chan);
 
298
}
 
299
 
 
300
 
 
301
/*
 
302
 * si470x_set_seek - set seek
 
303
 */
 
304
static int si470x_set_seek(struct si470x_device *radio,
 
305
                unsigned int wrap_around, unsigned int seek_upward)
 
306
{
 
307
        int retval = 0;
 
308
        unsigned long timeout;
 
309
        bool timed_out = 0;
 
310
 
 
311
        /* start seeking */
 
312
        radio->registers[POWERCFG] |= POWERCFG_SEEK;
 
313
        if (wrap_around == 1)
 
314
                radio->registers[POWERCFG] &= ~POWERCFG_SKMODE;
 
315
        else
 
316
                radio->registers[POWERCFG] |= POWERCFG_SKMODE;
 
317
        if (seek_upward == 1)
 
318
                radio->registers[POWERCFG] |= POWERCFG_SEEKUP;
 
319
        else
 
320
                radio->registers[POWERCFG] &= ~POWERCFG_SEEKUP;
 
321
        retval = si470x_set_register(radio, POWERCFG);
 
322
        if (retval < 0)
 
323
                goto done;
 
324
 
 
325
        /* currently I2C driver only uses interrupt way to seek */
 
326
        if (radio->stci_enabled) {
 
327
                INIT_COMPLETION(radio->completion);
 
328
 
 
329
                /* wait till seek operation has completed */
 
330
                retval = wait_for_completion_timeout(&radio->completion,
 
331
                                msecs_to_jiffies(seek_timeout));
 
332
                if (!retval)
 
333
                        timed_out = true;
 
334
        } else {
 
335
                /* wait till seek operation has completed */
 
336
                timeout = jiffies + msecs_to_jiffies(seek_timeout);
 
337
                do {
 
338
                        retval = si470x_get_register(radio, STATUSRSSI);
 
339
                        if (retval < 0)
 
340
                                goto stop;
 
341
                        timed_out = time_after(jiffies, timeout);
 
342
                } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
 
343
                                && (!timed_out));
 
344
        }
 
345
 
 
346
        if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
 
347
                dev_warn(&radio->videodev->dev, "seek does not complete\n");
 
348
        if (radio->registers[STATUSRSSI] & STATUSRSSI_SF)
 
349
                dev_warn(&radio->videodev->dev,
 
350
                        "seek failed / band limit reached\n");
 
351
        if (timed_out)
 
352
                dev_warn(&radio->videodev->dev,
 
353
                        "seek timed out after %u ms\n", seek_timeout);
 
354
 
 
355
stop:
 
356
        /* stop seeking */
 
357
        radio->registers[POWERCFG] &= ~POWERCFG_SEEK;
 
358
        retval = si470x_set_register(radio, POWERCFG);
 
359
 
 
360
done:
 
361
        /* try again, if timed out */
 
362
        if ((retval == 0) && timed_out)
 
363
                retval = -EAGAIN;
 
364
 
 
365
        return retval;
 
366
}
 
367
 
 
368
 
 
369
/*
 
370
 * si470x_start - switch on radio
 
371
 */
 
372
int si470x_start(struct si470x_device *radio)
 
373
{
 
374
        int retval;
 
375
 
 
376
        /* powercfg */
 
377
        radio->registers[POWERCFG] =
 
378
                POWERCFG_DMUTE | POWERCFG_ENABLE | POWERCFG_RDSM;
 
379
        retval = si470x_set_register(radio, POWERCFG);
 
380
        if (retval < 0)
 
381
                goto done;
 
382
 
 
383
        /* sysconfig 1 */
 
384
        radio->registers[SYSCONFIG1] =
 
385
                (de << 11) & SYSCONFIG1_DE;             /* DE*/
 
386
        retval = si470x_set_register(radio, SYSCONFIG1);
 
387
        if (retval < 0)
 
388
                goto done;
 
389
 
 
390
        /* sysconfig 2 */
 
391
        radio->registers[SYSCONFIG2] =
 
392
                (0x3f  << 8) |                          /* SEEKTH */
 
393
                ((band  << 6) & SYSCONFIG2_BAND)  |     /* BAND */
 
394
                ((space << 4) & SYSCONFIG2_SPACE) |     /* SPACE */
 
395
                15;                                     /* VOLUME (max) */
 
396
        retval = si470x_set_register(radio, SYSCONFIG2);
 
397
        if (retval < 0)
 
398
                goto done;
 
399
 
 
400
        /* reset last channel */
 
401
        retval = si470x_set_chan(radio,
 
402
                radio->registers[CHANNEL] & CHANNEL_CHAN);
 
403
 
 
404
done:
 
405
        return retval;
 
406
}
 
407
 
 
408
 
 
409
/*
 
410
 * si470x_stop - switch off radio
 
411
 */
 
412
int si470x_stop(struct si470x_device *radio)
 
413
{
 
414
        int retval;
 
415
 
 
416
        /* sysconfig 1 */
 
417
        radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS;
 
418
        retval = si470x_set_register(radio, SYSCONFIG1);
 
419
        if (retval < 0)
 
420
                goto done;
 
421
 
 
422
        /* powercfg */
 
423
        radio->registers[POWERCFG] &= ~POWERCFG_DMUTE;
 
424
        /* POWERCFG_ENABLE has to automatically go low */
 
425
        radio->registers[POWERCFG] |= POWERCFG_ENABLE | POWERCFG_DISABLE;
 
426
        retval = si470x_set_register(radio, POWERCFG);
 
427
 
 
428
done:
 
429
        return retval;
 
430
}
 
431
 
 
432
 
 
433
/*
 
434
 * si470x_rds_on - switch on rds reception
 
435
 */
 
436
static int si470x_rds_on(struct si470x_device *radio)
 
437
{
 
438
        int retval;
 
439
 
 
440
        /* sysconfig 1 */
 
441
        radio->registers[SYSCONFIG1] |= SYSCONFIG1_RDS;
 
442
        retval = si470x_set_register(radio, SYSCONFIG1);
 
443
        if (retval < 0)
 
444
                radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS;
 
445
 
 
446
        return retval;
 
447
}
 
448
 
 
449
 
 
450
 
 
451
/**************************************************************************
 
452
 * File Operations Interface
 
453
 **************************************************************************/
 
454
 
 
455
/*
 
456
 * si470x_fops_read - read RDS data
 
457
 */
 
458
static ssize_t si470x_fops_read(struct file *file, char __user *buf,
 
459
                size_t count, loff_t *ppos)
 
460
{
 
461
        struct si470x_device *radio = video_drvdata(file);
 
462
        int retval = 0;
 
463
        unsigned int block_count = 0;
 
464
 
 
465
        /* switch on rds reception */
 
466
        mutex_lock(&radio->lock);
 
467
        if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
 
468
                si470x_rds_on(radio);
 
469
 
 
470
        /* block if no new data available */
 
471
        while (radio->wr_index == radio->rd_index) {
 
472
                if (file->f_flags & O_NONBLOCK) {
 
473
                        retval = -EWOULDBLOCK;
 
474
                        goto done;
 
475
                }
 
476
                if (wait_event_interruptible(radio->read_queue,
 
477
                        radio->wr_index != radio->rd_index) < 0) {
 
478
                        retval = -EINTR;
 
479
                        goto done;
 
480
                }
 
481
        }
 
482
 
 
483
        /* calculate block count from byte count */
 
484
        count /= 3;
 
485
 
 
486
        /* copy RDS block out of internal buffer and to user buffer */
 
487
        while (block_count < count) {
 
488
                if (radio->rd_index == radio->wr_index)
 
489
                        break;
 
490
 
 
491
                /* always transfer rds complete blocks */
 
492
                if (copy_to_user(buf, &radio->buffer[radio->rd_index], 3))
 
493
                        /* retval = -EFAULT; */
 
494
                        break;
 
495
 
 
496
                /* increment and wrap read pointer */
 
497
                radio->rd_index += 3;
 
498
                if (radio->rd_index >= radio->buf_size)
 
499
                        radio->rd_index = 0;
 
500
 
 
501
                /* increment counters */
 
502
                block_count++;
 
503
                buf += 3;
 
504
                retval += 3;
 
505
        }
 
506
 
 
507
done:
 
508
        mutex_unlock(&radio->lock);
 
509
        return retval;
 
510
}
 
511
 
 
512
 
 
513
/*
 
514
 * si470x_fops_poll - poll RDS data
 
515
 */
 
516
static unsigned int si470x_fops_poll(struct file *file,
 
517
                struct poll_table_struct *pts)
 
518
{
 
519
        struct si470x_device *radio = video_drvdata(file);
 
520
        int retval = 0;
 
521
 
 
522
        /* switch on rds reception */
 
523
 
 
524
        mutex_lock(&radio->lock);
 
525
        if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
 
526
                si470x_rds_on(radio);
 
527
        mutex_unlock(&radio->lock);
 
528
 
 
529
        poll_wait(file, &radio->read_queue, pts);
 
530
 
 
531
        if (radio->rd_index != radio->wr_index)
 
532
                retval = POLLIN | POLLRDNORM;
 
533
 
 
534
        return retval;
 
535
}
 
536
 
 
537
 
 
538
/*
 
539
 * si470x_fops - file operations interface
 
540
 */
 
541
static const struct v4l2_file_operations si470x_fops = {
 
542
        .owner                  = THIS_MODULE,
 
543
        .read                   = si470x_fops_read,
 
544
        .poll                   = si470x_fops_poll,
 
545
        .unlocked_ioctl         = video_ioctl2,
 
546
        .open                   = si470x_fops_open,
 
547
        .release                = si470x_fops_release,
 
548
};
 
549
 
 
550
 
 
551
 
 
552
/**************************************************************************
 
553
 * Video4Linux Interface
 
554
 **************************************************************************/
 
555
 
 
556
/*
 
557
 * si470x_vidioc_queryctrl - enumerate control items
 
558
 */
 
559
static int si470x_vidioc_queryctrl(struct file *file, void *priv,
 
560
                struct v4l2_queryctrl *qc)
 
561
{
 
562
        struct si470x_device *radio = video_drvdata(file);
 
563
        int retval = -EINVAL;
 
564
 
 
565
        /* abort if qc->id is below V4L2_CID_BASE */
 
566
        if (qc->id < V4L2_CID_BASE)
 
567
                goto done;
 
568
 
 
569
        /* search video control */
 
570
        switch (qc->id) {
 
571
        case V4L2_CID_AUDIO_VOLUME:
 
572
                return v4l2_ctrl_query_fill(qc, 0, 15, 1, 15);
 
573
        case V4L2_CID_AUDIO_MUTE:
 
574
                return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
 
575
        }
 
576
 
 
577
        /* disable unsupported base controls */
 
578
        /* to satisfy kradio and such apps */
 
579
        if ((retval == -EINVAL) && (qc->id < V4L2_CID_LASTP1)) {
 
580
                qc->flags = V4L2_CTRL_FLAG_DISABLED;
 
581
                retval = 0;
 
582
        }
 
583
 
 
584
done:
 
585
        if (retval < 0)
 
586
                dev_warn(&radio->videodev->dev,
 
587
                        "query controls failed with %d\n", retval);
 
588
        return retval;
 
589
}
 
590
 
 
591
 
 
592
/*
 
593
 * si470x_vidioc_g_ctrl - get the value of a control
 
594
 */
 
595
static int si470x_vidioc_g_ctrl(struct file *file, void *priv,
 
596
                struct v4l2_control *ctrl)
 
597
{
 
598
        struct si470x_device *radio = video_drvdata(file);
 
599
        int retval = 0;
 
600
 
 
601
        mutex_lock(&radio->lock);
 
602
        /* safety checks */
 
603
        retval = si470x_disconnect_check(radio);
 
604
        if (retval)
 
605
                goto done;
 
606
 
 
607
        switch (ctrl->id) {
 
608
        case V4L2_CID_AUDIO_VOLUME:
 
609
                ctrl->value = radio->registers[SYSCONFIG2] &
 
610
                                SYSCONFIG2_VOLUME;
 
611
                break;
 
612
        case V4L2_CID_AUDIO_MUTE:
 
613
                ctrl->value = ((radio->registers[POWERCFG] &
 
614
                                POWERCFG_DMUTE) == 0) ? 1 : 0;
 
615
                break;
 
616
        default:
 
617
                retval = -EINVAL;
 
618
        }
 
619
 
 
620
done:
 
621
        if (retval < 0)
 
622
                dev_warn(&radio->videodev->dev,
 
623
                        "get control failed with %d\n", retval);
 
624
 
 
625
        mutex_unlock(&radio->lock);
 
626
        return retval;
 
627
}
 
628
 
 
629
 
 
630
/*
 
631
 * si470x_vidioc_s_ctrl - set the value of a control
 
632
 */
 
633
static int si470x_vidioc_s_ctrl(struct file *file, void *priv,
 
634
                struct v4l2_control *ctrl)
 
635
{
 
636
        struct si470x_device *radio = video_drvdata(file);
 
637
        int retval = 0;
 
638
 
 
639
        mutex_lock(&radio->lock);
 
640
        /* safety checks */
 
641
        retval = si470x_disconnect_check(radio);
 
642
        if (retval)
 
643
                goto done;
 
644
 
 
645
        switch (ctrl->id) {
 
646
        case V4L2_CID_AUDIO_VOLUME:
 
647
                radio->registers[SYSCONFIG2] &= ~SYSCONFIG2_VOLUME;
 
648
                radio->registers[SYSCONFIG2] |= ctrl->value;
 
649
                retval = si470x_set_register(radio, SYSCONFIG2);
 
650
                break;
 
651
        case V4L2_CID_AUDIO_MUTE:
 
652
                if (ctrl->value == 1)
 
653
                        radio->registers[POWERCFG] &= ~POWERCFG_DMUTE;
 
654
                else
 
655
                        radio->registers[POWERCFG] |= POWERCFG_DMUTE;
 
656
                retval = si470x_set_register(radio, POWERCFG);
 
657
                break;
 
658
        default:
 
659
                retval = -EINVAL;
 
660
        }
 
661
 
 
662
done:
 
663
        if (retval < 0)
 
664
                dev_warn(&radio->videodev->dev,
 
665
                        "set control failed with %d\n", retval);
 
666
        mutex_unlock(&radio->lock);
 
667
        return retval;
 
668
}
 
669
 
 
670
 
 
671
/*
 
672
 * si470x_vidioc_g_audio - get audio attributes
 
673
 */
 
674
static int si470x_vidioc_g_audio(struct file *file, void *priv,
 
675
                struct v4l2_audio *audio)
 
676
{
 
677
        /* driver constants */
 
678
        audio->index = 0;
 
679
        strcpy(audio->name, "Radio");
 
680
        audio->capability = V4L2_AUDCAP_STEREO;
 
681
        audio->mode = 0;
 
682
 
 
683
        return 0;
 
684
}
 
685
 
 
686
 
 
687
/*
 
688
 * si470x_vidioc_g_tuner - get tuner attributes
 
689
 */
 
690
static int si470x_vidioc_g_tuner(struct file *file, void *priv,
 
691
                struct v4l2_tuner *tuner)
 
692
{
 
693
        struct si470x_device *radio = video_drvdata(file);
 
694
        int retval = 0;
 
695
 
 
696
        mutex_lock(&radio->lock);
 
697
        /* safety checks */
 
698
        retval = si470x_disconnect_check(radio);
 
699
        if (retval)
 
700
                goto done;
 
701
 
 
702
        if (tuner->index != 0) {
 
703
                retval = -EINVAL;
 
704
                goto done;
 
705
        }
 
706
 
 
707
        retval = si470x_get_register(radio, STATUSRSSI);
 
708
        if (retval < 0)
 
709
                goto done;
 
710
 
 
711
        /* driver constants */
 
712
        strcpy(tuner->name, "FM");
 
713
        tuner->type = V4L2_TUNER_RADIO;
 
714
        tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
 
715
                            V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO;
 
716
 
 
717
        /* range limits */
 
718
        switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {
 
719
        /* 0: 87.5 - 108 MHz (USA, Europe, default) */
 
720
        default:
 
721
                tuner->rangelow  =  87.5 * FREQ_MUL;
 
722
                tuner->rangehigh = 108   * FREQ_MUL;
 
723
                break;
 
724
        /* 1: 76   - 108 MHz (Japan wide band) */
 
725
        case 1:
 
726
                tuner->rangelow  =  76   * FREQ_MUL;
 
727
                tuner->rangehigh = 108   * FREQ_MUL;
 
728
                break;
 
729
        /* 2: 76   -  90 MHz (Japan) */
 
730
        case 2:
 
731
                tuner->rangelow  =  76   * FREQ_MUL;
 
732
                tuner->rangehigh =  90   * FREQ_MUL;
 
733
                break;
 
734
        };
 
735
 
 
736
        /* stereo indicator == stereo (instead of mono) */
 
737
        if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 0)
 
738
                tuner->rxsubchans = V4L2_TUNER_SUB_MONO;
 
739
        else
 
740
                tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
 
741
        /* If there is a reliable method of detecting an RDS channel,
 
742
           then this code should check for that before setting this
 
743
           RDS subchannel. */
 
744
        tuner->rxsubchans |= V4L2_TUNER_SUB_RDS;
 
745
 
 
746
        /* mono/stereo selector */
 
747
        if ((radio->registers[POWERCFG] & POWERCFG_MONO) == 0)
 
748
                tuner->audmode = V4L2_TUNER_MODE_STEREO;
 
749
        else
 
750
                tuner->audmode = V4L2_TUNER_MODE_MONO;
 
751
 
 
752
        /* min is worst, max is best; signal:0..0xffff; rssi: 0..0xff */
 
753
        /* measured in units of dbµV in 1 db increments (max at ~75 dbµV) */
 
754
        tuner->signal = (radio->registers[STATUSRSSI] & STATUSRSSI_RSSI);
 
755
        /* the ideal factor is 0xffff/75 = 873,8 */
 
756
        tuner->signal = (tuner->signal * 873) + (8 * tuner->signal / 10);
 
757
 
 
758
        /* automatic frequency control: -1: freq to low, 1 freq to high */
 
759
        /* AFCRL does only indicate that freq. differs, not if too low/high */
 
760
        tuner->afc = (radio->registers[STATUSRSSI] & STATUSRSSI_AFCRL) ? 1 : 0;
 
761
 
 
762
done:
 
763
        if (retval < 0)
 
764
                dev_warn(&radio->videodev->dev,
 
765
                        "get tuner failed with %d\n", retval);
 
766
        mutex_unlock(&radio->lock);
 
767
        return retval;
 
768
}
 
769
 
 
770
 
 
771
/*
 
772
 * si470x_vidioc_s_tuner - set tuner attributes
 
773
 */
 
774
static int si470x_vidioc_s_tuner(struct file *file, void *priv,
 
775
                struct v4l2_tuner *tuner)
 
776
{
 
777
        struct si470x_device *radio = video_drvdata(file);
 
778
        int retval = 0;
 
779
 
 
780
        mutex_lock(&radio->lock);
 
781
        /* safety checks */
 
782
        retval = si470x_disconnect_check(radio);
 
783
        if (retval)
 
784
                goto done;
 
785
 
 
786
        if (tuner->index != 0)
 
787
                goto done;
 
788
 
 
789
        /* mono/stereo selector */
 
790
        switch (tuner->audmode) {
 
791
        case V4L2_TUNER_MODE_MONO:
 
792
                radio->registers[POWERCFG] |= POWERCFG_MONO;  /* force mono */
 
793
                break;
 
794
        case V4L2_TUNER_MODE_STEREO:
 
795
                radio->registers[POWERCFG] &= ~POWERCFG_MONO; /* try stereo */
 
796
                break;
 
797
        default:
 
798
                goto done;
 
799
        }
 
800
 
 
801
        retval = si470x_set_register(radio, POWERCFG);
 
802
 
 
803
done:
 
804
        if (retval < 0)
 
805
                dev_warn(&radio->videodev->dev,
 
806
                        "set tuner failed with %d\n", retval);
 
807
        mutex_unlock(&radio->lock);
 
808
        return retval;
 
809
}
 
810
 
 
811
 
 
812
/*
 
813
 * si470x_vidioc_g_frequency - get tuner or modulator radio frequency
 
814
 */
 
815
static int si470x_vidioc_g_frequency(struct file *file, void *priv,
 
816
                struct v4l2_frequency *freq)
 
817
{
 
818
        struct si470x_device *radio = video_drvdata(file);
 
819
        int retval = 0;
 
820
 
 
821
        /* safety checks */
 
822
        mutex_lock(&radio->lock);
 
823
        retval = si470x_disconnect_check(radio);
 
824
        if (retval)
 
825
                goto done;
 
826
 
 
827
        if (freq->tuner != 0) {
 
828
                retval = -EINVAL;
 
829
                goto done;
 
830
        }
 
831
 
 
832
        freq->type = V4L2_TUNER_RADIO;
 
833
        retval = si470x_get_freq(radio, &freq->frequency);
 
834
 
 
835
done:
 
836
        if (retval < 0)
 
837
                dev_warn(&radio->videodev->dev,
 
838
                        "get frequency failed with %d\n", retval);
 
839
        mutex_unlock(&radio->lock);
 
840
        return retval;
 
841
}
 
842
 
 
843
 
 
844
/*
 
845
 * si470x_vidioc_s_frequency - set tuner or modulator radio frequency
 
846
 */
 
847
static int si470x_vidioc_s_frequency(struct file *file, void *priv,
 
848
                struct v4l2_frequency *freq)
 
849
{
 
850
        struct si470x_device *radio = video_drvdata(file);
 
851
        int retval = 0;
 
852
 
 
853
        mutex_lock(&radio->lock);
 
854
        /* safety checks */
 
855
        retval = si470x_disconnect_check(radio);
 
856
        if (retval)
 
857
                goto done;
 
858
 
 
859
        if (freq->tuner != 0) {
 
860
                retval = -EINVAL;
 
861
                goto done;
 
862
        }
 
863
 
 
864
        retval = si470x_set_freq(radio, freq->frequency);
 
865
 
 
866
done:
 
867
        if (retval < 0)
 
868
                dev_warn(&radio->videodev->dev,
 
869
                        "set frequency failed with %d\n", retval);
 
870
        mutex_unlock(&radio->lock);
 
871
        return retval;
 
872
}
 
873
 
 
874
 
 
875
/*
 
876
 * si470x_vidioc_s_hw_freq_seek - set hardware frequency seek
 
877
 */
 
878
static int si470x_vidioc_s_hw_freq_seek(struct file *file, void *priv,
 
879
                struct v4l2_hw_freq_seek *seek)
 
880
{
 
881
        struct si470x_device *radio = video_drvdata(file);
 
882
        int retval = 0;
 
883
 
 
884
        mutex_lock(&radio->lock);
 
885
        /* safety checks */
 
886
        retval = si470x_disconnect_check(radio);
 
887
        if (retval)
 
888
                goto done;
 
889
 
 
890
        if (seek->tuner != 0) {
 
891
                retval = -EINVAL;
 
892
                goto done;
 
893
        }
 
894
 
 
895
        retval = si470x_set_seek(radio, seek->wrap_around, seek->seek_upward);
 
896
 
 
897
done:
 
898
        if (retval < 0)
 
899
                dev_warn(&radio->videodev->dev,
 
900
                        "set hardware frequency seek failed with %d\n", retval);
 
901
        mutex_unlock(&radio->lock);
 
902
        return retval;
 
903
}
 
904
 
 
905
 
 
906
/*
 
907
 * si470x_ioctl_ops - video device ioctl operations
 
908
 */
 
909
static const struct v4l2_ioctl_ops si470x_ioctl_ops = {
 
910
        .vidioc_querycap        = si470x_vidioc_querycap,
 
911
        .vidioc_queryctrl       = si470x_vidioc_queryctrl,
 
912
        .vidioc_g_ctrl          = si470x_vidioc_g_ctrl,
 
913
        .vidioc_s_ctrl          = si470x_vidioc_s_ctrl,
 
914
        .vidioc_g_audio         = si470x_vidioc_g_audio,
 
915
        .vidioc_g_tuner         = si470x_vidioc_g_tuner,
 
916
        .vidioc_s_tuner         = si470x_vidioc_s_tuner,
 
917
        .vidioc_g_frequency     = si470x_vidioc_g_frequency,
 
918
        .vidioc_s_frequency     = si470x_vidioc_s_frequency,
 
919
        .vidioc_s_hw_freq_seek  = si470x_vidioc_s_hw_freq_seek,
 
920
};
 
921
 
 
922
 
 
923
/*
 
924
 * si470x_viddev_template - video device interface
 
925
 */
 
926
struct video_device si470x_viddev_template = {
 
927
        .fops                   = &si470x_fops,
 
928
        .name                   = DRIVER_NAME,
 
929
        .release                = video_device_release,
 
930
        .ioctl_ops              = &si470x_ioctl_ops,
 
931
};