]> Pileus Git - ~andy/linux/blob - drivers/media/radio/radio-tea5777.c
8cfa364b8190c156f935ec714b35cfdaf66a81a5
[~andy/linux] / drivers / media / radio / radio-tea5777.c
1 /*
2  *   v4l2 driver for TEA5777 Philips AM/FM radio tuner chips
3  *
4  *      Copyright (c) 2012 Hans de Goede <hdegoede@redhat.com>
5  *
6  *   Based on the ALSA driver for TEA5757/5759 Philips AM/FM radio tuner chips:
7  *
8  *      Copyright (c) 2004 Jaroslav Kysela <perex@perex.cz>
9  *
10  *   This program is free software; you can redistribute it and/or modify
11  *   it under the terms of the GNU General Public License as published by
12  *   the Free Software Foundation; either version 2 of the License, or
13  *   (at your option) any later version.
14  *
15  *   This program is distributed in the hope that it will be useful,
16  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *   GNU General Public License for more details.
19  *
20  *   You should have received a copy of the GNU General Public License
21  *   along with this program; if not, write to the Free Software
22  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
23  *
24  */
25
26 #include <linux/delay.h>
27 #include <linux/init.h>
28 #include <linux/module.h>
29 #include <linux/sched.h>
30 #include <linux/slab.h>
31 #include <media/v4l2-device.h>
32 #include <media/v4l2-dev.h>
33 #include <media/v4l2-fh.h>
34 #include <media/v4l2-ioctl.h>
35 #include <media/v4l2-event.h>
36 #include "radio-tea5777.h"
37
38 MODULE_AUTHOR("Hans de Goede <perex@perex.cz>");
39 MODULE_DESCRIPTION("Routines for control of TEA5777 Philips AM/FM radio tuner chips");
40 MODULE_LICENSE("GPL");
41
42 /* Fixed FM only band for now, will implement multi-band support when the
43    VIDIOC_ENUM_FREQ_BANDS API is upstream */
44 #define TEA5777_FM_RANGELOW             (76000 * 16)
45 #define TEA5777_FM_RANGEHIGH            (108000 * 16)
46
47 #define TEA5777_FM_IF                   150 /* kHz */
48 #define TEA5777_FM_FREQ_STEP            50 /* kHz */
49
50 /* Write reg, common bits */
51 #define TEA5777_W_MUTE_MASK             (1LL << 47)
52 #define TEA5777_W_MUTE_SHIFT            47
53 #define TEA5777_W_AM_FM_MASK            (1LL << 46)
54 #define TEA5777_W_AM_FM_SHIFT           46
55 #define TEA5777_W_STB_MASK              (1LL << 45)
56 #define TEA5777_W_STB_SHIFT             45
57
58 #define TEA5777_W_IFCE_MASK             (1LL << 29)
59 #define TEA5777_W_IFCE_SHIFT            29
60 #define TEA5777_W_IFW_MASK              (1LL << 28)
61 #define TEA5777_W_IFW_SHIFT             28
62 #define TEA5777_W_HILO_MASK             (1LL << 27)
63 #define TEA5777_W_HILO_SHIFT            27
64 #define TEA5777_W_DBUS_MASK             (1LL << 26)
65 #define TEA5777_W_DBUS_SHIFT            26
66
67 #define TEA5777_W_INTEXT_MASK           (1LL << 24)
68 #define TEA5777_W_INTEXT_SHIFT          24
69 #define TEA5777_W_P1_MASK               (1LL << 23)
70 #define TEA5777_W_P1_SHIFT              23
71 #define TEA5777_W_P0_MASK               (1LL << 22)
72 #define TEA5777_W_P0_SHIFT              22
73 #define TEA5777_W_PEN1_MASK             (1LL << 21)
74 #define TEA5777_W_PEN1_SHIFT            21
75 #define TEA5777_W_PEN0_MASK             (1LL << 20)
76 #define TEA5777_W_PEN0_SHIFT            20
77
78 #define TEA5777_W_CHP0_MASK             (1LL << 18)
79 #define TEA5777_W_CHP0_SHIFT            18
80 #define TEA5777_W_DEEM_MASK             (1LL << 17)
81 #define TEA5777_W_DEEM_SHIFT            17
82
83 #define TEA5777_W_SEARCH_MASK           (1LL << 7)
84 #define TEA5777_W_SEARCH_SHIFT          7
85 #define TEA5777_W_PROGBLIM_MASK         (1LL << 6)
86 #define TEA5777_W_PROGBLIM_SHIFT        6
87 #define TEA5777_W_UPDWN_MASK            (1LL << 5)
88 #define TEA5777_W_UPDWN_SHIFT           5
89 #define TEA5777_W_SLEV_MASK             (3LL << 3)
90 #define TEA5777_W_SLEV_SHIFT            3
91
92 /* Write reg, FM specific bits */
93 #define TEA5777_W_FM_PLL_MASK           (0x1fffLL << 32)
94 #define TEA5777_W_FM_PLL_SHIFT          32
95 #define TEA5777_W_FM_FREF_MASK          (0x03LL << 30)
96 #define TEA5777_W_FM_FREF_SHIFT         30
97 #define TEA5777_W_FM_FREF_VALUE         0 /* 50 kHz tune steps, 150 kHz IF */
98
99 #define TEA5777_W_FM_FORCEMONO_MASK     (1LL << 15)
100 #define TEA5777_W_FM_FORCEMONO_SHIFT    15
101 #define TEA5777_W_FM_SDSOFF_MASK        (1LL << 14)
102 #define TEA5777_W_FM_SDSOFF_SHIFT       14
103 #define TEA5777_W_FM_DOFF_MASK          (1LL << 13)
104 #define TEA5777_W_FM_DOFF_SHIFT         13
105
106 #define TEA5777_W_FM_STEP_MASK          (3LL << 1)
107 #define TEA5777_W_FM_STEP_SHIFT         1
108
109 /* Write reg, AM specific bits */
110 #define TEA5777_W_AM_PLL_MASK           (0x7ffLL << 34)
111 #define TEA5777_W_AM_PLL_SHIFT          34
112 #define TEA5777_W_AM_AGCRF_MASK         (1LL << 33)
113 #define TEA5777_W_AM_AGCRF_SHIFT        33
114 #define TEA5777_W_AM_AGCIF_MASK         (1LL << 32)
115 #define TEA5777_W_AM_AGCIF_SHIFT        32
116 #define TEA5777_W_AM_MWLW_MASK          (1LL << 31)
117 #define TEA5777_W_AM_MWLW_SHIFT         31
118 #define TEA5777_W_AM_LNA_MASK           (1LL << 30)
119 #define TEA5777_W_AM_LNA_SHIFT          30
120
121 #define TEA5777_W_AM_PEAK_MASK          (1LL << 25)
122 #define TEA5777_W_AM_PEAK_SHIFT         25
123
124 #define TEA5777_W_AM_RFB_MASK           (1LL << 16)
125 #define TEA5777_W_AM_RFB_SHIFT          16
126 #define TEA5777_W_AM_CALLIGN_MASK       (1LL << 15)
127 #define TEA5777_W_AM_CALLIGN_SHIFT      15
128 #define TEA5777_W_AM_CBANK_MASK         (0x7fLL << 8)
129 #define TEA5777_W_AM_CBANK_SHIFT        8
130
131 #define TEA5777_W_AM_DELAY_MASK         (1LL << 2)
132 #define TEA5777_W_AM_DELAY_SHIFT        2
133 #define TEA5777_W_AM_STEP_MASK          (1LL << 1)
134 #define TEA5777_W_AM_STEP_SHIFT         1
135
136 /* Read reg, common bits */
137 #define TEA5777_R_LEVEL_MASK            (0x0f << 17)
138 #define TEA5777_R_LEVEL_SHIFT           17
139 #define TEA5777_R_SFOUND_MASK           (0x01 << 16)
140 #define TEA5777_R_SFOUND_SHIFT          16
141 #define TEA5777_R_BLIM_MASK             (0x01 << 15)
142 #define TEA5777_R_BLIM_SHIFT            15
143
144 /* Read reg, FM specific bits */
145 #define TEA5777_R_FM_STEREO_MASK        (0x01 << 21)
146 #define TEA5777_R_FM_STEREO_SHIFT       21
147 #define TEA5777_R_FM_PLL_MASK           0x1fff
148 #define TEA5777_R_FM_PLL_SHIFT          0
149
150 static u32 tea5777_freq_to_v4l2_freq(struct radio_tea5777 *tea, u32 freq)
151 {
152         return (freq * TEA5777_FM_FREQ_STEP + TEA5777_FM_IF) * 16;
153 }
154
155 static int radio_tea5777_set_freq(struct radio_tea5777 *tea)
156 {
157         u32 freq;
158         int res;
159
160         freq = clamp_t(u32, tea->freq,
161                        TEA5777_FM_RANGELOW, TEA5777_FM_RANGEHIGH);
162         freq = (freq + 8) / 16; /* to kHz */
163
164         freq = (freq - TEA5777_FM_IF) / TEA5777_FM_FREQ_STEP;
165
166         tea->write_reg &= ~(TEA5777_W_FM_PLL_MASK | TEA5777_W_FM_FREF_MASK);
167         tea->write_reg |= (u64)freq << TEA5777_W_FM_PLL_SHIFT;
168         tea->write_reg |= TEA5777_W_FM_FREF_VALUE << TEA5777_W_FM_FREF_SHIFT;
169
170         res = tea->ops->write_reg(tea, tea->write_reg);
171         if (res)
172                 return res;
173
174         tea->needs_write = false;
175         tea->read_reg = -1;
176         tea->freq = tea5777_freq_to_v4l2_freq(tea, freq);
177
178         return 0;
179 }
180
181 static int radio_tea5777_update_read_reg(struct radio_tea5777 *tea, int wait)
182 {
183         int res;
184
185         if (tea->read_reg != -1)
186                 return 0;
187
188         if (tea->write_before_read && tea->needs_write) {
189                 res = radio_tea5777_set_freq(tea);
190                 if (res)
191                         return res;
192         }
193
194         if (wait) {
195                 if (schedule_timeout_interruptible(msecs_to_jiffies(wait)))
196                         return -ERESTARTSYS;
197         }
198
199         res = tea->ops->read_reg(tea, &tea->read_reg);
200         if (res)
201                 return res;
202
203         tea->needs_write = true;
204         return 0;
205 }
206
207 /*
208  * Linux Video interface
209  */
210
211 static int vidioc_querycap(struct file *file, void  *priv,
212                                         struct v4l2_capability *v)
213 {
214         struct radio_tea5777 *tea = video_drvdata(file);
215
216         strlcpy(v->driver, tea->v4l2_dev->name, sizeof(v->driver));
217         strlcpy(v->card, tea->card, sizeof(v->card));
218         strlcat(v->card, " TEA5777", sizeof(v->card));
219         strlcpy(v->bus_info, tea->bus_info, sizeof(v->bus_info));
220         v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
221         v->device_caps |= V4L2_CAP_HW_FREQ_SEEK;
222         v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS;
223         return 0;
224 }
225
226 static int vidioc_g_tuner(struct file *file, void *priv,
227                                         struct v4l2_tuner *v)
228 {
229         struct radio_tea5777 *tea = video_drvdata(file);
230         int res;
231
232         if (v->index > 0)
233                 return -EINVAL;
234
235         res = radio_tea5777_update_read_reg(tea, 0);
236         if (res)
237                 return res;
238
239         memset(v, 0, sizeof(*v));
240         if (tea->has_am)
241                 strlcpy(v->name, "AM/FM", sizeof(v->name));
242         else
243                 strlcpy(v->name, "FM", sizeof(v->name));
244         v->type = V4L2_TUNER_RADIO;
245         v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
246                         V4L2_TUNER_CAP_HWSEEK_BOUNDED;
247         v->rangelow   = TEA5777_FM_RANGELOW;
248         v->rangehigh  = TEA5777_FM_RANGEHIGH;
249         v->rxsubchans = (tea->read_reg & TEA5777_R_FM_STEREO_MASK) ?
250                         V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
251         v->audmode = (tea->write_reg & TEA5777_W_FM_FORCEMONO_MASK) ?
252                 V4L2_TUNER_MODE_MONO : V4L2_TUNER_MODE_STEREO;
253         /* shift - 12 to convert 4-bits (0-15) scale to 16-bits (0-65535) */
254         v->signal = (tea->read_reg & TEA5777_R_LEVEL_MASK) >>
255                     (TEA5777_R_LEVEL_SHIFT - 12);
256
257         /* Invalidate read_reg, so that next call we return up2date signal */
258         tea->read_reg = -1;
259
260         return 0;
261 }
262
263 static int vidioc_s_tuner(struct file *file, void *priv,
264                                         struct v4l2_tuner *v)
265 {
266         struct radio_tea5777 *tea = video_drvdata(file);
267
268         if (v->index)
269                 return -EINVAL;
270
271         if (v->audmode == V4L2_TUNER_MODE_MONO)
272                 tea->write_reg |= TEA5777_W_FM_FORCEMONO_MASK;
273         else
274                 tea->write_reg &= ~TEA5777_W_FM_FORCEMONO_MASK;
275
276         return radio_tea5777_set_freq(tea);
277 }
278
279 static int vidioc_g_frequency(struct file *file, void *priv,
280                                         struct v4l2_frequency *f)
281 {
282         struct radio_tea5777 *tea = video_drvdata(file);
283
284         if (f->tuner != 0)
285                 return -EINVAL;
286         f->type = V4L2_TUNER_RADIO;
287         f->frequency = tea->freq;
288         return 0;
289 }
290
291 static int vidioc_s_frequency(struct file *file, void *priv,
292                                         struct v4l2_frequency *f)
293 {
294         struct radio_tea5777 *tea = video_drvdata(file);
295
296         if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
297                 return -EINVAL;
298
299         tea->freq = f->frequency;
300         return radio_tea5777_set_freq(tea);
301 }
302
303 static int vidioc_s_hw_freq_seek(struct file *file, void *fh,
304                                         struct v4l2_hw_freq_seek *a)
305 {
306         struct radio_tea5777 *tea = video_drvdata(file);
307         u32 orig_freq = tea->freq;
308         unsigned long timeout;
309         int res, spacing = 200 * 16; /* 200 kHz */
310         /* These are fixed *for now* */
311         const u32 seek_rangelow  = TEA5777_FM_RANGELOW;
312         const u32 seek_rangehigh = TEA5777_FM_RANGEHIGH;
313
314         if (a->tuner || a->wrap_around)
315                 return -EINVAL;
316
317         tea->write_reg |= TEA5777_W_PROGBLIM_MASK;
318         if (seek_rangelow != tea->seek_rangelow) {
319                 tea->write_reg &= ~TEA5777_W_UPDWN_MASK;
320                 tea->freq = seek_rangelow;
321                 res = radio_tea5777_set_freq(tea);
322                 if (res)
323                         goto leave;
324                 tea->seek_rangelow = tea->freq;
325         }
326         if (seek_rangehigh != tea->seek_rangehigh) {
327                 tea->write_reg |= TEA5777_W_UPDWN_MASK;
328                 tea->freq = seek_rangehigh;
329                 res = radio_tea5777_set_freq(tea);
330                 if (res)
331                         goto leave;
332                 tea->seek_rangehigh = tea->freq;
333         }
334         tea->write_reg &= ~TEA5777_W_PROGBLIM_MASK;
335
336         tea->write_reg |= TEA5777_W_SEARCH_MASK;
337         if (a->seek_upward) {
338                 tea->write_reg |= TEA5777_W_UPDWN_MASK;
339                 tea->freq = orig_freq + spacing;
340         } else {
341                 tea->write_reg &= ~TEA5777_W_UPDWN_MASK;
342                 tea->freq = orig_freq - spacing;
343         }
344         res = radio_tea5777_set_freq(tea);
345         if (res)
346                 goto leave;
347
348         timeout = jiffies + msecs_to_jiffies(5000);
349         for (;;) {
350                 if (time_after(jiffies, timeout)) {
351                         res = -ENODATA;
352                         break;
353                 }
354
355                 res = radio_tea5777_update_read_reg(tea, 100);
356                 if (res)
357                         break;
358
359                 /*
360                  * Note we use tea->freq to track how far we've searched sofar
361                  * this is necessary to ensure we continue seeking at the right
362                  * point, in the write_before_read case.
363                  */
364                 tea->freq = (tea->read_reg & TEA5777_R_FM_PLL_MASK);
365                 tea->freq = tea5777_freq_to_v4l2_freq(tea, tea->freq);
366
367                 if ((tea->read_reg & TEA5777_R_SFOUND_MASK)) {
368                         tea->write_reg &= ~TEA5777_W_SEARCH_MASK;
369                         return 0;
370                 }
371
372                 if (tea->read_reg & TEA5777_R_BLIM_MASK) {
373                         res = -ENODATA;
374                         break;
375                 }
376
377                 /* Force read_reg update */
378                 tea->read_reg = -1;
379         }
380 leave:
381         tea->write_reg &= ~TEA5777_W_PROGBLIM_MASK;
382         tea->write_reg &= ~TEA5777_W_SEARCH_MASK;
383         tea->freq = orig_freq;
384         radio_tea5777_set_freq(tea);
385         return res;
386 }
387
388 static int tea575x_s_ctrl(struct v4l2_ctrl *c)
389 {
390         struct radio_tea5777 *tea =
391                 container_of(c->handler, struct radio_tea5777, ctrl_handler);
392
393         switch (c->id) {
394         case V4L2_CID_AUDIO_MUTE:
395                 if (c->val)
396                         tea->write_reg |= TEA5777_W_MUTE_MASK;
397                 else
398                         tea->write_reg &= ~TEA5777_W_MUTE_MASK;
399
400                 return radio_tea5777_set_freq(tea);
401         }
402
403         return -EINVAL;
404 }
405
406 static const struct v4l2_file_operations tea575x_fops = {
407         .unlocked_ioctl = video_ioctl2,
408         .open           = v4l2_fh_open,
409         .release        = v4l2_fh_release,
410         .poll           = v4l2_ctrl_poll,
411 };
412
413 static const struct v4l2_ioctl_ops tea575x_ioctl_ops = {
414         .vidioc_querycap    = vidioc_querycap,
415         .vidioc_g_tuner     = vidioc_g_tuner,
416         .vidioc_s_tuner     = vidioc_s_tuner,
417         .vidioc_g_frequency = vidioc_g_frequency,
418         .vidioc_s_frequency = vidioc_s_frequency,
419         .vidioc_s_hw_freq_seek = vidioc_s_hw_freq_seek,
420         .vidioc_log_status  = v4l2_ctrl_log_status,
421         .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
422         .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
423 };
424
425 static const struct video_device tea575x_radio = {
426         .ioctl_ops      = &tea575x_ioctl_ops,
427         .release        = video_device_release_empty,
428 };
429
430 static const struct v4l2_ctrl_ops tea575x_ctrl_ops = {
431         .s_ctrl = tea575x_s_ctrl,
432 };
433
434 int radio_tea5777_init(struct radio_tea5777 *tea, struct module *owner)
435 {
436         int res;
437
438         tea->write_reg = (1LL << TEA5777_W_IFCE_SHIFT) |
439                          (1LL << TEA5777_W_IFW_SHIFT) |
440                          (1LL << TEA5777_W_INTEXT_SHIFT) |
441                          (1LL << TEA5777_W_CHP0_SHIFT) |
442                          (2LL << TEA5777_W_SLEV_SHIFT);
443         tea->freq = 90500 * 16; /* 90.5Mhz default */
444         res = radio_tea5777_set_freq(tea);
445         if (res) {
446                 v4l2_err(tea->v4l2_dev, "can't set initial freq (%d)\n", res);
447                 return res;
448         }
449
450         tea->vd = tea575x_radio;
451         video_set_drvdata(&tea->vd, tea);
452         mutex_init(&tea->mutex);
453         strlcpy(tea->vd.name, tea->v4l2_dev->name, sizeof(tea->vd.name));
454         tea->vd.lock = &tea->mutex;
455         tea->vd.v4l2_dev = tea->v4l2_dev;
456         tea->fops = tea575x_fops;
457         tea->fops.owner = owner;
458         tea->vd.fops = &tea->fops;
459         set_bit(V4L2_FL_USE_FH_PRIO, &tea->vd.flags);
460
461         tea->vd.ctrl_handler = &tea->ctrl_handler;
462         v4l2_ctrl_handler_init(&tea->ctrl_handler, 1);
463         v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops,
464                           V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
465         res = tea->ctrl_handler.error;
466         if (res) {
467                 v4l2_err(tea->v4l2_dev, "can't initialize controls\n");
468                 v4l2_ctrl_handler_free(&tea->ctrl_handler);
469                 return res;
470         }
471         v4l2_ctrl_handler_setup(&tea->ctrl_handler);
472
473         res = video_register_device(&tea->vd, VFL_TYPE_RADIO, -1);
474         if (res) {
475                 v4l2_err(tea->v4l2_dev, "can't register video device!\n");
476                 v4l2_ctrl_handler_free(tea->vd.ctrl_handler);
477                 return res;
478         }
479
480         return 0;
481 }
482 EXPORT_SYMBOL_GPL(radio_tea5777_init);
483
484 void radio_tea5777_exit(struct radio_tea5777 *tea)
485 {
486         video_unregister_device(&tea->vd);
487         v4l2_ctrl_handler_free(tea->vd.ctrl_handler);
488 }
489 EXPORT_SYMBOL_GPL(radio_tea5777_exit);