1 /* Typhoon Radio Card driver for radio support
2 * (c) 1999 Dr. Henrik Seidel <Henrik.Seidel@gmx.de>
4 * Notes on the hardware
6 * This card has two output sockets, one for speakers and one for line.
7 * The speaker output has volume control, but only in four discrete
8 * steps. The line output has neither volume control nor mute.
10 * The card has auto-stereo according to its manual, although it all
11 * sounds mono to me (even with the Win/DOS drivers). Maybe it's my
12 * antenna - I really don't know for sure.
14 * Frequency control is done digitally.
16 * Volume control is done digitally, but there are only four different
17 * possible values. So you should better always turn the volume up and
18 * use line control. I got the best results by connecting line output
19 * to the sound card microphone input. For such a configuration the
20 * volume control has no effect, since volume control only influences
23 * There is no explicit mute/unmute. So I set the radio frequency to a
24 * value where I do expect just noise and turn the speaker volume down.
25 * The frequency change is necessary since the card never seems to be
28 * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
31 #include <linux/module.h> /* Modules */
32 #include <linux/init.h> /* Initdata */
33 #include <linux/ioport.h> /* request_region */
34 #include <linux/videodev2.h> /* kernel radio structs */
35 #include <linux/io.h> /* outb, outb_p */
36 #include <linux/slab.h>
37 #include <media/v4l2-device.h>
38 #include <media/v4l2-ioctl.h>
39 #include "radio-isa.h"
41 #define DRIVER_VERSION "0.1.2"
43 MODULE_AUTHOR("Dr. Henrik Seidel");
44 MODULE_DESCRIPTION("A driver for the Typhoon radio card (a.k.a. EcoRadio).");
45 MODULE_LICENSE("GPL");
46 MODULE_VERSION("0.1.99");
48 #ifndef CONFIG_RADIO_TYPHOON_PORT
49 #define CONFIG_RADIO_TYPHOON_PORT -1
52 #ifndef CONFIG_RADIO_TYPHOON_MUTEFREQ
53 #define CONFIG_RADIO_TYPHOON_MUTEFREQ 87000
58 static int io
[TYPHOON_MAX
] = { [0] = CONFIG_RADIO_TYPHOON_PORT
,
59 [1 ... (TYPHOON_MAX
- 1)] = -1 };
60 static int radio_nr
[TYPHOON_MAX
] = { [0 ... (TYPHOON_MAX
- 1)] = -1 };
61 static unsigned long mutefreq
= CONFIG_RADIO_TYPHOON_MUTEFREQ
;
63 module_param_array(io
, int, NULL
, 0444);
64 MODULE_PARM_DESC(io
, "I/O addresses of the Typhoon card (0x316 or 0x336)");
65 module_param_array(radio_nr
, int, NULL
, 0444);
66 MODULE_PARM_DESC(radio_nr
, "Radio device numbers");
67 module_param(mutefreq
, ulong
, 0);
68 MODULE_PARM_DESC(mutefreq
, "Frequency used when muting the card (in kHz)");
71 struct radio_isa_card isa
;
75 static struct radio_isa_card
*typhoon_alloc(void)
77 struct typhoon
*ty
= kzalloc(sizeof(*ty
), GFP_KERNEL
);
79 return ty
? &ty
->isa
: NULL
;
82 static int typhoon_s_frequency(struct radio_isa_card
*isa
, u32 freq
)
88 * The frequency transfer curve is not linear. The best fit I could
91 * outval = -155 + exp((f + 15.55) * 0.057))
93 * where frequency f is in MHz. Since we don't have exp in the kernel,
94 * I approximate this function by a third order polynomial.
99 outval
= (x
* x
+ 2500) / 5000;
100 outval
= (outval
* x
+ 5000) / 10000;
101 outval
-= (10 * x
* x
+ 10433) / 20866;
102 outval
+= 4 * x
- 11505;
104 outb_p((outval
>> 8) & 0x01, isa
->io
+ 4);
105 outb_p(outval
>> 9, isa
->io
+ 6);
106 outb_p(outval
& 0xff, isa
->io
+ 8);
110 static int typhoon_s_mute_volume(struct radio_isa_card
*isa
, bool mute
, int vol
)
112 struct typhoon
*ty
= container_of(isa
, struct typhoon
, isa
);
116 vol
>>= 14; /* Map 16 bit to 2 bit */
118 outb_p(vol
/ 2, isa
->io
); /* Set the volume, high bit. */
119 outb_p(vol
% 2, isa
->io
+ 2); /* Set the volume, low bit. */
121 if (vol
== 0 && !ty
->muted
) {
123 return typhoon_s_frequency(isa
, mutefreq
<< 4);
125 if (vol
&& ty
->muted
) {
127 return typhoon_s_frequency(isa
, isa
->freq
);
132 static const struct radio_isa_ops typhoon_ops
= {
133 .alloc
= typhoon_alloc
,
134 .s_mute_volume
= typhoon_s_mute_volume
,
135 .s_frequency
= typhoon_s_frequency
,
138 static const int typhoon_ioports
[] = { 0x316, 0x336 };
140 static struct radio_isa_driver typhoon_driver
= {
142 .match
= radio_isa_match
,
143 .probe
= radio_isa_probe
,
144 .remove
= radio_isa_remove
,
146 .name
= "radio-typhoon",
150 .radio_nr_params
= radio_nr
,
151 .io_ports
= typhoon_ioports
,
152 .num_of_io_ports
= ARRAY_SIZE(typhoon_ioports
),
154 .card
= "Typhoon Radio",
160 static int __init
typhoon_init(void)
162 if (mutefreq
< 87000 || mutefreq
> 108000) {
163 printk(KERN_ERR
"%s: You must set a frequency (in kHz) used when muting the card,\n",
164 typhoon_driver
.driver
.driver
.name
);
165 printk(KERN_ERR
"%s: e.g. with \"mutefreq=87500\" (87000 <= mutefreq <= 108000)\n",
166 typhoon_driver
.driver
.driver
.name
);
169 return isa_register_driver(&typhoon_driver
.driver
, TYPHOON_MAX
);
172 static void __exit
typhoon_exit(void)
174 isa_unregister_driver(&typhoon_driver
.driver
);
178 module_init(typhoon_init
);
179 module_exit(typhoon_exit
);