Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
radio-tea5777.c
Go to the documentation of this file.
1 /*
2  * v4l2 driver for TEA5777 Philips AM/FM radio tuner chips
3  *
4  * Copyright (c) 2012 Hans de Goede <[email protected]>
5  *
6  * Based on the ALSA driver for TEA5757/5759 Philips AM/FM radio tuner chips:
7  *
8  * Copyright (c) 2004 Jaroslav Kysela <[email protected]>
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 <[email protected]>");
39 MODULE_DESCRIPTION("Routines for control of TEA5777 Philips AM/FM radio tuner chips");
40 MODULE_LICENSE("GPL");
41 
42 #define TEA5777_FM_IF 150 /* kHz */
43 #define TEA5777_FM_FREQ_STEP 50 /* kHz */
44 
45 #define TEA5777_AM_IF 21 /* kHz */
46 #define TEA5777_AM_FREQ_STEP 1 /* kHz */
47 
48 /* Write reg, common bits */
49 #define TEA5777_W_MUTE_MASK (1LL << 47)
50 #define TEA5777_W_MUTE_SHIFT 47
51 #define TEA5777_W_AM_FM_MASK (1LL << 46)
52 #define TEA5777_W_AM_FM_SHIFT 46
53 #define TEA5777_W_STB_MASK (1LL << 45)
54 #define TEA5777_W_STB_SHIFT 45
55 
56 #define TEA5777_W_IFCE_MASK (1LL << 29)
57 #define TEA5777_W_IFCE_SHIFT 29
58 #define TEA5777_W_IFW_MASK (1LL << 28)
59 #define TEA5777_W_IFW_SHIFT 28
60 #define TEA5777_W_HILO_MASK (1LL << 27)
61 #define TEA5777_W_HILO_SHIFT 27
62 #define TEA5777_W_DBUS_MASK (1LL << 26)
63 #define TEA5777_W_DBUS_SHIFT 26
64 
65 #define TEA5777_W_INTEXT_MASK (1LL << 24)
66 #define TEA5777_W_INTEXT_SHIFT 24
67 #define TEA5777_W_P1_MASK (1LL << 23)
68 #define TEA5777_W_P1_SHIFT 23
69 #define TEA5777_W_P0_MASK (1LL << 22)
70 #define TEA5777_W_P0_SHIFT 22
71 #define TEA5777_W_PEN1_MASK (1LL << 21)
72 #define TEA5777_W_PEN1_SHIFT 21
73 #define TEA5777_W_PEN0_MASK (1LL << 20)
74 #define TEA5777_W_PEN0_SHIFT 20
75 
76 #define TEA5777_W_CHP0_MASK (1LL << 18)
77 #define TEA5777_W_CHP0_SHIFT 18
78 #define TEA5777_W_DEEM_MASK (1LL << 17)
79 #define TEA5777_W_DEEM_SHIFT 17
80 
81 #define TEA5777_W_SEARCH_MASK (1LL << 7)
82 #define TEA5777_W_SEARCH_SHIFT 7
83 #define TEA5777_W_PROGBLIM_MASK (1LL << 6)
84 #define TEA5777_W_PROGBLIM_SHIFT 6
85 #define TEA5777_W_UPDWN_MASK (1LL << 5)
86 #define TEA5777_W_UPDWN_SHIFT 5
87 #define TEA5777_W_SLEV_MASK (3LL << 3)
88 #define TEA5777_W_SLEV_SHIFT 3
89 
90 /* Write reg, FM specific bits */
91 #define TEA5777_W_FM_PLL_MASK (0x1fffLL << 32)
92 #define TEA5777_W_FM_PLL_SHIFT 32
93 #define TEA5777_W_FM_FREF_MASK (0x03LL << 30)
94 #define TEA5777_W_FM_FREF_SHIFT 30
95 #define TEA5777_W_FM_FREF_VALUE 0LL /* 50k steps, 150k IF */
96 
97 #define TEA5777_W_FM_FORCEMONO_MASK (1LL << 15)
98 #define TEA5777_W_FM_FORCEMONO_SHIFT 15
99 #define TEA5777_W_FM_SDSOFF_MASK (1LL << 14)
100 #define TEA5777_W_FM_SDSOFF_SHIFT 14
101 #define TEA5777_W_FM_DOFF_MASK (1LL << 13)
102 #define TEA5777_W_FM_DOFF_SHIFT 13
103 
104 #define TEA5777_W_FM_STEP_MASK (3LL << 1)
105 #define TEA5777_W_FM_STEP_SHIFT 1
106 
107 /* Write reg, AM specific bits */
108 #define TEA5777_W_AM_PLL_MASK (0x7ffLL << 34)
109 #define TEA5777_W_AM_PLL_SHIFT 34
110 #define TEA5777_W_AM_AGCRF_MASK (1LL << 33)
111 #define TEA5777_W_AM_AGCRF_SHIFT 33
112 #define TEA5777_W_AM_AGCIF_MASK (1LL << 32)
113 #define TEA5777_W_AM_AGCIF_SHIFT 32
114 #define TEA5777_W_AM_MWLW_MASK (1LL << 31)
115 #define TEA5777_W_AM_MWLW_SHIFT 31
116 #define TEA5777_W_AM_LW 0LL
117 #define TEA5777_W_AM_MW 1LL
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 enum { BAND_FM, BAND_AM };
151 
152 static const struct v4l2_frequency_band bands[] = {
153  {
154  .type = V4L2_TUNER_RADIO,
155  .index = 0,
156  .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
160  .rangelow = 76000 * 16,
161  .rangehigh = 108000 * 16,
162  .modulation = V4L2_BAND_MODULATION_FM,
163  },
164  {
165  .type = V4L2_TUNER_RADIO,
166  .index = 1,
170  .rangelow = 530 * 16,
171  .rangehigh = 1710 * 16,
172  .modulation = V4L2_BAND_MODULATION_AM,
173  },
174 };
175 
176 static u32 tea5777_freq_to_v4l2_freq(struct radio_tea5777 *tea, u32 freq)
177 {
178  switch (tea->band) {
179  case BAND_FM:
180  return (freq * TEA5777_FM_FREQ_STEP + TEA5777_FM_IF) * 16;
181  case BAND_AM:
182  return (freq * TEA5777_AM_FREQ_STEP + TEA5777_AM_IF) * 16;
183  }
184  return 0; /* Never reached */
185 }
186 
188 {
189  u32 freq;
190  int res;
191 
192  freq = clamp(tea->freq, bands[tea->band].rangelow,
193  bands[tea->band].rangehigh);
194  freq = (freq + 8) / 16; /* to kHz */
195 
196  switch (tea->band) {
197  case BAND_FM:
199  freq = (freq - TEA5777_FM_IF) / TEA5777_FM_FREQ_STEP;
201  tea->write_reg |= (u64)freq << TEA5777_W_FM_PLL_SHIFT;
206  if (tea->audmode == V4L2_TUNER_MODE_MONO)
208  break;
209  case BAND_AM:
211  tea->write_reg |= (1LL << TEA5777_W_AM_FM_SHIFT);
212  freq = (freq - TEA5777_AM_IF) / TEA5777_AM_FREQ_STEP;
214  tea->write_reg |= (u64)freq << TEA5777_W_AM_PLL_SHIFT;
220  tea->write_reg |= 1LL << TEA5777_W_AM_LNA_SHIFT;
224  break;
225  }
226 
227  res = tea->ops->write_reg(tea, tea->write_reg);
228  if (res)
229  return res;
230 
231  tea->needs_write = false;
232  tea->read_reg = -1;
233  tea->freq = tea5777_freq_to_v4l2_freq(tea, freq);
234 
235  return 0;
236 }
237 
238 static int radio_tea5777_update_read_reg(struct radio_tea5777 *tea, int wait)
239 {
240  int res;
241 
242  if (tea->read_reg != -1)
243  return 0;
244 
245  if (tea->write_before_read && tea->needs_write) {
246  res = radio_tea5777_set_freq(tea);
247  if (res)
248  return res;
249  }
250 
251  if (wait) {
253  return -ERESTARTSYS;
254  }
255 
256  res = tea->ops->read_reg(tea, &tea->read_reg);
257  if (res)
258  return res;
259 
260  tea->needs_write = true;
261  return 0;
262 }
263 
264 /*
265  * Linux Video interface
266  */
267 
268 static int vidioc_querycap(struct file *file, void *priv,
269  struct v4l2_capability *v)
270 {
271  struct radio_tea5777 *tea = video_drvdata(file);
272 
273  strlcpy(v->driver, tea->v4l2_dev->name, sizeof(v->driver));
274  strlcpy(v->card, tea->card, sizeof(v->card));
275  strlcat(v->card, " TEA5777", sizeof(v->card));
276  strlcpy(v->bus_info, tea->bus_info, sizeof(v->bus_info));
280  return 0;
281 }
282 
283 static int vidioc_enum_freq_bands(struct file *file, void *priv,
284  struct v4l2_frequency_band *band)
285 {
286  struct radio_tea5777 *tea = video_drvdata(file);
287 
288  if (band->tuner != 0 || band->index >= ARRAY_SIZE(bands) ||
289  (!tea->has_am && band->index == BAND_AM))
290  return -EINVAL;
291 
292  *band = bands[band->index];
293  return 0;
294 }
295 
296 static int vidioc_g_tuner(struct file *file, void *priv,
297  struct v4l2_tuner *v)
298 {
299  struct radio_tea5777 *tea = video_drvdata(file);
300  int res;
301 
302  if (v->index > 0)
303  return -EINVAL;
304 
305  res = radio_tea5777_update_read_reg(tea, 0);
306  if (res)
307  return res;
308 
309  memset(v, 0, sizeof(*v));
310  if (tea->has_am)
311  strlcpy(v->name, "AM/FM", sizeof(v->name));
312  else
313  strlcpy(v->name, "FM", sizeof(v->name));
314  v->type = V4L2_TUNER_RADIO;
319  v->rangelow = tea->has_am ? bands[BAND_AM].rangelow :
320  bands[BAND_FM].rangelow;
321  v->rangehigh = bands[BAND_FM].rangehigh;
322  if (tea->band == BAND_FM &&
325  else
327  v->audmode = tea->audmode;
328  /* shift - 12 to convert 4-bits (0-15) scale to 16-bits (0-65535) */
329  v->signal = (tea->read_reg & TEA5777_R_LEVEL_MASK) >>
330  (TEA5777_R_LEVEL_SHIFT - 12);
331 
332  /* Invalidate read_reg, so that next call we return up2date signal */
333  tea->read_reg = -1;
334 
335  return 0;
336 }
337 
338 static int vidioc_s_tuner(struct file *file, void *priv,
339  struct v4l2_tuner *v)
340 {
341  struct radio_tea5777 *tea = video_drvdata(file);
342  u32 orig_audmode = tea->audmode;
343 
344  if (v->index)
345  return -EINVAL;
346 
349 
350  tea->audmode = v->audmode;
351 
352  if (tea->audmode != orig_audmode && tea->band == BAND_FM)
353  return radio_tea5777_set_freq(tea);
354 
355  return 0;
356 }
357 
358 static int vidioc_g_frequency(struct file *file, void *priv,
359  struct v4l2_frequency *f)
360 {
361  struct radio_tea5777 *tea = video_drvdata(file);
362 
363  if (f->tuner != 0)
364  return -EINVAL;
365  f->type = V4L2_TUNER_RADIO;
366  f->frequency = tea->freq;
367  return 0;
368 }
369 
370 static int vidioc_s_frequency(struct file *file, void *priv,
371  struct v4l2_frequency *f)
372 {
373  struct radio_tea5777 *tea = video_drvdata(file);
374 
375  if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
376  return -EINVAL;
377 
378  if (tea->has_am && f->frequency < (20000 * 16))
379  tea->band = BAND_AM;
380  else
381  tea->band = BAND_FM;
382 
383  tea->freq = f->frequency;
384  return radio_tea5777_set_freq(tea);
385 }
386 
387 static int vidioc_s_hw_freq_seek(struct file *file, void *fh,
388  const struct v4l2_hw_freq_seek *a)
389 {
390  struct radio_tea5777 *tea = video_drvdata(file);
391  unsigned long timeout;
392  u32 rangelow = a->rangelow;
393  u32 rangehigh = a->rangehigh;
394  int i, res, spacing;
395  u32 orig_freq;
396 
397  if (a->tuner || a->wrap_around)
398  return -EINVAL;
399 
400  if (file->f_flags & O_NONBLOCK)
401  return -EWOULDBLOCK;
402 
403  if (rangelow || rangehigh) {
404  for (i = 0; i < ARRAY_SIZE(bands); i++) {
405  if (i == BAND_AM && !tea->has_am)
406  continue;
407  if (bands[i].rangelow >= rangelow &&
408  bands[i].rangehigh <= rangehigh)
409  break;
410  }
411  if (i == ARRAY_SIZE(bands))
412  return -EINVAL; /* No matching band found */
413 
414  tea->band = i;
415  if (tea->freq < rangelow || tea->freq > rangehigh) {
416  tea->freq = clamp(tea->freq, rangelow,
417  rangehigh);
418  res = radio_tea5777_set_freq(tea);
419  if (res)
420  return res;
421  }
422  } else {
423  rangelow = bands[tea->band].rangelow;
424  rangehigh = bands[tea->band].rangehigh;
425  }
426 
427  spacing = (tea->band == BAND_AM) ? (5 * 16) : (200 * 16); /* kHz */
428  orig_freq = tea->freq;
429 
431  if (tea->seek_rangelow != rangelow) {
433  tea->freq = rangelow;
434  res = radio_tea5777_set_freq(tea);
435  if (res)
436  goto leave;
437  tea->seek_rangelow = rangelow;
438  }
439  if (tea->seek_rangehigh != rangehigh) {
441  tea->freq = rangehigh;
442  res = radio_tea5777_set_freq(tea);
443  if (res)
444  goto leave;
445  tea->seek_rangehigh = rangehigh;
446  }
448 
450  if (a->seek_upward) {
452  tea->freq = orig_freq + spacing;
453  } else {
455  tea->freq = orig_freq - spacing;
456  }
457  res = radio_tea5777_set_freq(tea);
458  if (res)
459  goto leave;
460 
461  timeout = jiffies + msecs_to_jiffies(5000);
462  for (;;) {
463  if (time_after(jiffies, timeout)) {
464  res = -ENODATA;
465  break;
466  }
467 
468  res = radio_tea5777_update_read_reg(tea, 100);
469  if (res)
470  break;
471 
472  /*
473  * Note we use tea->freq to track how far we've searched sofar
474  * this is necessary to ensure we continue seeking at the right
475  * point, in the write_before_read case.
476  */
477  tea->freq = (tea->read_reg & TEA5777_R_FM_PLL_MASK);
478  tea->freq = tea5777_freq_to_v4l2_freq(tea, tea->freq);
479 
480  if ((tea->read_reg & TEA5777_R_SFOUND_MASK)) {
482  return 0;
483  }
484 
485  if (tea->read_reg & TEA5777_R_BLIM_MASK) {
486  res = -ENODATA;
487  break;
488  }
489 
490  /* Force read_reg update */
491  tea->read_reg = -1;
492  }
493 leave:
496  tea->freq = orig_freq;
498  return res;
499 }
500 
501 static int tea575x_s_ctrl(struct v4l2_ctrl *c)
502 {
503  struct radio_tea5777 *tea =
505 
506  switch (c->id) {
507  case V4L2_CID_AUDIO_MUTE:
508  if (c->val)
510  else
512 
513  return radio_tea5777_set_freq(tea);
514  }
515 
516  return -EINVAL;
517 }
518 
519 static const struct v4l2_file_operations tea575x_fops = {
520  .unlocked_ioctl = video_ioctl2,
521  .open = v4l2_fh_open,
522  .release = v4l2_fh_release,
523  .poll = v4l2_ctrl_poll,
524 };
525 
526 static const struct v4l2_ioctl_ops tea575x_ioctl_ops = {
527  .vidioc_querycap = vidioc_querycap,
528  .vidioc_g_tuner = vidioc_g_tuner,
529  .vidioc_s_tuner = vidioc_s_tuner,
530  .vidioc_g_frequency = vidioc_g_frequency,
531  .vidioc_s_frequency = vidioc_s_frequency,
532  .vidioc_s_hw_freq_seek = vidioc_s_hw_freq_seek,
533  .vidioc_enum_freq_bands = vidioc_enum_freq_bands,
534  .vidioc_log_status = v4l2_ctrl_log_status,
535  .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
536  .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
537 };
538 
539 static const struct video_device tea575x_radio = {
540  .ioctl_ops = &tea575x_ioctl_ops,
541  .release = video_device_release_empty,
542 };
543 
544 static const struct v4l2_ctrl_ops tea575x_ctrl_ops = {
545  .s_ctrl = tea575x_s_ctrl,
546 };
547 
548 int radio_tea5777_init(struct radio_tea5777 *tea, struct module *owner)
549 {
550  int res;
551 
552  tea->write_reg = (1LL << TEA5777_W_IFCE_SHIFT) |
553  (1LL << TEA5777_W_IFW_SHIFT) |
554  (1LL << TEA5777_W_INTEXT_SHIFT) |
555  (1LL << TEA5777_W_CHP0_SHIFT) |
556  (1LL << TEA5777_W_SLEV_SHIFT);
557  tea->freq = 90500 * 16; /* 90.5Mhz default */
559  res = radio_tea5777_set_freq(tea);
560  if (res) {
561  v4l2_err(tea->v4l2_dev, "can't set initial freq (%d)\n", res);
562  return res;
563  }
564 
565  tea->vd = tea575x_radio;
566  video_set_drvdata(&tea->vd, tea);
567  mutex_init(&tea->mutex);
568  strlcpy(tea->vd.name, tea->v4l2_dev->name, sizeof(tea->vd.name));
569  tea->vd.lock = &tea->mutex;
570  tea->vd.v4l2_dev = tea->v4l2_dev;
571  tea->fops = tea575x_fops;
572  tea->fops.owner = owner;
573  tea->vd.fops = &tea->fops;
574  set_bit(V4L2_FL_USE_FH_PRIO, &tea->vd.flags);
575 
576  tea->vd.ctrl_handler = &tea->ctrl_handler;
578  v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops,
579  V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
580  res = tea->ctrl_handler.error;
581  if (res) {
582  v4l2_err(tea->v4l2_dev, "can't initialize controls\n");
584  return res;
585  }
587 
588  res = video_register_device(&tea->vd, VFL_TYPE_RADIO, -1);
589  if (res) {
590  v4l2_err(tea->v4l2_dev, "can't register video device!\n");
591  v4l2_ctrl_handler_free(tea->vd.ctrl_handler);
592  return res;
593  }
594 
595  return 0;
596 }
598 
600 {
602  v4l2_ctrl_handler_free(tea->vd.ctrl_handler);
603 }