aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/media/radio/Kconfig25
-rw-r--r--drivers/media/radio/radio-terratec.c364
2 files changed, 64 insertions, 325 deletions
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig
index f76cbbc9a784..0b6d807f44c2 100644
--- a/drivers/media/radio/Kconfig
+++ b/drivers/media/radio/Kconfig
@@ -363,32 +363,17 @@ config RADIO_SF16FMR2
363config RADIO_TERRATEC 363config RADIO_TERRATEC
364 tristate "TerraTec ActiveRadio ISA Standalone" 364 tristate "TerraTec ActiveRadio ISA Standalone"
365 depends on ISA && VIDEO_V4L2 365 depends on ISA && VIDEO_V4L2
366 select RADIO_ISA
366 ---help--- 367 ---help---
367 Choose Y here if you have this FM radio card, and then fill in the 368 Choose Y here if you have this FM radio card.
368 port address below. (TODO)
369
370 Note: This driver is in its early stages. Right now volume and
371 frequency control and muting works at least for me, but
372 unfortunately I have not found anybody who wants to use this card
373 with Linux. So if it is this what YOU are trying to do right now,
374 PLEASE DROP ME A NOTE!! Rolf Offermanns <rolf@offermanns.de>.
375 369
376 In order to control your radio card, you will need to use programs 370 Note: this driver hasn't been tested since a long time due to lack
377 that are compatible with the Video For Linux API. Information on 371 of hardware. If you have this hardware, then please contact the
378 this API and pointers to "v4l" programs may be found at 372 linux-media mailinglist.
379 <file:Documentation/video4linux/API.html>.
380 373
381 To compile this driver as a module, choose M here: the 374 To compile this driver as a module, choose M here: the
382 module will be called radio-terratec. 375 module will be called radio-terratec.
383 376
384config RADIO_TERRATEC_PORT
385 hex "Terratec i/o port (normally 0x590)"
386 depends on RADIO_TERRATEC=y
387 default "590"
388 help
389 Fill in the I/O port of your TerraTec FM radio card. If unsure, go
390 with the default.
391
392config RADIO_TRUST 377config RADIO_TRUST
393 tristate "Trust FM radio card" 378 tristate "Trust FM radio card"
394 depends on ISA && VIDEO_V4L2 379 depends on ISA && VIDEO_V4L2
diff --git a/drivers/media/radio/radio-terratec.c b/drivers/media/radio/radio-terratec.c
index f2ed9cc3cf3b..2b82dd76a83b 100644
--- a/drivers/media/radio/radio-terratec.c
+++ b/drivers/media/radio/radio-terratec.c
@@ -16,11 +16,7 @@
16 * Frequency control is done digitally -- ie out(port,encodefreq(95.8)); 16 * Frequency control is done digitally -- ie out(port,encodefreq(95.8));
17 * Volume Control is done digitally 17 * Volume Control is done digitally
18 * 18 *
19 * there is a I2C controlled RDS decoder (SAA6588) onboard, which i would like to support someday 19 * Converted to the radio-isa framework by Hans Verkuil <hans.verkuil@cisco.com>
20 * (as soon i have understand how to get started :)
21 * If you can help me out with that, please contact me!!
22 *
23 *
24 * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> 20 * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
25 */ 21 */
26 22
@@ -32,41 +28,21 @@
32#include <linux/io.h> /* outb, outb_p */ 28#include <linux/io.h> /* outb, outb_p */
33#include <media/v4l2-device.h> 29#include <media/v4l2-device.h>
34#include <media/v4l2-ioctl.h> 30#include <media/v4l2-ioctl.h>
31#include "radio-isa.h"
35 32
36MODULE_AUTHOR("R.OFFERMANNS & others"); 33MODULE_AUTHOR("R. Offermans & others");
37MODULE_DESCRIPTION("A driver for the TerraTec ActiveRadio Standalone radio card."); 34MODULE_DESCRIPTION("A driver for the TerraTec ActiveRadio Standalone radio card.");
38MODULE_LICENSE("GPL"); 35MODULE_LICENSE("GPL");
39MODULE_VERSION("0.0.3"); 36MODULE_VERSION("0.1.99");
40
41#ifndef CONFIG_RADIO_TERRATEC_PORT
42#define CONFIG_RADIO_TERRATEC_PORT 0x590
43#endif
44 37
45static int io = CONFIG_RADIO_TERRATEC_PORT; 38/* Note: there seems to be only one possible port (0x590), but without
39 hardware this is hard to verify. For now, this is the only one we will
40 support. */
41static int io = 0x590;
46static int radio_nr = -1; 42static int radio_nr = -1;
47 43
48module_param(io, int, 0); 44module_param(radio_nr, int, 0444);
49MODULE_PARM_DESC(io, "I/O address of the TerraTec ActiveRadio card (0x590 or 0x591)"); 45MODULE_PARM_DESC(radio_nr, "Radio device number");
50module_param(radio_nr, int, 0);
51
52static struct v4l2_queryctrl radio_qctrl[] = {
53 {
54 .id = V4L2_CID_AUDIO_MUTE,
55 .name = "Mute",
56 .minimum = 0,
57 .maximum = 1,
58 .default_value = 1,
59 .type = V4L2_CTRL_TYPE_BOOLEAN,
60 },{
61 .id = V4L2_CID_AUDIO_VOLUME,
62 .name = "Volume",
63 .minimum = 0,
64 .maximum = 0xff,
65 .step = 1,
66 .default_value = 0xff,
67 .type = V4L2_CTRL_TYPE_INTEGER,
68 }
69};
70 46
71#define WRT_DIS 0x00 47#define WRT_DIS 0x00
72#define CLK_OFF 0x00 48#define CLK_OFF 0x00
@@ -76,63 +52,24 @@ static struct v4l2_queryctrl radio_qctrl[] = {
76#define CLK_ON 0x08 52#define CLK_ON 0x08
77#define WRT_EN 0x10 53#define WRT_EN 0x10
78 54
79struct terratec 55static struct radio_isa_card *terratec_alloc(void)
80{ 56{
81 struct v4l2_device v4l2_dev; 57 return kzalloc(sizeof(struct radio_isa_card), GFP_KERNEL);
82 struct video_device vdev; 58}
83 int io;
84 int curvol;
85 unsigned long curfreq;
86 int muted;
87 struct mutex lock;
88};
89
90static struct terratec terratec_card;
91
92/* local things */
93 59
94static void tt_write_vol(struct terratec *tt, int volume) 60static int terratec_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol)
95{ 61{
96 int i; 62 int i;
97 63
98 volume = volume + (volume * 32); /* change both channels */ 64 if (mute)
99 mutex_lock(&tt->lock); 65 vol = 0;
66 vol = vol + (vol * 32); /* change both channels */
100 for (i = 0; i < 8; i++) { 67 for (i = 0; i < 8; i++) {
101 if (volume & (0x80 >> i)) 68 if (vol & (0x80 >> i))
102 outb(0x80, tt->io + 1); 69 outb(0x80, isa->io + 1);
103 else 70 else
104 outb(0x00, tt->io + 1); 71 outb(0x00, isa->io + 1);
105 }
106 mutex_unlock(&tt->lock);
107}
108
109
110
111static void tt_mute(struct terratec *tt)
112{
113 tt->muted = 1;
114 tt_write_vol(tt, 0);
115}
116
117static int tt_setvol(struct terratec *tt, int vol)
118{
119 if (vol == tt->curvol) { /* requested volume = current */
120 if (tt->muted) { /* user is unmuting the card */
121 tt->muted = 0;
122 tt_write_vol(tt, vol); /* enable card */
123 }
124 return 0;
125 }
126
127 if (vol == 0) { /* volume = 0 means mute the card */
128 tt_write_vol(tt, 0); /* "turn off card" by setting vol to 0 */
129 tt->curvol = vol; /* track the volume state! */
130 return 0;
131 } 72 }
132
133 tt->muted = 0;
134 tt_write_vol(tt, vol);
135 tt->curvol = vol;
136 return 0; 73 return 0;
137} 74}
138 75
@@ -140,20 +77,15 @@ static int tt_setvol(struct terratec *tt, int vol)
140/* this is the worst part in this driver */ 77/* this is the worst part in this driver */
141/* many more or less strange things are going on here, but hey, it works :) */ 78/* many more or less strange things are going on here, but hey, it works :) */
142 79
143static int tt_setfreq(struct terratec *tt, unsigned long freq1) 80static int terratec_s_frequency(struct radio_isa_card *isa, u32 freq)
144{ 81{
145 int freq;
146 int i; 82 int i;
147 int p; 83 int p;
148 int temp; 84 int temp;
149 long rest; 85 long rest;
150 unsigned char buffer[25]; /* we have to bit shift 25 registers */ 86 unsigned char buffer[25]; /* we have to bit shift 25 registers */
151 87
152 mutex_lock(&tt->lock); 88 freq = freq / 160; /* convert the freq. to a nice to handle value */
153
154 tt->curfreq = freq1;
155
156 freq = freq1 / 160; /* convert the freq. to a nice to handle value */
157 memset(buffer, 0, sizeof(buffer)); 89 memset(buffer, 0, sizeof(buffer));
158 90
159 rest = freq * 10 + 10700; /* I once had understood what is going on here */ 91 rest = freq * 10 + 10700; /* I once had understood what is going on here */
@@ -175,239 +107,61 @@ static int tt_setfreq(struct terratec *tt, unsigned long freq1)
175 107
176 for (i = 24; i > -1; i--) { /* bit shift the values to the radiocard */ 108 for (i = 24; i > -1; i--) { /* bit shift the values to the radiocard */
177 if (buffer[i] == 1) { 109 if (buffer[i] == 1) {
178 outb(WRT_EN | DATA, tt->io); 110 outb(WRT_EN | DATA, isa->io);
179 outb(WRT_EN | DATA | CLK_ON, tt->io); 111 outb(WRT_EN | DATA | CLK_ON, isa->io);
180 outb(WRT_EN | DATA, tt->io); 112 outb(WRT_EN | DATA, isa->io);
181 } else { 113 } else {
182 outb(WRT_EN | 0x00, tt->io); 114 outb(WRT_EN | 0x00, isa->io);
183 outb(WRT_EN | 0x00 | CLK_ON, tt->io); 115 outb(WRT_EN | 0x00 | CLK_ON, isa->io);
184 }
185 }
186 outb(0x00, tt->io);
187
188 mutex_unlock(&tt->lock);
189
190 return 0;
191}
192
193static int tt_getsigstr(struct terratec *tt)
194{
195 if (inb(tt->io) & 2) /* bit set = no signal present */
196 return 0;
197 return 1; /* signal present */
198}
199
200static int vidioc_querycap(struct file *file, void *priv,
201 struct v4l2_capability *v)
202{
203 strlcpy(v->driver, "radio-terratec", sizeof(v->driver));
204 strlcpy(v->card, "ActiveRadio", sizeof(v->card));
205 strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
206 v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
207 return 0;
208}
209
210static int vidioc_g_tuner(struct file *file, void *priv,
211 struct v4l2_tuner *v)
212{
213 struct terratec *tt = video_drvdata(file);
214
215 if (v->index > 0)
216 return -EINVAL;
217
218 strlcpy(v->name, "FM", sizeof(v->name));
219 v->type = V4L2_TUNER_RADIO;
220 v->rangelow = 87 * 16000;
221 v->rangehigh = 108 * 16000;
222 v->rxsubchans = V4L2_TUNER_SUB_MONO;
223 v->capability = V4L2_TUNER_CAP_LOW;
224 v->audmode = V4L2_TUNER_MODE_MONO;
225 v->signal = 0xFFFF * tt_getsigstr(tt);
226 return 0;
227}
228
229static int vidioc_s_tuner(struct file *file, void *priv,
230 struct v4l2_tuner *v)
231{
232 return v->index ? -EINVAL : 0;
233}
234
235static int vidioc_s_frequency(struct file *file, void *priv,
236 struct v4l2_frequency *f)
237{
238 struct terratec *tt = video_drvdata(file);
239
240 if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
241 return -EINVAL;
242 tt_setfreq(tt, f->frequency);
243 return 0;
244}
245
246static int vidioc_g_frequency(struct file *file, void *priv,
247 struct v4l2_frequency *f)
248{
249 struct terratec *tt = video_drvdata(file);
250
251 if (f->tuner != 0)
252 return -EINVAL;
253 f->type = V4L2_TUNER_RADIO;
254 f->frequency = tt->curfreq;
255 return 0;
256}
257
258static int vidioc_queryctrl(struct file *file, void *priv,
259 struct v4l2_queryctrl *qc)
260{
261 int i;
262
263 for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
264 if (qc->id && qc->id == radio_qctrl[i].id) {
265 memcpy(qc, &(radio_qctrl[i]), sizeof(*qc));
266 return 0;
267 } 116 }
268 } 117 }
269 return -EINVAL; 118 outb(0x00, isa->io);
270}
271
272static int vidioc_g_ctrl(struct file *file, void *priv,
273 struct v4l2_control *ctrl)
274{
275 struct terratec *tt = video_drvdata(file);
276
277 switch (ctrl->id) {
278 case V4L2_CID_AUDIO_MUTE:
279 if (tt->muted)
280 ctrl->value = 1;
281 else
282 ctrl->value = 0;
283 return 0;
284 case V4L2_CID_AUDIO_VOLUME:
285 ctrl->value = tt->curvol * 6554;
286 return 0;
287 }
288 return -EINVAL;
289}
290
291static int vidioc_s_ctrl(struct file *file, void *priv,
292 struct v4l2_control *ctrl)
293{
294 struct terratec *tt = video_drvdata(file);
295
296 switch (ctrl->id) {
297 case V4L2_CID_AUDIO_MUTE:
298 if (ctrl->value)
299 tt_mute(tt);
300 else
301 tt_setvol(tt,tt->curvol);
302 return 0;
303 case V4L2_CID_AUDIO_VOLUME:
304 tt_setvol(tt,ctrl->value);
305 return 0;
306 }
307 return -EINVAL;
308}
309
310static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
311{
312 *i = 0;
313 return 0;
314}
315
316static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
317{
318 return i ? -EINVAL : 0;
319}
320
321static int vidioc_g_audio(struct file *file, void *priv,
322 struct v4l2_audio *a)
323{
324 a->index = 0;
325 strlcpy(a->name, "Radio", sizeof(a->name));
326 a->capability = V4L2_AUDCAP_STEREO;
327 return 0; 119 return 0;
328} 120}
329 121
330static int vidioc_s_audio(struct file *file, void *priv, 122static u32 terratec_g_signal(struct radio_isa_card *isa)
331 struct v4l2_audio *a)
332{ 123{
333 return a->index ? -EINVAL : 0; 124 /* bit set = no signal present */
125 return (inb(isa->io) & 2) ? 0 : 0xffff;
334} 126}
335 127
336static const struct v4l2_file_operations terratec_fops = { 128static const struct radio_isa_ops terratec_ops = {
337 .owner = THIS_MODULE, 129 .alloc = terratec_alloc,
338 .unlocked_ioctl = video_ioctl2, 130 .s_mute_volume = terratec_s_mute_volume,
131 .s_frequency = terratec_s_frequency,
132 .g_signal = terratec_g_signal,
339}; 133};
340 134
341static const struct v4l2_ioctl_ops terratec_ioctl_ops = { 135static const int terratec_ioports[] = { 0x590 };
342 .vidioc_querycap = vidioc_querycap, 136
343 .vidioc_g_tuner = vidioc_g_tuner, 137static struct radio_isa_driver terratec_driver = {
344 .vidioc_s_tuner = vidioc_s_tuner, 138 .driver = {
345 .vidioc_g_frequency = vidioc_g_frequency, 139 .match = radio_isa_match,
346 .vidioc_s_frequency = vidioc_s_frequency, 140 .probe = radio_isa_probe,
347 .vidioc_queryctrl = vidioc_queryctrl, 141 .remove = radio_isa_remove,
348 .vidioc_g_ctrl = vidioc_g_ctrl, 142 .driver = {
349 .vidioc_s_ctrl = vidioc_s_ctrl, 143 .name = "radio-terratec",
350 .vidioc_g_audio = vidioc_g_audio, 144 },
351 .vidioc_s_audio = vidioc_s_audio, 145 },
352 .vidioc_g_input = vidioc_g_input, 146 .io_params = &io,
353 .vidioc_s_input = vidioc_s_input, 147 .radio_nr_params = &radio_nr,
148 .io_ports = terratec_ioports,
149 .num_of_io_ports = ARRAY_SIZE(terratec_ioports),
150 .region_size = 2,
151 .card = "TerraTec ActiveRadio",
152 .ops = &terratec_ops,
153 .has_stereo = true,
154 .max_volume = 10,
354}; 155};
355 156
356static int __init terratec_init(void) 157static int __init terratec_init(void)
357{ 158{
358 struct terratec *tt = &terratec_card; 159 return isa_register_driver(&terratec_driver.driver, 1);
359 struct v4l2_device *v4l2_dev = &tt->v4l2_dev;
360 int res;
361
362 strlcpy(v4l2_dev->name, "terratec", sizeof(v4l2_dev->name));
363 tt->io = io;
364 if (tt->io == -1) {
365 v4l2_err(v4l2_dev, "you must set an I/O address with io=0x590 or 0x591\n");
366 return -EINVAL;
367 }
368 if (!request_region(tt->io, 2, "terratec")) {
369 v4l2_err(v4l2_dev, "port 0x%x already in use\n", io);
370 return -EBUSY;
371 }
372
373 res = v4l2_device_register(NULL, v4l2_dev);
374 if (res < 0) {
375 release_region(tt->io, 2);
376 v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
377 return res;
378 }
379
380 strlcpy(tt->vdev.name, v4l2_dev->name, sizeof(tt->vdev.name));
381 tt->vdev.v4l2_dev = v4l2_dev;
382 tt->vdev.fops = &terratec_fops;
383 tt->vdev.ioctl_ops = &terratec_ioctl_ops;
384 tt->vdev.release = video_device_release_empty;
385 video_set_drvdata(&tt->vdev, tt);
386
387 mutex_init(&tt->lock);
388
389 /* mute card - prevents noisy bootups */
390 tt_write_vol(tt, 0);
391
392 if (video_register_device(&tt->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
393 v4l2_device_unregister(&tt->v4l2_dev);
394 release_region(tt->io, 2);
395 return -EINVAL;
396 }
397
398 v4l2_info(v4l2_dev, "TERRATEC ActivRadio Standalone card driver.\n");
399 return 0;
400} 160}
401 161
402static void __exit terratec_exit(void) 162static void __exit terratec_exit(void)
403{ 163{
404 struct terratec *tt = &terratec_card; 164 isa_unregister_driver(&terratec_driver.driver);
405 struct v4l2_device *v4l2_dev = &tt->v4l2_dev;
406
407 video_unregister_device(&tt->vdev);
408 v4l2_device_unregister(&tt->v4l2_dev);
409 release_region(tt->io, 2);
410 v4l2_info(v4l2_dev, "TERRATEC ActivRadio Standalone card driver unloaded.\n");
411} 165}
412 166
413module_init(terratec_init); 167module_init(terratec_init);