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/io.h> /* outb, outb_p */
33 #include <media/v4l2-device.h>
34 #include <media/v4l2-ioctl.h>
36 MODULE_AUTHOR("R.OFFERMANNS & others");
37 MODULE_DESCRIPTION("A driver for the TerraTec ActiveRadio Standalone radio card.");
38 MODULE_LICENSE("GPL");
39 MODULE_VERSION("0.0.3");
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 static struct v4l2_queryctrl radio_qctrl
[] = {
54 .id
= V4L2_CID_AUDIO_MUTE
,
59 .type
= V4L2_CTRL_TYPE_BOOLEAN
,
61 .id
= V4L2_CID_AUDIO_VOLUME
,
66 .default_value
= 0xff,
67 .type
= V4L2_CTRL_TYPE_INTEGER
,
81 struct v4l2_device v4l2_dev
;
82 struct video_device vdev
;
85 unsigned long curfreq
;
90 static struct terratec terratec_card
;
94 static void tt_write_vol(struct terratec
*tt
, int volume
)
98 volume
= volume
+ (volume
* 32); /* change both channels */
99 mutex_lock(&tt
->lock
);
100 for (i
= 0; i
< 8; i
++) {
101 if (volume
& (0x80 >> i
))
102 outb(0x80, tt
->io
+ 1);
104 outb(0x00, tt
->io
+ 1);
106 mutex_unlock(&tt
->lock
);
111 static void tt_mute(struct terratec
*tt
)
117 static int tt_setvol(struct terratec
*tt
, int vol
)
119 if (vol
== tt
->curvol
) { /* requested volume = current */
120 if (tt
->muted
) { /* user is unmuting the card */
122 tt_write_vol(tt
, vol
); /* enable card */
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! */
134 tt_write_vol(tt
, vol
);
140 /* this is the worst part in this driver */
141 /* many more or less strange things are going on here, but hey, it works :) */
143 static int tt_setfreq(struct terratec
*tt
, unsigned long freq1
)
150 unsigned char buffer
[25]; /* we have to bit shift 25 registers */
152 mutex_lock(&tt
->lock
);
156 freq
= freq1
/ 160; /* convert the freq. to a nice to handle value */
157 memset(buffer
, 0, sizeof(buffer
));
159 rest
= freq
* 10 + 10700; /* I once had understood what is going on here */
160 /* maybe some wise guy (friedhelm?) can comment this stuff */
165 if (rest
% temp
== rest
)
176 for (i
= 24; i
> -1; i
--) { /* bit shift the values to the radiocard */
177 if (buffer
[i
] == 1) {
178 outb(WRT_EN
| DATA
, tt
->io
);
179 outb(WRT_EN
| DATA
| CLK_ON
, tt
->io
);
180 outb(WRT_EN
| DATA
, tt
->io
);
182 outb(WRT_EN
| 0x00, tt
->io
);
183 outb(WRT_EN
| 0x00 | CLK_ON
, tt
->io
);
188 mutex_unlock(&tt
->lock
);
193 static int tt_getsigstr(struct terratec
*tt
)
195 if (inb(tt
->io
) & 2) /* bit set = no signal present */
197 return 1; /* signal present */
200 static int vidioc_querycap(struct file
*file
, void *priv
,
201 struct v4l2_capability
*v
)
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
;
210 static int vidioc_g_tuner(struct file
*file
, void *priv
,
211 struct v4l2_tuner
*v
)
213 struct terratec
*tt
= video_drvdata(file
);
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
);
229 static int vidioc_s_tuner(struct file
*file
, void *priv
,
230 struct v4l2_tuner
*v
)
232 return v
->index
? -EINVAL
: 0;
235 static int vidioc_s_frequency(struct file
*file
, void *priv
,
236 struct v4l2_frequency
*f
)
238 struct terratec
*tt
= video_drvdata(file
);
240 if (f
->tuner
!= 0 || f
->type
!= V4L2_TUNER_RADIO
)
242 tt_setfreq(tt
, f
->frequency
);
246 static int vidioc_g_frequency(struct file
*file
, void *priv
,
247 struct v4l2_frequency
*f
)
249 struct terratec
*tt
= video_drvdata(file
);
253 f
->type
= V4L2_TUNER_RADIO
;
254 f
->frequency
= tt
->curfreq
;
258 static int vidioc_queryctrl(struct file
*file
, void *priv
,
259 struct v4l2_queryctrl
*qc
)
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
));
272 static int vidioc_g_ctrl(struct file
*file
, void *priv
,
273 struct v4l2_control
*ctrl
)
275 struct terratec
*tt
= video_drvdata(file
);
278 case V4L2_CID_AUDIO_MUTE
:
284 case V4L2_CID_AUDIO_VOLUME
:
285 ctrl
->value
= tt
->curvol
* 6554;
291 static int vidioc_s_ctrl(struct file
*file
, void *priv
,
292 struct v4l2_control
*ctrl
)
294 struct terratec
*tt
= video_drvdata(file
);
297 case V4L2_CID_AUDIO_MUTE
:
301 tt_setvol(tt
,tt
->curvol
);
303 case V4L2_CID_AUDIO_VOLUME
:
304 tt_setvol(tt
,ctrl
->value
);
310 static int vidioc_g_input(struct file
*filp
, void *priv
, unsigned int *i
)
316 static int vidioc_s_input(struct file
*filp
, void *priv
, unsigned int i
)
318 return i
? -EINVAL
: 0;
321 static int vidioc_g_audio(struct file
*file
, void *priv
,
322 struct v4l2_audio
*a
)
325 strlcpy(a
->name
, "Radio", sizeof(a
->name
));
326 a
->capability
= V4L2_AUDCAP_STEREO
;
330 static int vidioc_s_audio(struct file
*file
, void *priv
,
331 struct v4l2_audio
*a
)
333 return a
->index
? -EINVAL
: 0;
336 static const struct v4l2_file_operations terratec_fops
= {
337 .owner
= THIS_MODULE
,
338 .unlocked_ioctl
= video_ioctl2
,
341 static const struct v4l2_ioctl_ops terratec_ioctl_ops
= {
342 .vidioc_querycap
= vidioc_querycap
,
343 .vidioc_g_tuner
= vidioc_g_tuner
,
344 .vidioc_s_tuner
= vidioc_s_tuner
,
345 .vidioc_g_frequency
= vidioc_g_frequency
,
346 .vidioc_s_frequency
= vidioc_s_frequency
,
347 .vidioc_queryctrl
= vidioc_queryctrl
,
348 .vidioc_g_ctrl
= vidioc_g_ctrl
,
349 .vidioc_s_ctrl
= vidioc_s_ctrl
,
350 .vidioc_g_audio
= vidioc_g_audio
,
351 .vidioc_s_audio
= vidioc_s_audio
,
352 .vidioc_g_input
= vidioc_g_input
,
353 .vidioc_s_input
= vidioc_s_input
,
356 static int __init
terratec_init(void)
358 struct terratec
*tt
= &terratec_card
;
359 struct v4l2_device
*v4l2_dev
= &tt
->v4l2_dev
;
362 strlcpy(v4l2_dev
->name
, "terratec", sizeof(v4l2_dev
->name
));
365 v4l2_err(v4l2_dev
, "you must set an I/O address with io=0x590 or 0x591\n");
368 if (!request_region(tt
->io
, 2, "terratec")) {
369 v4l2_err(v4l2_dev
, "port 0x%x already in use\n", io
);
373 res
= v4l2_device_register(NULL
, v4l2_dev
);
375 release_region(tt
->io
, 2);
376 v4l2_err(v4l2_dev
, "Could not register v4l2_device\n");
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
);
387 mutex_init(&tt
->lock
);
389 /* mute card - prevents noisy bootups */
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);
398 v4l2_info(v4l2_dev
, "TERRATEC ActivRadio Standalone card driver.\n");
402 static void __exit
terratec_exit(void)
404 struct terratec
*tt
= &terratec_card
;
405 struct v4l2_device
*v4l2_dev
= &tt
->v4l2_dev
;
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");
413 module_init(terratec_init
);
414 module_exit(terratec_exit
);