aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/radio
diff options
context:
space:
mode:
authorHans Verkuil <hans.verkuil@cisco.com>2012-07-10 07:26:04 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2012-07-30 19:23:10 -0400
commit6652c716a5e46f0631d26a7f83a8f7247d293ae7 (patch)
tree36d8e7638a4bea897a7e3b482b8ae3cf6b6851b7 /drivers/media/radio
parentcc0d32665f9f8d4e7297a470e91b8848c7f0436c (diff)
[media] radio-cadet: implement frequency band enumeration
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/radio')
-rw-r--r--drivers/media/radio/radio-cadet.c131
1 files changed, 74 insertions, 57 deletions
diff --git a/drivers/media/radio/radio-cadet.c b/drivers/media/radio/radio-cadet.c
index d1fb42746fc2..697a421c9940 100644
--- a/drivers/media/radio/radio-cadet.c
+++ b/drivers/media/radio/radio-cadet.c
@@ -66,7 +66,8 @@ struct cadet {
66 struct video_device vdev; 66 struct video_device vdev;
67 struct v4l2_ctrl_handler ctrl_handler; 67 struct v4l2_ctrl_handler ctrl_handler;
68 int io; 68 int io;
69 int curtuner; 69 bool is_fm_band;
70 u32 curfreq;
70 int tunestat; 71 int tunestat;
71 int sigstrength; 72 int sigstrength;
72 wait_queue_head_t read_queue; 73 wait_queue_head_t read_queue;
@@ -84,9 +85,9 @@ static struct cadet cadet_card;
84 * The V4L API spec does not define any particular unit for the signal 85 * The V4L API spec does not define any particular unit for the signal
85 * strength value. These values are in microvolts of RF at the tuner's input. 86 * strength value. These values are in microvolts of RF at the tuner's input.
86 */ 87 */
87static __u16 sigtable[2][4] = { 88static u16 sigtable[2][4] = {
89 { 1835, 2621, 4128, 65535 },
88 { 2185, 4369, 13107, 65535 }, 90 { 2185, 4369, 13107, 65535 },
89 { 1835, 2621, 4128, 65535 }
90}; 91};
91 92
92 93
@@ -94,7 +95,7 @@ static int cadet_getstereo(struct cadet *dev)
94{ 95{
95 int ret = V4L2_TUNER_SUB_MONO; 96 int ret = V4L2_TUNER_SUB_MONO;
96 97
97 if (dev->curtuner != 0) /* Only FM has stereo capability! */ 98 if (!dev->is_fm_band) /* Only FM has stereo capability! */
98 return V4L2_TUNER_SUB_MONO; 99 return V4L2_TUNER_SUB_MONO;
99 100
100 outb(7, dev->io); /* Select tuner control */ 101 outb(7, dev->io); /* Select tuner control */
@@ -149,20 +150,18 @@ static unsigned cadet_getfreq(struct cadet *dev)
149 /* 150 /*
150 * Convert to actual frequency 151 * Convert to actual frequency
151 */ 152 */
152 if (dev->curtuner == 0) { /* FM */ 153 if (!dev->is_fm_band) /* AM */
153 test = 12500; 154 return ((fifo & 0x7fff) - 450) * 16;
154 for (i = 0; i < 14; i++) { 155
155 if ((fifo & 0x01) != 0) 156 test = 12500;
156 freq += test; 157 for (i = 0; i < 14; i++) {
157 test = test << 1; 158 if ((fifo & 0x01) != 0)
158 fifo = fifo >> 1; 159 freq += test;
159 } 160 test = test << 1;
160 freq -= 10700000; /* IF frequency is 10.7 MHz */ 161 fifo = fifo >> 1;
161 freq = (freq * 16) / 1000; /* Make it 1/16 kHz */
162 } 162 }
163 if (dev->curtuner == 1) /* AM */ 163 freq -= 10700000; /* IF frequency is 10.7 MHz */
164 freq = ((fifo & 0x7fff) - 2010) * 16; 164 freq = (freq * 16) / 1000; /* Make it 1/16 kHz */
165
166 return freq; 165 return freq;
167} 166}
168 167
@@ -197,11 +196,12 @@ static void cadet_setfreq(struct cadet *dev, unsigned freq)
197 int i, j, test; 196 int i, j, test;
198 int curvol; 197 int curvol;
199 198
199 dev->curfreq = freq;
200 /* 200 /*
201 * Formulate a fifo command 201 * Formulate a fifo command
202 */ 202 */
203 fifo = 0; 203 fifo = 0;
204 if (dev->curtuner == 0) { /* FM */ 204 if (dev->is_fm_band) { /* FM */
205 test = 102400; 205 test = 102400;
206 freq = freq / 16; /* Make it kHz */ 206 freq = freq / 16; /* Make it kHz */
207 freq += 10700; /* IF is 10700 kHz */ 207 freq += 10700; /* IF is 10700 kHz */
@@ -213,10 +213,9 @@ static void cadet_setfreq(struct cadet *dev, unsigned freq)
213 } 213 }
214 test = test >> 1; 214 test = test >> 1;
215 } 215 }
216 } 216 } else { /* AM */
217 if (dev->curtuner == 1) { /* AM */ 217 fifo = (freq / 16) + 450; /* Make it kHz */
218 fifo = (freq / 16) + 2010; /* Make it kHz */ 218 fifo |= 0x100000; /* Select AM Band */
219 fifo |= 0x100000; /* Select AM Band */
220 } 219 }
221 220
222 /* 221 /*
@@ -239,7 +238,7 @@ static void cadet_setfreq(struct cadet *dev, unsigned freq)
239 238
240 cadet_gettune(dev); 239 cadet_gettune(dev);
241 if ((dev->tunestat & 0x40) == 0) { /* Tuned */ 240 if ((dev->tunestat & 0x40) == 0) { /* Tuned */
242 dev->sigstrength = sigtable[dev->curtuner][j]; 241 dev->sigstrength = sigtable[dev->is_fm_band][j];
243 goto reset_rds; 242 goto reset_rds;
244 } 243 }
245 } 244 }
@@ -338,39 +337,52 @@ static int vidioc_querycap(struct file *file, void *priv,
338 return 0; 337 return 0;
339} 338}
340 339
340static const struct v4l2_frequency_band bands[] = {
341 {
342 .index = 0,
343 .type = V4L2_TUNER_RADIO,
344 .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS,
345 .rangelow = 8320, /* 520 kHz */
346 .rangehigh = 26400, /* 1650 kHz */
347 .modulation = V4L2_BAND_MODULATION_AM,
348 }, {
349 .index = 1,
350 .type = V4L2_TUNER_RADIO,
351 .capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS |
352 V4L2_TUNER_CAP_RDS_BLOCK_IO | V4L2_TUNER_CAP_LOW |
353 V4L2_TUNER_CAP_FREQ_BANDS,
354 .rangelow = 1400000, /* 87.5 MHz */
355 .rangehigh = 1728000, /* 108.0 MHz */
356 .modulation = V4L2_BAND_MODULATION_FM,
357 },
358};
359
341static int vidioc_g_tuner(struct file *file, void *priv, 360static int vidioc_g_tuner(struct file *file, void *priv,
342 struct v4l2_tuner *v) 361 struct v4l2_tuner *v)
343{ 362{
344 struct cadet *dev = video_drvdata(file); 363 struct cadet *dev = video_drvdata(file);
345 364
365 if (v->index)
366 return -EINVAL;
346 v->type = V4L2_TUNER_RADIO; 367 v->type = V4L2_TUNER_RADIO;
347 switch (v->index) { 368 strlcpy(v->name, "Radio", sizeof(v->name));
348 case 0: 369 v->capability = bands[0].capability | bands[1].capability;
349 strlcpy(v->name, "FM", sizeof(v->name)); 370 v->rangelow = bands[0].rangelow; /* 520 kHz (start of AM band) */
350 v->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS | 371 v->rangehigh = bands[1].rangehigh; /* 108.0 MHz (end of FM band) */
351 V4L2_TUNER_CAP_RDS_BLOCK_IO | V4L2_TUNER_CAP_LOW; 372 if (dev->is_fm_band) {
352 v->rangelow = 1400000; /* 87.5 MHz */
353 v->rangehigh = 1728000; /* 108.0 MHz */
354 v->rxsubchans = cadet_getstereo(dev); 373 v->rxsubchans = cadet_getstereo(dev);
355 v->audmode = V4L2_TUNER_MODE_STEREO;
356 outb(3, dev->io); 374 outb(3, dev->io);
357 outb(inb(dev->io + 1) & 0x7f, dev->io + 1); 375 outb(inb(dev->io + 1) & 0x7f, dev->io + 1);
358 mdelay(100); 376 mdelay(100);
359 outb(3, dev->io); 377 outb(3, dev->io);
360 if (inb(dev->io + 1) & 0x80) 378 if (inb(dev->io + 1) & 0x80)
361 v->rxsubchans |= V4L2_TUNER_SUB_RDS; 379 v->rxsubchans |= V4L2_TUNER_SUB_RDS;
362 break; 380 } else {
363 case 1:
364 strlcpy(v->name, "AM", sizeof(v->name));
365 v->capability = V4L2_TUNER_CAP_LOW;
366 v->rangelow = 8320; /* 520 kHz */ 381 v->rangelow = 8320; /* 520 kHz */
367 v->rangehigh = 26400; /* 1650 kHz */ 382 v->rangehigh = 26400; /* 1650 kHz */
368 v->rxsubchans = V4L2_TUNER_SUB_MONO; 383 v->rxsubchans = V4L2_TUNER_SUB_MONO;
369 v->audmode = V4L2_TUNER_MODE_MONO;
370 break;
371 default:
372 return -EINVAL;
373 } 384 }
385 v->audmode = V4L2_TUNER_MODE_STEREO;
374 v->signal = dev->sigstrength; /* We might need to modify scaling of this */ 386 v->signal = dev->sigstrength; /* We might need to modify scaling of this */
375 return 0; 387 return 0;
376} 388}
@@ -378,8 +390,17 @@ static int vidioc_g_tuner(struct file *file, void *priv,
378static int vidioc_s_tuner(struct file *file, void *priv, 390static int vidioc_s_tuner(struct file *file, void *priv,
379 struct v4l2_tuner *v) 391 struct v4l2_tuner *v)
380{ 392{
381 if (v->index != 0 && v->index != 1) 393 return v->index ? -EINVAL : 0;
394}
395
396static int vidioc_enum_freq_bands(struct file *file, void *priv,
397 struct v4l2_frequency_band *band)
398{
399 if (band->tuner)
400 return -EINVAL;
401 if (band->index >= ARRAY_SIZE(bands))
382 return -EINVAL; 402 return -EINVAL;
403 *band = bands[band->index];
383 return 0; 404 return 0;
384} 405}
385 406
@@ -388,10 +409,10 @@ static int vidioc_g_frequency(struct file *file, void *priv,
388{ 409{
389 struct cadet *dev = video_drvdata(file); 410 struct cadet *dev = video_drvdata(file);
390 411
391 if (f->tuner > 1) 412 if (f->tuner)
392 return -EINVAL; 413 return -EINVAL;
393 f->type = V4L2_TUNER_RADIO; 414 f->type = V4L2_TUNER_RADIO;
394 f->frequency = cadet_getfreq(dev); 415 f->frequency = dev->curfreq;
395 return 0; 416 return 0;
396} 417}
397 418
@@ -401,20 +422,12 @@ static int vidioc_s_frequency(struct file *file, void *priv,
401{ 422{
402 struct cadet *dev = video_drvdata(file); 423 struct cadet *dev = video_drvdata(file);
403 424
404 if (f->type != V4L2_TUNER_RADIO) 425 if (f->tuner)
405 return -EINVAL;
406 if (f->tuner == 0) {
407 if (f->frequency < 1400000)
408 f->frequency = 1400000;
409 else if (f->frequency > 1728000)
410 f->frequency = 1728000;
411 } else if (f->tuner == 1) {
412 if (f->frequency < 8320)
413 f->frequency = 8320;
414 else if (f->frequency > 26400)
415 f->frequency = 26400;
416 } else
417 return -EINVAL; 426 return -EINVAL;
427 dev->is_fm_band =
428 f->frequency >= (bands[0].rangehigh + bands[1].rangelow) / 2;
429 clamp(f->frequency, bands[dev->is_fm_band].rangelow,
430 bands[dev->is_fm_band].rangehigh);
418 cadet_setfreq(dev, f->frequency); 431 cadet_setfreq(dev, f->frequency);
419 return 0; 432 return 0;
420} 433}
@@ -499,6 +512,7 @@ static const struct v4l2_ioctl_ops cadet_ioctl_ops = {
499 .vidioc_s_tuner = vidioc_s_tuner, 512 .vidioc_s_tuner = vidioc_s_tuner,
500 .vidioc_g_frequency = vidioc_g_frequency, 513 .vidioc_g_frequency = vidioc_g_frequency,
501 .vidioc_s_frequency = vidioc_s_frequency, 514 .vidioc_s_frequency = vidioc_s_frequency,
515 .vidioc_enum_freq_bands = vidioc_enum_freq_bands,
502 .vidioc_log_status = v4l2_ctrl_log_status, 516 .vidioc_log_status = v4l2_ctrl_log_status,
503 .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 517 .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
504 .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 518 .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
@@ -555,8 +569,8 @@ static void cadet_probe(struct cadet *dev)
555 for (i = 0; i < 8; i++) { 569 for (i = 0; i < 8; i++) {
556 dev->io = iovals[i]; 570 dev->io = iovals[i];
557 if (request_region(dev->io, 2, "cadet-probe")) { 571 if (request_region(dev->io, 2, "cadet-probe")) {
558 cadet_setfreq(dev, 1410); 572 cadet_setfreq(dev, bands[1].rangelow);
559 if (cadet_getfreq(dev) == 1410) { 573 if (cadet_getfreq(dev) == bands[1].rangelow) {
560 release_region(dev->io, 2); 574 release_region(dev->io, 2);
561 return; 575 return;
562 } 576 }
@@ -619,6 +633,9 @@ static int __init cadet_init(void)
619 goto err_hdl; 633 goto err_hdl;
620 } 634 }
621 635
636 dev->is_fm_band = true;
637 dev->curfreq = bands[dev->is_fm_band].rangelow;
638 cadet_setfreq(dev, dev->curfreq);
622 strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name)); 639 strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
623 dev->vdev.v4l2_dev = v4l2_dev; 640 dev->vdev.v4l2_dev = v4l2_dev;
624 dev->vdev.fops = &cadet_fops; 641 dev->vdev.fops = &cadet_fops;