1 /* Terratec ActiveRadio ISA Standalone card driver for Linux radio support
2 * (c) 1999 R. Offermanns (rolf@offermanns.de)
3 * based on the aimslab radio driver from M. Kirkwood
4 * many thanks to Michael Becker and Friedhelm Birth (from TerraTec)
8 * 1999-05-21 First preview release
10 * Notes on the hardware:
11 * There are two "main" chips on the card:
12 * - Philips OM5610 (http://www-us.semiconductors.philips.com/acrobat/datasheets/OM5610_2.pdf)
13 * - Philips SAA6588 (http://www-us.semiconductors.philips.com/acrobat/datasheets/SAA6588_1.pdf)
14 * (you can get the datasheet at the above links)
16 * Frequency control is done digitally -- ie out(port,encodefreq(95.8));
17 * Volume Control is done digitally
19 * there is a I2C controlled RDS decoder (SAA6588) onboard, which i would like to support someday
20 * (as soon i have understand how to get started :)
21 * If you can help me out with that, please contact me!!
24 * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
27 #include <linux/module.h> /* Modules */
28 #include <linux/init.h> /* Initdata */
29 #include <linux/ioport.h> /* request_region */
30 #include <linux/videodev2.h> /* kernel radio structs */
31 #include <linux/mutex.h>
32 #include <linux/version.h> /* for KERNEL_VERSION MACRO */
33 #include <linux/io.h> /* outb, outb_p */
34 #include <media/v4l2-device.h>
35 #include <media/v4l2-ioctl.h>
37 MODULE_AUTHOR("R.OFFERMANNS & others");
38 MODULE_DESCRIPTION("A driver for the TerraTec ActiveRadio Standalone radio card.");
39 MODULE_LICENSE("GPL");
41 #ifndef CONFIG_RADIO_TERRATEC_PORT
42 #define CONFIG_RADIO_TERRATEC_PORT 0x590
45 static int io
= CONFIG_RADIO_TERRATEC_PORT
;
46 static int radio_nr
= -1;
48 module_param(io
, int, 0);
49 MODULE_PARM_DESC(io
, "I/O address of the TerraTec ActiveRadio card (0x590 or 0x591)");
50 module_param(radio_nr
, int, 0);
52 #define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
54 static struct v4l2_queryctrl radio_qctrl
[] = {
56 .id
= V4L2_CID_AUDIO_MUTE
,
61 .type
= V4L2_CTRL_TYPE_BOOLEAN
,
63 .id
= V4L2_CID_AUDIO_VOLUME
,
68 .default_value
= 0xff,
69 .type
= V4L2_CTRL_TYPE_INTEGER
,
83 struct v4l2_device v4l2_dev
;
84 struct video_device vdev
;
87 unsigned long curfreq
;
92 static struct terratec terratec_card
;
96 static void tt_write_vol(struct terratec
*tt
, int volume
)
100 volume
= volume
+ (volume
* 32); /* change both channels */
101 mutex_lock(&tt
->lock
);
102 for (i
= 0; i
< 8; i
++) {
103 if (volume
& (0x80 >> i
))
104 outb(0x80, tt
->io
+ 1);
106 outb(0x00, tt
->io
+ 1);
108 mutex_unlock(&tt
->lock
);
113 static void tt_mute(struct terratec
*tt
)
119 static int tt_setvol(struct terratec
*tt
, int vol
)
121 if (vol
== tt
->curvol
) { /* requested volume = current */
122 if (tt
->muted
) { /* user is unmuting the card */
124 tt_write_vol(tt
, vol
); /* enable card */
129 if (vol
== 0) { /* volume = 0 means mute the card */
130 tt_write_vol(tt
, 0); /* "turn off card" by setting vol to 0 */
131 tt
->curvol
= vol
; /* track the volume state! */
136 tt_write_vol(tt
, vol
);
142 /* this is the worst part in this driver */
143 /* many more or less strange things are going on here, but hey, it works :) */
145 static int tt_setfreq(struct terratec
*tt
, unsigned long freq1
)
152 unsigned char buffer
[25]; /* we have to bit shift 25 registers */
154 mutex_lock(&tt
->lock
);
158 freq
= freq1
/ 160; /* convert the freq. to a nice to handle value */
159 memset(buffer
, 0, sizeof(buffer
));
161 rest
= freq
* 10 + 10700; /* I once had understood what is going on here */
162 /* maybe some wise guy (friedhelm?) can comment this stuff */
167 if (rest
% temp
== rest
)
178 for (i
= 24; i
> -1; i
--) { /* bit shift the values to the radiocard */
179 if (buffer
[i
] == 1) {
180 outb(WRT_EN
| DATA
, tt
->io
);
181 outb(WRT_EN
| DATA
| CLK_ON
, tt
->io
);
182 outb(WRT_EN
| DATA
, tt
->io
);
184 outb(WRT_EN
| 0x00, tt
->io
);
185 outb(WRT_EN
| 0x00 | CLK_ON
, tt
->io
);
190 mutex_unlock(&tt
->lock
);
195 static int tt_getsigstr(struct terratec
*tt
)
197 if (inb(tt
->io
) & 2) /* bit set = no signal present */
199 return 1; /* signal present */
202 static int vidioc_querycap(struct file
*file
, void *priv
,
203 struct v4l2_capability
*v
)
205 strlcpy(v
->driver
, "radio-terratec", sizeof(v
->driver
));
206 strlcpy(v
->card
, "ActiveRadio", sizeof(v
->card
));
207 strlcpy(v
->bus_info
, "ISA", sizeof(v
->bus_info
));
208 v
->version
= RADIO_VERSION
;
209 v
->capabilities
= V4L2_CAP_TUNER
| V4L2_CAP_RADIO
;
213 static int vidioc_g_tuner(struct file
*file
, void *priv
,
214 struct v4l2_tuner
*v
)
216 struct terratec
*tt
= video_drvdata(file
);
221 strlcpy(v
->name
, "FM", sizeof(v
->name
));
222 v
->type
= V4L2_TUNER_RADIO
;
223 v
->rangelow
= 87 * 16000;
224 v
->rangehigh
= 108 * 16000;
225 v
->rxsubchans
= V4L2_TUNER_SUB_MONO
;
226 v
->capability
= V4L2_TUNER_CAP_LOW
;
227 v
->audmode
= V4L2_TUNER_MODE_MONO
;
228 v
->signal
= 0xFFFF * tt_getsigstr(tt
);
232 static int vidioc_s_tuner(struct file
*file
, void *priv
,
233 struct v4l2_tuner
*v
)
235 return v
->index
? -EINVAL
: 0;
238 static int vidioc_s_frequency(struct file
*file
, void *priv
,
239 struct v4l2_frequency
*f
)
241 struct terratec
*tt
= video_drvdata(file
);
243 tt_setfreq(tt
, f
->frequency
);
247 static int vidioc_g_frequency(struct file
*file
, void *priv
,
248 struct v4l2_frequency
*f
)
250 struct terratec
*tt
= video_drvdata(file
);
252 f
->type
= V4L2_TUNER_RADIO
;
253 f
->frequency
= tt
->curfreq
;
257 static int vidioc_queryctrl(struct file
*file
, void *priv
,
258 struct v4l2_queryctrl
*qc
)
262 for (i
= 0; i
< ARRAY_SIZE(radio_qctrl
); i
++) {
263 if (qc
->id
&& qc
->id
== radio_qctrl
[i
].id
) {
264 memcpy(qc
, &(radio_qctrl
[i
]), sizeof(*qc
));
271 static int vidioc_g_ctrl(struct file
*file
, void *priv
,
272 struct v4l2_control
*ctrl
)
274 struct terratec
*tt
= video_drvdata(file
);
277 case V4L2_CID_AUDIO_MUTE
:
283 case V4L2_CID_AUDIO_VOLUME
:
284 ctrl
->value
= tt
->curvol
* 6554;
290 static int vidioc_s_ctrl(struct file
*file
, void *priv
,
291 struct v4l2_control
*ctrl
)
293 struct terratec
*tt
= video_drvdata(file
);
296 case V4L2_CID_AUDIO_MUTE
:
300 tt_setvol(tt
,tt
->curvol
);
302 case V4L2_CID_AUDIO_VOLUME
:
303 tt_setvol(tt
,ctrl
->value
);
309 static int vidioc_g_input(struct file
*filp
, void *priv
, unsigned int *i
)
315 static int vidioc_s_input(struct file
*filp
, void *priv
, unsigned int i
)
317 return i
? -EINVAL
: 0;
320 static int vidioc_g_audio(struct file
*file
, void *priv
,
321 struct v4l2_audio
*a
)
324 strlcpy(a
->name
, "Radio", sizeof(a
->name
));
325 a
->capability
= V4L2_AUDCAP_STEREO
;
329 static int vidioc_s_audio(struct file
*file
, void *priv
,
330 struct v4l2_audio
*a
)
332 return a
->index
? -EINVAL
: 0;
335 static const struct v4l2_file_operations terratec_fops
= {
336 .owner
= THIS_MODULE
,
337 .ioctl
= video_ioctl2
,
340 static const struct v4l2_ioctl_ops terratec_ioctl_ops
= {
341 .vidioc_querycap
= vidioc_querycap
,
342 .vidioc_g_tuner
= vidioc_g_tuner
,
343 .vidioc_s_tuner
= vidioc_s_tuner
,
344 .vidioc_g_frequency
= vidioc_g_frequency
,
345 .vidioc_s_frequency
= vidioc_s_frequency
,
346 .vidioc_queryctrl
= vidioc_queryctrl
,
347 .vidioc_g_ctrl
= vidioc_g_ctrl
,
348 .vidioc_s_ctrl
= vidioc_s_ctrl
,
349 .vidioc_g_audio
= vidioc_g_audio
,
350 .vidioc_s_audio
= vidioc_s_audio
,
351 .vidioc_g_input
= vidioc_g_input
,
352 .vidioc_s_input
= vidioc_s_input
,
355 static int __init
terratec_init(void)
357 struct terratec
*tt
= &terratec_card
;
358 struct v4l2_device
*v4l2_dev
= &tt
->v4l2_dev
;
361 strlcpy(v4l2_dev
->name
, "terratec", sizeof(v4l2_dev
->name
));
364 v4l2_err(v4l2_dev
, "you must set an I/O address with io=0x590 or 0x591\n");
367 if (!request_region(tt
->io
, 2, "terratec")) {
368 v4l2_err(v4l2_dev
, "port 0x%x already in use\n", io
);
372 res
= v4l2_device_register(NULL
, v4l2_dev
);
374 release_region(tt
->io
, 2);
375 v4l2_err(v4l2_dev
, "Could not register v4l2_device\n");
379 strlcpy(tt
->vdev
.name
, v4l2_dev
->name
, sizeof(tt
->vdev
.name
));
380 tt
->vdev
.v4l2_dev
= v4l2_dev
;
381 tt
->vdev
.fops
= &terratec_fops
;
382 tt
->vdev
.ioctl_ops
= &terratec_ioctl_ops
;
383 tt
->vdev
.release
= video_device_release_empty
;
384 video_set_drvdata(&tt
->vdev
, tt
);
386 mutex_init(&tt
->lock
);
388 if (video_register_device(&tt
->vdev
, VFL_TYPE_RADIO
, radio_nr
) < 0) {
389 v4l2_device_unregister(&tt
->v4l2_dev
);
390 release_region(tt
->io
, 2);
394 v4l2_info(v4l2_dev
, "TERRATEC ActivRadio Standalone card driver.\n");
396 /* mute card - prevents noisy bootups */
401 static void __exit
terratec_exit(void)
403 struct terratec
*tt
= &terratec_card
;
404 struct v4l2_device
*v4l2_dev
= &tt
->v4l2_dev
;
406 video_unregister_device(&tt
->vdev
);
407 v4l2_device_unregister(&tt
->v4l2_dev
);
408 release_region(tt
->io
, 2);
409 v4l2_info(v4l2_dev
, "TERRATEC ActivRadio Standalone card driver unloaded.\n");
412 module_init(terratec_init
);
413 module_exit(terratec_exit
);