aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/radio/si470x
diff options
context:
space:
mode:
authorHans de Goede <hdegoede@redhat.com>2012-07-12 15:55:48 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2012-07-30 19:23:14 -0400
commitf140612d025f2b6a00651e7c2a9cc26b61dca119 (patch)
treec9a6f3d446420c5c699b606e5d5fba5c8be93b1d /drivers/media/radio/si470x
parent8d8c1b375cd5b239607037a859661e98cd0dac00 (diff)
[media] radio-si470x: Add support for the new band APIs
Signed-off-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/radio/si470x')
-rw-r--r--drivers/media/radio/si470x/radio-si470x-common.c215
-rw-r--r--drivers/media/radio/si470x/radio-si470x-i2c.c1
-rw-r--r--drivers/media/radio/si470x/radio-si470x-usb.c1
-rw-r--r--drivers/media/radio/si470x/radio-si470x.h1
4 files changed, 126 insertions, 92 deletions
diff --git a/drivers/media/radio/si470x/radio-si470x-common.c b/drivers/media/radio/si470x/radio-si470x-common.c
index 84ab3d5776da..9e38132afec6 100644
--- a/drivers/media/radio/si470x/radio-si470x-common.c
+++ b/drivers/media/radio/si470x/radio-si470x-common.c
@@ -4,6 +4,7 @@
4 * Driver for radios with Silicon Labs Si470x FM Radio Receivers 4 * Driver for radios with Silicon Labs Si470x FM Radio Receivers
5 * 5 *
6 * Copyright (c) 2009 Tobias Lorenz <tobias.lorenz@gmx.net> 6 * Copyright (c) 2009 Tobias Lorenz <tobias.lorenz@gmx.net>
7 * Copyright (c) 2012 Hans de Goede <hdegoede@redhat.com>
7 * 8 *
8 * This program is free software; you can redistribute it and/or modify 9 * 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 * it under the terms of the GNU General Public License as published by
@@ -127,14 +128,6 @@ static unsigned short space = 2;
127module_param(space, ushort, 0444); 128module_param(space, ushort, 0444);
128MODULE_PARM_DESC(space, "Spacing: 0=200kHz 1=100kHz *2=50kHz*"); 129MODULE_PARM_DESC(space, "Spacing: 0=200kHz 1=100kHz *2=50kHz*");
129 130
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) */
134static unsigned short band = 1;
135module_param(band, ushort, 0444);
136MODULE_PARM_DESC(band, "Band: 0=87.5..108MHz *1=76..108MHz* 2=76..90MHz");
137
138/* De-emphasis */ 131/* De-emphasis */
139/* 0: 75 us (USA) */ 132/* 0: 75 us (USA) */
140/* 1: 50 us (Europe, Australia, Japan) */ 133/* 1: 50 us (Europe, Australia, Japan) */
@@ -152,13 +145,61 @@ static unsigned int seek_timeout = 5000;
152module_param(seek_timeout, uint, 0644); 145module_param(seek_timeout, uint, 0644);
153MODULE_PARM_DESC(seek_timeout, "Seek timeout: *5000*"); 146MODULE_PARM_DESC(seek_timeout, "Seek timeout: *5000*");
154 147
155 148static const struct v4l2_frequency_band bands[] = {
149 {
150 .type = V4L2_TUNER_RADIO,
151 .index = 0,
152 .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
153 V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO |
154 V4L2_TUNER_CAP_HWSEEK_BOUNDED |
155 V4L2_TUNER_CAP_HWSEEK_WRAP,
156 .rangelow = 87500 * 16,
157 .rangehigh = 108000 * 16,
158 .modulation = V4L2_BAND_MODULATION_FM,
159 },
160 {
161 .type = V4L2_TUNER_RADIO,
162 .index = 1,
163 .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
164 V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO |
165 V4L2_TUNER_CAP_HWSEEK_BOUNDED |
166 V4L2_TUNER_CAP_HWSEEK_WRAP,
167 .rangelow = 76000 * 16,
168 .rangehigh = 108000 * 16,
169 .modulation = V4L2_BAND_MODULATION_FM,
170 },
171 {
172 .type = V4L2_TUNER_RADIO,
173 .index = 2,
174 .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
175 V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO |
176 V4L2_TUNER_CAP_HWSEEK_BOUNDED |
177 V4L2_TUNER_CAP_HWSEEK_WRAP,
178 .rangelow = 76000 * 16,
179 .rangehigh = 90000 * 16,
180 .modulation = V4L2_BAND_MODULATION_FM,
181 },
182};
156 183
157/************************************************************************** 184/**************************************************************************
158 * Generic Functions 185 * Generic Functions
159 **************************************************************************/ 186 **************************************************************************/
160 187
161/* 188/*
189 * si470x_set_band - set the band
190 */
191static int si470x_set_band(struct si470x_device *radio, int band)
192{
193 if (radio->band == band)
194 return 0;
195
196 radio->band = band;
197 radio->registers[SYSCONFIG2] &= ~SYSCONFIG2_BAND;
198 radio->registers[SYSCONFIG2] |= radio->band << 6;
199 return si470x_set_register(radio, SYSCONFIG2);
200}
201
202/*
162 * si470x_set_chan - set the channel 203 * si470x_set_chan - set the channel
163 */ 204 */
164static int si470x_set_chan(struct si470x_device *radio, unsigned short chan) 205static int si470x_set_chan(struct si470x_device *radio, unsigned short chan)
@@ -194,48 +235,39 @@ done:
194 return retval; 235 return retval;
195} 236}
196 237
197
198/* 238/*
199 * si470x_get_freq - get the frequency 239 * si470x_get_step - get channel spacing
200 */ 240 */
201static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq) 241static unsigned int si470x_get_step(struct si470x_device *radio)
202{ 242{
203 unsigned int spacing, band_bottom;
204 unsigned short chan;
205 int retval;
206
207 /* Spacing (kHz) */ 243 /* Spacing (kHz) */
208 switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) { 244 switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) {
209 /* 0: 200 kHz (USA, Australia) */ 245 /* 0: 200 kHz (USA, Australia) */
210 case 0: 246 case 0:
211 spacing = 0.200 * FREQ_MUL; break; 247 return 200 * 16;
212 /* 1: 100 kHz (Europe, Japan) */ 248 /* 1: 100 kHz (Europe, Japan) */
213 case 1: 249 case 1:
214 spacing = 0.100 * FREQ_MUL; break; 250 return 100 * 16;
215 /* 2: 50 kHz */ 251 /* 2: 50 kHz */
216 default: 252 default:
217 spacing = 0.050 * FREQ_MUL; break; 253 return 50 * 16;
218 }; 254 };
255}
219 256
220 /* Bottom of Band (MHz) */ 257
221 switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) { 258/*
222 /* 0: 87.5 - 108 MHz (USA, Europe) */ 259 * si470x_get_freq - get the frequency
223 case 0: 260 */
224 band_bottom = 87.5 * FREQ_MUL; break; 261static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq)
225 /* 1: 76 - 108 MHz (Japan wide band) */ 262{
226 default: 263 int chan, retval;
227 band_bottom = 76 * FREQ_MUL; break;
228 /* 2: 76 - 90 MHz (Japan) */
229 case 2:
230 band_bottom = 76 * FREQ_MUL; break;
231 };
232 264
233 /* read channel */ 265 /* read channel */
234 retval = si470x_get_register(radio, READCHAN); 266 retval = si470x_get_register(radio, READCHAN);
235 chan = radio->registers[READCHAN] & READCHAN_READCHAN; 267 chan = radio->registers[READCHAN] & READCHAN_READCHAN;
236 268
237 /* Frequency (MHz) = Spacing (kHz) x Channel + Bottom of Band (MHz) */ 269 /* Frequency (MHz) = Spacing (kHz) x Channel + Bottom of Band (MHz) */
238 *freq = chan * spacing + band_bottom; 270 *freq = chan * si470x_get_step(radio) + bands[radio->band].rangelow;
239 271
240 return retval; 272 return retval;
241} 273}
@@ -246,44 +278,12 @@ static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq)
246 */ 278 */
247int si470x_set_freq(struct si470x_device *radio, unsigned int freq) 279int si470x_set_freq(struct si470x_device *radio, unsigned int freq)
248{ 280{
249 unsigned int spacing, band_bottom, band_top;
250 unsigned short chan; 281 unsigned short chan;
251 282
252 /* Spacing (kHz) */ 283 freq = clamp(freq, bands[radio->band].rangelow,
253 switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) { 284 bands[radio->band].rangehigh);
254 /* 0: 200 kHz (USA, Australia) */
255 case 0:
256 spacing = 0.200 * FREQ_MUL; break;
257 /* 1: 100 kHz (Europe, Japan) */
258 case 1:
259 spacing = 0.100 * FREQ_MUL; break;
260 /* 2: 50 kHz */
261 default:
262 spacing = 0.050 * FREQ_MUL; break;
263 };
264
265 /* Bottom/Top of Band (MHz) */
266 switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {
267 /* 0: 87.5 - 108 MHz (USA, Europe) */
268 case 0:
269 band_bottom = 87.5 * FREQ_MUL;
270 band_top = 108 * FREQ_MUL;
271 break;
272 /* 1: 76 - 108 MHz (Japan wide band) */
273 default:
274 band_bottom = 76 * FREQ_MUL;
275 band_top = 108 * FREQ_MUL;
276 break;
277 /* 2: 76 - 90 MHz (Japan) */
278 case 2:
279 band_bottom = 76 * FREQ_MUL;
280 band_top = 90 * FREQ_MUL;
281 break;
282 };
283
284 freq = clamp(freq, band_bottom, band_top);
285 /* Chan = [ Freq (Mhz) - Bottom of Band (MHz) ] / Spacing (kHz) */ 285 /* Chan = [ Freq (Mhz) - Bottom of Band (MHz) ] / Spacing (kHz) */
286 chan = (freq - band_bottom) / spacing; 286 chan = (freq - bands[radio->band].rangelow) / si470x_get_step(radio);
287 287
288 return si470x_set_chan(radio, chan); 288 return si470x_set_chan(radio, chan);
289} 289}
@@ -293,18 +293,43 @@ int si470x_set_freq(struct si470x_device *radio, unsigned int freq)
293 * si470x_set_seek - set seek 293 * si470x_set_seek - set seek
294 */ 294 */
295static int si470x_set_seek(struct si470x_device *radio, 295static int si470x_set_seek(struct si470x_device *radio,
296 unsigned int wrap_around, unsigned int seek_upward) 296 struct v4l2_hw_freq_seek *seek)
297{ 297{
298 int retval = 0; 298 int band, retval;
299 unsigned int freq;
299 bool timed_out = 0; 300 bool timed_out = 0;
300 301
302 /* set band */
303 if (seek->rangelow || seek->rangehigh) {
304 for (band = 0; band < ARRAY_SIZE(bands); band++) {
305 if (bands[band].rangelow == seek->rangelow &&
306 bands[band].rangehigh == seek->rangehigh)
307 break;
308 }
309 if (band == ARRAY_SIZE(bands))
310 return -EINVAL; /* No matching band found */
311 } else
312 band = 1; /* If nothing is specified seek 76 - 108 Mhz */
313
314 if (radio->band != band) {
315 retval = si470x_get_freq(radio, &freq);
316 if (retval)
317 return retval;
318 retval = si470x_set_band(radio, band);
319 if (retval)
320 return retval;
321 retval = si470x_set_freq(radio, freq);
322 if (retval)
323 return retval;
324 }
325
301 /* start seeking */ 326 /* start seeking */
302 radio->registers[POWERCFG] |= POWERCFG_SEEK; 327 radio->registers[POWERCFG] |= POWERCFG_SEEK;
303 if (wrap_around == 1) 328 if (seek->wrap_around)
304 radio->registers[POWERCFG] &= ~POWERCFG_SKMODE; 329 radio->registers[POWERCFG] &= ~POWERCFG_SKMODE;
305 else 330 else
306 radio->registers[POWERCFG] |= POWERCFG_SKMODE; 331 radio->registers[POWERCFG] |= POWERCFG_SKMODE;
307 if (seek_upward == 1) 332 if (seek->seek_upward)
308 radio->registers[POWERCFG] |= POWERCFG_SEEKUP; 333 radio->registers[POWERCFG] |= POWERCFG_SEEKUP;
309 else 334 else
310 radio->registers[POWERCFG] &= ~POWERCFG_SEEKUP; 335 radio->registers[POWERCFG] &= ~POWERCFG_SEEKUP;
@@ -360,7 +385,7 @@ int si470x_start(struct si470x_device *radio)
360 /* sysconfig 2 */ 385 /* sysconfig 2 */
361 radio->registers[SYSCONFIG2] = 386 radio->registers[SYSCONFIG2] =
362 (0x1f << 8) | /* SEEKTH */ 387 (0x1f << 8) | /* SEEKTH */
363 ((band << 6) & SYSCONFIG2_BAND) | /* BAND */ 388 ((radio->band << 6) & SYSCONFIG2_BAND) |/* BAND */
364 ((space << 4) & SYSCONFIG2_SPACE) | /* SPACE */ 389 ((space << 4) & SYSCONFIG2_SPACE) | /* SPACE */
365 15; /* VOLUME (max) */ 390 15; /* VOLUME (max) */
366 retval = si470x_set_register(radio, SYSCONFIG2); 391 retval = si470x_set_register(radio, SYSCONFIG2);
@@ -569,25 +594,8 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv,
569 V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO | 594 V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO |
570 V4L2_TUNER_CAP_HWSEEK_BOUNDED | 595 V4L2_TUNER_CAP_HWSEEK_BOUNDED |
571 V4L2_TUNER_CAP_HWSEEK_WRAP; 596 V4L2_TUNER_CAP_HWSEEK_WRAP;
572 597 tuner->rangelow = 76 * FREQ_MUL;
573 /* range limits */ 598 tuner->rangehigh = 108 * FREQ_MUL;
574 switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {
575 /* 0: 87.5 - 108 MHz (USA, Europe, default) */
576 default:
577 tuner->rangelow = 87.5 * FREQ_MUL;
578 tuner->rangehigh = 108 * FREQ_MUL;
579 break;
580 /* 1: 76 - 108 MHz (Japan wide band) */
581 case 1:
582 tuner->rangelow = 76 * FREQ_MUL;
583 tuner->rangehigh = 108 * FREQ_MUL;
584 break;
585 /* 2: 76 - 90 MHz (Japan) */
586 case 2:
587 tuner->rangelow = 76 * FREQ_MUL;
588 tuner->rangehigh = 90 * FREQ_MUL;
589 break;
590 };
591 599
592 /* stereo indicator == stereo (instead of mono) */ 600 /* stereo indicator == stereo (instead of mono) */
593 if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 0) 601 if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 0)
@@ -670,10 +678,18 @@ static int si470x_vidioc_s_frequency(struct file *file, void *priv,
670 struct v4l2_frequency *freq) 678 struct v4l2_frequency *freq)
671{ 679{
672 struct si470x_device *radio = video_drvdata(file); 680 struct si470x_device *radio = video_drvdata(file);
681 int retval;
673 682
674 if (freq->tuner != 0) 683 if (freq->tuner != 0)
675 return -EINVAL; 684 return -EINVAL;
676 685
686 if (freq->frequency < bands[radio->band].rangelow ||
687 freq->frequency > bands[radio->band].rangehigh) {
688 /* Switch to band 1 which covers everything we support */
689 retval = si470x_set_band(radio, 1);
690 if (retval)
691 return retval;
692 }
677 return si470x_set_freq(radio, freq->frequency); 693 return si470x_set_freq(radio, freq->frequency);
678} 694}
679 695
@@ -689,7 +705,21 @@ static int si470x_vidioc_s_hw_freq_seek(struct file *file, void *priv,
689 if (seek->tuner != 0) 705 if (seek->tuner != 0)
690 return -EINVAL; 706 return -EINVAL;
691 707
692 return si470x_set_seek(radio, seek->wrap_around, seek->seek_upward); 708 return si470x_set_seek(radio, seek);
709}
710
711/*
712 * si470x_vidioc_enum_freq_bands - enumerate supported bands
713 */
714static int si470x_vidioc_enum_freq_bands(struct file *file, void *priv,
715 struct v4l2_frequency_band *band)
716{
717 if (band->tuner != 0)
718 return -EINVAL;
719 if (band->index >= ARRAY_SIZE(bands))
720 return -EINVAL;
721 *band = bands[band->index];
722 return 0;
693} 723}
694 724
695const struct v4l2_ctrl_ops si470x_ctrl_ops = { 725const struct v4l2_ctrl_ops si470x_ctrl_ops = {
@@ -706,6 +736,7 @@ static const struct v4l2_ioctl_ops si470x_ioctl_ops = {
706 .vidioc_g_frequency = si470x_vidioc_g_frequency, 736 .vidioc_g_frequency = si470x_vidioc_g_frequency,
707 .vidioc_s_frequency = si470x_vidioc_s_frequency, 737 .vidioc_s_frequency = si470x_vidioc_s_frequency,
708 .vidioc_s_hw_freq_seek = si470x_vidioc_s_hw_freq_seek, 738 .vidioc_s_hw_freq_seek = si470x_vidioc_s_hw_freq_seek,
739 .vidioc_enum_freq_bands = si470x_vidioc_enum_freq_bands,
709 .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 740 .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
710 .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 741 .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
711}; 742};
diff --git a/drivers/media/radio/si470x/radio-si470x-i2c.c b/drivers/media/radio/si470x/radio-si470x-i2c.c
index fb401a22b033..643a6ff7c5d0 100644
--- a/drivers/media/radio/si470x/radio-si470x-i2c.c
+++ b/drivers/media/radio/si470x/radio-si470x-i2c.c
@@ -350,6 +350,7 @@ static int __devinit si470x_i2c_probe(struct i2c_client *client,
350 } 350 }
351 351
352 radio->client = client; 352 radio->client = client;
353 radio->band = 1; /* Default to 76 - 108 MHz */
353 mutex_init(&radio->lock); 354 mutex_init(&radio->lock);
354 init_completion(&radio->completion); 355 init_completion(&radio->completion);
355 356
diff --git a/drivers/media/radio/si470x/radio-si470x-usb.c b/drivers/media/radio/si470x/radio-si470x-usb.c
index 0204cf445387..146be4263ea1 100644
--- a/drivers/media/radio/si470x/radio-si470x-usb.c
+++ b/drivers/media/radio/si470x/radio-si470x-usb.c
@@ -597,6 +597,7 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
597 } 597 }
598 radio->usbdev = interface_to_usbdev(intf); 598 radio->usbdev = interface_to_usbdev(intf);
599 radio->intf = intf; 599 radio->intf = intf;
600 radio->band = 1; /* Default to 76 - 108 MHz */
600 mutex_init(&radio->lock); 601 mutex_init(&radio->lock);
601 init_completion(&radio->completion); 602 init_completion(&radio->completion);
602 603
diff --git a/drivers/media/radio/si470x/radio-si470x.h b/drivers/media/radio/si470x/radio-si470x.h
index b3586e1ec2c4..2f089b4252df 100644
--- a/drivers/media/radio/si470x/radio-si470x.h
+++ b/drivers/media/radio/si470x/radio-si470x.h
@@ -147,6 +147,7 @@ struct si470x_device {
147 struct v4l2_device v4l2_dev; 147 struct v4l2_device v4l2_dev;
148 struct video_device videodev; 148 struct video_device videodev;
149 struct v4l2_ctrl_handler hdl; 149 struct v4l2_ctrl_handler hdl;
150 int band;
150 151
151 /* Silabs internal registers (0..15) */ 152 /* Silabs internal registers (0..15) */
152 unsigned short registers[RADIO_REGISTER_NUM]; 153 unsigned short registers[RADIO_REGISTER_NUM];