Commit | Line | Data |
---|---|---|
1da177e4 LT |
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) | |
4286c6f6 | 5 | * |
1da177e4 LT |
6 | * |
7 | * History: | |
8 | * 1999-05-21 First preview release | |
4286c6f6 | 9 | * |
1da177e4 LT |
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) | |
15 | * | |
16 | * Frequency control is done digitally -- ie out(port,encodefreq(95.8)); | |
17 | * Volume Control is done digitally | |
18 | * | |
32c51836 | 19 | * Converted to the radio-isa framework by Hans Verkuil <hans.verkuil@cisco.com> |
55ac7b69 | 20 | * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> |
1da177e4 LT |
21 | */ |
22 | ||
23 | #include <linux/module.h> /* Modules */ | |
24 | #include <linux/init.h> /* Initdata */ | |
fb911ee8 | 25 | #include <linux/ioport.h> /* request_region */ |
55ac7b69 | 26 | #include <linux/videodev2.h> /* kernel radio structs */ |
5ac3d5bf | 27 | #include <linux/mutex.h> |
5ac3d5bf | 28 | #include <linux/io.h> /* outb, outb_p */ |
9f1dfccf | 29 | #include <linux/slab.h> |
5ac3d5bf | 30 | #include <media/v4l2-device.h> |
35ea11ff | 31 | #include <media/v4l2-ioctl.h> |
32c51836 | 32 | #include "radio-isa.h" |
1da177e4 | 33 | |
32c51836 | 34 | MODULE_AUTHOR("R. Offermans & others"); |
5ac3d5bf HV |
35 | MODULE_DESCRIPTION("A driver for the TerraTec ActiveRadio Standalone radio card."); |
36 | MODULE_LICENSE("GPL"); | |
32c51836 | 37 | MODULE_VERSION("0.1.99"); |
5ac3d5bf | 38 | |
32c51836 HV |
39 | /* Note: there seems to be only one possible port (0x590), but without |
40 | hardware this is hard to verify. For now, this is the only one we will | |
41 | support. */ | |
42 | static int io = 0x590; | |
5ac3d5bf HV |
43 | static int radio_nr = -1; |
44 | ||
32c51836 HV |
45 | module_param(radio_nr, int, 0444); |
46 | MODULE_PARM_DESC(radio_nr, "Radio device number"); | |
55ac7b69 | 47 | |
1da177e4 LT |
48 | #define WRT_DIS 0x00 |
49 | #define CLK_OFF 0x00 | |
50 | #define IIC_DATA 0x01 | |
51 | #define IIC_CLK 0x02 | |
52 | #define DATA 0x04 | |
53 | #define CLK_ON 0x08 | |
54 | #define WRT_EN 0x10 | |
1da177e4 | 55 | |
32c51836 | 56 | static struct radio_isa_card *terratec_alloc(void) |
1da177e4 | 57 | { |
32c51836 HV |
58 | return kzalloc(sizeof(struct radio_isa_card), GFP_KERNEL); |
59 | } | |
1da177e4 | 60 | |
32c51836 | 61 | static int terratec_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol) |
1da177e4 LT |
62 | { |
63 | int i; | |
5ac3d5bf | 64 | |
32c51836 HV |
65 | if (mute) |
66 | vol = 0; | |
67 | vol = vol + (vol * 32); /* change both channels */ | |
5ac3d5bf | 68 | for (i = 0; i < 8; i++) { |
32c51836 HV |
69 | if (vol & (0x80 >> i)) |
70 | outb(0x80, isa->io + 1); | |
5ac3d5bf | 71 | else |
32c51836 | 72 | outb(0x00, isa->io + 1); |
1da177e4 | 73 | } |
1da177e4 | 74 | return 0; |
1da177e4 LT |
75 | } |
76 | ||
77 | ||
78 | /* this is the worst part in this driver */ | |
79 | /* many more or less strange things are going on here, but hey, it works :) */ | |
80 | ||
32c51836 | 81 | static int terratec_s_frequency(struct radio_isa_card *isa, u32 freq) |
4286c6f6 | 82 | { |
1da177e4 LT |
83 | int i; |
84 | int p; | |
32c51836 | 85 | int temp; |
1da177e4 | 86 | long rest; |
1da177e4 | 87 | unsigned char buffer[25]; /* we have to bit shift 25 registers */ |
1da177e4 | 88 | |
32c51836 | 89 | freq = freq / 160; /* convert the freq. to a nice to handle value */ |
5ac3d5bf HV |
90 | memset(buffer, 0, sizeof(buffer)); |
91 | ||
92 | rest = freq * 10 + 10700; /* I once had understood what is going on here */ | |
1da177e4 | 93 | /* maybe some wise guy (friedhelm?) can comment this stuff */ |
5ac3d5bf HV |
94 | i = 13; |
95 | p = 10; | |
96 | temp = 102400; | |
97 | while (rest != 0) { | |
98 | if (rest % temp == rest) | |
1da177e4 | 99 | buffer[i] = 0; |
5ac3d5bf | 100 | else { |
4286c6f6 | 101 | buffer[i] = 1; |
5ac3d5bf | 102 | rest = rest - temp; |
1da177e4 LT |
103 | } |
104 | i--; | |
105 | p--; | |
5ac3d5bf | 106 | temp = temp / 2; |
b930e1d8 | 107 | } |
1da177e4 | 108 | |
5ac3d5bf HV |
109 | for (i = 24; i > -1; i--) { /* bit shift the values to the radiocard */ |
110 | if (buffer[i] == 1) { | |
32c51836 HV |
111 | outb(WRT_EN | DATA, isa->io); |
112 | outb(WRT_EN | DATA | CLK_ON, isa->io); | |
113 | outb(WRT_EN | DATA, isa->io); | |
5ac3d5bf | 114 | } else { |
32c51836 HV |
115 | outb(WRT_EN | 0x00, isa->io); |
116 | outb(WRT_EN | 0x00 | CLK_ON, isa->io); | |
1da177e4 | 117 | } |
1de69238 | 118 | } |
32c51836 | 119 | outb(0x00, isa->io); |
1de69238 | 120 | return 0; |
1da177e4 LT |
121 | } |
122 | ||
32c51836 | 123 | static u32 terratec_g_signal(struct radio_isa_card *isa) |
5ac3d5bf | 124 | { |
32c51836 HV |
125 | /* bit set = no signal present */ |
126 | return (inb(isa->io) & 2) ? 0 : 0xffff; | |
5ac3d5bf | 127 | } |
1da177e4 | 128 | |
32c51836 HV |
129 | static const struct radio_isa_ops terratec_ops = { |
130 | .alloc = terratec_alloc, | |
131 | .s_mute_volume = terratec_s_mute_volume, | |
132 | .s_frequency = terratec_s_frequency, | |
133 | .g_signal = terratec_g_signal, | |
1da177e4 LT |
134 | }; |
135 | ||
32c51836 HV |
136 | static const int terratec_ioports[] = { 0x590 }; |
137 | ||
138 | static struct radio_isa_driver terratec_driver = { | |
139 | .driver = { | |
140 | .match = radio_isa_match, | |
141 | .probe = radio_isa_probe, | |
142 | .remove = radio_isa_remove, | |
143 | .driver = { | |
144 | .name = "radio-terratec", | |
145 | }, | |
146 | }, | |
147 | .io_params = &io, | |
148 | .radio_nr_params = &radio_nr, | |
149 | .io_ports = terratec_ioports, | |
150 | .num_of_io_ports = ARRAY_SIZE(terratec_ioports), | |
151 | .region_size = 2, | |
152 | .card = "TerraTec ActiveRadio", | |
153 | .ops = &terratec_ops, | |
154 | .has_stereo = true, | |
155 | .max_volume = 10, | |
1da177e4 LT |
156 | }; |
157 | ||
158 | static int __init terratec_init(void) | |
159 | { | |
32c51836 | 160 | return isa_register_driver(&terratec_driver.driver, 1); |
1da177e4 LT |
161 | } |
162 | ||
5ac3d5bf | 163 | static void __exit terratec_exit(void) |
1da177e4 | 164 | { |
32c51836 | 165 | isa_unregister_driver(&terratec_driver.driver); |
1da177e4 LT |
166 | } |
167 | ||
168 | module_init(terratec_init); | |
5ac3d5bf | 169 | module_exit(terratec_exit); |
1da177e4 | 170 |