1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Terratec ActiveRadio ISA Standalone card driver for Linux radio support
3 * (c) 1999 R. Offermanns (rolf@offermanns.de)
4 * based on the aimslab radio driver from M. Kirkwood
5 * many thanks to Michael Becker and Friedhelm Birth (from TerraTec)
9 * 1999-05-21 First preview release
11 * Notes on the hardware:
12 * There are two "main" chips on the card:
13 * - Philips OM5610 (http://www-us.semiconductors.philips.com/acrobat/datasheets/OM5610_2.pdf)
14 * - Philips SAA6588 (http://www-us.semiconductors.philips.com/acrobat/datasheets/SAA6588_1.pdf)
15 * (you can get the datasheet at the above links)
17 * Frequency control is done digitally -- ie out(port,encodefreq(95.8));
18 * Volume Control is done digitally
20 * Converted to the radio-isa framework by Hans Verkuil <hansverk@cisco.com>
21 * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@kernel.org>
24 #include <linux/module.h> /* Modules */
25 #include <linux/init.h> /* Initdata */
26 #include <linux/ioport.h> /* request_region */
27 #include <linux/videodev2.h> /* kernel radio structs */
28 #include <linux/mutex.h>
29 #include <linux/io.h> /* outb, outb_p */
30 #include <linux/slab.h>
31 #include <media/v4l2-device.h>
32 #include <media/v4l2-ioctl.h>
33 #include "radio-isa.h"
35 MODULE_AUTHOR("R. Offermans & others");
36 MODULE_DESCRIPTION("A driver for the TerraTec ActiveRadio Standalone radio card.");
37 MODULE_LICENSE("GPL");
38 MODULE_VERSION("0.1.99");
40 /* Note: there seems to be only one possible port (0x590), but without
41 hardware this is hard to verify. For now, this is the only one we will
43 static int io
= 0x590;
44 static int radio_nr
= -1;
46 module_param(radio_nr
, int, 0444);
47 MODULE_PARM_DESC(radio_nr
, "Radio device number");
57 static struct radio_isa_card
*terratec_alloc(void)
59 return kzalloc(sizeof(struct radio_isa_card
), GFP_KERNEL
);
62 static int terratec_s_mute_volume(struct radio_isa_card
*isa
, bool mute
, int vol
)
68 vol
= vol
+ (vol
* 32); /* change both channels */
69 for (i
= 0; i
< 8; i
++) {
70 if (vol
& (0x80 >> i
))
71 outb(0x80, isa
->io
+ 1);
73 outb(0x00, isa
->io
+ 1);
79 /* this is the worst part in this driver */
80 /* many more or less strange things are going on here, but hey, it works :) */
82 static int terratec_s_frequency(struct radio_isa_card
*isa
, u32 freq
)
87 unsigned char buffer
[25]; /* we have to bit shift 25 registers */
89 freq
= freq
/ 160; /* convert the freq. to a nice to handle value */
90 memset(buffer
, 0, sizeof(buffer
));
92 rest
= freq
* 10 + 10700; /* I once had understood what is going on here */
93 /* maybe some wise guy (friedhelm?) can comment this stuff */
97 if (rest
% temp
== rest
)
107 for (i
= 24; i
> -1; i
--) { /* bit shift the values to the radiocard */
108 if (buffer
[i
] == 1) {
109 outb(WRT_EN
| DATA
, isa
->io
);
110 outb(WRT_EN
| DATA
| CLK_ON
, isa
->io
);
111 outb(WRT_EN
| DATA
, isa
->io
);
113 outb(WRT_EN
| 0x00, isa
->io
);
114 outb(WRT_EN
| 0x00 | CLK_ON
, isa
->io
);
121 static u32
terratec_g_signal(struct radio_isa_card
*isa
)
123 /* bit set = no signal present */
124 return (inb(isa
->io
) & 2) ? 0 : 0xffff;
127 static const struct radio_isa_ops terratec_ops
= {
128 .alloc
= terratec_alloc
,
129 .s_mute_volume
= terratec_s_mute_volume
,
130 .s_frequency
= terratec_s_frequency
,
131 .g_signal
= terratec_g_signal
,
134 static const int terratec_ioports
[] = { 0x590 };
136 static struct radio_isa_driver terratec_driver
= {
138 .match
= radio_isa_match
,
139 .probe
= radio_isa_probe
,
140 .remove
= radio_isa_remove
,
142 .name
= "radio-terratec",
146 .radio_nr_params
= &radio_nr
,
147 .io_ports
= terratec_ioports
,
148 .num_of_io_ports
= ARRAY_SIZE(terratec_ioports
),
150 .card
= "TerraTec ActiveRadio",
151 .ops
= &terratec_ops
,
156 static int __init
terratec_init(void)
158 return isa_register_driver(&terratec_driver
.driver
, 1);
161 static void __exit
terratec_exit(void)
163 isa_unregister_driver(&terratec_driver
.driver
);
166 module_init(terratec_init
);
167 module_exit(terratec_exit
);