aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/radio
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/media/radio
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/media/radio')
-rw-r--r--drivers/media/radio/Kconfig354
-rw-r--r--drivers/media/radio/Makefile22
-rw-r--r--drivers/media/radio/miropcm20-radio.c264
-rw-r--r--drivers/media/radio/miropcm20-rds-core.c210
-rw-r--r--drivers/media/radio/miropcm20-rds-core.h19
-rw-r--r--drivers/media/radio/miropcm20-rds.c133
-rw-r--r--drivers/media/radio/radio-aimslab.c368
-rw-r--r--drivers/media/radio/radio-aztech.c315
-rw-r--r--drivers/media/radio/radio-cadet.c620
-rw-r--r--drivers/media/radio/radio-gemtek-pci.c416
-rw-r--r--drivers/media/radio/radio-gemtek.c304
-rw-r--r--drivers/media/radio/radio-maestro.c332
-rw-r--r--drivers/media/radio/radio-maxiradio.c349
-rw-r--r--drivers/media/radio/radio-rtrack2.c266
-rw-r--r--drivers/media/radio/radio-sf16fmi.c328
-rw-r--r--drivers/media/radio/radio-sf16fmr2.c434
-rw-r--r--drivers/media/radio/radio-terratec.c341
-rw-r--r--drivers/media/radio/radio-trust.c320
-rw-r--r--drivers/media/radio/radio-typhoon.c383
-rw-r--r--drivers/media/radio/radio-zoltrix.c385
20 files changed, 6163 insertions, 0 deletions
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig
new file mode 100644
index 000000000000..d318be383de6
--- /dev/null
+++ b/drivers/media/radio/Kconfig
@@ -0,0 +1,354 @@
1#
2# Multimedia Video device configuration
3#
4
5menu "Radio Adapters"
6 depends on VIDEO_DEV!=n
7
8config RADIO_CADET
9 tristate "ADS Cadet AM/FM Tuner"
10 depends on ISA && VIDEO_DEV
11 ---help---
12 Choose Y here if you have one of these AM/FM radio cards, and then
13 fill in the port address below.
14
15 In order to control your radio card, you will need to use programs
16 that are compatible with the Video For Linux API. Information on
17 this API and pointers to "v4l" programs may be found at
18 <file:Documentation/video4linux/API.html>.
19
20 Further documentation on this driver can be found on the WWW at
21 <http://linux.blackhawke.net/cadet/>.
22
23 To compile this driver as a module, choose M here: the
24 module will be called radio-cadet.
25
26config RADIO_RTRACK
27 tristate "AIMSlab RadioTrack (aka RadioReveal) support"
28 depends on ISA && VIDEO_DEV
29 ---help---
30 Choose Y here if you have one of these FM radio cards, and then fill
31 in the port address below.
32
33 Note that newer AIMSlab RadioTrack cards have a different chipset
34 and are not supported by this driver. For these cards, use the
35 RadioTrack II driver below.
36
37 If you have a GemTeks combined (PnP) sound- and radio card you must
38 use this driver as a module and setup the card with isapnptools.
39 You must also pass the module a suitable io parameter, 0x248 has
40 been reported to be used by these cards.
41
42 In order to control your radio card, you will need to use programs
43 that are compatible with the Video For Linux API. Information on
44 this API and pointers to "v4l" programs may be found at
45 <file:Documentation/video4linux/API.html>. More information is
46 contained in the file
47 <file:Documentation/video4linux/radiotrack.txt>.
48
49 To compile this driver as a module, choose M here: the
50 module will be called radio-aimslab.
51
52config RADIO_RTRACK_PORT
53 hex "RadioTrack i/o port (0x20f or 0x30f)"
54 depends on RADIO_RTRACK=y
55 default "20f"
56 help
57 Enter either 0x30f or 0x20f here. The card default is 0x30f, if you
58 haven't changed the jumper setting on the card.
59
60config RADIO_RTRACK2
61 tristate "AIMSlab RadioTrack II support"
62 depends on ISA && VIDEO_DEV
63 ---help---
64 Choose Y here if you have this FM radio card, and then fill in the
65 port address below.
66
67 In order to control your radio card, you will need to use programs
68 that are compatible with the Video For Linux API. Information on
69 this API and pointers to "v4l" programs may be found at
70 <file:Documentation/video4linux/API.html>.
71
72 To compile this driver as a module, choose M here: the
73 module will be called radio-rtrack2.
74
75config RADIO_RTRACK2_PORT
76 hex "RadioTrack II i/o port (0x20c or 0x30c)"
77 depends on RADIO_RTRACK2=y
78 default "30c"
79 help
80 Enter either 0x30c or 0x20c here. The card default is 0x30c, if you
81 haven't changed the jumper setting on the card.
82
83config RADIO_AZTECH
84 tristate "Aztech/Packard Bell Radio"
85 depends on ISA && VIDEO_DEV
86 ---help---
87 Choose Y here if you have one of these FM radio cards, and then fill
88 in the port address below.
89
90 In order to control your radio card, you will need to use programs
91 that are compatible with the Video For Linux API. Information on
92 this API and pointers to "v4l" programs may be found at
93 <file:Documentation/video4linux/API.html>.
94
95 To compile this driver as a module, choose M here: the
96 module will be called radio-aztech.
97
98config RADIO_AZTECH_PORT
99 hex "Aztech/Packard Bell I/O port (0x350 or 0x358)"
100 depends on RADIO_AZTECH=y
101 default "350"
102 help
103 Enter either 0x350 or 0x358 here. The card default is 0x350, if you
104 haven't changed the setting of jumper JP3 on the card. Removing the
105 jumper sets the card to 0x358.
106
107config RADIO_GEMTEK
108 tristate "GemTek Radio Card support"
109 depends on ISA && VIDEO_DEV
110 ---help---
111 Choose Y here if you have this FM radio card, and then fill in the
112 port address below.
113
114 In order to control your radio card, you will need to use programs
115 that are compatible with the Video For Linux API. Information on
116 this API and pointers to "v4l" programs may be found at
117 <file:Documentation/video4linux/API.html>.
118
119 To compile this driver as a module, choose M here: the
120 module will be called radio-gemtek.
121
122config RADIO_GEMTEK_PORT
123 hex "GemTek i/o port (0x20c, 0x30c, 0x24c or 0x34c)"
124 depends on RADIO_GEMTEK=y
125 default "34c"
126 help
127 Enter either 0x20c, 0x30c, 0x24c or 0x34c here. The card default is
128 0x34c, if you haven't changed the jumper setting on the card. On
129 Sound Vision 16 Gold PnP with FM Radio (ESS1869+FM Gemtek), the I/O
130 port is 0x28c.
131
132config RADIO_GEMTEK_PCI
133 tristate "GemTek PCI Radio Card support"
134 depends on VIDEO_DEV && PCI
135 ---help---
136 Choose Y here if you have this PCI FM radio card.
137
138 In order to control your radio card, you will need to use programs
139 that are compatible with the Video for Linux API. Information on
140 this API and pointers to "v4l" programs may be found at
141 <file:Documentation/video4linux/API.html>.
142
143 To compile this driver as a module, choose M here: the
144 module will be called radio-gemtek-pci.
145
146config RADIO_MAXIRADIO
147 tristate "Guillemot MAXI Radio FM 2000 radio"
148 depends on VIDEO_DEV && PCI
149 ---help---
150 Choose Y here if you have this radio card. This card may also be
151 found as Gemtek PCI FM.
152
153 In order to control your radio card, you will need to use programs
154 that are compatible with the Video For Linux API. Information on
155 this API and pointers to "v4l" programs may be found at
156 <file:Documentation/video4linux/API.html>.
157
158 To compile this driver as a module, choose M here: the
159 module will be called radio-maxiradio.
160
161config RADIO_MAESTRO
162 tristate "Maestro on board radio"
163 depends on VIDEO_DEV
164 ---help---
165 Say Y here to directly support the on-board radio tuner on the
166 Maestro 2 or 2E sound card.
167
168 In order to control your radio card, you will need to use programs
169 that are compatible with the Video For Linux API. Information on
170 this API and pointers to "v4l" programs may be found at
171 <file:Documentation/video4linux/API.html>.
172
173 To compile this driver as a module, choose M here: the
174 module will be called radio-maestro.
175
176config RADIO_MIROPCM20
177 tristate "miroSOUND PCM20 radio"
178 depends on ISA && VIDEO_DEV && SOUND_ACI_MIXER
179 ---help---
180 Choose Y here if you have this FM radio card. You also need to say Y
181 to "ACI mixer (miroSOUND PCM1-pro/PCM12/PCM20 radio)" (in "Sound")
182 for this to work.
183
184 In order to control your radio card, you will need to use programs
185 that are compatible with the Video For Linux API. Information on
186 this API and pointers to "v4l" programs may be found at
187 <file:Documentation/video4linux/API.html>.
188
189 To compile this driver as a module, choose M here: the
190 module will be called miropcm20.
191
192config RADIO_MIROPCM20_RDS
193 tristate "miroSOUND PCM20 radio RDS user interface (EXPERIMENTAL)"
194 depends on RADIO_MIROPCM20 && EXPERIMENTAL
195 ---help---
196 Choose Y here if you want to see RDS/RBDS information like
197 RadioText, Programme Service name, Clock Time and date, Programme
198 TYpe and Traffic Announcement/Programme identification. You also
199 need to say Y to "miroSOUND PCM20 radio" and devfs!
200
201 It's not possible to read the raw RDS packets from the device, so
202 the driver cant provide an V4L interface for this. But the
203 availability of RDS is reported over V4L by the basic driver
204 already. Here RDS can be read from files in /dev/v4l/rds.
205
206 To compile this driver as a module, choose M here: the
207 module will be called miropcm20-rds.
208
209config RADIO_SF16FMI
210 tristate "SF16FMI Radio"
211 depends on ISA && VIDEO_DEV
212 ---help---
213 Choose Y here if you have one of these FM radio cards. If you
214 compile the driver into the kernel and your card is not PnP one, you
215 have to add "sf16fm=<io>" to the kernel command line (I/O address is
216 0x284 or 0x384).
217
218 In order to control your radio card, you will need to use programs
219 that are compatible with the Video For Linux API. Information on
220 this API and pointers to "v4l" programs may be found at
221 <file:Documentation/video4linux/API.html>.
222
223 To compile this driver as a module, choose M here: the
224 module will be called radio-sf16fmi.
225
226config RADIO_SF16FMR2
227 tristate "SF16FMR2 Radio"
228 depends on ISA && VIDEO_DEV
229 ---help---
230 Choose Y here if you have one of these FM radio cards.
231
232 In order to control your radio card, you will need to use programs
233 that are compatible with the Video For Linux API. Information on
234 this API and pointers to "v4l" programs may be found on the WWW at
235 <http://roadrunner.swansea.uk.linux.org/v4l.shtml>.
236
237 To compile this driver as a module, choose M here: the
238 module will be called radio-sf16fmr2.
239
240config RADIO_TERRATEC
241 tristate "TerraTec ActiveRadio ISA Standalone"
242 depends on ISA && VIDEO_DEV
243 ---help---
244 Choose Y here if you have this FM radio card, and then fill in the
245 port address below. (TODO)
246
247 Note: This driver is in its early stages. Right now volume and
248 frequency control and muting works at least for me, but
249 unfortunately I have not found anybody who wants to use this card
250 with Linux. So if it is this what YOU are trying to do right now,
251 PLEASE DROP ME A NOTE!! Rolf Offermanns <rolf@offermanns.de>.
252
253 In order to control your radio card, you will need to use programs
254 that are compatible with the Video For Linux API. Information on
255 this API and pointers to "v4l" programs may be found at
256 <file:Documentation/video4linux/API.html>.
257
258 To compile this driver as a module, choose M here: the
259 module will be called radio-terratec.
260
261config RADIO_TERRATEC_PORT
262 hex "Terratec i/o port (normally 0x590)"
263 depends on RADIO_TERRATEC=y
264 default "590"
265 help
266 Fill in the I/O port of your TerraTec FM radio card. If unsure, go
267 with the default.
268
269config RADIO_TRUST
270 tristate "Trust FM radio card"
271 depends on ISA && VIDEO_DEV
272 help
273 This is a driver for the Trust FM radio cards. Say Y if you have
274 such a card and want to use it under Linux.
275
276 To compile this driver as a module, choose M here: the
277 module will be called radio-trust.
278
279config RADIO_TRUST_PORT
280 hex "Trust i/o port (usually 0x350 or 0x358)"
281 depends on RADIO_TRUST=y
282 default "350"
283 help
284 Enter the I/O port of your Trust FM radio card. If unsure, try the
285 values "0x350" or "0x358".
286
287config RADIO_TYPHOON
288 tristate "Typhoon Radio (a.k.a. EcoRadio)"
289 depends on ISA && VIDEO_DEV
290 ---help---
291 Choose Y here if you have one of these FM radio cards, and then fill
292 in the port address and the frequency used for muting below.
293
294 In order to control your radio card, you will need to use programs
295 that are compatible with the Video For Linux API. Information on
296 this API and pointers to "v4l" programs may be found at
297 <file:Documentation/video4linux/API.html>.
298
299 To compile this driver as a module, choose M here: the
300 module will be called radio-typhoon.
301
302config RADIO_TYPHOON_PROC_FS
303 bool "Support for /proc/radio-typhoon"
304 depends on PROC_FS && RADIO_TYPHOON
305 help
306 Say Y here if you want the typhoon radio card driver to write
307 status information (frequency, volume, muted, mute frequency,
308 base address) to /proc/radio-typhoon. The file can be viewed with
309 your favorite pager (i.e. use "more /proc/radio-typhoon" or "less
310 /proc/radio-typhoon" or simply "cat /proc/radio-typhoon").
311
312config RADIO_TYPHOON_PORT
313 hex "Typhoon I/O port (0x316 or 0x336)"
314 depends on RADIO_TYPHOON=y
315 default "316"
316 help
317 Enter the I/O port of your Typhoon or EcoRadio radio card.
318
319config RADIO_TYPHOON_MUTEFREQ
320 int "Typhoon frequency set when muting the device (kHz)"
321 depends on RADIO_TYPHOON=y
322 default "87500"
323 help
324 Enter the frequency used for muting the radio. The device is never
325 completely silent. If the volume is just turned down, you can still
326 hear silent voices and music. For that reason, the frequency of the
327 radio device is set to the frequency you can enter here whenever
328 the device is muted. There should be no local radio station at that
329 frequency.
330
331config RADIO_ZOLTRIX
332 tristate "Zoltrix Radio"
333 depends on ISA && VIDEO_DEV
334 ---help---
335 Choose Y here if you have one of these FM radio cards, and then fill
336 in the port address below.
337
338 In order to control your radio card, you will need to use programs
339 that are compatible with the Video For Linux API. Information on
340 this API and pointers to "v4l" programs may be found at
341 <file:Documentation/video4linux/API.html>.
342
343 To compile this driver as a module, choose M here: the
344 module will be called radio-zoltrix.
345
346config RADIO_ZOLTRIX_PORT
347 hex "ZOLTRIX I/O port (0x20c or 0x30c)"
348 depends on RADIO_ZOLTRIX=y
349 default "20c"
350 help
351 Enter the I/O port of your Zoltrix radio card.
352
353endmenu
354
diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile
new file mode 100644
index 000000000000..8b351945d066
--- /dev/null
+++ b/drivers/media/radio/Makefile
@@ -0,0 +1,22 @@
1#
2# Makefile for the kernel character device drivers.
3#
4
5miropcm20-objs := miropcm20-rds-core.o miropcm20-radio.o
6
7obj-$(CONFIG_RADIO_AZTECH) += radio-aztech.o
8obj-$(CONFIG_RADIO_RTRACK2) += radio-rtrack2.o
9obj-$(CONFIG_RADIO_SF16FMI) += radio-sf16fmi.o
10obj-$(CONFIG_RADIO_SF16FMR2) += radio-sf16fmr2.o
11obj-$(CONFIG_RADIO_CADET) += radio-cadet.o
12obj-$(CONFIG_RADIO_TYPHOON) += radio-typhoon.o
13obj-$(CONFIG_RADIO_TERRATEC) += radio-terratec.o
14obj-$(CONFIG_RADIO_MAXIRADIO) += radio-maxiradio.o
15obj-$(CONFIG_RADIO_RTRACK) += radio-aimslab.o
16obj-$(CONFIG_RADIO_ZOLTRIX) += radio-zoltrix.o
17obj-$(CONFIG_RADIO_MIROPCM20) += miropcm20.o
18obj-$(CONFIG_RADIO_MIROPCM20_RDS) += miropcm20-rds.o
19obj-$(CONFIG_RADIO_GEMTEK) += radio-gemtek.o
20obj-$(CONFIG_RADIO_GEMTEK_PCI) += radio-gemtek-pci.o
21obj-$(CONFIG_RADIO_TRUST) += radio-trust.o
22obj-$(CONFIG_RADIO_MAESTRO) += radio-maestro.o
diff --git a/drivers/media/radio/miropcm20-radio.c b/drivers/media/radio/miropcm20-radio.c
new file mode 100644
index 000000000000..c2ebe8754a95
--- /dev/null
+++ b/drivers/media/radio/miropcm20-radio.c
@@ -0,0 +1,264 @@
1/* Miro PCM20 radio driver for Linux radio support
2 * (c) 1998 Ruurd Reitsma <R.A.Reitsma@wbmt.tudelft.nl>
3 * Thanks to Norberto Pellici for the ACI device interface specification
4 * The API part is based on the radiotrack driver by M. Kirkwood
5 * This driver relies on the aci mixer (drivers/sound/aci.c)
6 * Look there for further info...
7 */
8
9/* Revision history:
10 *
11 * 1998 Ruurd Reitsma <R.A.Reitsma@wbmt.tudelft.nl>
12 * 2000-09-05 Robert Siemer <Robert.Siemer@gmx.de>
13 * removed unfinished volume control (maybe adding it later again)
14 * use OSS-mixer; added stereo control
15 */
16
17/* What ever you think about the ACI, version 0x07 is not very well!
18 * I can't get frequency, 'tuner status', 'tuner flags' or mute/mono
19 * conditions... Robert
20 */
21
22#include <linux/module.h>
23#include <linux/init.h>
24#include <linux/videodev.h>
25#include "../../../sound/oss/aci.h"
26#include "miropcm20-rds-core.h"
27
28static int radio_nr = -1;
29module_param(radio_nr, int, 0);
30
31struct pcm20_device {
32 unsigned long freq;
33 int muted;
34 int stereo;
35};
36
37
38static int pcm20_mute(struct pcm20_device *dev, unsigned char mute)
39{
40 dev->muted = mute;
41 return aci_write_cmd(ACI_SET_TUNERMUTE, mute);
42}
43
44static int pcm20_stereo(struct pcm20_device *dev, unsigned char stereo)
45{
46 dev->stereo = stereo;
47 return aci_write_cmd(ACI_SET_TUNERMONO, !stereo);
48}
49
50static int pcm20_setfreq(struct pcm20_device *dev, unsigned long freq)
51{
52 unsigned char freql;
53 unsigned char freqh;
54
55 dev->freq=freq;
56
57 freq /= 160;
58 if (!(aci_version==0x07 || aci_version>=0xb0))
59 freq /= 10; /* I don't know exactly which version
60 * needs this hack */
61 freql = freq & 0xff;
62 freqh = freq >> 8;
63
64 aci_rds_cmd(RDS_RESET, NULL, 0);
65 pcm20_stereo(dev, 1);
66
67 return aci_rw_cmd(ACI_WRITE_TUNE, freql, freqh);
68}
69
70static int pcm20_getflags(struct pcm20_device *dev, __u32 *flags, __u16 *signal)
71{
72 /* okay, check for signal, stereo and rds here... */
73 int i;
74 unsigned char buf;
75
76 if ((i=aci_rw_cmd(ACI_READ_TUNERSTATION, -1, -1))<0)
77 return i;
78 pr_debug("check_sig: 0x%x\n", i);
79 if (i & 0x80) {
80 /* no signal from tuner */
81 *flags=0;
82 *signal=0;
83 return 0;
84 } else
85 *signal=0xffff;
86
87 if ((i=aci_rw_cmd(ACI_READ_TUNERSTEREO, -1, -1))<0)
88 return i;
89 if (i & 0x40) {
90 *flags=0;
91 } else {
92 /* stereo */
93 *flags=VIDEO_TUNER_STEREO_ON;
94 /* I can't see stereo, when forced to mono */
95 dev->stereo=1;
96 }
97
98 if ((i=aci_rds_cmd(RDS_STATUS, &buf, 1))<0)
99 return i;
100 if (buf & 1)
101 /* RDS available */
102 *flags|=VIDEO_TUNER_RDS_ON;
103 else
104 return 0;
105
106 if ((i=aci_rds_cmd(RDS_RXVALUE, &buf, 1))<0)
107 return i;
108 pr_debug("rds-signal: %d\n", buf);
109 if (buf > 15) {
110 printk("miropcm20-radio: RX strengths unexpected high...\n");
111 buf=15;
112 }
113 /* refine signal */
114 if ((*signal=SCALE(15, 0xffff, buf))==0)
115 *signal = 1;
116
117 return 0;
118}
119
120static int pcm20_do_ioctl(struct inode *inode, struct file *file,
121 unsigned int cmd, void *arg)
122{
123 struct video_device *dev = video_devdata(file);
124 struct pcm20_device *pcm20 = dev->priv;
125 int i;
126
127 switch(cmd)
128 {
129 case VIDIOCGCAP:
130 {
131 struct video_capability *v = arg;
132 memset(v,0,sizeof(*v));
133 v->type=VID_TYPE_TUNER;
134 strcpy(v->name, "Miro PCM20");
135 v->channels=1;
136 v->audios=1;
137 return 0;
138 }
139 case VIDIOCGTUNER:
140 {
141 struct video_tuner *v = arg;
142 if(v->tuner) /* Only 1 tuner */
143 return -EINVAL;
144 v->rangelow=87*16000;
145 v->rangehigh=108*16000;
146 pcm20_getflags(pcm20, &v->flags, &v->signal);
147 v->flags|=VIDEO_TUNER_LOW;
148 v->mode=VIDEO_MODE_AUTO;
149 strcpy(v->name, "FM");
150 return 0;
151 }
152 case VIDIOCSTUNER:
153 {
154 struct video_tuner *v = arg;
155 if(v->tuner!=0)
156 return -EINVAL;
157 /* Only 1 tuner so no setting needed ! */
158 return 0;
159 }
160 case VIDIOCGFREQ:
161 {
162 unsigned long *freq = arg;
163 *freq = pcm20->freq;
164 return 0;
165 }
166 case VIDIOCSFREQ:
167 {
168 unsigned long *freq = arg;
169 pcm20->freq = *freq;
170 i=pcm20_setfreq(pcm20, pcm20->freq);
171 pr_debug("First view (setfreq): 0x%x\n", i);
172 return i;
173 }
174 case VIDIOCGAUDIO:
175 {
176 struct video_audio *v = arg;
177 memset(v,0, sizeof(*v));
178 v->flags=VIDEO_AUDIO_MUTABLE;
179 if (pcm20->muted)
180 v->flags|=VIDEO_AUDIO_MUTE;
181 v->mode=VIDEO_SOUND_STEREO;
182 if (pcm20->stereo)
183 v->mode|=VIDEO_SOUND_MONO;
184 /* v->step=2048; */
185 strcpy(v->name, "Radio");
186 return 0;
187 }
188 case VIDIOCSAUDIO:
189 {
190 struct video_audio *v = arg;
191 if(v->audio)
192 return -EINVAL;
193
194 pcm20_mute(pcm20, !!(v->flags&VIDEO_AUDIO_MUTE));
195 if(v->flags&VIDEO_SOUND_MONO)
196 pcm20_stereo(pcm20, 0);
197 if(v->flags&VIDEO_SOUND_STEREO)
198 pcm20_stereo(pcm20, 1);
199
200 return 0;
201 }
202 default:
203 return -ENOIOCTLCMD;
204 }
205}
206
207static int pcm20_ioctl(struct inode *inode, struct file *file,
208 unsigned int cmd, unsigned long arg)
209{
210 return video_usercopy(inode, file, cmd, arg, pcm20_do_ioctl);
211}
212
213static struct pcm20_device pcm20_unit = {
214 .freq = 87*16000,
215 .muted = 1,
216};
217
218static struct file_operations pcm20_fops = {
219 .owner = THIS_MODULE,
220 .open = video_exclusive_open,
221 .release = video_exclusive_release,
222 .ioctl = pcm20_ioctl,
223 .llseek = no_llseek,
224};
225
226static struct video_device pcm20_radio = {
227 .owner = THIS_MODULE,
228 .name = "Miro PCM 20 radio",
229 .type = VID_TYPE_TUNER,
230 .hardware = VID_HARDWARE_RTRACK,
231 .fops = &pcm20_fops,
232 .priv = &pcm20_unit
233};
234
235static int __init pcm20_init(void)
236{
237 if(video_register_device(&pcm20_radio, VFL_TYPE_RADIO, radio_nr)==-1)
238 goto video_register_device;
239
240 if(attach_aci_rds()<0)
241 goto attach_aci_rds;
242
243 printk(KERN_INFO "Miro PCM20 radio card driver.\n");
244
245 return 0;
246
247 attach_aci_rds:
248 video_unregister_device(&pcm20_radio);
249 video_register_device:
250 return -EINVAL;
251}
252
253MODULE_AUTHOR("Ruurd Reitsma");
254MODULE_DESCRIPTION("A driver for the Miro PCM20 radio card.");
255MODULE_LICENSE("GPL");
256
257static void __exit pcm20_cleanup(void)
258{
259 unload_aci_rds();
260 video_unregister_device(&pcm20_radio);
261}
262
263module_init(pcm20_init);
264module_exit(pcm20_cleanup);
diff --git a/drivers/media/radio/miropcm20-rds-core.c b/drivers/media/radio/miropcm20-rds-core.c
new file mode 100644
index 000000000000..a917a90cb5dc
--- /dev/null
+++ b/drivers/media/radio/miropcm20-rds-core.c
@@ -0,0 +1,210 @@
1/*
2 * Many thanks to Fred Seidel <seidel@metabox.de>, the
3 * designer of the RDS decoder hardware. With his help
4 * I was able to code this driver.
5 * Thanks also to Norberto Pellicci, Dominic Mounteney
6 * <DMounteney@pinnaclesys.com> and www.teleauskunft.de
7 * for good hints on finding Fred. It was somewhat hard
8 * to locate him here in Germany... [:
9 *
10 * Revision history:
11 *
12 * 2000-08-09 Robert Siemer <Robert.Siemer@gmx.de>
13 * RDS support for MiroSound PCM20 radio
14 */
15
16#include <linux/module.h>
17#include <linux/errno.h>
18#include <linux/string.h>
19#include <linux/init.h>
20#include <linux/slab.h>
21#include <asm/semaphore.h>
22#include <asm/io.h>
23#include "../../../sound/oss/aci.h"
24#include "miropcm20-rds-core.h"
25
26#define DEBUG 0
27
28static struct semaphore aci_rds_sem;
29
30#define RDS_DATASHIFT 2 /* Bit 2 */
31#define RDS_DATAMASK (1 << RDS_DATASHIFT)
32#define RDS_BUSYMASK 0x10 /* Bit 4 */
33#define RDS_CLOCKMASK 0x08 /* Bit 3 */
34
35#define RDS_DATA(x) (((x) >> RDS_DATASHIFT) & 1)
36
37
38#if DEBUG
39static void print_matrix(char array[], unsigned int length)
40{
41 int i, j;
42
43 for (i=0; i<length; i++) {
44 printk(KERN_DEBUG "aci-rds: ");
45 for (j=7; j>=0; j--) {
46 printk("%d", (array[i] >> j) & 0x1);
47 }
48 if (i%8 == 0)
49 printk(" byte-border\n");
50 else
51 printk("\n");
52 }
53}
54#endif /* DEBUG */
55
56static int byte2trans(unsigned char byte, unsigned char sendbuffer[], int size)
57{
58 int i;
59
60 if (size != 8)
61 return -1;
62 for (i = 7; i >= 0; i--)
63 sendbuffer[7-i] = (byte & (1 << i)) ? RDS_DATAMASK : 0;
64 sendbuffer[0] |= RDS_CLOCKMASK;
65
66 return 0;
67}
68
69static int rds_waitread(void)
70{
71 unsigned char byte;
72 int i=2000;
73
74 do {
75 byte=inb(RDS_REGISTER);
76 i--;
77 }
78 while ((byte & RDS_BUSYMASK) && i);
79
80 if (i) {
81 #if DEBUG
82 printk(KERN_DEBUG "rds_waitread()");
83 print_matrix(&byte, 1);
84 #endif
85 return (byte);
86 } else {
87 printk(KERN_WARNING "aci-rds: rds_waitread() timeout...\n");
88 return -1;
89 }
90}
91
92/* don't use any ..._nowait() function if you are not sure what you do... */
93
94static inline void rds_rawwrite_nowait(unsigned char byte)
95{
96 #if DEBUG
97 printk(KERN_DEBUG "rds_rawwrite()");
98 print_matrix(&byte, 1);
99 #endif
100 outb(byte, RDS_REGISTER);
101}
102
103static int rds_rawwrite(unsigned char byte)
104{
105 if (rds_waitread() >= 0) {
106 rds_rawwrite_nowait(byte);
107 return 0;
108 } else
109 return -1;
110}
111
112static int rds_write(unsigned char cmd)
113{
114 unsigned char sendbuffer[8];
115 int i;
116
117 if (byte2trans(cmd, sendbuffer, 8) != 0){
118 return -1;
119 } else {
120 for (i=0; i<8; i++) {
121 rds_rawwrite(sendbuffer[i]);
122 }
123 }
124 return 0;
125}
126
127static int rds_readcycle_nowait(void)
128{
129 rds_rawwrite_nowait(0);
130 return rds_waitread();
131}
132
133static int rds_readcycle(void)
134{
135 if (rds_rawwrite(0) < 0)
136 return -1;
137 return rds_waitread();
138}
139
140static int rds_read(unsigned char databuffer[], int datasize)
141{
142 #define READSIZE (8*datasize)
143
144 int i,j;
145
146 if (datasize < 1) /* nothing to read */
147 return 0;
148
149 /* to be able to use rds_readcycle_nowait()
150 I have to waitread() here */
151 if (rds_waitread() < 0)
152 return -1;
153
154 memset(databuffer, 0, datasize);
155
156 for (i=0; i< READSIZE; i++)
157 if((j=rds_readcycle_nowait()) < 0) {
158 return -1;
159 } else {
160 databuffer[i/8]|=(RDS_DATA(j) << (7-(i%8)));
161 }
162
163 return 0;
164}
165
166static int rds_ack(void)
167{
168 int i=rds_readcycle();
169
170 if (i < 0)
171 return -1;
172 if (i & RDS_DATAMASK) {
173 return 0; /* ACK */
174 } else {
175 printk(KERN_DEBUG "aci-rds: NACK\n");
176 return 1; /* NACK */
177 }
178}
179
180int aci_rds_cmd(unsigned char cmd, unsigned char databuffer[], int datasize)
181{
182 int ret;
183
184 if (down_interruptible(&aci_rds_sem))
185 return -EINTR;
186
187 rds_write(cmd);
188
189 /* RDS_RESET doesn't need further processing */
190 if (cmd!=RDS_RESET && (rds_ack() || rds_read(databuffer, datasize)))
191 ret = -1;
192 else
193 ret = 0;
194
195 up(&aci_rds_sem);
196
197 return ret;
198}
199EXPORT_SYMBOL(aci_rds_cmd);
200
201int __init attach_aci_rds(void)
202{
203 init_MUTEX(&aci_rds_sem);
204 return 0;
205}
206
207void __exit unload_aci_rds(void)
208{
209}
210MODULE_LICENSE("GPL");
diff --git a/drivers/media/radio/miropcm20-rds-core.h b/drivers/media/radio/miropcm20-rds-core.h
new file mode 100644
index 000000000000..aeb5761f0469
--- /dev/null
+++ b/drivers/media/radio/miropcm20-rds-core.h
@@ -0,0 +1,19 @@
1#ifndef _MIROPCM20_RDS_CORE_H_
2#define _MIROPCM20_RDS_CORE_H_
3
4extern int aci_rds_cmd(unsigned char cmd, unsigned char databuffer[], int datasize);
5
6#define RDS_STATUS 0x01
7#define RDS_STATIONNAME 0x02
8#define RDS_TEXT 0x03
9#define RDS_ALTFREQ 0x04
10#define RDS_TIMEDATE 0x05
11#define RDS_PI_CODE 0x06
12#define RDS_PTYTATP 0x07
13#define RDS_RESET 0x08
14#define RDS_RXVALUE 0x09
15
16extern void __exit unload_aci_rds(void);
17extern int __init attach_aci_rds(void);
18
19#endif /* _MIROPCM20_RDS_CORE_H_ */
diff --git a/drivers/media/radio/miropcm20-rds.c b/drivers/media/radio/miropcm20-rds.c
new file mode 100644
index 000000000000..df79d5e0aaed
--- /dev/null
+++ b/drivers/media/radio/miropcm20-rds.c
@@ -0,0 +1,133 @@
1/* MiroSOUND PCM20 radio rds interface driver
2 * (c) 2001 Robert Siemer <Robert.Siemer@gmx.de>
3 * Thanks to Fred Seidel. See miropcm20-rds-core.c for further information.
4 */
5
6/* Revision history:
7 *
8 * 2001-04-18 Robert Siemer <Robert.Siemer@gmx.de>
9 * separate file for user interface driver
10 */
11
12#include <linux/module.h>
13#include <linux/init.h>
14#include <linux/slab.h>
15#include <linux/fs.h>
16#include <linux/miscdevice.h>
17#include <linux/delay.h>
18#include <asm/uaccess.h>
19#include "miropcm20-rds-core.h"
20
21static char * text_buffer;
22static int rds_users = 0;
23
24
25static int rds_f_open(struct inode *in, struct file *fi)
26{
27 if (rds_users)
28 return -EBUSY;
29
30 rds_users++;
31 if ((text_buffer=kmalloc(66, GFP_KERNEL)) == 0) {
32 rds_users--;
33 printk(KERN_NOTICE "aci-rds: Out of memory by open()...\n");
34 return -ENOMEM;
35 }
36
37 return 0;
38}
39
40static int rds_f_release(struct inode *in, struct file *fi)
41{
42 kfree(text_buffer);
43
44 rds_users--;
45 return 0;
46}
47
48static void print_matrix(char *ch, char out[])
49{
50 int j;
51
52 for (j=7; j>=0; j--) {
53 out[7-j] = ((*ch >> j) & 0x1) + '0';
54 }
55}
56
57static ssize_t rds_f_read(struct file *file, char __user *buffer, size_t length, loff_t *offset)
58{
59// i = sprintf(text_buffer, "length: %d, offset: %d\n", length, *offset);
60
61 char c;
62 char bits[8];
63
64 msleep(2000);
65 aci_rds_cmd(RDS_STATUS, &c, 1);
66 print_matrix(&c, bits);
67 if (copy_to_user(buffer, bits, 8))
68 return -EFAULT;
69
70/* if ((c >> 3) & 1) {
71 aci_rds_cmd(RDS_STATIONNAME, text_buffer+1, 8);
72 text_buffer[0] = ' ' ;
73 text_buffer[9] = '\n';
74 return copy_to_user(buffer+8, text_buffer, 10) ? -EFAULT: 18;
75 }
76*/
77/* if ((c >> 6) & 1) {
78 aci_rds_cmd(RDS_PTYTATP, &c, 1);
79 if ( c & 1)
80 sprintf(text_buffer, " M");
81 else
82 sprintf(text_buffer, " S");
83 if ((c >> 1) & 1)
84 sprintf(text_buffer+2, " TA");
85 else
86 sprintf(text_buffer+2, " --");
87 if ((c >> 7) & 1)
88 sprintf(text_buffer+5, " TP");
89 else
90 sprintf(text_buffer+5, " --");
91 sprintf(text_buffer+8, " %2d\n", (c >> 2) & 0x1f);
92 return copy_to_user(buffer+8, text_buffer, 12) ? -EFAULT: 20;
93 }
94*/
95
96 if ((c >> 4) & 1) {
97 aci_rds_cmd(RDS_TEXT, text_buffer, 65);
98 text_buffer[0] = ' ' ;
99 text_buffer[65] = '\n';
100 return copy_to_user(buffer+8, text_buffer,66) ? -EFAULT : 66+8;
101 } else {
102 put_user('\n', buffer+8);
103 return 9;
104 }
105}
106
107static struct file_operations rds_fops = {
108 .owner = THIS_MODULE,
109 .read = rds_f_read,
110 .open = rds_f_open,
111 .release = rds_f_release
112};
113
114static struct miscdevice rds_miscdev = {
115 .minor = MISC_DYNAMIC_MINOR,
116 .name = "radiotext",
117 .devfs_name = "v4l/rds/radiotext",
118 .fops = &rds_fops,
119};
120
121static int __init miropcm20_rds_init(void)
122{
123 return misc_register(&rds_miscdev);
124}
125
126static void __exit miropcm20_rds_cleanup(void)
127{
128 misc_deregister(&rds_miscdev);
129}
130
131module_init(miropcm20_rds_init);
132module_exit(miropcm20_rds_cleanup);
133MODULE_LICENSE("GPL");
diff --git a/drivers/media/radio/radio-aimslab.c b/drivers/media/radio/radio-aimslab.c
new file mode 100644
index 000000000000..8b4ad70dd1b2
--- /dev/null
+++ b/drivers/media/radio/radio-aimslab.c
@@ -0,0 +1,368 @@
1/* radiotrack (radioreveal) driver for Linux radio support
2 * (c) 1997 M. Kirkwood
3 * Converted to new API by Alan Cox <Alan.Cox@linux.org>
4 * Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org>
5 *
6 * History:
7 * 1999-02-24 Russell Kroll <rkroll@exploits.org>
8 * Fine tuning/VIDEO_TUNER_LOW
9 * Frequency range expanded to start at 87 MHz
10 *
11 * TODO: Allow for more than one of these foolish entities :-)
12 *
13 * Notes on the hardware (reverse engineered from other peoples'
14 * reverse engineering of AIMS' code :-)
15 *
16 * Frequency control is done digitally -- ie out(port,encodefreq(95.8));
17 *
18 * The signal strength query is unsurprisingly inaccurate. And it seems
19 * to indicate that (on my card, at least) the frequency setting isn't
20 * too great. (I have to tune up .025MHz from what the freq should be
21 * to get a report that the thing is tuned.)
22 *
23 * Volume control is (ugh) analogue:
24 * out(port, start_increasing_volume);
25 * wait(a_wee_while);
26 * out(port, stop_changing_the_volume);
27 *
28 */
29
30#include <linux/module.h> /* Modules */
31#include <linux/init.h> /* Initdata */
32#include <linux/ioport.h> /* check_region, request_region */
33#include <linux/delay.h> /* udelay */
34#include <asm/io.h> /* outb, outb_p */
35#include <asm/uaccess.h> /* copy to/from user */
36#include <linux/videodev.h> /* kernel radio structs */
37#include <linux/config.h> /* CONFIG_RADIO_RTRACK_PORT */
38#include <asm/semaphore.h> /* Lock for the I/O */
39
40#ifndef CONFIG_RADIO_RTRACK_PORT
41#define CONFIG_RADIO_RTRACK_PORT -1
42#endif
43
44static int io = CONFIG_RADIO_RTRACK_PORT;
45static int radio_nr = -1;
46static struct semaphore lock;
47
48struct rt_device
49{
50 int port;
51 int curvol;
52 unsigned long curfreq;
53 int muted;
54};
55
56
57/* local things */
58
59static void sleep_delay(long n)
60{
61 /* Sleep nicely for 'n' uS */
62 int d=n/(1000000/HZ);
63 if(!d)
64 udelay(n);
65 else
66 msleep(jiffies_to_msecs(d));
67}
68
69static void rt_decvol(void)
70{
71 outb(0x58, io); /* volume down + sigstr + on */
72 sleep_delay(100000);
73 outb(0xd8, io); /* volume steady + sigstr + on */
74}
75
76static void rt_incvol(void)
77{
78 outb(0x98, io); /* volume up + sigstr + on */
79 sleep_delay(100000);
80 outb(0xd8, io); /* volume steady + sigstr + on */
81}
82
83static void rt_mute(struct rt_device *dev)
84{
85 dev->muted = 1;
86 down(&lock);
87 outb(0xd0, io); /* volume steady, off */
88 up(&lock);
89}
90
91static int rt_setvol(struct rt_device *dev, int vol)
92{
93 int i;
94
95 down(&lock);
96
97 if(vol == dev->curvol) { /* requested volume = current */
98 if (dev->muted) { /* user is unmuting the card */
99 dev->muted = 0;
100 outb (0xd8, io); /* enable card */
101 }
102 up(&lock);
103 return 0;
104 }
105
106 if(vol == 0) { /* volume = 0 means mute the card */
107 outb(0x48, io); /* volume down but still "on" */
108 sleep_delay(2000000); /* make sure it's totally down */
109 outb(0xd0, io); /* volume steady, off */
110 dev->curvol = 0; /* track the volume state! */
111 up(&lock);
112 return 0;
113 }
114
115 dev->muted = 0;
116 if(vol > dev->curvol)
117 for(i = dev->curvol; i < vol; i++)
118 rt_incvol();
119 else
120 for(i = dev->curvol; i > vol; i--)
121 rt_decvol();
122
123 dev->curvol = vol;
124 up(&lock);
125 return 0;
126}
127
128/* the 128+64 on these outb's is to keep the volume stable while tuning
129 * without them, the volume _will_ creep up with each frequency change
130 * and bit 4 (+16) is to keep the signal strength meter enabled
131 */
132
133static void send_0_byte(int port, struct rt_device *dev)
134{
135 if ((dev->curvol == 0) || (dev->muted)) {
136 outb_p(128+64+16+ 1, port); /* wr-enable + data low */
137 outb_p(128+64+16+2+1, port); /* clock */
138 }
139 else {
140 outb_p(128+64+16+8+ 1, port); /* on + wr-enable + data low */
141 outb_p(128+64+16+8+2+1, port); /* clock */
142 }
143 sleep_delay(1000);
144}
145
146static void send_1_byte(int port, struct rt_device *dev)
147{
148 if ((dev->curvol == 0) || (dev->muted)) {
149 outb_p(128+64+16+4 +1, port); /* wr-enable+data high */
150 outb_p(128+64+16+4+2+1, port); /* clock */
151 }
152 else {
153 outb_p(128+64+16+8+4 +1, port); /* on+wr-enable+data high */
154 outb_p(128+64+16+8+4+2+1, port); /* clock */
155 }
156
157 sleep_delay(1000);
158}
159
160static int rt_setfreq(struct rt_device *dev, unsigned long freq)
161{
162 int i;
163
164 /* adapted from radio-aztech.c */
165
166 /* now uses VIDEO_TUNER_LOW for fine tuning */
167
168 freq += 171200; /* Add 10.7 MHz IF */
169 freq /= 800; /* Convert to 50 kHz units */
170
171 down(&lock); /* Stop other ops interfering */
172
173 send_0_byte (io, dev); /* 0: LSB of frequency */
174
175 for (i = 0; i < 13; i++) /* : frequency bits (1-13) */
176 if (freq & (1 << i))
177 send_1_byte (io, dev);
178 else
179 send_0_byte (io, dev);
180
181 send_0_byte (io, dev); /* 14: test bit - always 0 */
182 send_0_byte (io, dev); /* 15: test bit - always 0 */
183
184 send_0_byte (io, dev); /* 16: band data 0 - always 0 */
185 send_0_byte (io, dev); /* 17: band data 1 - always 0 */
186 send_0_byte (io, dev); /* 18: band data 2 - always 0 */
187 send_0_byte (io, dev); /* 19: time base - always 0 */
188
189 send_0_byte (io, dev); /* 20: spacing (0 = 25 kHz) */
190 send_1_byte (io, dev); /* 21: spacing (1 = 25 kHz) */
191 send_0_byte (io, dev); /* 22: spacing (0 = 25 kHz) */
192 send_1_byte (io, dev); /* 23: AM/FM (FM = 1, always) */
193
194 if ((dev->curvol == 0) || (dev->muted))
195 outb (0xd0, io); /* volume steady + sigstr */
196 else
197 outb (0xd8, io); /* volume steady + sigstr + on */
198
199 up(&lock);
200
201 return 0;
202}
203
204static int rt_getsigstr(struct rt_device *dev)
205{
206 if (inb(io) & 2) /* bit set = no signal present */
207 return 0;
208 return 1; /* signal present */
209}
210
211static int rt_do_ioctl(struct inode *inode, struct file *file,
212 unsigned int cmd, void *arg)
213{
214 struct video_device *dev = video_devdata(file);
215 struct rt_device *rt=dev->priv;
216
217 switch(cmd)
218 {
219 case VIDIOCGCAP:
220 {
221 struct video_capability *v = arg;
222 memset(v,0,sizeof(*v));
223 v->type=VID_TYPE_TUNER;
224 v->channels=1;
225 v->audios=1;
226 strcpy(v->name, "RadioTrack");
227 return 0;
228 }
229 case VIDIOCGTUNER:
230 {
231 struct video_tuner *v = arg;
232 if(v->tuner) /* Only 1 tuner */
233 return -EINVAL;
234 v->rangelow=(87*16000);
235 v->rangehigh=(108*16000);
236 v->flags=VIDEO_TUNER_LOW;
237 v->mode=VIDEO_MODE_AUTO;
238 strcpy(v->name, "FM");
239 v->signal=0xFFFF*rt_getsigstr(rt);
240 return 0;
241 }
242 case VIDIOCSTUNER:
243 {
244 struct video_tuner *v = arg;
245 if(v->tuner!=0)
246 return -EINVAL;
247 /* Only 1 tuner so no setting needed ! */
248 return 0;
249 }
250 case VIDIOCGFREQ:
251 {
252 unsigned long *freq = arg;
253 *freq = rt->curfreq;
254 return 0;
255 }
256 case VIDIOCSFREQ:
257 {
258 unsigned long *freq = arg;
259 rt->curfreq = *freq;
260 rt_setfreq(rt, rt->curfreq);
261 return 0;
262 }
263 case VIDIOCGAUDIO:
264 {
265 struct video_audio *v = arg;
266 memset(v,0, sizeof(*v));
267 v->flags|=VIDEO_AUDIO_MUTABLE|VIDEO_AUDIO_VOLUME;
268 v->volume=rt->curvol * 6554;
269 v->step=6554;
270 strcpy(v->name, "Radio");
271 return 0;
272 }
273 case VIDIOCSAUDIO:
274 {
275 struct video_audio *v = arg;
276 if(v->audio)
277 return -EINVAL;
278 if(v->flags&VIDEO_AUDIO_MUTE)
279 rt_mute(rt);
280 else
281 rt_setvol(rt,v->volume/6554);
282 return 0;
283 }
284 default:
285 return -ENOIOCTLCMD;
286 }
287}
288
289static int rt_ioctl(struct inode *inode, struct file *file,
290 unsigned int cmd, unsigned long arg)
291{
292 return video_usercopy(inode, file, cmd, arg, rt_do_ioctl);
293}
294
295static struct rt_device rtrack_unit;
296
297static struct file_operations rtrack_fops = {
298 .owner = THIS_MODULE,
299 .open = video_exclusive_open,
300 .release = video_exclusive_release,
301 .ioctl = rt_ioctl,
302 .llseek = no_llseek,
303};
304
305static struct video_device rtrack_radio=
306{
307 .owner = THIS_MODULE,
308 .name = "RadioTrack radio",
309 .type = VID_TYPE_TUNER,
310 .hardware = VID_HARDWARE_RTRACK,
311 .fops = &rtrack_fops,
312};
313
314static int __init rtrack_init(void)
315{
316 if(io==-1)
317 {
318 printk(KERN_ERR "You must set an I/O address with io=0x???\n");
319 return -EINVAL;
320 }
321
322 if (!request_region(io, 2, "rtrack"))
323 {
324 printk(KERN_ERR "rtrack: port 0x%x already in use\n", io);
325 return -EBUSY;
326 }
327
328 rtrack_radio.priv=&rtrack_unit;
329
330 if(video_register_device(&rtrack_radio, VFL_TYPE_RADIO, radio_nr)==-1)
331 {
332 release_region(io, 2);
333 return -EINVAL;
334 }
335 printk(KERN_INFO "AIMSlab RadioTrack/RadioReveal card driver.\n");
336
337 /* Set up the I/O locking */
338
339 init_MUTEX(&lock);
340
341 /* mute card - prevents noisy bootups */
342
343 /* this ensures that the volume is all the way down */
344 outb(0x48, io); /* volume down but still "on" */
345 sleep_delay(2000000); /* make sure it's totally down */
346 outb(0xc0, io); /* steady volume, mute card */
347 rtrack_unit.curvol = 0;
348
349 return 0;
350}
351
352MODULE_AUTHOR("M.Kirkwood");
353MODULE_DESCRIPTION("A driver for the RadioTrack/RadioReveal radio card.");
354MODULE_LICENSE("GPL");
355
356module_param(io, int, 0);
357MODULE_PARM_DESC(io, "I/O address of the RadioTrack card (0x20f or 0x30f)");
358module_param(radio_nr, int, 0);
359
360static void __exit cleanup_rtrack_module(void)
361{
362 video_unregister_device(&rtrack_radio);
363 release_region(io,2);
364}
365
366module_init(rtrack_init);
367module_exit(cleanup_rtrack_module);
368
diff --git a/drivers/media/radio/radio-aztech.c b/drivers/media/radio/radio-aztech.c
new file mode 100644
index 000000000000..013c835ed910
--- /dev/null
+++ b/drivers/media/radio/radio-aztech.c
@@ -0,0 +1,315 @@
1/* radio-aztech.c - Aztech radio card driver for Linux 2.2
2 *
3 * Adapted to support the Video for Linux API by
4 * Russell Kroll <rkroll@exploits.org>. Based on original tuner code by:
5 *
6 * Quay Ly
7 * Donald Song
8 * Jason Lewis (jlewis@twilight.vtc.vsc.edu)
9 * Scott McGrath (smcgrath@twilight.vtc.vsc.edu)
10 * William McGrath (wmcgrath@twilight.vtc.vsc.edu)
11 *
12 * The basis for this code may be found at http://bigbang.vtc.vsc.edu/fmradio/
13 * along with more information on the card itself.
14 *
15 * History:
16 * 1999-02-24 Russell Kroll <rkroll@exploits.org>
17 * Fine tuning/VIDEO_TUNER_LOW
18 * Range expanded to 87-108 MHz (from 87.9-107.8)
19 *
20 * Notable changes from the original source:
21 * - includes stripped down to the essentials
22 * - for loops used as delays replaced with udelay()
23 * - #defines removed, changed to static values
24 * - tuning structure changed - no more character arrays, other changes
25*/
26
27#include <linux/module.h> /* Modules */
28#include <linux/init.h> /* Initdata */
29#include <linux/ioport.h> /* check_region, request_region */
30#include <linux/delay.h> /* udelay */
31#include <asm/io.h> /* outb, outb_p */
32#include <asm/uaccess.h> /* copy to/from user */
33#include <linux/videodev.h> /* kernel radio structs */
34#include <linux/config.h> /* CONFIG_RADIO_AZTECH_PORT */
35
36/* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */
37
38#ifndef CONFIG_RADIO_AZTECH_PORT
39#define CONFIG_RADIO_AZTECH_PORT -1
40#endif
41
42static int io = CONFIG_RADIO_AZTECH_PORT;
43static int radio_nr = -1;
44static int radio_wait_time = 1000;
45static struct semaphore lock;
46
47struct az_device
48{
49 int curvol;
50 unsigned long curfreq;
51 int stereo;
52};
53
54static int volconvert(int level)
55{
56 level>>=14; /* Map 16bits down to 2 bit */
57 level&=3;
58
59 /* convert to card-friendly values */
60 switch (level)
61 {
62 case 0:
63 return 0;
64 case 1:
65 return 1;
66 case 2:
67 return 4;
68 case 3:
69 return 5;
70 }
71 return 0; /* Quieten gcc */
72}
73
74static void send_0_byte (struct az_device *dev)
75{
76 udelay(radio_wait_time);
77 outb_p(2+volconvert(dev->curvol), io);
78 outb_p(64+2+volconvert(dev->curvol), io);
79}
80
81static void send_1_byte (struct az_device *dev)
82{
83 udelay (radio_wait_time);
84 outb_p(128+2+volconvert(dev->curvol), io);
85 outb_p(128+64+2+volconvert(dev->curvol), io);
86}
87
88static int az_setvol(struct az_device *dev, int vol)
89{
90 down(&lock);
91 outb (volconvert(vol), io);
92 up(&lock);
93 return 0;
94}
95
96/* thanks to Michael Dwyer for giving me a dose of clues in
97 * the signal strength department..
98 *
99 * This card has a stereo bit - bit 0 set = mono, not set = stereo
100 * It also has a "signal" bit - bit 1 set = bad signal, not set = good
101 *
102 */
103
104static int az_getsigstr(struct az_device *dev)
105{
106 if (inb(io) & 2) /* bit set = no signal present */
107 return 0;
108 return 1; /* signal present */
109}
110
111static int az_getstereo(struct az_device *dev)
112{
113 if (inb(io) & 1) /* bit set = mono */
114 return 0;
115 return 1; /* stereo */
116}
117
118static int az_setfreq(struct az_device *dev, unsigned long frequency)
119{
120 int i;
121
122 frequency += 171200; /* Add 10.7 MHz IF */
123 frequency /= 800; /* Convert to 50 kHz units */
124
125 down(&lock);
126
127 send_0_byte (dev); /* 0: LSB of frequency */
128
129 for (i = 0; i < 13; i++) /* : frequency bits (1-13) */
130 if (frequency & (1 << i))
131 send_1_byte (dev);
132 else
133 send_0_byte (dev);
134
135 send_0_byte (dev); /* 14: test bit - always 0 */
136 send_0_byte (dev); /* 15: test bit - always 0 */
137 send_0_byte (dev); /* 16: band data 0 - always 0 */
138 if (dev->stereo) /* 17: stereo (1 to enable) */
139 send_1_byte (dev);
140 else
141 send_0_byte (dev);
142
143 send_1_byte (dev); /* 18: band data 1 - unknown */
144 send_0_byte (dev); /* 19: time base - always 0 */
145 send_0_byte (dev); /* 20: spacing (0 = 25 kHz) */
146 send_1_byte (dev); /* 21: spacing (1 = 25 kHz) */
147 send_0_byte (dev); /* 22: spacing (0 = 25 kHz) */
148 send_1_byte (dev); /* 23: AM/FM (FM = 1, always) */
149
150 /* latch frequency */
151
152 udelay (radio_wait_time);
153 outb_p(128+64+volconvert(dev->curvol), io);
154
155 up(&lock);
156
157 return 0;
158}
159
160static int az_do_ioctl(struct inode *inode, struct file *file,
161 unsigned int cmd, void *arg)
162{
163 struct video_device *dev = video_devdata(file);
164 struct az_device *az = dev->priv;
165
166 switch(cmd)
167 {
168 case VIDIOCGCAP:
169 {
170 struct video_capability *v = arg;
171 memset(v,0,sizeof(*v));
172 v->type=VID_TYPE_TUNER;
173 v->channels=1;
174 v->audios=1;
175 strcpy(v->name, "Aztech Radio");
176 return 0;
177 }
178 case VIDIOCGTUNER:
179 {
180 struct video_tuner *v = arg;
181 if(v->tuner) /* Only 1 tuner */
182 return -EINVAL;
183 v->rangelow=(87*16000);
184 v->rangehigh=(108*16000);
185 v->flags=VIDEO_TUNER_LOW;
186 v->mode=VIDEO_MODE_AUTO;
187 v->signal=0xFFFF*az_getsigstr(az);
188 if(az_getstereo(az))
189 v->flags|=VIDEO_TUNER_STEREO_ON;
190 strcpy(v->name, "FM");
191 return 0;
192 }
193 case VIDIOCSTUNER:
194 {
195 struct video_tuner *v = arg;
196 if(v->tuner!=0)
197 return -EINVAL;
198 return 0;
199 }
200 case VIDIOCGFREQ:
201 {
202 unsigned long *freq = arg;
203 *freq = az->curfreq;
204 return 0;
205 }
206 case VIDIOCSFREQ:
207 {
208 unsigned long *freq = arg;
209 az->curfreq = *freq;
210 az_setfreq(az, az->curfreq);
211 return 0;
212 }
213 case VIDIOCGAUDIO:
214 {
215 struct video_audio *v = arg;
216 memset(v,0, sizeof(*v));
217 v->flags|=VIDEO_AUDIO_MUTABLE|VIDEO_AUDIO_VOLUME;
218 if(az->stereo)
219 v->mode=VIDEO_SOUND_STEREO;
220 else
221 v->mode=VIDEO_SOUND_MONO;
222 v->volume=az->curvol;
223 v->step=16384;
224 strcpy(v->name, "Radio");
225 return 0;
226 }
227 case VIDIOCSAUDIO:
228 {
229 struct video_audio *v = arg;
230 if(v->audio)
231 return -EINVAL;
232 az->curvol=v->volume;
233
234 az->stereo=(v->mode&VIDEO_SOUND_STEREO)?1:0;
235 if(v->flags&VIDEO_AUDIO_MUTE)
236 az_setvol(az,0);
237 else
238 az_setvol(az,az->curvol);
239 return 0;
240 }
241 default:
242 return -ENOIOCTLCMD;
243 }
244}
245
246static int az_ioctl(struct inode *inode, struct file *file,
247 unsigned int cmd, unsigned long arg)
248{
249 return video_usercopy(inode, file, cmd, arg, az_do_ioctl);
250}
251
252static struct az_device aztech_unit;
253
254static struct file_operations aztech_fops = {
255 .owner = THIS_MODULE,
256 .open = video_exclusive_open,
257 .release = video_exclusive_release,
258 .ioctl = az_ioctl,
259 .llseek = no_llseek,
260};
261
262static struct video_device aztech_radio=
263{
264 .owner = THIS_MODULE,
265 .name = "Aztech radio",
266 .type = VID_TYPE_TUNER,
267 .hardware = VID_HARDWARE_AZTECH,
268 .fops = &aztech_fops,
269};
270
271static int __init aztech_init(void)
272{
273 if(io==-1)
274 {
275 printk(KERN_ERR "You must set an I/O address with io=0x???\n");
276 return -EINVAL;
277 }
278
279 if (!request_region(io, 2, "aztech"))
280 {
281 printk(KERN_ERR "aztech: port 0x%x already in use\n", io);
282 return -EBUSY;
283 }
284
285 init_MUTEX(&lock);
286 aztech_radio.priv=&aztech_unit;
287
288 if(video_register_device(&aztech_radio, VFL_TYPE_RADIO, radio_nr)==-1)
289 {
290 release_region(io,2);
291 return -EINVAL;
292 }
293
294 printk(KERN_INFO "Aztech radio card driver v1.00/19990224 rkroll@exploits.org\n");
295 /* mute card - prevents noisy bootups */
296 outb (0, io);
297 return 0;
298}
299
300MODULE_AUTHOR("Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
301MODULE_DESCRIPTION("A driver for the Aztech radio card.");
302MODULE_LICENSE("GPL");
303
304module_param(io, int, 0);
305module_param(radio_nr, int, 0);
306MODULE_PARM_DESC(io, "I/O address of the Aztech card (0x350 or 0x358)");
307
308static void __exit aztech_cleanup(void)
309{
310 video_unregister_device(&aztech_radio);
311 release_region(io,2);
312}
313
314module_init(aztech_init);
315module_exit(aztech_cleanup);
diff --git a/drivers/media/radio/radio-cadet.c b/drivers/media/radio/radio-cadet.c
new file mode 100644
index 000000000000..53d399b6652b
--- /dev/null
+++ b/drivers/media/radio/radio-cadet.c
@@ -0,0 +1,620 @@
1/* radio-cadet.c - A video4linux driver for the ADS Cadet AM/FM Radio Card
2 *
3 * by Fred Gleason <fredg@wava.com>
4 * Version 0.3.3
5 *
6 * (Loosely) based on code for the Aztech radio card by
7 *
8 * Russell Kroll (rkroll@exploits.org)
9 * Quay Ly
10 * Donald Song
11 * Jason Lewis (jlewis@twilight.vtc.vsc.edu)
12 * Scott McGrath (smcgrath@twilight.vtc.vsc.edu)
13 * William McGrath (wmcgrath@twilight.vtc.vsc.edu)
14 *
15 * History:
16 * 2000-04-29 Russell Kroll <rkroll@exploits.org>
17 * Added ISAPnP detection for Linux 2.3/2.4
18 *
19 * 2001-01-10 Russell Kroll <rkroll@exploits.org>
20 * Removed dead CONFIG_RADIO_CADET_PORT code
21 * PnP detection on load is now default (no args necessary)
22 *
23 * 2002-01-17 Adam Belay <ambx1@neo.rr.com>
24 * Updated to latest pnp code
25 *
26 * 2003-01-31 Alan Cox <alan@redhat.com>
27 * Cleaned up locking, delay code, general odds and ends
28 */
29
30#include <linux/module.h> /* Modules */
31#include <linux/init.h> /* Initdata */
32#include <linux/ioport.h> /* check_region, request_region */
33#include <linux/delay.h> /* udelay */
34#include <asm/io.h> /* outb, outb_p */
35#include <asm/uaccess.h> /* copy to/from user */
36#include <linux/videodev.h> /* kernel radio structs */
37#include <linux/param.h>
38#include <linux/pnp.h>
39
40#define RDS_BUFFER 256
41
42static int io=-1; /* default to isapnp activation */
43static int radio_nr = -1;
44static int users=0;
45static int curtuner=0;
46static int tunestat=0;
47static int sigstrength=0;
48static wait_queue_head_t read_queue;
49static struct timer_list readtimer;
50static __u8 rdsin=0,rdsout=0,rdsstat=0;
51static unsigned char rdsbuf[RDS_BUFFER];
52static spinlock_t cadet_io_lock;
53
54static int cadet_probe(void);
55
56/*
57 * Signal Strength Threshold Values
58 * The V4L API spec does not define any particular unit for the signal
59 * strength value. These values are in microvolts of RF at the tuner's input.
60 */
61static __u16 sigtable[2][4]={{5,10,30,150},{28,40,63,1000}};
62
63static int cadet_getrds(void)
64{
65 int rdsstat=0;
66
67 spin_lock(&cadet_io_lock);
68 outb(3,io); /* Select Decoder Control/Status */
69 outb(inb(io+1)&0x7f,io+1); /* Reset RDS detection */
70 spin_unlock(&cadet_io_lock);
71
72 msleep(100);
73
74 spin_lock(&cadet_io_lock);
75 outb(3,io); /* Select Decoder Control/Status */
76 if((inb(io+1)&0x80)!=0) {
77 rdsstat|=VIDEO_TUNER_RDS_ON;
78 }
79 if((inb(io+1)&0x10)!=0) {
80 rdsstat|=VIDEO_TUNER_MBS_ON;
81 }
82 spin_unlock(&cadet_io_lock);
83 return rdsstat;
84}
85
86static int cadet_getstereo(void)
87{
88 int ret = 0;
89 if(curtuner != 0) /* Only FM has stereo capability! */
90 return 0;
91
92 spin_lock(&cadet_io_lock);
93 outb(7,io); /* Select tuner control */
94 if( (inb(io+1) & 0x40) == 0)
95 ret = 1;
96 spin_unlock(&cadet_io_lock);
97 return ret;
98}
99
100static unsigned cadet_gettune(void)
101{
102 int curvol,i;
103 unsigned fifo=0;
104
105 /*
106 * Prepare for read
107 */
108
109 spin_lock(&cadet_io_lock);
110
111 outb(7,io); /* Select tuner control */
112 curvol=inb(io+1); /* Save current volume/mute setting */
113 outb(0x00,io+1); /* Ensure WRITE-ENABLE is LOW */
114 tunestat=0xffff;
115
116 /*
117 * Read the shift register
118 */
119 for(i=0;i<25;i++) {
120 fifo=(fifo<<1)|((inb(io+1)>>7)&0x01);
121 if(i<24) {
122 outb(0x01,io+1);
123 tunestat&=inb(io+1);
124 outb(0x00,io+1);
125 }
126 }
127
128 /*
129 * Restore volume/mute setting
130 */
131 outb(curvol,io+1);
132 spin_unlock(&cadet_io_lock);
133
134 return fifo;
135}
136
137static unsigned cadet_getfreq(void)
138{
139 int i;
140 unsigned freq=0,test,fifo=0;
141
142 /*
143 * Read current tuning
144 */
145 fifo=cadet_gettune();
146
147 /*
148 * Convert to actual frequency
149 */
150 if(curtuner==0) { /* FM */
151 test=12500;
152 for(i=0;i<14;i++) {
153 if((fifo&0x01)!=0) {
154 freq+=test;
155 }
156 test=test<<1;
157 fifo=fifo>>1;
158 }
159 freq-=10700000; /* IF frequency is 10.7 MHz */
160 freq=(freq*16)/1000000; /* Make it 1/16 MHz */
161 }
162 if(curtuner==1) { /* AM */
163 freq=((fifo&0x7fff)-2010)*16;
164 }
165
166 return freq;
167}
168
169static void cadet_settune(unsigned fifo)
170{
171 int i;
172 unsigned test;
173
174 spin_lock(&cadet_io_lock);
175
176 outb(7,io); /* Select tuner control */
177 /*
178 * Write the shift register
179 */
180 test=0;
181 test=(fifo>>23)&0x02; /* Align data for SDO */
182 test|=0x1c; /* SDM=1, SWE=1, SEN=1, SCK=0 */
183 outb(7,io); /* Select tuner control */
184 outb(test,io+1); /* Initialize for write */
185 for(i=0;i<25;i++) {
186 test|=0x01; /* Toggle SCK High */
187 outb(test,io+1);
188 test&=0xfe; /* Toggle SCK Low */
189 outb(test,io+1);
190 fifo=fifo<<1; /* Prepare the next bit */
191 test=0x1c|((fifo>>23)&0x02);
192 outb(test,io+1);
193 }
194 spin_unlock(&cadet_io_lock);
195}
196
197static void cadet_setfreq(unsigned freq)
198{
199 unsigned fifo;
200 int i,j,test;
201 int curvol;
202
203 /*
204 * Formulate a fifo command
205 */
206 fifo=0;
207 if(curtuner==0) { /* FM */
208 test=102400;
209 freq=(freq*1000)/16; /* Make it kHz */
210 freq+=10700; /* IF is 10700 kHz */
211 for(i=0;i<14;i++) {
212 fifo=fifo<<1;
213 if(freq>=test) {
214 fifo|=0x01;
215 freq-=test;
216 }
217 test=test>>1;
218 }
219 }
220 if(curtuner==1) { /* AM */
221 fifo=(freq/16)+2010; /* Make it kHz */
222 fifo|=0x100000; /* Select AM Band */
223 }
224
225 /*
226 * Save current volume/mute setting
227 */
228
229 spin_lock(&cadet_io_lock);
230 outb(7,io); /* Select tuner control */
231 curvol=inb(io+1);
232 spin_unlock(&cadet_io_lock);
233
234 /*
235 * Tune the card
236 */
237 for(j=3;j>-1;j--) {
238 cadet_settune(fifo|(j<<16));
239
240 spin_lock(&cadet_io_lock);
241 outb(7,io); /* Select tuner control */
242 outb(curvol,io+1);
243 spin_unlock(&cadet_io_lock);
244
245 msleep(100);
246
247 cadet_gettune();
248 if((tunestat & 0x40) == 0) { /* Tuned */
249 sigstrength=sigtable[curtuner][j];
250 return;
251 }
252 }
253 sigstrength=0;
254}
255
256
257static int cadet_getvol(void)
258{
259 int ret = 0;
260
261 spin_lock(&cadet_io_lock);
262
263 outb(7,io); /* Select tuner control */
264 if((inb(io + 1) & 0x20) != 0)
265 ret = 0xffff;
266
267 spin_unlock(&cadet_io_lock);
268 return ret;
269}
270
271
272static void cadet_setvol(int vol)
273{
274 spin_lock(&cadet_io_lock);
275 outb(7,io); /* Select tuner control */
276 if(vol>0)
277 outb(0x20,io+1);
278 else
279 outb(0x00,io+1);
280 spin_unlock(&cadet_io_lock);
281}
282
283static void cadet_handler(unsigned long data)
284{
285 /*
286 * Service the RDS fifo
287 */
288
289 if(spin_trylock(&cadet_io_lock))
290 {
291 outb(0x3,io); /* Select RDS Decoder Control */
292 if((inb(io+1)&0x20)!=0) {
293 printk(KERN_CRIT "cadet: RDS fifo overflow\n");
294 }
295 outb(0x80,io); /* Select RDS fifo */
296 while((inb(io)&0x80)!=0) {
297 rdsbuf[rdsin]=inb(io+1);
298 if(rdsin==rdsout)
299 printk(KERN_WARNING "cadet: RDS buffer overflow\n");
300 else
301 rdsin++;
302 }
303 spin_unlock(&cadet_io_lock);
304 }
305
306 /*
307 * Service pending read
308 */
309 if( rdsin!=rdsout)
310 wake_up_interruptible(&read_queue);
311
312 /*
313 * Clean up and exit
314 */
315 init_timer(&readtimer);
316 readtimer.function=cadet_handler;
317 readtimer.data=(unsigned long)0;
318 readtimer.expires=jiffies+(HZ/20);
319 add_timer(&readtimer);
320}
321
322
323
324static ssize_t cadet_read(struct file *file, char __user *data,
325 size_t count, loff_t *ppos)
326{
327 int i=0;
328 unsigned char readbuf[RDS_BUFFER];
329
330 if(rdsstat==0) {
331 spin_lock(&cadet_io_lock);
332 rdsstat=1;
333 outb(0x80,io); /* Select RDS fifo */
334 spin_unlock(&cadet_io_lock);
335 init_timer(&readtimer);
336 readtimer.function=cadet_handler;
337 readtimer.data=(unsigned long)0;
338 readtimer.expires=jiffies+(HZ/20);
339 add_timer(&readtimer);
340 }
341 if(rdsin==rdsout) {
342 if (file->f_flags & O_NONBLOCK)
343 return -EWOULDBLOCK;
344 interruptible_sleep_on(&read_queue);
345 }
346 while( i<count && rdsin!=rdsout)
347 readbuf[i++]=rdsbuf[rdsout++];
348
349 if (copy_to_user(data,readbuf,i))
350 return -EFAULT;
351 return i;
352}
353
354
355
356static int cadet_do_ioctl(struct inode *inode, struct file *file,
357 unsigned int cmd, void *arg)
358{
359 switch(cmd)
360 {
361 case VIDIOCGCAP:
362 {
363 struct video_capability *v = arg;
364 memset(v,0,sizeof(*v));
365 v->type=VID_TYPE_TUNER;
366 v->channels=2;
367 v->audios=1;
368 strcpy(v->name, "ADS Cadet");
369 return 0;
370 }
371 case VIDIOCGTUNER:
372 {
373 struct video_tuner *v = arg;
374 if((v->tuner<0)||(v->tuner>1)) {
375 return -EINVAL;
376 }
377 switch(v->tuner) {
378 case 0:
379 strcpy(v->name,"FM");
380 v->rangelow=1400; /* 87.5 MHz */
381 v->rangehigh=1728; /* 108.0 MHz */
382 v->flags=0;
383 v->mode=0;
384 v->mode|=VIDEO_MODE_AUTO;
385 v->signal=sigstrength;
386 if(cadet_getstereo()==1) {
387 v->flags|=VIDEO_TUNER_STEREO_ON;
388 }
389 v->flags|=cadet_getrds();
390 break;
391 case 1:
392 strcpy(v->name,"AM");
393 v->rangelow=8320; /* 520 kHz */
394 v->rangehigh=26400; /* 1650 kHz */
395 v->flags=0;
396 v->flags|=VIDEO_TUNER_LOW;
397 v->mode=0;
398 v->mode|=VIDEO_MODE_AUTO;
399 v->signal=sigstrength;
400 break;
401 }
402 return 0;
403 }
404 case VIDIOCSTUNER:
405 {
406 struct video_tuner *v = arg;
407 if((v->tuner<0)||(v->tuner>1)) {
408 return -EINVAL;
409 }
410 curtuner=v->tuner;
411 return 0;
412 }
413 case VIDIOCGFREQ:
414 {
415 unsigned long *freq = arg;
416 *freq = cadet_getfreq();
417 return 0;
418 }
419 case VIDIOCSFREQ:
420 {
421 unsigned long *freq = arg;
422 if((curtuner==0)&&((*freq<1400)||(*freq>1728))) {
423 return -EINVAL;
424 }
425 if((curtuner==1)&&((*freq<8320)||(*freq>26400))) {
426 return -EINVAL;
427 }
428 cadet_setfreq(*freq);
429 return 0;
430 }
431 case VIDIOCGAUDIO:
432 {
433 struct video_audio *v = arg;
434 memset(v,0, sizeof(*v));
435 v->flags=VIDEO_AUDIO_MUTABLE|VIDEO_AUDIO_VOLUME;
436 if(cadet_getstereo()==0) {
437 v->mode=VIDEO_SOUND_MONO;
438 } else {
439 v->mode=VIDEO_SOUND_STEREO;
440 }
441 v->volume=cadet_getvol();
442 v->step=0xffff;
443 strcpy(v->name, "Radio");
444 return 0;
445 }
446 case VIDIOCSAUDIO:
447 {
448 struct video_audio *v = arg;
449 if(v->audio)
450 return -EINVAL;
451 cadet_setvol(v->volume);
452 if(v->flags&VIDEO_AUDIO_MUTE)
453 cadet_setvol(0);
454 else
455 cadet_setvol(0xffff);
456 return 0;
457 }
458 default:
459 return -ENOIOCTLCMD;
460 }
461}
462
463static int cadet_ioctl(struct inode *inode, struct file *file,
464 unsigned int cmd, unsigned long arg)
465{
466 return video_usercopy(inode, file, cmd, arg, cadet_do_ioctl);
467}
468
469static int cadet_open(struct inode *inode, struct file *file)
470{
471 if(users)
472 return -EBUSY;
473 users++;
474 init_waitqueue_head(&read_queue);
475 return 0;
476}
477
478static int cadet_release(struct inode *inode, struct file *file)
479{
480 del_timer_sync(&readtimer);
481 rdsstat=0;
482 users--;
483 return 0;
484}
485
486
487static struct file_operations cadet_fops = {
488 .owner = THIS_MODULE,
489 .open = cadet_open,
490 .release = cadet_release,
491 .read = cadet_read,
492 .ioctl = cadet_ioctl,
493 .llseek = no_llseek,
494};
495
496static struct video_device cadet_radio=
497{
498 .owner = THIS_MODULE,
499 .name = "Cadet radio",
500 .type = VID_TYPE_TUNER,
501 .hardware = VID_HARDWARE_CADET,
502 .fops = &cadet_fops,
503};
504
505static struct pnp_device_id cadet_pnp_devices[] = {
506 /* ADS Cadet AM/FM Radio Card */
507 {.id = "MSM0c24", .driver_data = 0},
508 {.id = ""}
509};
510
511MODULE_DEVICE_TABLE(pnp, cadet_pnp_devices);
512
513static int cadet_pnp_probe(struct pnp_dev * dev, const struct pnp_device_id *dev_id)
514{
515 if (!dev)
516 return -ENODEV;
517 /* only support one device */
518 if (io > 0)
519 return -EBUSY;
520
521 if (!pnp_port_valid(dev, 0)) {
522 return -ENODEV;
523 }
524
525 io = pnp_port_start(dev, 0);
526
527 printk ("radio-cadet: PnP reports device at %#x\n", io);
528
529 return io;
530}
531
532static struct pnp_driver cadet_pnp_driver = {
533 .name = "radio-cadet",
534 .id_table = cadet_pnp_devices,
535 .probe = cadet_pnp_probe,
536 .remove = NULL,
537};
538
539static int cadet_probe(void)
540{
541 static int iovals[8]={0x330,0x332,0x334,0x336,0x338,0x33a,0x33c,0x33e};
542 int i;
543
544 for(i=0;i<8;i++) {
545 io=iovals[i];
546 if(request_region(io,2, "cadet-probe")>=0) {
547 cadet_setfreq(1410);
548 if(cadet_getfreq()==1410) {
549 release_region(io, 2);
550 return io;
551 }
552 release_region(io, 2);
553 }
554 }
555 return -1;
556}
557
558/*
559 * io should only be set if the user has used something like
560 * isapnp (the userspace program) to initialize this card for us
561 */
562
563static int __init cadet_init(void)
564{
565 spin_lock_init(&cadet_io_lock);
566
567 /*
568 * If a probe was requested then probe ISAPnP first (safest)
569 */
570 if (io < 0)
571 pnp_register_driver(&cadet_pnp_driver);
572 /*
573 * If that fails then probe unsafely if probe is requested
574 */
575 if(io < 0)
576 io = cadet_probe ();
577
578 /*
579 * Else we bail out
580 */
581
582 if(io < 0) {
583#ifdef MODULE
584 printk(KERN_ERR "You must set an I/O address with io=0x???\n");
585#endif
586 goto fail;
587 }
588 if (!request_region(io,2,"cadet"))
589 goto fail;
590 if(video_register_device(&cadet_radio,VFL_TYPE_RADIO,radio_nr)==-1) {
591 release_region(io,2);
592 goto fail;
593 }
594 printk(KERN_INFO "ADS Cadet Radio Card at 0x%x\n",io);
595 return 0;
596fail:
597 pnp_unregister_driver(&cadet_pnp_driver);
598 return -1;
599}
600
601
602
603MODULE_AUTHOR("Fred Gleason, Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
604MODULE_DESCRIPTION("A driver for the ADS Cadet AM/FM/RDS radio card.");
605MODULE_LICENSE("GPL");
606
607module_param(io, int, 0);
608MODULE_PARM_DESC(io, "I/O address of Cadet card (0x330,0x332,0x334,0x336,0x338,0x33a,0x33c,0x33e)");
609module_param(radio_nr, int, 0);
610
611static void __exit cadet_cleanup_module(void)
612{
613 video_unregister_device(&cadet_radio);
614 release_region(io,2);
615 pnp_unregister_driver(&cadet_pnp_driver);
616}
617
618module_init(cadet_init);
619module_exit(cadet_cleanup_module);
620
diff --git a/drivers/media/radio/radio-gemtek-pci.c b/drivers/media/radio/radio-gemtek-pci.c
new file mode 100644
index 000000000000..630cc786d0a4
--- /dev/null
+++ b/drivers/media/radio/radio-gemtek-pci.c
@@ -0,0 +1,416 @@
1/*
2 ***************************************************************************
3 *
4 * radio-gemtek-pci.c - Gemtek PCI Radio driver
5 * (C) 2001 Vladimir Shebordaev <vshebordaev@mail.ru>
6 *
7 ***************************************************************************
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of
12 * the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public
20 * License along with this program; if not, write to the Free
21 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
22 * USA.
23 *
24 ***************************************************************************
25 *
26 * Gemtek Corp still silently refuses to release any specifications
27 * of their multimedia devices, so the protocol still has to be
28 * reverse engineered.
29 *
30 * The v4l code was inspired by Jonas Munsin's Gemtek serial line
31 * radio device driver.
32 *
33 * Please, let me know if this piece of code was useful :)
34 *
35 * TODO: multiple device support and portability were not tested
36 *
37 ***************************************************************************
38 */
39
40#include <linux/config.h>
41#include <linux/types.h>
42#include <linux/list.h>
43#include <linux/module.h>
44#include <linux/init.h>
45#include <linux/pci.h>
46#include <linux/videodev.h>
47#include <linux/errno.h>
48
49#include <asm/io.h>
50#include <asm/uaccess.h>
51
52#ifndef PCI_VENDOR_ID_GEMTEK
53#define PCI_VENDOR_ID_GEMTEK 0x5046
54#endif
55
56#ifndef PCI_DEVICE_ID_GEMTEK_PR103
57#define PCI_DEVICE_ID_GEMTEK_PR103 0x1001
58#endif
59
60#ifndef GEMTEK_PCI_RANGE_LOW
61#define GEMTEK_PCI_RANGE_LOW (87*16000)
62#endif
63
64#ifndef GEMTEK_PCI_RANGE_HIGH
65#define GEMTEK_PCI_RANGE_HIGH (108*16000)
66#endif
67
68#ifndef TRUE
69#define TRUE (1)
70#endif
71
72#ifndef FALSE
73#define FALSE (0)
74#endif
75
76struct gemtek_pci_card {
77 struct video_device *videodev;
78
79 u32 iobase;
80 u32 length;
81 u8 chiprev;
82 u16 model;
83
84 u32 current_frequency;
85 u8 mute;
86};
87
88static const char rcsid[] = "$Id: radio-gemtek-pci.c,v 1.1 2001/07/23 08:08:16 ted Exp ted $";
89
90static int nr_radio = -1;
91
92static inline u8 gemtek_pci_out( u16 value, u32 port )
93{
94 outw( value, port );
95
96 return (u8)value;
97}
98
99#define _b0( v ) *((u8 *)&v)
100static void __gemtek_pci_cmd( u16 value, u32 port, u8 *last_byte, int keep )
101{
102 register u8 byte = *last_byte;
103
104 if ( !value ) {
105 if ( !keep )
106 value = (u16)port;
107 byte &= 0xfd;
108 } else
109 byte |= 2;
110
111 _b0( value ) = byte;
112 outw( value, port );
113 byte |= 1;
114 _b0( value ) = byte;
115 outw( value, port );
116 byte &= 0xfe;
117 _b0( value ) = byte;
118 outw( value, port );
119
120 *last_byte = byte;
121}
122
123static inline void gemtek_pci_nil( u32 port, u8 *last_byte )
124{
125 __gemtek_pci_cmd( 0x00, port, last_byte, FALSE );
126}
127
128static inline void gemtek_pci_cmd( u16 cmd, u32 port, u8 *last_byte )
129{
130 __gemtek_pci_cmd( cmd, port, last_byte, TRUE );
131}
132
133static void gemtek_pci_setfrequency( struct gemtek_pci_card *card, unsigned long frequency )
134{
135 register int i;
136 register u32 value = frequency / 200 + 856;
137 register u16 mask = 0x8000;
138 u8 last_byte;
139 u32 port = card->iobase;
140
141 last_byte = gemtek_pci_out( 0x06, port );
142
143 i = 0;
144 do {
145 gemtek_pci_nil( port, &last_byte );
146 i++;
147 } while ( i < 9 );
148
149 i = 0;
150 do {
151 gemtek_pci_cmd( value & mask, port, &last_byte );
152 mask >>= 1;
153 i++;
154 } while ( i < 16 );
155
156 outw( 0x10, port );
157}
158
159
160static inline void gemtek_pci_mute( struct gemtek_pci_card *card )
161{
162 outb( 0x1f, card->iobase );
163 card->mute = TRUE;
164}
165
166static inline void gemtek_pci_unmute( struct gemtek_pci_card *card )
167{
168 if ( card->mute ) {
169 gemtek_pci_setfrequency( card, card->current_frequency );
170 card->mute = FALSE;
171 }
172}
173
174static inline unsigned int gemtek_pci_getsignal( struct gemtek_pci_card *card )
175{
176 return ( inb( card->iobase ) & 0x08 ) ? 0 : 1;
177}
178
179static int gemtek_pci_do_ioctl(struct inode *inode, struct file *file,
180 unsigned int cmd, void *arg)
181{
182 struct video_device *dev = video_devdata(file);
183 struct gemtek_pci_card *card = dev->priv;
184
185 switch ( cmd ) {
186 case VIDIOCGCAP:
187 {
188 struct video_capability *c = arg;
189
190 memset(c,0,sizeof(*c));
191 c->type = VID_TYPE_TUNER;
192 c->channels = 1;
193 c->audios = 1;
194 strcpy( c->name, "Gemtek PCI Radio" );
195 return 0;
196 }
197
198 case VIDIOCGTUNER:
199 {
200 struct video_tuner *t = arg;
201
202 if ( t->tuner )
203 return -EINVAL;
204
205 t->rangelow = GEMTEK_PCI_RANGE_LOW;
206 t->rangehigh = GEMTEK_PCI_RANGE_HIGH;
207 t->flags = VIDEO_TUNER_LOW;
208 t->mode = VIDEO_MODE_AUTO;
209 t->signal = 0xFFFF * gemtek_pci_getsignal( card );
210 strcpy( t->name, "FM" );
211 return 0;
212 }
213
214 case VIDIOCSTUNER:
215 {
216 struct video_tuner *t = arg;
217 if ( t->tuner )
218 return -EINVAL;
219 return 0;
220 }
221
222 case VIDIOCGFREQ:
223 {
224 unsigned long *freq = arg;
225 *freq = card->current_frequency;
226 return 0;
227 }
228 case VIDIOCSFREQ:
229 {
230 unsigned long *freq = arg;
231
232 if ( (*freq < GEMTEK_PCI_RANGE_LOW) ||
233 (*freq > GEMTEK_PCI_RANGE_HIGH) )
234 return -EINVAL;
235
236 gemtek_pci_setfrequency( card, *freq );
237 card->current_frequency = *freq;
238 card->mute = FALSE;
239
240 return 0;
241 }
242
243 case VIDIOCGAUDIO:
244 {
245 struct video_audio *a = arg;
246
247 memset( a, 0, sizeof( *a ) );
248 a->flags |= VIDEO_AUDIO_MUTABLE;
249 a->volume = 1;
250 a->step = 65535;
251 strcpy( a->name, "Radio" );
252 return 0;
253 }
254
255 case VIDIOCSAUDIO:
256 {
257 struct video_audio *a = arg;
258
259 if ( a->audio )
260 return -EINVAL;
261
262 if ( a->flags & VIDEO_AUDIO_MUTE )
263 gemtek_pci_mute( card );
264 else
265 gemtek_pci_unmute( card );
266 return 0;
267 }
268
269 default:
270 return -ENOIOCTLCMD;
271 }
272}
273
274static int gemtek_pci_ioctl(struct inode *inode, struct file *file,
275 unsigned int cmd, unsigned long arg)
276{
277 return video_usercopy(inode, file, cmd, arg, gemtek_pci_do_ioctl);
278}
279
280enum {
281 GEMTEK_PR103
282};
283
284static char *card_names[] __devinitdata = {
285 "GEMTEK_PR103"
286};
287
288static struct pci_device_id gemtek_pci_id[] =
289{
290 { PCI_VENDOR_ID_GEMTEK, PCI_DEVICE_ID_GEMTEK_PR103,
291 PCI_ANY_ID, PCI_ANY_ID, 0, 0, GEMTEK_PR103 },
292 { 0 }
293};
294
295MODULE_DEVICE_TABLE( pci, gemtek_pci_id );
296
297static int mx = 1;
298
299static struct file_operations gemtek_pci_fops = {
300 .owner = THIS_MODULE,
301 .open = video_exclusive_open,
302 .release = video_exclusive_release,
303 .ioctl = gemtek_pci_ioctl,
304 .llseek = no_llseek,
305};
306
307static struct video_device vdev_template = {
308 .owner = THIS_MODULE,
309 .name = "Gemtek PCI Radio",
310 .type = VID_TYPE_TUNER,
311 .hardware = VID_HARDWARE_GEMTEK,
312 .fops = &gemtek_pci_fops,
313};
314
315static int __devinit gemtek_pci_probe( struct pci_dev *pci_dev, const struct pci_device_id *pci_id )
316{
317 struct gemtek_pci_card *card;
318 struct video_device *devradio;
319
320 if ( (card = kmalloc( sizeof( struct gemtek_pci_card ), GFP_KERNEL )) == NULL ) {
321 printk( KERN_ERR "gemtek_pci: out of memory\n" );
322 return -ENOMEM;
323 }
324 memset( card, 0, sizeof( struct gemtek_pci_card ) );
325
326 if ( pci_enable_device( pci_dev ) )
327 goto err_pci;
328
329 card->iobase = pci_resource_start( pci_dev, 0 );
330 card->length = pci_resource_len( pci_dev, 0 );
331
332 if ( request_region( card->iobase, card->length, card_names[pci_id->driver_data] ) == NULL ) {
333 printk( KERN_ERR "gemtek_pci: i/o port already in use\n" );
334 goto err_pci;
335 }
336
337 pci_read_config_byte( pci_dev, PCI_REVISION_ID, &card->chiprev );
338 pci_read_config_word( pci_dev, PCI_SUBSYSTEM_ID, &card->model );
339
340 pci_set_drvdata( pci_dev, card );
341
342 if ( (devradio = kmalloc( sizeof( struct video_device ), GFP_KERNEL )) == NULL ) {
343 printk( KERN_ERR "gemtek_pci: out of memory\n" );
344 goto err_video;
345 }
346 *devradio = vdev_template;
347
348 if ( video_register_device( devradio, VFL_TYPE_RADIO , nr_radio) == -1 ) {
349 kfree( devradio );
350 goto err_video;
351 }
352
353 card->videodev = devradio;
354 devradio->priv = card;
355 gemtek_pci_mute( card );
356
357 printk( KERN_INFO "Gemtek PCI Radio (rev. %d) found at 0x%04x-0x%04x.\n",
358 card->chiprev, card->iobase, card->iobase + card->length - 1 );
359
360 return 0;
361
362err_video:
363 release_region( card->iobase, card->length );
364
365err_pci:
366 kfree( card );
367 return -ENODEV;
368}
369
370static void __devexit gemtek_pci_remove( struct pci_dev *pci_dev )
371{
372 struct gemtek_pci_card *card = pci_get_drvdata( pci_dev );
373
374 video_unregister_device( card->videodev );
375 kfree( card->videodev );
376
377 release_region( card->iobase, card->length );
378
379 if ( mx )
380 gemtek_pci_mute( card );
381
382 kfree( card );
383
384 pci_set_drvdata( pci_dev, NULL );
385}
386
387static struct pci_driver gemtek_pci_driver =
388{
389 .name = "gemtek_pci",
390 .id_table = gemtek_pci_id,
391 .probe = gemtek_pci_probe,
392 .remove = __devexit_p(gemtek_pci_remove),
393};
394
395static int __init gemtek_pci_init_module( void )
396{
397 return pci_module_init( &gemtek_pci_driver );
398}
399
400static void __exit gemtek_pci_cleanup_module( void )
401{
402 return pci_unregister_driver( &gemtek_pci_driver );
403}
404
405MODULE_AUTHOR( "Vladimir Shebordaev <vshebordaev@mail.ru>" );
406MODULE_DESCRIPTION( "The video4linux driver for the Gemtek PCI Radio Card" );
407MODULE_LICENSE("GPL");
408
409module_param(mx, bool, 0);
410MODULE_PARM_DESC( mx, "single digit: 1 - turn off the turner upon module exit (default), 0 - do not" );
411module_param(nr_radio, int, 0);
412MODULE_PARM_DESC( nr_radio, "video4linux device number to use");
413
414module_init( gemtek_pci_init_module );
415module_exit( gemtek_pci_cleanup_module );
416
diff --git a/drivers/media/radio/radio-gemtek.c b/drivers/media/radio/radio-gemtek.c
new file mode 100644
index 000000000000..202bfe6819b8
--- /dev/null
+++ b/drivers/media/radio/radio-gemtek.c
@@ -0,0 +1,304 @@
1/* GemTek radio card driver for Linux (C) 1998 Jonas Munsin <jmunsin@iki.fi>
2 *
3 * GemTek hasn't released any specs on the card, so the protocol had to
4 * be reverse engineered with dosemu.
5 *
6 * Besides the protocol changes, this is mostly a copy of:
7 *
8 * RadioTrack II driver for Linux radio support (C) 1998 Ben Pfaff
9 *
10 * Based on RadioTrack I/RadioReveal (C) 1997 M. Kirkwood
11 * Converted to new API by Alan Cox <Alan.Cox@linux.org>
12 * Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org>
13 *
14 * TODO: Allow for more than one of these foolish entities :-)
15 *
16 */
17
18#include <linux/module.h> /* Modules */
19#include <linux/init.h> /* Initdata */
20#include <linux/ioport.h> /* check_region, request_region */
21#include <linux/delay.h> /* udelay */
22#include <asm/io.h> /* outb, outb_p */
23#include <asm/uaccess.h> /* copy to/from user */
24#include <linux/videodev.h> /* kernel radio structs */
25#include <linux/config.h> /* CONFIG_RADIO_GEMTEK_PORT */
26#include <linux/spinlock.h>
27
28#ifndef CONFIG_RADIO_GEMTEK_PORT
29#define CONFIG_RADIO_GEMTEK_PORT -1
30#endif
31
32static int io = CONFIG_RADIO_GEMTEK_PORT;
33static int radio_nr = -1;
34static spinlock_t lock;
35
36struct gemtek_device
37{
38 int port;
39 unsigned long curfreq;
40 int muted;
41};
42
43
44/* local things */
45
46/* the correct way to mute the gemtek may be to write the last written
47 * frequency || 0x10, but just writing 0x10 once seems to do it as well
48 */
49static void gemtek_mute(struct gemtek_device *dev)
50{
51 if(dev->muted)
52 return;
53 spin_lock(&lock);
54 outb(0x10, io);
55 spin_unlock(&lock);
56 dev->muted = 1;
57}
58
59static void gemtek_unmute(struct gemtek_device *dev)
60{
61 if(dev->muted == 0)
62 return;
63 spin_lock(&lock);
64 outb(0x20, io);
65 spin_unlock(&lock);
66 dev->muted = 0;
67}
68
69static void zero(void)
70{
71 outb_p(0x04, io);
72 udelay(5);
73 outb_p(0x05, io);
74 udelay(5);
75}
76
77static void one(void)
78{
79 outb_p(0x06, io);
80 udelay(5);
81 outb_p(0x07, io);
82 udelay(5);
83}
84
85static int gemtek_setfreq(struct gemtek_device *dev, unsigned long freq)
86{
87 int i;
88
89/* freq = 78.25*((float)freq/16000.0 + 10.52); */
90
91 freq /= 16;
92 freq += 10520;
93 freq *= 7825;
94 freq /= 100000;
95
96 spin_lock(&lock);
97
98 /* 2 start bits */
99 outb_p(0x03, io);
100 udelay(5);
101 outb_p(0x07, io);
102 udelay(5);
103
104 /* 28 frequency bits (lsb first) */
105 for (i = 0; i < 14; i++)
106 if (freq & (1 << i))
107 one();
108 else
109 zero();
110 /* 36 unknown bits */
111 for (i = 0; i < 11; i++)
112 zero();
113 one();
114 for (i = 0; i < 4; i++)
115 zero();
116 one();
117 zero();
118
119 /* 2 end bits */
120 outb_p(0x03, io);
121 udelay(5);
122 outb_p(0x07, io);
123 udelay(5);
124
125 spin_unlock(&lock);
126
127 return 0;
128}
129
130static int gemtek_getsigstr(struct gemtek_device *dev)
131{
132 spin_lock(&lock);
133 inb(io);
134 udelay(5);
135 spin_unlock(&lock);
136 if (inb(io) & 8) /* bit set = no signal present */
137 return 0;
138 return 1; /* signal present */
139}
140
141static int gemtek_do_ioctl(struct inode *inode, struct file *file,
142 unsigned int cmd, void *arg)
143{
144 struct video_device *dev = video_devdata(file);
145 struct gemtek_device *rt=dev->priv;
146
147 switch(cmd)
148 {
149 case VIDIOCGCAP:
150 {
151 struct video_capability *v = arg;
152 memset(v,0,sizeof(*v));
153 v->type=VID_TYPE_TUNER;
154 v->channels=1;
155 v->audios=1;
156 strcpy(v->name, "GemTek");
157 return 0;
158 }
159 case VIDIOCGTUNER:
160 {
161 struct video_tuner *v = arg;
162 if(v->tuner) /* Only 1 tuner */
163 return -EINVAL;
164 v->rangelow=87*16000;
165 v->rangehigh=108*16000;
166 v->flags=VIDEO_TUNER_LOW;
167 v->mode=VIDEO_MODE_AUTO;
168 v->signal=0xFFFF*gemtek_getsigstr(rt);
169 strcpy(v->name, "FM");
170 return 0;
171 }
172 case VIDIOCSTUNER:
173 {
174 struct video_tuner *v = arg;
175 if(v->tuner!=0)
176 return -EINVAL;
177 /* Only 1 tuner so no setting needed ! */
178 return 0;
179 }
180 case VIDIOCGFREQ:
181 {
182 unsigned long *freq = arg;
183 *freq = rt->curfreq;
184 return 0;
185 }
186 case VIDIOCSFREQ:
187 {
188 unsigned long *freq = arg;
189 rt->curfreq = *freq;
190 /* needs to be called twice in order for getsigstr to work */
191 gemtek_setfreq(rt, rt->curfreq);
192 gemtek_setfreq(rt, rt->curfreq);
193 return 0;
194 }
195 case VIDIOCGAUDIO:
196 {
197 struct video_audio *v = arg;
198 memset(v,0, sizeof(*v));
199 v->flags|=VIDEO_AUDIO_MUTABLE;
200 v->volume=1;
201 v->step=65535;
202 strcpy(v->name, "Radio");
203 return 0;
204 }
205 case VIDIOCSAUDIO:
206 {
207 struct video_audio *v = arg;
208 if(v->audio)
209 return -EINVAL;
210
211 if(v->flags&VIDEO_AUDIO_MUTE)
212 gemtek_mute(rt);
213 else
214 gemtek_unmute(rt);
215
216 return 0;
217 }
218 default:
219 return -ENOIOCTLCMD;
220 }
221}
222
223static int gemtek_ioctl(struct inode *inode, struct file *file,
224 unsigned int cmd, unsigned long arg)
225{
226 return video_usercopy(inode, file, cmd, arg, gemtek_do_ioctl);
227}
228
229static struct gemtek_device gemtek_unit;
230
231static struct file_operations gemtek_fops = {
232 .owner = THIS_MODULE,
233 .open = video_exclusive_open,
234 .release = video_exclusive_release,
235 .ioctl = gemtek_ioctl,
236 .llseek = no_llseek,
237};
238
239static struct video_device gemtek_radio=
240{
241 .owner = THIS_MODULE,
242 .name = "GemTek radio",
243 .type = VID_TYPE_TUNER,
244 .hardware = VID_HARDWARE_GEMTEK,
245 .fops = &gemtek_fops,
246};
247
248static int __init gemtek_init(void)
249{
250 if(io==-1)
251 {
252 printk(KERN_ERR "You must set an I/O address with io=0x20c, io=0x30c, io=0x24c or io=0x34c (io=0x020c or io=0x248 for the combined sound/radiocard)\n");
253 return -EINVAL;
254 }
255
256 if (!request_region(io, 4, "gemtek"))
257 {
258 printk(KERN_ERR "gemtek: port 0x%x already in use\n", io);
259 return -EBUSY;
260 }
261
262 gemtek_radio.priv=&gemtek_unit;
263
264 if(video_register_device(&gemtek_radio, VFL_TYPE_RADIO, radio_nr)==-1)
265 {
266 release_region(io, 4);
267 return -EINVAL;
268 }
269 printk(KERN_INFO "GemTek Radio Card driver.\n");
270
271 spin_lock_init(&lock);
272
273 /* this is _maybe_ unnecessary */
274 outb(0x01, io);
275
276 /* mute card - prevents noisy bootups */
277 gemtek_unit.muted = 0;
278 gemtek_mute(&gemtek_unit);
279
280 return 0;
281}
282
283MODULE_AUTHOR("Jonas Munsin");
284MODULE_DESCRIPTION("A driver for the GemTek Radio Card");
285MODULE_LICENSE("GPL");
286
287module_param(io, int, 0);
288MODULE_PARM_DESC(io, "I/O address of the GemTek card (0x20c, 0x30c, 0x24c or 0x34c (0x20c or 0x248 have been reported to work for the combined sound/radiocard)).");
289module_param(radio_nr, int, 0);
290
291static void __exit gemtek_cleanup(void)
292{
293 video_unregister_device(&gemtek_radio);
294 release_region(io,4);
295}
296
297module_init(gemtek_init);
298module_exit(gemtek_cleanup);
299
300/*
301 Local variables:
302 compile-command: "gcc -c -DMODVERSIONS -D__KERNEL__ -DMODULE -O6 -Wall -Wstrict-prototypes -I /home/blp/tmp/linux-2.1.111-rtrack/include radio-rtrack2.c"
303 End:
304*/
diff --git a/drivers/media/radio/radio-maestro.c b/drivers/media/radio/radio-maestro.c
new file mode 100644
index 000000000000..e62147e4ed1b
--- /dev/null
+++ b/drivers/media/radio/radio-maestro.c
@@ -0,0 +1,332 @@
1/* Maestro PCI sound card radio driver for Linux support
2 * (c) 2000 A. Tlalka, atlka@pg.gda.pl
3 * Notes on the hardware
4 *
5 * + Frequency control is done digitally
6 * + No volume control - only mute/unmute - you have to use Aux line volume
7 * control on Maestro card to set the volume
8 * + Radio status (tuned/not_tuned and stereo/mono) is valid some time after
9 * frequency setting (>100ms) and only when the radio is unmuted.
10 * version 0.02
11 * + io port is automatically detected - only the first radio is used
12 * version 0.03
13 * + thread access locking additions
14 * version 0.04
15 * + code improvements
16 * + VIDEO_TUNER_LOW is permanent
17 */
18
19#include <linux/module.h>
20#include <linux/init.h>
21#include <linux/ioport.h>
22#include <linux/delay.h>
23#include <linux/sched.h>
24#include <asm/io.h>
25#include <asm/uaccess.h>
26#include <asm/semaphore.h>
27#include <linux/pci.h>
28#include <linux/videodev.h>
29
30#define DRIVER_VERSION "0.04"
31
32#define PCI_VENDOR_ESS 0x125D
33#define PCI_DEVICE_ID_ESS_ESS1968 0x1968 /* Maestro 2 */
34#define PCI_DEVICE_ID_ESS_ESS1978 0x1978 /* Maestro 2E */
35
36#define GPIO_DATA 0x60 /* port offset from ESS_IO_BASE */
37
38#define IO_MASK 4 /* mask register offset from GPIO_DATA
39 bits 1=unmask write to given bit */
40#define IO_DIR 8 /* direction register offset from GPIO_DATA
41 bits 0/1=read/write direction */
42
43#define GPIO6 0x0040 /* mask bits for GPIO lines */
44#define GPIO7 0x0080
45#define GPIO8 0x0100
46#define GPIO9 0x0200
47
48#define STR_DATA GPIO6 /* radio TEA5757 pins and GPIO bits */
49#define STR_CLK GPIO7
50#define STR_WREN GPIO8
51#define STR_MOST GPIO9
52
53#define FREQ_LO 50*16000
54#define FREQ_HI 150*16000
55
56#define FREQ_IF 171200 /* 10.7*16000 */
57#define FREQ_STEP 200 /* 12.5*16 */
58
59#define FREQ2BITS(x) ((((unsigned int)(x)+FREQ_IF+(FREQ_STEP<<1))\
60 /(FREQ_STEP<<2))<<2) /* (x==fmhz*16*1000) -> bits */
61
62#define BITS2FREQ(x) ((x) * FREQ_STEP - FREQ_IF)
63
64static int radio_nr = -1;
65module_param(radio_nr, int, 0);
66
67static int radio_ioctl(struct inode *inode, struct file *file,
68 unsigned int cmd, unsigned long arg);
69
70static struct file_operations maestro_fops = {
71 .owner = THIS_MODULE,
72 .open = video_exclusive_open,
73 .release = video_exclusive_release,
74 .ioctl = radio_ioctl,
75 .llseek = no_llseek,
76};
77
78static struct video_device maestro_radio=
79{
80 .owner = THIS_MODULE,
81 .name = "Maestro radio",
82 .type = VID_TYPE_TUNER,
83 .hardware = VID_HARDWARE_SF16MI,
84 .fops = &maestro_fops,
85};
86
87static struct radio_device
88{
89 __u16 io, /* base of Maestro card radio io (GPIO_DATA)*/
90 muted, /* VIDEO_AUDIO_MUTE */
91 stereo, /* VIDEO_TUNER_STEREO_ON */
92 tuned; /* signal strength (0 or 0xffff) */
93 struct semaphore lock;
94} radio_unit = {0, 0, 0, 0, };
95
96static __u32 radio_bits_get(struct radio_device *dev)
97{
98 register __u16 io=dev->io, l, rdata;
99 register __u32 data=0;
100 __u16 omask;
101 omask = inw(io + IO_MASK);
102 outw(~(STR_CLK | STR_WREN), io + IO_MASK);
103 outw(0, io);
104 udelay(16);
105
106 for (l=24;l--;) {
107 outw(STR_CLK, io); /* HI state */
108 udelay(2);
109 if(!l)
110 dev->tuned = inw(io) & STR_MOST ? 0 : 0xffff;
111 outw(0, io); /* LO state */
112 udelay(2);
113 data <<= 1; /* shift data */
114 rdata = inw(io);
115 if(!l)
116 dev->stereo = rdata & STR_MOST ?
117 0 : VIDEO_TUNER_STEREO_ON;
118 else
119 if(rdata & STR_DATA)
120 data++;
121 udelay(2);
122 }
123 if(dev->muted)
124 outw(STR_WREN, io);
125 udelay(4);
126 outw(omask, io + IO_MASK);
127 return data & 0x3ffe;
128}
129
130static void radio_bits_set(struct radio_device *dev, __u32 data)
131{
132 register __u16 io=dev->io, l, bits;
133 __u16 omask, odir;
134 omask = inw(io + IO_MASK);
135 odir = (inw(io + IO_DIR) & ~STR_DATA) | (STR_CLK | STR_WREN);
136 outw(odir | STR_DATA, io + IO_DIR);
137 outw(~(STR_DATA | STR_CLK | STR_WREN), io + IO_MASK);
138 udelay(16);
139 for (l=25;l;l--) {
140 bits = ((data >> 18) & STR_DATA) | STR_WREN ;
141 data <<= 1; /* shift data */
142 outw(bits, io); /* start strobe */
143 udelay(2);
144 outw(bits | STR_CLK, io); /* HI level */
145 udelay(2);
146 outw(bits, io); /* LO level */
147 udelay(4);
148 }
149 if(!dev->muted)
150 outw(0, io);
151 udelay(4);
152 outw(omask, io + IO_MASK);
153 outw(odir, io + IO_DIR);
154 msleep(125);
155}
156
157inline static int radio_function(struct inode *inode, struct file *file,
158 unsigned int cmd, void *arg)
159{
160 struct video_device *dev = video_devdata(file);
161 struct radio_device *card=dev->priv;
162
163 switch(cmd) {
164 case VIDIOCGCAP: {
165 struct video_capability *v = arg;
166 memset(v,0,sizeof(*v));
167 strcpy(v->name, "Maestro radio");
168 v->type=VID_TYPE_TUNER;
169 v->channels=v->audios=1;
170 return 0;
171 }
172 case VIDIOCGTUNER: {
173 struct video_tuner *v = arg;
174 if(v->tuner)
175 return -EINVAL;
176 (void)radio_bits_get(card);
177 v->flags = VIDEO_TUNER_LOW | card->stereo;
178 v->signal = card->tuned;
179 strcpy(v->name, "FM");
180 v->rangelow = FREQ_LO;
181 v->rangehigh = FREQ_HI;
182 v->mode = VIDEO_MODE_AUTO;
183 return 0;
184 }
185 case VIDIOCSTUNER: {
186 struct video_tuner *v = arg;
187 if(v->tuner!=0)
188 return -EINVAL;
189 return 0;
190 }
191 case VIDIOCGFREQ: {
192 unsigned long *freq = arg;
193 *freq = BITS2FREQ(radio_bits_get(card));
194 return 0;
195 }
196 case VIDIOCSFREQ: {
197 unsigned long *freq = arg;
198 if (*freq<FREQ_LO || *freq>FREQ_HI )
199 return -EINVAL;
200 radio_bits_set(card, FREQ2BITS(*freq));
201 return 0;
202 }
203 case VIDIOCGAUDIO: {
204 struct video_audio *v = arg;
205 memset(v,0,sizeof(*v));
206 strcpy(v->name, "Radio");
207 v->flags=VIDEO_AUDIO_MUTABLE | card->muted;
208 v->mode=VIDEO_SOUND_STEREO;
209 return 0;
210 }
211 case VIDIOCSAUDIO: {
212 struct video_audio *v = arg;
213 if(v->audio)
214 return -EINVAL;
215 {
216 register __u16 io=card->io;
217 register __u16 omask = inw(io + IO_MASK);
218 outw(~STR_WREN, io + IO_MASK);
219 outw((card->muted = v->flags & VIDEO_AUDIO_MUTE)
220 ? STR_WREN : 0, io);
221 udelay(4);
222 outw(omask, io + IO_MASK);
223 msleep(125);
224 return 0;
225 }
226 }
227 case VIDIOCGUNIT: {
228 struct video_unit *v = arg;
229 v->video=VIDEO_NO_UNIT;
230 v->vbi=VIDEO_NO_UNIT;
231 v->radio=dev->minor;
232 v->audio=0;
233 v->teletext=VIDEO_NO_UNIT;
234 return 0;
235 }
236 default: return -ENOIOCTLCMD;
237 }
238}
239
240static int radio_ioctl(struct inode *inode, struct file *file,
241 unsigned int cmd, unsigned long arg)
242{
243 struct video_device *dev = video_devdata(file);
244 struct radio_device *card=dev->priv;
245 int ret;
246
247 down(&card->lock);
248 ret = video_usercopy(inode, file, cmd, arg, radio_function);
249 up(&card->lock);
250 return ret;
251}
252
253static __u16 radio_install(struct pci_dev *pcidev);
254
255MODULE_AUTHOR("Adam Tlalka, atlka@pg.gda.pl");
256MODULE_DESCRIPTION("Radio driver for the Maestro PCI sound card radio.");
257MODULE_LICENSE("GPL");
258
259static void __exit maestro_radio_exit(void)
260{
261 video_unregister_device(&maestro_radio);
262}
263
264static int __init maestro_radio_init(void)
265{
266 register __u16 found=0;
267 struct pci_dev *pcidev = NULL;
268 while(!found && (pcidev = pci_find_device(PCI_VENDOR_ESS,
269 PCI_DEVICE_ID_ESS_ESS1968,
270 pcidev)))
271 found |= radio_install(pcidev);
272 while(!found && (pcidev = pci_find_device(PCI_VENDOR_ESS,
273 PCI_DEVICE_ID_ESS_ESS1978,
274 pcidev)))
275 found |= radio_install(pcidev);
276 if(!found) {
277 printk(KERN_INFO "radio-maestro: no devices found.\n");
278 return -ENODEV;
279 }
280 return 0;
281}
282
283module_init(maestro_radio_init);
284module_exit(maestro_radio_exit);
285
286inline static __u16 radio_power_on(struct radio_device *dev)
287{
288 register __u16 io=dev->io;
289 register __u32 ofreq;
290 __u16 omask, odir;
291 omask = inw(io + IO_MASK);
292 odir = (inw(io + IO_DIR) & ~STR_DATA) | (STR_CLK | STR_WREN);
293 outw(odir & ~STR_WREN, io + IO_DIR);
294 dev->muted = inw(io) & STR_WREN ? 0 : VIDEO_AUDIO_MUTE;
295 outw(odir, io + IO_DIR);
296 outw(~(STR_WREN | STR_CLK), io + IO_MASK);
297 outw(dev->muted ? 0 : STR_WREN, io);
298 udelay(16);
299 outw(omask, io + IO_MASK);
300 ofreq = radio_bits_get(dev);
301 if((ofreq<FREQ2BITS(FREQ_LO)) || (ofreq>FREQ2BITS(FREQ_HI)))
302 ofreq = FREQ2BITS(FREQ_LO);
303 radio_bits_set(dev, ofreq);
304 return (ofreq == radio_bits_get(dev));
305}
306
307static __u16 radio_install(struct pci_dev *pcidev)
308{
309 if(((pcidev->class >> 8) & 0xffff) != PCI_CLASS_MULTIMEDIA_AUDIO)
310 return 0;
311
312 radio_unit.io = pcidev->resource[0].start + GPIO_DATA;
313 maestro_radio.priv = &radio_unit;
314 init_MUTEX(&radio_unit.lock);
315
316 if(radio_power_on(&radio_unit)) {
317 if(video_register_device(&maestro_radio, VFL_TYPE_RADIO, radio_nr)==-1) {
318 printk("radio-maestro: can't register device!");
319 return 0;
320 }
321 printk(KERN_INFO "radio-maestro: version "
322 DRIVER_VERSION
323 " time "
324 __TIME__ " "
325 __DATE__
326 "\n");
327 printk(KERN_INFO "radio-maestro: radio chip initialized\n");
328 return 1;
329 } else
330 return 0;
331}
332
diff --git a/drivers/media/radio/radio-maxiradio.c b/drivers/media/radio/radio-maxiradio.c
new file mode 100644
index 000000000000..5b748a48ce72
--- /dev/null
+++ b/drivers/media/radio/radio-maxiradio.c
@@ -0,0 +1,349 @@
1/*
2 * Guillemot Maxi Radio FM 2000 PCI radio card driver for Linux
3 * (C) 2001 Dimitromanolakis Apostolos <apdim@grecian.net>
4 *
5 * Based in the radio Maestro PCI driver. Actually it uses the same chip
6 * for radio but different pci controller.
7 *
8 * I didn't have any specs I reversed engineered the protocol from
9 * the windows driver (radio.dll).
10 *
11 * The card uses the TEA5757 chip that includes a search function but it
12 * is useless as I haven't found any way to read back the frequency. If
13 * anybody does please mail me.
14 *
15 * For the pdf file see:
16 * http://www.semiconductors.philips.com/pip/TEA5757H/V1
17 *
18 *
19 * CHANGES:
20 * 0.75b
21 * - better pci interface thanks to Francois Romieu <romieu@cogenit.fr>
22 *
23 * 0.75
24 * - tiding up
25 * - removed support for multiple devices as it didn't work anyway
26 *
27 * BUGS:
28 * - card unmutes if you change frequency
29 *
30 */
31
32
33#include <linux/module.h>
34#include <linux/init.h>
35#include <linux/ioport.h>
36#include <linux/delay.h>
37#include <linux/sched.h>
38#include <asm/io.h>
39#include <asm/uaccess.h>
40#include <asm/semaphore.h>
41#include <linux/pci.h>
42#include <linux/videodev.h>
43
44/* version 0.75 Sun Feb 4 22:51:27 EET 2001 */
45#define DRIVER_VERSION "0.75"
46
47#ifndef PCI_VENDOR_ID_GUILLEMOT
48#define PCI_VENDOR_ID_GUILLEMOT 0x5046
49#endif
50
51#ifndef PCI_DEVICE_ID_GUILLEMOT
52#define PCI_DEVICE_ID_GUILLEMOT_MAXIRADIO 0x1001
53#endif
54
55
56/* TEA5757 pin mappings */
57static const int clk = 1, data = 2, wren = 4, mo_st = 8, power = 16 ;
58
59static int radio_nr = -1;
60module_param(radio_nr, int, 0);
61
62
63#define FREQ_LO 50*16000
64#define FREQ_HI 150*16000
65
66#define FREQ_IF 171200 /* 10.7*16000 */
67#define FREQ_STEP 200 /* 12.5*16 */
68
69#define FREQ2BITS(x) ((( (unsigned int)(x)+FREQ_IF+(FREQ_STEP<<1))\
70 /(FREQ_STEP<<2))<<2) /* (x==fmhz*16*1000) -> bits */
71
72#define BITS2FREQ(x) ((x) * FREQ_STEP - FREQ_IF)
73
74
75static int radio_ioctl(struct inode *inode, struct file *file,
76 unsigned int cmd, unsigned long arg);
77
78static struct file_operations maxiradio_fops = {
79 .owner = THIS_MODULE,
80 .open = video_exclusive_open,
81 .release = video_exclusive_release,
82 .ioctl = radio_ioctl,
83 .llseek = no_llseek,
84};
85static struct video_device maxiradio_radio =
86{
87 .owner = THIS_MODULE,
88 .name = "Maxi Radio FM2000 radio",
89 .type = VID_TYPE_TUNER,
90 .hardware = VID_HARDWARE_SF16MI,
91 .fops = &maxiradio_fops,
92};
93
94static struct radio_device
95{
96 __u16 io, /* base of radio io */
97 muted, /* VIDEO_AUDIO_MUTE */
98 stereo, /* VIDEO_TUNER_STEREO_ON */
99 tuned; /* signal strength (0 or 0xffff) */
100
101 unsigned long freq;
102
103 struct semaphore lock;
104} radio_unit = {0, 0, 0, 0, };
105
106
107static void outbit(unsigned long bit, __u16 io)
108{
109 if(bit != 0)
110 {
111 outb( power|wren|data ,io); udelay(4);
112 outb( power|wren|data|clk ,io); udelay(4);
113 outb( power|wren|data ,io); udelay(4);
114 }
115 else
116 {
117 outb( power|wren ,io); udelay(4);
118 outb( power|wren|clk ,io); udelay(4);
119 outb( power|wren ,io); udelay(4);
120 }
121}
122
123static void turn_power(__u16 io, int p)
124{
125 if(p != 0) outb(power, io); else outb(0,io);
126}
127
128
129static void set_freq(__u16 io, __u32 data)
130{
131 unsigned long int si;
132 int bl;
133
134 /* TEA5757 shift register bits (see pdf) */
135
136 outbit(0,io); // 24 search
137 outbit(1,io); // 23 search up/down
138
139 outbit(0,io); // 22 stereo/mono
140
141 outbit(0,io); // 21 band
142 outbit(0,io); // 20 band (only 00=FM works I think)
143
144 outbit(0,io); // 19 port ?
145 outbit(0,io); // 18 port ?
146
147 outbit(0,io); // 17 search level
148 outbit(0,io); // 16 search level
149
150 si = 0x8000;
151 for(bl = 1; bl <= 16 ; bl++) { outbit(data & si,io); si >>=1; }
152
153 outb(power,io);
154}
155
156static int get_stereo(__u16 io)
157{
158 outb(power,io); udelay(4);
159 return !(inb(io) & mo_st);
160}
161
162static int get_tune(__u16 io)
163{
164 outb(power+clk,io); udelay(4);
165 return !(inb(io) & mo_st);
166}
167
168
169inline static int radio_function(struct inode *inode, struct file *file,
170 unsigned int cmd, void *arg)
171{
172 struct video_device *dev = video_devdata(file);
173 struct radio_device *card=dev->priv;
174
175 switch(cmd) {
176 case VIDIOCGCAP: {
177 struct video_capability *v = arg;
178
179 memset(v,0,sizeof(*v));
180 strcpy(v->name, "Maxi Radio FM2000 radio");
181 v->type=VID_TYPE_TUNER;
182 v->channels=v->audios=1;
183 return 0;
184 }
185 case VIDIOCGTUNER: {
186 struct video_tuner *v = arg;
187
188 if(v->tuner)
189 return -EINVAL;
190
191 card->stereo = 0xffff * get_stereo(card->io);
192 card->tuned = 0xffff * get_tune(card->io);
193
194 v->flags = VIDEO_TUNER_LOW | card->stereo;
195 v->signal = card->tuned;
196
197 strcpy(v->name, "FM");
198
199 v->rangelow = FREQ_LO;
200 v->rangehigh = FREQ_HI;
201 v->mode = VIDEO_MODE_AUTO;
202
203 return 0;
204 }
205 case VIDIOCSTUNER: {
206 struct video_tuner *v = arg;
207 if(v->tuner!=0)
208 return -EINVAL;
209 return 0;
210 }
211 case VIDIOCGFREQ: {
212 unsigned long *freq = arg;
213
214 *freq = card->freq;
215 return 0;
216 }
217 case VIDIOCSFREQ: {
218 unsigned long *freq = arg;
219
220 if (*freq < FREQ_LO || *freq > FREQ_HI)
221 return -EINVAL;
222 card->freq = *freq;
223 set_freq(card->io, FREQ2BITS(card->freq));
224 msleep(125);
225 return 0;
226 }
227 case VIDIOCGAUDIO: {
228 struct video_audio *v = arg;
229 memset(v,0,sizeof(*v));
230 strcpy(v->name, "Radio");
231 v->flags=VIDEO_AUDIO_MUTABLE | card->muted;
232 v->mode=VIDEO_SOUND_STEREO;
233 return 0;
234 }
235
236 case VIDIOCSAUDIO: {
237 struct video_audio *v = arg;
238
239 if(v->audio)
240 return -EINVAL;
241 card->muted = v->flags & VIDEO_AUDIO_MUTE;
242 if(card->muted)
243 turn_power(card->io, 0);
244 else
245 set_freq(card->io, FREQ2BITS(card->freq));
246 return 0;
247 }
248 case VIDIOCGUNIT: {
249 struct video_unit *v = arg;
250
251 v->video=VIDEO_NO_UNIT;
252 v->vbi=VIDEO_NO_UNIT;
253 v->radio=dev->minor;
254 v->audio=0;
255 v->teletext=VIDEO_NO_UNIT;
256 return 0;
257 }
258 default: return -ENOIOCTLCMD;
259 }
260}
261
262static int radio_ioctl(struct inode *inode, struct file *file,
263 unsigned int cmd, unsigned long arg)
264{
265 struct video_device *dev = video_devdata(file);
266 struct radio_device *card=dev->priv;
267 int ret;
268
269 down(&card->lock);
270 ret = video_usercopy(inode, file, cmd, arg, radio_function);
271 up(&card->lock);
272 return ret;
273}
274
275MODULE_AUTHOR("Dimitromanolakis Apostolos, apdim@grecian.net");
276MODULE_DESCRIPTION("Radio driver for the Guillemot Maxi Radio FM2000 radio.");
277MODULE_LICENSE("GPL");
278
279
280static int __devinit maxiradio_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
281{
282 if(!request_region(pci_resource_start(pdev, 0),
283 pci_resource_len(pdev, 0), "Maxi Radio FM 2000")) {
284 printk(KERN_ERR "radio-maxiradio: can't reserve I/O ports\n");
285 goto err_out;
286 }
287
288 if (pci_enable_device(pdev))
289 goto err_out_free_region;
290
291 radio_unit.io = pci_resource_start(pdev, 0);
292 init_MUTEX(&radio_unit.lock);
293 maxiradio_radio.priv = &radio_unit;
294
295 if(video_register_device(&maxiradio_radio, VFL_TYPE_RADIO, radio_nr)==-1) {
296 printk("radio-maxiradio: can't register device!");
297 goto err_out_free_region;
298 }
299
300 printk(KERN_INFO "radio-maxiradio: version "
301 DRIVER_VERSION
302 " time "
303 __TIME__ " "
304 __DATE__
305 "\n");
306
307 printk(KERN_INFO "radio-maxiradio: found Guillemot MAXI Radio device (io = 0x%x)\n",
308 radio_unit.io);
309 return 0;
310
311err_out_free_region:
312 release_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
313err_out:
314 return -ENODEV;
315}
316
317static void __devexit maxiradio_remove_one(struct pci_dev *pdev)
318{
319 video_unregister_device(&maxiradio_radio);
320 release_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
321}
322
323static struct pci_device_id maxiradio_pci_tbl[] = {
324 { PCI_VENDOR_ID_GUILLEMOT, PCI_DEVICE_ID_GUILLEMOT_MAXIRADIO,
325 PCI_ANY_ID, PCI_ANY_ID, },
326 { 0,}
327};
328
329MODULE_DEVICE_TABLE(pci, maxiradio_pci_tbl);
330
331static struct pci_driver maxiradio_driver = {
332 .name = "radio-maxiradio",
333 .id_table = maxiradio_pci_tbl,
334 .probe = maxiradio_init_one,
335 .remove = __devexit_p(maxiradio_remove_one),
336};
337
338static int __init maxiradio_radio_init(void)
339{
340 return pci_module_init(&maxiradio_driver);
341}
342
343static void __exit maxiradio_radio_exit(void)
344{
345 pci_unregister_driver(&maxiradio_driver);
346}
347
348module_init(maxiradio_radio_init);
349module_exit(maxiradio_radio_exit);
diff --git a/drivers/media/radio/radio-rtrack2.c b/drivers/media/radio/radio-rtrack2.c
new file mode 100644
index 000000000000..c00245d4d249
--- /dev/null
+++ b/drivers/media/radio/radio-rtrack2.c
@@ -0,0 +1,266 @@
1/* RadioTrack II driver for Linux radio support (C) 1998 Ben Pfaff
2 *
3 * Based on RadioTrack I/RadioReveal (C) 1997 M. Kirkwood
4 * Converted to new API by Alan Cox <Alan.Cox@linux.org>
5 * Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org>
6 *
7 * TODO: Allow for more than one of these foolish entities :-)
8 *
9 */
10
11#include <linux/module.h> /* Modules */
12#include <linux/init.h> /* Initdata */
13#include <linux/ioport.h> /* check_region, request_region */
14#include <linux/delay.h> /* udelay */
15#include <asm/io.h> /* outb, outb_p */
16#include <asm/uaccess.h> /* copy to/from user */
17#include <linux/videodev.h> /* kernel radio structs */
18#include <linux/config.h> /* CONFIG_RADIO_RTRACK2_PORT */
19#include <linux/spinlock.h>
20
21#ifndef CONFIG_RADIO_RTRACK2_PORT
22#define CONFIG_RADIO_RTRACK2_PORT -1
23#endif
24
25static int io = CONFIG_RADIO_RTRACK2_PORT;
26static int radio_nr = -1;
27static spinlock_t lock;
28
29struct rt_device
30{
31 int port;
32 unsigned long curfreq;
33 int muted;
34};
35
36
37/* local things */
38
39static void rt_mute(struct rt_device *dev)
40{
41 if(dev->muted)
42 return;
43 spin_lock(&lock);
44 outb(1, io);
45 spin_unlock(&lock);
46 dev->muted = 1;
47}
48
49static void rt_unmute(struct rt_device *dev)
50{
51 if(dev->muted == 0)
52 return;
53 spin_lock(&lock);
54 outb(0, io);
55 spin_unlock(&lock);
56 dev->muted = 0;
57}
58
59static void zero(void)
60{
61 outb_p(1, io);
62 outb_p(3, io);
63 outb_p(1, io);
64}
65
66static void one(void)
67{
68 outb_p(5, io);
69 outb_p(7, io);
70 outb_p(5, io);
71}
72
73static int rt_setfreq(struct rt_device *dev, unsigned long freq)
74{
75 int i;
76
77 freq = freq / 200 + 856;
78
79 spin_lock(&lock);
80
81 outb_p(0xc8, io);
82 outb_p(0xc9, io);
83 outb_p(0xc9, io);
84
85 for (i = 0; i < 10; i++)
86 zero ();
87
88 for (i = 14; i >= 0; i--)
89 if (freq & (1 << i))
90 one ();
91 else
92 zero ();
93
94 outb_p(0xc8, io);
95 if (!dev->muted)
96 outb_p(0, io);
97
98 spin_unlock(&lock);
99 return 0;
100}
101
102static int rt_getsigstr(struct rt_device *dev)
103{
104 if (inb(io) & 2) /* bit set = no signal present */
105 return 0;
106 return 1; /* signal present */
107}
108
109static int rt_do_ioctl(struct inode *inode, struct file *file,
110 unsigned int cmd, void *arg)
111{
112 struct video_device *dev = video_devdata(file);
113 struct rt_device *rt=dev->priv;
114
115 switch(cmd)
116 {
117 case VIDIOCGCAP:
118 {
119 struct video_capability *v = arg;
120 memset(v,0,sizeof(*v));
121 v->type=VID_TYPE_TUNER;
122 v->channels=1;
123 v->audios=1;
124 strcpy(v->name, "RadioTrack II");
125 return 0;
126 }
127 case VIDIOCGTUNER:
128 {
129 struct video_tuner *v = arg;
130 if(v->tuner) /* Only 1 tuner */
131 return -EINVAL;
132 v->rangelow=88*16000;
133 v->rangehigh=108*16000;
134 v->flags=VIDEO_TUNER_LOW;
135 v->mode=VIDEO_MODE_AUTO;
136 v->signal=0xFFFF*rt_getsigstr(rt);
137 strcpy(v->name, "FM");
138 return 0;
139 }
140 case VIDIOCSTUNER:
141 {
142 struct video_tuner *v = arg;
143 if(v->tuner!=0)
144 return -EINVAL;
145 /* Only 1 tuner so no setting needed ! */
146 return 0;
147 }
148 case VIDIOCGFREQ:
149 {
150 unsigned long *freq = arg;
151 *freq = rt->curfreq;
152 return 0;
153 }
154 case VIDIOCSFREQ:
155 {
156 unsigned long *freq = arg;
157 rt->curfreq = *freq;
158 rt_setfreq(rt, rt->curfreq);
159 return 0;
160 }
161 case VIDIOCGAUDIO:
162 {
163 struct video_audio *v = arg;
164 memset(v,0, sizeof(*v));
165 v->flags|=VIDEO_AUDIO_MUTABLE;
166 v->volume=1;
167 v->step=65535;
168 strcpy(v->name, "Radio");
169 return 0;
170 }
171 case VIDIOCSAUDIO:
172 {
173 struct video_audio *v = arg;
174 if(v->audio)
175 return -EINVAL;
176
177 if(v->flags&VIDEO_AUDIO_MUTE)
178 rt_mute(rt);
179 else
180 rt_unmute(rt);
181
182 return 0;
183 }
184 default:
185 return -ENOIOCTLCMD;
186 }
187}
188
189static int rt_ioctl(struct inode *inode, struct file *file,
190 unsigned int cmd, unsigned long arg)
191{
192 return video_usercopy(inode, file, cmd, arg, rt_do_ioctl);
193}
194
195static struct rt_device rtrack2_unit;
196
197static struct file_operations rtrack2_fops = {
198 .owner = THIS_MODULE,
199 .open = video_exclusive_open,
200 .release = video_exclusive_release,
201 .ioctl = rt_ioctl,
202 .llseek = no_llseek,
203};
204
205static struct video_device rtrack2_radio=
206{
207 .owner = THIS_MODULE,
208 .name = "RadioTrack II radio",
209 .type = VID_TYPE_TUNER,
210 .hardware = VID_HARDWARE_RTRACK2,
211 .fops = &rtrack2_fops,
212};
213
214static int __init rtrack2_init(void)
215{
216 if(io==-1)
217 {
218 printk(KERN_ERR "You must set an I/O address with io=0x20c or io=0x30c\n");
219 return -EINVAL;
220 }
221 if (!request_region(io, 4, "rtrack2"))
222 {
223 printk(KERN_ERR "rtrack2: port 0x%x already in use\n", io);
224 return -EBUSY;
225 }
226
227 rtrack2_radio.priv=&rtrack2_unit;
228
229 spin_lock_init(&lock);
230 if(video_register_device(&rtrack2_radio, VFL_TYPE_RADIO, radio_nr)==-1)
231 {
232 release_region(io, 4);
233 return -EINVAL;
234 }
235
236 printk(KERN_INFO "AIMSlab Radiotrack II card driver.\n");
237
238 /* mute card - prevents noisy bootups */
239 outb(1, io);
240 rtrack2_unit.muted = 1;
241
242 return 0;
243}
244
245MODULE_AUTHOR("Ben Pfaff");
246MODULE_DESCRIPTION("A driver for the RadioTrack II radio card.");
247MODULE_LICENSE("GPL");
248
249module_param(io, int, 0);
250MODULE_PARM_DESC(io, "I/O address of the RadioTrack card (0x20c or 0x30c)");
251module_param(radio_nr, int, 0);
252
253static void __exit rtrack2_cleanup_module(void)
254{
255 video_unregister_device(&rtrack2_radio);
256 release_region(io,4);
257}
258
259module_init(rtrack2_init);
260module_exit(rtrack2_cleanup_module);
261
262/*
263 Local variables:
264 compile-command: "mmake"
265 End:
266*/
diff --git a/drivers/media/radio/radio-sf16fmi.c b/drivers/media/radio/radio-sf16fmi.c
new file mode 100644
index 000000000000..3a464a09221f
--- /dev/null
+++ b/drivers/media/radio/radio-sf16fmi.c
@@ -0,0 +1,328 @@
1/* SF16FMI radio driver for Linux radio support
2 * heavily based on rtrack driver...
3 * (c) 1997 M. Kirkwood
4 * (c) 1998 Petr Vandrovec, vandrove@vc.cvut.cz
5 *
6 * Fitted to new interface by Alan Cox <alan.cox@linux.org>
7 * Made working and cleaned up functions <mikael.hedin@irf.se>
8 * Support for ISAPnP by Ladislav Michl <ladis@psi.cz>
9 *
10 * Notes on the hardware
11 *
12 * Frequency control is done digitally -- ie out(port,encodefreq(95.8));
13 * No volume control - only mute/unmute - you have to use line volume
14 * control on SB-part of SF16FMI
15 *
16 */
17
18#include <linux/kernel.h> /* __setup */
19#include <linux/module.h> /* Modules */
20#include <linux/init.h> /* Initdata */
21#include <linux/ioport.h> /* check_region, request_region */
22#include <linux/delay.h> /* udelay */
23#include <linux/videodev.h> /* kernel radio structs */
24#include <linux/isapnp.h>
25#include <asm/io.h> /* outb, outb_p */
26#include <asm/uaccess.h> /* copy to/from user */
27#include <asm/semaphore.h>
28
29struct fmi_device
30{
31 int port;
32 int curvol; /* 1 or 0 */
33 unsigned long curfreq; /* freq in kHz */
34 __u32 flags;
35};
36
37static int io = -1;
38static int radio_nr = -1;
39static struct pnp_dev *dev = NULL;
40static struct semaphore lock;
41
42/* freq is in 1/16 kHz to internal number, hw precision is 50 kHz */
43/* It is only useful to give freq in intervall of 800 (=0.05Mhz),
44 * other bits will be truncated, e.g 92.7400016 -> 92.7, but
45 * 92.7400017 -> 92.75
46 */
47#define RSF16_ENCODE(x) ((x)/800+214)
48#define RSF16_MINFREQ 87*16000
49#define RSF16_MAXFREQ 108*16000
50
51static void outbits(int bits, unsigned int data, int port)
52{
53 while(bits--) {
54 if(data & 1) {
55 outb(5, port);
56 udelay(6);
57 outb(7, port);
58 udelay(6);
59 } else {
60 outb(1, port);
61 udelay(6);
62 outb(3, port);
63 udelay(6);
64 }
65 data>>=1;
66 }
67}
68
69static inline void fmi_mute(int port)
70{
71 down(&lock);
72 outb(0x00, port);
73 up(&lock);
74}
75
76static inline void fmi_unmute(int port)
77{
78 down(&lock);
79 outb(0x08, port);
80 up(&lock);
81}
82
83static inline int fmi_setfreq(struct fmi_device *dev)
84{
85 int myport = dev->port;
86 unsigned long freq = dev->curfreq;
87
88 down(&lock);
89
90 outbits(16, RSF16_ENCODE(freq), myport);
91 outbits(8, 0xC0, myport);
92 msleep(143); /* was schedule_timeout(HZ/7) */
93 up(&lock);
94 if (dev->curvol) fmi_unmute(myport);
95 return 0;
96}
97
98static inline int fmi_getsigstr(struct fmi_device *dev)
99{
100 int val;
101 int res;
102 int myport = dev->port;
103
104
105 down(&lock);
106 val = dev->curvol ? 0x08 : 0x00; /* unmute/mute */
107 outb(val, myport);
108 outb(val | 0x10, myport);
109 msleep(143); /* was schedule_timeout(HZ/7) */
110 res = (int)inb(myport+1);
111 outb(val, myport);
112
113 up(&lock);
114 return (res & 2) ? 0 : 0xFFFF;
115}
116
117static int fmi_do_ioctl(struct inode *inode, struct file *file,
118 unsigned int cmd, void *arg)
119{
120 struct video_device *dev = video_devdata(file);
121 struct fmi_device *fmi=dev->priv;
122
123 switch(cmd)
124 {
125 case VIDIOCGCAP:
126 {
127 struct video_capability *v = arg;
128 memset(v,0,sizeof(*v));
129 strcpy(v->name, "SF16-FMx radio");
130 v->type=VID_TYPE_TUNER;
131 v->channels=1;
132 v->audios=1;
133 return 0;
134 }
135 case VIDIOCGTUNER:
136 {
137 struct video_tuner *v = arg;
138 int mult;
139
140 if(v->tuner) /* Only 1 tuner */
141 return -EINVAL;
142 strcpy(v->name, "FM");
143 mult = (fmi->flags & VIDEO_TUNER_LOW) ? 1 : 1000;
144 v->rangelow = RSF16_MINFREQ/mult;
145 v->rangehigh = RSF16_MAXFREQ/mult;
146 v->flags=fmi->flags;
147 v->mode=VIDEO_MODE_AUTO;
148 v->signal = fmi_getsigstr(fmi);
149 return 0;
150 }
151 case VIDIOCSTUNER:
152 {
153 struct video_tuner *v = arg;
154 if(v->tuner!=0)
155 return -EINVAL;
156 fmi->flags = v->flags & VIDEO_TUNER_LOW;
157 /* Only 1 tuner so no setting needed ! */
158 return 0;
159 }
160 case VIDIOCGFREQ:
161 {
162 unsigned long *freq = arg;
163 *freq = fmi->curfreq;
164 if (!(fmi->flags & VIDEO_TUNER_LOW))
165 *freq /= 1000;
166 return 0;
167 }
168 case VIDIOCSFREQ:
169 {
170 unsigned long *freq = arg;
171 if (!(fmi->flags & VIDEO_TUNER_LOW))
172 *freq *= 1000;
173 if (*freq < RSF16_MINFREQ || *freq > RSF16_MAXFREQ )
174 return -EINVAL;
175 /*rounding in steps of 800 to match th freq
176 that will be used */
177 fmi->curfreq = (*freq/800)*800;
178 fmi_setfreq(fmi);
179 return 0;
180 }
181 case VIDIOCGAUDIO:
182 {
183 struct video_audio *v = arg;
184 memset(v,0,sizeof(*v));
185 v->flags=( (!fmi->curvol)*VIDEO_AUDIO_MUTE | VIDEO_AUDIO_MUTABLE);
186 strcpy(v->name, "Radio");
187 v->mode=VIDEO_SOUND_STEREO;
188 return 0;
189 }
190 case VIDIOCSAUDIO:
191 {
192 struct video_audio *v = arg;
193 if(v->audio)
194 return -EINVAL;
195 fmi->curvol= v->flags&VIDEO_AUDIO_MUTE ? 0 : 1;
196 fmi->curvol ?
197 fmi_unmute(fmi->port) : fmi_mute(fmi->port);
198 return 0;
199 }
200 case VIDIOCGUNIT:
201 {
202 struct video_unit *v = arg;
203 v->video=VIDEO_NO_UNIT;
204 v->vbi=VIDEO_NO_UNIT;
205 v->radio=dev->minor;
206 v->audio=0; /* How do we find out this??? */
207 v->teletext=VIDEO_NO_UNIT;
208 return 0;
209 }
210 default:
211 return -ENOIOCTLCMD;
212 }
213}
214
215static int fmi_ioctl(struct inode *inode, struct file *file,
216 unsigned int cmd, unsigned long arg)
217{
218 return video_usercopy(inode, file, cmd, arg, fmi_do_ioctl);
219}
220
221static struct fmi_device fmi_unit;
222
223static struct file_operations fmi_fops = {
224 .owner = THIS_MODULE,
225 .open = video_exclusive_open,
226 .release = video_exclusive_release,
227 .ioctl = fmi_ioctl,
228 .llseek = no_llseek,
229};
230
231static struct video_device fmi_radio=
232{
233 .owner = THIS_MODULE,
234 .name = "SF16FMx radio",
235 .type = VID_TYPE_TUNER,
236 .hardware = VID_HARDWARE_SF16MI,
237 .fops = &fmi_fops,
238};
239
240/* ladis: this is my card. does any other types exist? */
241static struct isapnp_device_id id_table[] __devinitdata = {
242 { ISAPNP_ANY_ID, ISAPNP_ANY_ID,
243 ISAPNP_VENDOR('M','F','R'), ISAPNP_FUNCTION(0xad10), 0},
244 { ISAPNP_CARD_END, },
245};
246
247MODULE_DEVICE_TABLE(isapnp, id_table);
248
249static int isapnp_fmi_probe(void)
250{
251 int i = 0;
252
253 while (id_table[i].card_vendor != 0 && dev == NULL) {
254 dev = pnp_find_dev(NULL, id_table[i].vendor,
255 id_table[i].function, NULL);
256 i++;
257 }
258
259 if (!dev)
260 return -ENODEV;
261 if (pnp_device_attach(dev) < 0)
262 return -EAGAIN;
263 if (pnp_activate_dev(dev) < 0) {
264 printk ("radio-sf16fmi: PnP configure failed (out of resources?)\n");
265 pnp_device_detach(dev);
266 return -ENOMEM;
267 }
268 if (!pnp_port_valid(dev, 0)) {
269 pnp_device_detach(dev);
270 return -ENODEV;
271 }
272
273 i = pnp_port_start(dev, 0);
274 printk ("radio-sf16fmi: PnP reports card at %#x\n", i);
275
276 return i;
277}
278
279static int __init fmi_init(void)
280{
281 if (io < 0)
282 io = isapnp_fmi_probe();
283 if (io < 0) {
284 printk(KERN_ERR "radio-sf16fmi: No PnP card found.\n");
285 return io;
286 }
287 if (!request_region(io, 2, "radio-sf16fmi")) {
288 printk(KERN_ERR "radio-sf16fmi: port 0x%x already in use\n", io);
289 return -EBUSY;
290 }
291
292 fmi_unit.port = io;
293 fmi_unit.curvol = 0;
294 fmi_unit.curfreq = 0;
295 fmi_unit.flags = VIDEO_TUNER_LOW;
296 fmi_radio.priv = &fmi_unit;
297
298 init_MUTEX(&lock);
299
300 if (video_register_device(&fmi_radio, VFL_TYPE_RADIO, radio_nr) == -1) {
301 release_region(io, 2);
302 return -EINVAL;
303 }
304
305 printk(KERN_INFO "SF16FMx radio card driver at 0x%x\n", io);
306 /* mute card - prevents noisy bootups */
307 fmi_mute(io);
308 return 0;
309}
310
311MODULE_AUTHOR("Petr Vandrovec, vandrove@vc.cvut.cz and M. Kirkwood");
312MODULE_DESCRIPTION("A driver for the SF16MI radio.");
313MODULE_LICENSE("GPL");
314
315module_param(io, int, 0);
316MODULE_PARM_DESC(io, "I/O address of the SF16MI card (0x284 or 0x384)");
317module_param(radio_nr, int, 0);
318
319static void __exit fmi_cleanup_module(void)
320{
321 video_unregister_device(&fmi_radio);
322 release_region(io, 2);
323 if (dev)
324 pnp_device_detach(dev);
325}
326
327module_init(fmi_init);
328module_exit(fmi_cleanup_module);
diff --git a/drivers/media/radio/radio-sf16fmr2.c b/drivers/media/radio/radio-sf16fmr2.c
new file mode 100644
index 000000000000..0732efda6a98
--- /dev/null
+++ b/drivers/media/radio/radio-sf16fmr2.c
@@ -0,0 +1,434 @@
1/* SF16FMR2 radio driver for Linux radio support
2 * heavily based on fmi driver...
3 * (c) 2000-2002 Ziglio Frediano, freddy77@angelfire.com
4 *
5 * Notes on the hardware
6 *
7 * Frequency control is done digitally -- ie out(port,encodefreq(95.8));
8 * No volume control - only mute/unmute - you have to use line volume
9 *
10 * For read stereo/mono you must wait 0.1 sec after set frequency and
11 * card unmuted so I set frequency on unmute
12 * Signal handling seem to work only on autoscanning (not implemented)
13 */
14
15#include <linux/module.h> /* Modules */
16#include <linux/init.h> /* Initdata */
17#include <linux/ioport.h> /* check_region, request_region */
18#include <linux/delay.h> /* udelay */
19#include <asm/io.h> /* outb, outb_p */
20#include <asm/uaccess.h> /* copy to/from user */
21#include <linux/videodev.h> /* kernel radio structs */
22#include <asm/semaphore.h>
23
24static struct semaphore lock;
25
26#undef DEBUG
27//#define DEBUG 1
28
29#ifdef DEBUG
30# define debug_print(s) printk s
31#else
32# define debug_print(s)
33#endif
34
35/* this should be static vars for module size */
36struct fmr2_device
37{
38 int port;
39 int curvol; /* 0-65535, if not volume 0 or 65535 */
40 int mute;
41 int stereo; /* card is producing stereo audio */
42 unsigned long curfreq; /* freq in kHz */
43 int card_type;
44 __u32 flags;
45};
46
47static int io = 0x384;
48static int radio_nr = -1;
49
50/* hw precision is 12.5 kHz
51 * It is only usefull to give freq in intervall of 200 (=0.0125Mhz),
52 * other bits will be truncated
53 */
54#define RSF16_ENCODE(x) ((x)/200+856)
55#define RSF16_MINFREQ 87*16000
56#define RSF16_MAXFREQ 108*16000
57
58static inline void wait(int n,int port)
59{
60 for (;n;--n) inb(port);
61}
62
63static void outbits(int bits, unsigned int data, int nWait, int port)
64{
65 int bit;
66 for(;--bits>=0;) {
67 bit = (data>>bits) & 1;
68 outb(bit,port);
69 wait(nWait,port);
70 outb(bit|2,port);
71 wait(nWait,port);
72 outb(bit,port);
73 wait(nWait,port);
74 }
75}
76
77static inline void fmr2_mute(int port)
78{
79 outb(0x00, port);
80 wait(4,port);
81}
82
83static inline void fmr2_unmute(int port)
84{
85 outb(0x04, port);
86 wait(4,port);
87}
88
89static inline int fmr2_stereo_mode(int port)
90{
91 int n = inb(port);
92 outb(6,port);
93 inb(port);
94 n = ((n>>3)&1)^1;
95 debug_print((KERN_DEBUG "stereo: %d\n", n));
96 return n;
97}
98
99static int fmr2_product_info(struct fmr2_device *dev)
100{
101 int n = inb(dev->port);
102 n &= 0xC1;
103 if (n == 0)
104 {
105 /* this should support volume set */
106 dev->card_type = 12;
107 return 0;
108 }
109 /* not volume (mine is 11) */
110 dev->card_type = (n==128)?11:0;
111 return n;
112}
113
114static inline int fmr2_getsigstr(struct fmr2_device *dev)
115{
116 /* !!! work only if scanning freq */
117 int port = dev->port, res = 0xffff;
118 outb(5,port);
119 wait(4,port);
120 if (!(inb(port)&1)) res = 0;
121 debug_print((KERN_DEBUG "signal: %d\n", res));
122 return res;
123}
124
125/* set frequency and unmute card */
126static int fmr2_setfreq(struct fmr2_device *dev)
127{
128 int port = dev->port;
129 unsigned long freq = dev->curfreq;
130
131 fmr2_mute(port);
132
133 /* 0x42 for mono output
134 * 0x102 forward scanning
135 * 0x182 scansione avanti
136 */
137 outbits(9,0x2,3,port);
138 outbits(16,RSF16_ENCODE(freq),2,port);
139
140 fmr2_unmute(port);
141
142 /* wait 0.11 sec */
143 msleep(110);
144
145 /* NOTE if mute this stop radio
146 you must set freq on unmute */
147 dev->stereo = fmr2_stereo_mode(port);
148 return 0;
149}
150
151/* !!! not tested, in my card this does't work !!! */
152static int fmr2_setvolume(struct fmr2_device *dev)
153{
154 int i,a,n, port = dev->port;
155
156 if (dev->card_type != 11) return 1;
157
158 switch( (dev->curvol+(1<<11)) >> 12 )
159 {
160 case 0: case 1: n = 0x21; break;
161 case 2: n = 0x84; break;
162 case 3: n = 0x90; break;
163 case 4: n = 0x104; break;
164 case 5: n = 0x110; break;
165 case 6: n = 0x204; break;
166 case 7: n = 0x210; break;
167 case 8: n = 0x402; break;
168 case 9: n = 0x404; break;
169 default:
170 case 10: n = 0x408; break;
171 case 11: n = 0x410; break;
172 case 12: n = 0x801; break;
173 case 13: n = 0x802; break;
174 case 14: n = 0x804; break;
175 case 15: n = 0x808; break;
176 case 16: n = 0x810; break;
177 }
178 for(i=12;--i>=0;)
179 {
180 a = ((n >> i) & 1) << 6; /* if (a=0) a= 0; else a= 0x40; */
181 outb(a|4, port);
182 wait(4,port);
183 outb(a|0x24, port);
184 wait(4,port);
185 outb(a|4, port);
186 wait(4,port);
187 }
188 for(i=6;--i>=0;)
189 {
190 a = ((0x18 >> i) & 1) << 6;
191 outb(a|4, port);
192 wait(4,port);
193 outb(a|0x24, port);
194 wait(4,port);
195 outb(a|4, port);
196 wait(4,port);
197 }
198 wait(4,port);
199 outb(0x14, port);
200
201 return 0;
202}
203
204static int fmr2_do_ioctl(struct inode *inode, struct file *file,
205 unsigned int cmd, void *arg)
206{
207 struct video_device *dev = video_devdata(file);
208 struct fmr2_device *fmr2 = dev->priv;
209 debug_print((KERN_DEBUG "freq %ld flags %d vol %d mute %d "
210 "stereo %d type %d\n",
211 fmr2->curfreq, fmr2->flags, fmr2->curvol, fmr2->mute,
212 fmr2->stereo, fmr2->card_type));
213
214 switch(cmd)
215 {
216 case VIDIOCGCAP:
217 {
218 struct video_capability *v = arg;
219 memset(v,0,sizeof(*v));
220 strcpy(v->name, "SF16-FMR2 radio");
221 v->type=VID_TYPE_TUNER;
222 v->channels=1;
223 v->audios=1;
224 return 0;
225 }
226 case VIDIOCGTUNER:
227 {
228 struct video_tuner *v = arg;
229 int mult;
230
231 if(v->tuner) /* Only 1 tuner */
232 return -EINVAL;
233 strcpy(v->name, "FM");
234 mult = (fmr2->flags & VIDEO_TUNER_LOW) ? 1 : 1000;
235 v->rangelow = RSF16_MINFREQ/mult;
236 v->rangehigh = RSF16_MAXFREQ/mult;
237 v->flags = fmr2->flags | VIDEO_AUDIO_MUTABLE;
238 if (fmr2->mute)
239 v->flags |= VIDEO_AUDIO_MUTE;
240 v->mode=VIDEO_MODE_AUTO;
241 down(&lock);
242 v->signal = fmr2_getsigstr(fmr2);
243 up(&lock);
244 return 0;
245 }
246 case VIDIOCSTUNER:
247 {
248 struct video_tuner *v = arg;
249 if (v->tuner!=0)
250 return -EINVAL;
251 fmr2->flags = v->flags & VIDEO_TUNER_LOW;
252 return 0;
253 }
254 case VIDIOCGFREQ:
255 {
256 unsigned long *freq = arg;
257 *freq = fmr2->curfreq;
258 if (!(fmr2->flags & VIDEO_TUNER_LOW))
259 *freq /= 1000;
260 return 0;
261 }
262 case VIDIOCSFREQ:
263 {
264 unsigned long *freq = arg;
265 if (!(fmr2->flags & VIDEO_TUNER_LOW))
266 *freq *= 1000;
267 if ( *freq < RSF16_MINFREQ || *freq > RSF16_MAXFREQ )
268 return -EINVAL;
269 /* rounding in steps of 200 to match th freq
270 * that will be used
271 */
272 fmr2->curfreq = (*freq/200)*200;
273
274 /* set card freq (if not muted) */
275 if (fmr2->curvol && !fmr2->mute)
276 {
277 down(&lock);
278 fmr2_setfreq(fmr2);
279 up(&lock);
280 }
281 return 0;
282 }
283 case VIDIOCGAUDIO:
284 {
285 struct video_audio *v = arg;
286 memset(v,0,sizeof(*v));
287 /* !!! do not return VIDEO_AUDIO_MUTE */
288 v->flags = VIDEO_AUDIO_MUTABLE;
289 strcpy(v->name, "Radio");
290 /* get current stereo mode */
291 v->mode = fmr2->stereo ? VIDEO_SOUND_STEREO: VIDEO_SOUND_MONO;
292 /* volume supported ? */
293 if (fmr2->card_type == 11)
294 {
295 v->flags |= VIDEO_AUDIO_VOLUME;
296 v->step = 1 << 12;
297 v->volume = fmr2->curvol;
298 }
299 debug_print((KERN_DEBUG "Get flags %d vol %d\n", v->flags, v->volume));
300 return 0;
301 }
302 case VIDIOCSAUDIO:
303 {
304 struct video_audio *v = arg;
305 if(v->audio)
306 return -EINVAL;
307 debug_print((KERN_DEBUG "Set flags %d vol %d\n", v->flags, v->volume));
308 /* set volume */
309 if (v->flags & VIDEO_AUDIO_VOLUME)
310 fmr2->curvol = v->volume; /* !!! set with precision */
311 if (fmr2->card_type != 11) fmr2->curvol = 65535;
312 fmr2->mute = 0;
313 if (v->flags & VIDEO_AUDIO_MUTE)
314 fmr2->mute = 1;
315#ifdef DEBUG
316 if (fmr2->curvol && !fmr2->mute)
317 printk(KERN_DEBUG "unmute\n");
318 else
319 printk(KERN_DEBUG "mute\n");
320#endif
321 down(&lock);
322 if (fmr2->curvol && !fmr2->mute)
323 {
324 fmr2_setvolume(fmr2);
325 fmr2_setfreq(fmr2);
326 }
327 else fmr2_mute(fmr2->port);
328 up(&lock);
329 return 0;
330 }
331 case VIDIOCGUNIT:
332 {
333 struct video_unit *v = arg;
334 v->video=VIDEO_NO_UNIT;
335 v->vbi=VIDEO_NO_UNIT;
336 v->radio=dev->minor;
337 v->audio=0; /* How do we find out this??? */
338 v->teletext=VIDEO_NO_UNIT;
339 return 0;
340 }
341 default:
342 return -ENOIOCTLCMD;
343 }
344}
345
346static int fmr2_ioctl(struct inode *inode, struct file *file,
347 unsigned int cmd, unsigned long arg)
348 {
349 return video_usercopy(inode, file, cmd, arg, fmr2_do_ioctl);
350}
351
352static struct fmr2_device fmr2_unit;
353
354static struct file_operations fmr2_fops = {
355 .owner = THIS_MODULE,
356 .open = video_exclusive_open,
357 .release = video_exclusive_release,
358 .ioctl = fmr2_ioctl,
359 .llseek = no_llseek,
360};
361
362static struct video_device fmr2_radio=
363{
364 .owner = THIS_MODULE,
365 .name = "SF16FMR2 radio",
366 . type = VID_TYPE_TUNER,
367 .hardware = VID_HARDWARE_SF16FMR2,
368 .fops = &fmr2_fops,
369};
370
371static int __init fmr2_init(void)
372{
373 fmr2_unit.port = io;
374 fmr2_unit.curvol = 0;
375 fmr2_unit.mute = 0;
376 fmr2_unit.curfreq = 0;
377 fmr2_unit.stereo = 1;
378 fmr2_unit.flags = VIDEO_TUNER_LOW;
379 fmr2_unit.card_type = 0;
380 fmr2_radio.priv = &fmr2_unit;
381
382 init_MUTEX(&lock);
383
384 if (request_region(io, 2, "sf16fmr2"))
385 {
386 printk(KERN_ERR "fmr2: port 0x%x already in use\n", io);
387 return -EBUSY;
388 }
389
390 if(video_register_device(&fmr2_radio, VFL_TYPE_RADIO, radio_nr)==-1)
391 {
392 release_region(io, 2);
393 return -EINVAL;
394 }
395
396 printk(KERN_INFO "SF16FMR2 radio card driver at 0x%x.\n", io);
397 debug_print((KERN_DEBUG "Mute %d Low %d\n",VIDEO_AUDIO_MUTE,VIDEO_TUNER_LOW));
398 /* mute card - prevents noisy bootups */
399 down(&lock);
400 fmr2_mute(io);
401 fmr2_product_info(&fmr2_unit);
402 up(&lock);
403 debug_print((KERN_DEBUG "card_type %d\n", fmr2_unit.card_type));
404 return 0;
405}
406
407MODULE_AUTHOR("Ziglio Frediano, freddy77@angelfire.com");
408MODULE_DESCRIPTION("A driver for the SF16FMR2 radio.");
409MODULE_LICENSE("GPL");
410
411module_param(io, int, 0);
412MODULE_PARM_DESC(io, "I/O address of the SF16FMR2 card (should be 0x384, if do not work try 0x284)");
413module_param(radio_nr, int, 0);
414
415static void __exit fmr2_cleanup_module(void)
416{
417 video_unregister_device(&fmr2_radio);
418 release_region(io,2);
419}
420
421module_init(fmr2_init);
422module_exit(fmr2_cleanup_module);
423
424#ifndef MODULE
425
426static int __init fmr2_setup_io(char *str)
427{
428 get_option(&str, &io);
429 return 1;
430}
431
432__setup("sf16fmr2=", fmr2_setup_io);
433
434#endif
diff --git a/drivers/media/radio/radio-terratec.c b/drivers/media/radio/radio-terratec.c
new file mode 100644
index 000000000000..248d67fde037
--- /dev/null
+++ b/drivers/media/radio/radio-terratec.c
@@ -0,0 +1,341 @@
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)
5 *
6 *
7 * History:
8 * 1999-05-21 First preview release
9 *
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 *
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!!
22 *
23 *
24 */
25
26#include <linux/module.h> /* Modules */
27#include <linux/init.h> /* Initdata */
28#include <linux/ioport.h> /* check_region, request_region */
29#include <linux/delay.h> /* udelay */
30#include <asm/io.h> /* outb, outb_p */
31#include <asm/uaccess.h> /* copy to/from user */
32#include <linux/videodev.h> /* kernel radio structs */
33#include <linux/config.h> /* CONFIG_RADIO_TERRATEC_PORT */
34#include <linux/spinlock.h>
35
36#ifndef CONFIG_RADIO_TERRATEC_PORT
37#define CONFIG_RADIO_TERRATEC_PORT 0x590
38#endif
39
40/**************** this ones are for the terratec *******************/
41#define BASEPORT 0x590
42#define VOLPORT 0x591
43#define WRT_DIS 0x00
44#define CLK_OFF 0x00
45#define IIC_DATA 0x01
46#define IIC_CLK 0x02
47#define DATA 0x04
48#define CLK_ON 0x08
49#define WRT_EN 0x10
50/*******************************************************************/
51
52static int io = CONFIG_RADIO_TERRATEC_PORT;
53static int radio_nr = -1;
54static spinlock_t lock;
55
56struct tt_device
57{
58 int port;
59 int curvol;
60 unsigned long curfreq;
61 int muted;
62};
63
64
65/* local things */
66
67static void cardWriteVol(int volume)
68{
69 int i;
70 volume = volume+(volume * 32); // change both channels
71 spin_lock(&lock);
72 for (i=0;i<8;i++)
73 {
74 if (volume & (0x80>>i))
75 outb(0x80, VOLPORT);
76 else outb(0x00, VOLPORT);
77 }
78 spin_unlock(&lock);
79}
80
81
82
83static void tt_mute(struct tt_device *dev)
84{
85 dev->muted = 1;
86 cardWriteVol(0);
87}
88
89static int tt_setvol(struct tt_device *dev, int vol)
90{
91
92// printk(KERN_ERR "setvol called, vol = %d\n", vol);
93
94 if(vol == dev->curvol) { /* requested volume = current */
95 if (dev->muted) { /* user is unmuting the card */
96 dev->muted = 0;
97 cardWriteVol(vol); /* enable card */
98 }
99
100 return 0;
101 }
102
103 if(vol == 0) { /* volume = 0 means mute the card */
104 cardWriteVol(0); /* "turn off card" by setting vol to 0 */
105 dev->curvol = vol; /* track the volume state! */
106 return 0;
107 }
108
109 dev->muted = 0;
110
111 cardWriteVol(vol);
112
113 dev->curvol = vol;
114
115 return 0;
116
117}
118
119
120/* this is the worst part in this driver */
121/* many more or less strange things are going on here, but hey, it works :) */
122
123static int tt_setfreq(struct tt_device *dev, unsigned long freq1)
124{
125 int freq;
126 int i;
127 int p;
128 int temp;
129 long rest;
130
131 unsigned char buffer[25]; /* we have to bit shift 25 registers */
132 freq = freq1/160; /* convert the freq. to a nice to handle value */
133 for(i=24;i>-1;i--)
134 buffer[i]=0;
135
136 rest = freq*10+10700; /* i once had understood what is going on here */
137 /* maybe some wise guy (friedhelm?) can comment this stuff */
138 i=13;
139 p=10;
140 temp=102400;
141 while (rest!=0)
142 {
143 if (rest%temp == rest)
144 buffer[i] = 0;
145 else
146 {
147 buffer[i] = 1;
148 rest = rest-temp;
149 }
150 i--;
151 p--;
152 temp = temp/2;
153 }
154
155 spin_lock(&lock);
156
157 for (i=24;i>-1;i--) /* bit shift the values to the radiocard */
158 {
159 if (buffer[i]==1)
160 {
161 outb(WRT_EN|DATA, BASEPORT);
162 outb(WRT_EN|DATA|CLK_ON , BASEPORT);
163 outb(WRT_EN|DATA, BASEPORT);
164 }
165 else
166 {
167 outb(WRT_EN|0x00, BASEPORT);
168 outb(WRT_EN|0x00|CLK_ON , BASEPORT);
169 }
170 }
171 outb(0x00, BASEPORT);
172
173 spin_unlock(&lock);
174
175 return 0;
176}
177
178static int tt_getsigstr(struct tt_device *dev) /* TODO */
179{
180 if (inb(io) & 2) /* bit set = no signal present */
181 return 0;
182 return 1; /* signal present */
183}
184
185
186/* implement the video4linux api */
187
188static int tt_do_ioctl(struct inode *inode, struct file *file,
189 unsigned int cmd, void *arg)
190{
191 struct video_device *dev = video_devdata(file);
192 struct tt_device *tt=dev->priv;
193
194 switch(cmd)
195 {
196 case VIDIOCGCAP:
197 {
198 struct video_capability *v = arg;
199 memset(v,0,sizeof(*v));
200 v->type=VID_TYPE_TUNER;
201 v->channels=1;
202 v->audios=1;
203 strcpy(v->name, "ActiveRadio");
204 return 0;
205 }
206 case VIDIOCGTUNER:
207 {
208 struct video_tuner *v = arg;
209 if(v->tuner) /* Only 1 tuner */
210 return -EINVAL;
211 v->rangelow=(87*16000);
212 v->rangehigh=(108*16000);
213 v->flags=VIDEO_TUNER_LOW;
214 v->mode=VIDEO_MODE_AUTO;
215 strcpy(v->name, "FM");
216 v->signal=0xFFFF*tt_getsigstr(tt);
217 return 0;
218 }
219 case VIDIOCSTUNER:
220 {
221 struct video_tuner *v = arg;
222 if(v->tuner!=0)
223 return -EINVAL;
224 /* Only 1 tuner so no setting needed ! */
225 return 0;
226 }
227 case VIDIOCGFREQ:
228 {
229 unsigned long *freq = arg;
230 *freq = tt->curfreq;
231 return 0;
232 }
233 case VIDIOCSFREQ:
234 {
235 unsigned long *freq = arg;
236 tt->curfreq = *freq;
237 tt_setfreq(tt, tt->curfreq);
238 return 0;
239 }
240 case VIDIOCGAUDIO:
241 {
242 struct video_audio *v = arg;
243 memset(v,0, sizeof(*v));
244 v->flags|=VIDEO_AUDIO_MUTABLE|VIDEO_AUDIO_VOLUME;
245 v->volume=tt->curvol * 6554;
246 v->step=6554;
247 strcpy(v->name, "Radio");
248 return 0;
249 }
250 case VIDIOCSAUDIO:
251 {
252 struct video_audio *v = arg;
253 if(v->audio)
254 return -EINVAL;
255 if(v->flags&VIDEO_AUDIO_MUTE)
256 tt_mute(tt);
257 else
258 tt_setvol(tt,v->volume/6554);
259 return 0;
260 }
261 default:
262 return -ENOIOCTLCMD;
263 }
264}
265
266static int tt_ioctl(struct inode *inode, struct file *file,
267 unsigned int cmd, unsigned long arg)
268{
269 return video_usercopy(inode, file, cmd, arg, tt_do_ioctl);
270}
271
272static struct tt_device terratec_unit;
273
274static struct file_operations terratec_fops = {
275 .owner = THIS_MODULE,
276 .open = video_exclusive_open,
277 .release = video_exclusive_release,
278 .ioctl = tt_ioctl,
279 .llseek = no_llseek,
280};
281
282static struct video_device terratec_radio=
283{
284 .owner = THIS_MODULE,
285 .name = "TerraTec ActiveRadio",
286 .type = VID_TYPE_TUNER,
287 .hardware = VID_HARDWARE_TERRATEC,
288 .fops = &terratec_fops,
289};
290
291static int __init terratec_init(void)
292{
293 if(io==-1)
294 {
295 printk(KERN_ERR "You must set an I/O address with io=0x???\n");
296 return -EINVAL;
297 }
298 if (!request_region(io, 2, "terratec"))
299 {
300 printk(KERN_ERR "TerraTec: port 0x%x already in use\n", io);
301 return -EBUSY;
302 }
303
304 terratec_radio.priv=&terratec_unit;
305
306 spin_lock_init(&lock);
307
308 if(video_register_device(&terratec_radio, VFL_TYPE_RADIO, radio_nr)==-1)
309 {
310 release_region(io,2);
311 return -EINVAL;
312 }
313
314 printk(KERN_INFO "TERRATEC ActivRadio Standalone card driver.\n");
315
316 /* mute card - prevents noisy bootups */
317
318 /* this ensures that the volume is all the way down */
319 cardWriteVol(0);
320 terratec_unit.curvol = 0;
321
322 return 0;
323}
324
325MODULE_AUTHOR("R.OFFERMANNS & others");
326MODULE_DESCRIPTION("A driver for the TerraTec ActiveRadio Standalone radio card.");
327MODULE_LICENSE("GPL");
328module_param(io, int, 0);
329MODULE_PARM_DESC(io, "I/O address of the TerraTec ActiveRadio card (0x590 or 0x591)");
330module_param(radio_nr, int, 0);
331
332static void __exit terratec_cleanup_module(void)
333{
334 video_unregister_device(&terratec_radio);
335 release_region(io,2);
336 printk(KERN_INFO "TERRATEC ActivRadio Standalone card driver unloaded.\n");
337}
338
339module_init(terratec_init);
340module_exit(terratec_cleanup_module);
341
diff --git a/drivers/media/radio/radio-trust.c b/drivers/media/radio/radio-trust.c
new file mode 100644
index 000000000000..b300bedf7c74
--- /dev/null
+++ b/drivers/media/radio/radio-trust.c
@@ -0,0 +1,320 @@
1/* radio-trust.c - Trust FM Radio card driver for Linux 2.2
2 * by Eric Lammerts <eric@scintilla.utwente.nl>
3 *
4 * Based on radio-aztech.c. Original notes:
5 *
6 * Adapted to support the Video for Linux API by
7 * Russell Kroll <rkroll@exploits.org>. Based on original tuner code by:
8 *
9 * Quay Ly
10 * Donald Song
11 * Jason Lewis (jlewis@twilight.vtc.vsc.edu)
12 * Scott McGrath (smcgrath@twilight.vtc.vsc.edu)
13 * William McGrath (wmcgrath@twilight.vtc.vsc.edu)
14 *
15 * The basis for this code may be found at http://bigbang.vtc.vsc.edu/fmradio/
16 */
17
18#include <stdarg.h>
19#include <linux/module.h>
20#include <linux/init.h>
21#include <linux/ioport.h>
22#include <asm/io.h>
23#include <asm/uaccess.h>
24#include <linux/videodev.h>
25#include <linux/config.h> /* CONFIG_RADIO_TRUST_PORT */
26
27/* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */
28
29#ifndef CONFIG_RADIO_TRUST_PORT
30#define CONFIG_RADIO_TRUST_PORT -1
31#endif
32
33static int io = CONFIG_RADIO_TRUST_PORT;
34static int radio_nr = -1;
35static int ioval = 0xf;
36static __u16 curvol;
37static __u16 curbass;
38static __u16 curtreble;
39static unsigned long curfreq;
40static int curstereo;
41static int curmute;
42
43/* i2c addresses */
44#define TDA7318_ADDR 0x88
45#define TSA6060T_ADDR 0xc4
46
47#define TR_DELAY do { inb(io); inb(io); inb(io); } while(0)
48#define TR_SET_SCL outb(ioval |= 2, io)
49#define TR_CLR_SCL outb(ioval &= 0xfd, io)
50#define TR_SET_SDA outb(ioval |= 1, io)
51#define TR_CLR_SDA outb(ioval &= 0xfe, io)
52
53static void write_i2c(int n, ...)
54{
55 unsigned char val, mask;
56 va_list args;
57
58 va_start(args, n);
59
60 /* start condition */
61 TR_SET_SDA;
62 TR_SET_SCL;
63 TR_DELAY;
64 TR_CLR_SDA;
65 TR_CLR_SCL;
66 TR_DELAY;
67
68 for(; n; n--) {
69 val = va_arg(args, unsigned);
70 for(mask = 0x80; mask; mask >>= 1) {
71 if(val & mask)
72 TR_SET_SDA;
73 else
74 TR_CLR_SDA;
75 TR_SET_SCL;
76 TR_DELAY;
77 TR_CLR_SCL;
78 TR_DELAY;
79 }
80 /* acknowledge bit */
81 TR_SET_SDA;
82 TR_SET_SCL;
83 TR_DELAY;
84 TR_CLR_SCL;
85 TR_DELAY;
86 }
87
88 /* stop condition */
89 TR_CLR_SDA;
90 TR_DELAY;
91 TR_SET_SCL;
92 TR_DELAY;
93 TR_SET_SDA;
94 TR_DELAY;
95
96 va_end(args);
97}
98
99static void tr_setvol(__u16 vol)
100{
101 curvol = vol / 2048;
102 write_i2c(2, TDA7318_ADDR, curvol ^ 0x1f);
103}
104
105static int basstreble2chip[15] = {
106 0, 1, 2, 3, 4, 5, 6, 7, 14, 13, 12, 11, 10, 9, 8
107};
108
109static void tr_setbass(__u16 bass)
110{
111 curbass = bass / 4370;
112 write_i2c(2, TDA7318_ADDR, 0x60 | basstreble2chip[curbass]);
113}
114
115static void tr_settreble(__u16 treble)
116{
117 curtreble = treble / 4370;
118 write_i2c(2, TDA7318_ADDR, 0x70 | basstreble2chip[curtreble]);
119}
120
121static void tr_setstereo(int stereo)
122{
123 curstereo = !!stereo;
124 ioval = (ioval & 0xfb) | (!curstereo << 2);
125 outb(ioval, io);
126}
127
128static void tr_setmute(int mute)
129{
130 curmute = !!mute;
131 ioval = (ioval & 0xf7) | (curmute << 3);
132 outb(ioval, io);
133}
134
135static int tr_getsigstr(void)
136{
137 int i, v;
138
139 for(i = 0, v = 0; i < 100; i++) v |= inb(io);
140 return (v & 1)? 0 : 0xffff;
141}
142
143static int tr_getstereo(void)
144{
145 /* don't know how to determine it, just return the setting */
146 return curstereo;
147}
148
149static void tr_setfreq(unsigned long f)
150{
151 f /= 160; /* Convert to 10 kHz units */
152 f += 1070; /* Add 10.7 MHz IF */
153
154 write_i2c(5, TSA6060T_ADDR, (f << 1) | 1, f >> 7, 0x60 | ((f >> 15) & 1), 0);
155}
156
157static int tr_do_ioctl(struct inode *inode, struct file *file,
158 unsigned int cmd, void *arg)
159{
160 switch(cmd)
161 {
162 case VIDIOCGCAP:
163 {
164 struct video_capability *v = arg;
165
166 memset(v,0,sizeof(*v));
167 v->type=VID_TYPE_TUNER;
168 v->channels=1;
169 v->audios=1;
170 strcpy(v->name, "Trust FM Radio");
171
172 return 0;
173 }
174 case VIDIOCGTUNER:
175 {
176 struct video_tuner *v = arg;
177
178 if(v->tuner) /* Only 1 tuner */
179 return -EINVAL;
180
181 v->rangelow = 87500 * 16;
182 v->rangehigh = 108000 * 16;
183 v->flags = VIDEO_TUNER_LOW;
184 v->mode = VIDEO_MODE_AUTO;
185
186 v->signal = tr_getsigstr();
187 if(tr_getstereo())
188 v->flags |= VIDEO_TUNER_STEREO_ON;
189
190 strcpy(v->name, "FM");
191
192 return 0;
193 }
194 case VIDIOCSTUNER:
195 {
196 struct video_tuner *v = arg;
197 if(v->tuner != 0)
198 return -EINVAL;
199 return 0;
200 }
201 case VIDIOCGFREQ:
202 {
203 unsigned long *freq = arg;
204 *freq = curfreq;
205 return 0;
206 }
207 case VIDIOCSFREQ:
208 {
209 unsigned long *freq = arg;
210 tr_setfreq(*freq);
211 return 0;
212 }
213 case VIDIOCGAUDIO:
214 {
215 struct video_audio *v = arg;
216
217 memset(v,0, sizeof(*v));
218 v->flags = VIDEO_AUDIO_MUTABLE | VIDEO_AUDIO_VOLUME |
219 VIDEO_AUDIO_BASS | VIDEO_AUDIO_TREBLE;
220 v->mode = curstereo? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
221 v->volume = curvol * 2048;
222 v->step = 2048;
223 v->bass = curbass * 4370;
224 v->treble = curtreble * 4370;
225
226 strcpy(v->name, "Trust FM Radio");
227 return 0;
228 }
229 case VIDIOCSAUDIO:
230 {
231 struct video_audio *v = arg;
232
233 if(v->audio)
234 return -EINVAL;
235 tr_setvol(v->volume);
236 tr_setbass(v->bass);
237 tr_settreble(v->treble);
238 tr_setstereo(v->mode & VIDEO_SOUND_STEREO);
239 tr_setmute(v->flags & VIDEO_AUDIO_MUTE);
240 return 0;
241 }
242 default:
243 return -ENOIOCTLCMD;
244 }
245}
246
247static int tr_ioctl(struct inode *inode, struct file *file,
248 unsigned int cmd, unsigned long arg)
249{
250 return video_usercopy(inode, file, cmd, arg, tr_do_ioctl);
251}
252
253static struct file_operations trust_fops = {
254 .owner = THIS_MODULE,
255 .open = video_exclusive_open,
256 .release = video_exclusive_release,
257 .ioctl = tr_ioctl,
258 .llseek = no_llseek,
259};
260
261static struct video_device trust_radio=
262{
263 .owner = THIS_MODULE,
264 .name = "Trust FM Radio",
265 .type = VID_TYPE_TUNER,
266 .hardware = VID_HARDWARE_TRUST,
267 .fops = &trust_fops,
268};
269
270static int __init trust_init(void)
271{
272 if(io == -1) {
273 printk(KERN_ERR "You must set an I/O address with io=0x???\n");
274 return -EINVAL;
275 }
276 if(!request_region(io, 2, "Trust FM Radio")) {
277 printk(KERN_ERR "trust: port 0x%x already in use\n", io);
278 return -EBUSY;
279 }
280 if(video_register_device(&trust_radio, VFL_TYPE_RADIO, radio_nr)==-1)
281 {
282 release_region(io, 2);
283 return -EINVAL;
284 }
285
286 printk(KERN_INFO "Trust FM Radio card driver v1.0.\n");
287
288 write_i2c(2, TDA7318_ADDR, 0x80); /* speaker att. LF = 0 dB */
289 write_i2c(2, TDA7318_ADDR, 0xa0); /* speaker att. RF = 0 dB */
290 write_i2c(2, TDA7318_ADDR, 0xc0); /* speaker att. LR = 0 dB */
291 write_i2c(2, TDA7318_ADDR, 0xe0); /* speaker att. RR = 0 dB */
292 write_i2c(2, TDA7318_ADDR, 0x40); /* stereo 1 input, gain = 18.75 dB */
293
294 tr_setvol(0x8000);
295 tr_setbass(0x8000);
296 tr_settreble(0x8000);
297 tr_setstereo(1);
298
299 /* mute card - prevents noisy bootups */
300 tr_setmute(1);
301
302 return 0;
303}
304
305MODULE_AUTHOR("Eric Lammerts, Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
306MODULE_DESCRIPTION("A driver for the Trust FM Radio card.");
307MODULE_LICENSE("GPL");
308
309module_param(io, int, 0);
310MODULE_PARM_DESC(io, "I/O address of the Trust FM Radio card (0x350 or 0x358)");
311module_param(radio_nr, int, 0);
312
313static void __exit cleanup_trust_module(void)
314{
315 video_unregister_device(&trust_radio);
316 release_region(io, 2);
317}
318
319module_init(trust_init);
320module_exit(cleanup_trust_module);
diff --git a/drivers/media/radio/radio-typhoon.c b/drivers/media/radio/radio-typhoon.c
new file mode 100644
index 000000000000..d7da901ebe90
--- /dev/null
+++ b/drivers/media/radio/radio-typhoon.c
@@ -0,0 +1,383 @@
1/* Typhoon Radio Card driver for radio support
2 * (c) 1999 Dr. Henrik Seidel <Henrik.Seidel@gmx.de>
3 *
4 * Card manufacturer:
5 * http://194.18.155.92/idc/prod2.idc?nr=50753&lang=e
6 *
7 * Notes on the hardware
8 *
9 * This card has two output sockets, one for speakers and one for line.
10 * The speaker output has volume control, but only in four discrete
11 * steps. The line output has neither volume control nor mute.
12 *
13 * The card has auto-stereo according to its manual, although it all
14 * sounds mono to me (even with the Win/DOS drivers). Maybe it's my
15 * antenna - I really don't know for sure.
16 *
17 * Frequency control is done digitally.
18 *
19 * Volume control is done digitally, but there are only four different
20 * possible values. So you should better always turn the volume up and
21 * use line control. I got the best results by connecting line output
22 * to the sound card microphone input. For such a configuration the
23 * volume control has no effect, since volume control only influences
24 * the speaker output.
25 *
26 * There is no explicit mute/unmute. So I set the radio frequency to a
27 * value where I do expect just noise and turn the speaker volume down.
28 * The frequency change is necessary since the card never seems to be
29 * completely silent.
30 */
31
32#include <linux/module.h> /* Modules */
33#include <linux/init.h> /* Initdata */
34#include <linux/ioport.h> /* check_region, request_region */
35#include <linux/proc_fs.h> /* radio card status report */
36#include <asm/io.h> /* outb, outb_p */
37#include <asm/uaccess.h> /* copy to/from user */
38#include <linux/videodev.h> /* kernel radio structs */
39#include <linux/config.h> /* CONFIG_RADIO_TYPHOON_* */
40
41#define BANNER "Typhoon Radio Card driver v0.1\n"
42
43#ifndef CONFIG_RADIO_TYPHOON_PORT
44#define CONFIG_RADIO_TYPHOON_PORT -1
45#endif
46
47#ifndef CONFIG_RADIO_TYPHOON_MUTEFREQ
48#define CONFIG_RADIO_TYPHOON_MUTEFREQ 0
49#endif
50
51#ifndef CONFIG_PROC_FS
52#undef CONFIG_RADIO_TYPHOON_PROC_FS
53#endif
54
55struct typhoon_device {
56 int users;
57 int iobase;
58 int curvol;
59 int muted;
60 unsigned long curfreq;
61 unsigned long mutefreq;
62 struct semaphore lock;
63};
64
65static void typhoon_setvol_generic(struct typhoon_device *dev, int vol);
66static int typhoon_setfreq_generic(struct typhoon_device *dev,
67 unsigned long frequency);
68static int typhoon_setfreq(struct typhoon_device *dev, unsigned long frequency);
69static void typhoon_mute(struct typhoon_device *dev);
70static void typhoon_unmute(struct typhoon_device *dev);
71static int typhoon_setvol(struct typhoon_device *dev, int vol);
72static int typhoon_ioctl(struct inode *inode, struct file *file,
73 unsigned int cmd, unsigned long arg);
74#ifdef CONFIG_RADIO_TYPHOON_PROC_FS
75static int typhoon_get_info(char *buf, char **start, off_t offset, int len);
76#endif
77
78static void typhoon_setvol_generic(struct typhoon_device *dev, int vol)
79{
80 down(&dev->lock);
81 vol >>= 14; /* Map 16 bit to 2 bit */
82 vol &= 3;
83 outb_p(vol / 2, dev->iobase); /* Set the volume, high bit. */
84 outb_p(vol % 2, dev->iobase + 2); /* Set the volume, low bit. */
85 up(&dev->lock);
86}
87
88static int typhoon_setfreq_generic(struct typhoon_device *dev,
89 unsigned long frequency)
90{
91 unsigned long outval;
92 unsigned long x;
93
94 /*
95 * The frequency transfer curve is not linear. The best fit I could
96 * get is
97 *
98 * outval = -155 + exp((f + 15.55) * 0.057))
99 *
100 * where frequency f is in MHz. Since we don't have exp in the kernel,
101 * I approximate this function by a third order polynomial.
102 *
103 */
104
105 down(&dev->lock);
106 x = frequency / 160;
107 outval = (x * x + 2500) / 5000;
108 outval = (outval * x + 5000) / 10000;
109 outval -= (10 * x * x + 10433) / 20866;
110 outval += 4 * x - 11505;
111
112 outb_p((outval >> 8) & 0x01, dev->iobase + 4);
113 outb_p(outval >> 9, dev->iobase + 6);
114 outb_p(outval & 0xff, dev->iobase + 8);
115 up(&dev->lock);
116
117 return 0;
118}
119
120static int typhoon_setfreq(struct typhoon_device *dev, unsigned long frequency)
121{
122 typhoon_setfreq_generic(dev, frequency);
123 dev->curfreq = frequency;
124 return 0;
125}
126
127static void typhoon_mute(struct typhoon_device *dev)
128{
129 if (dev->muted == 1)
130 return;
131 typhoon_setvol_generic(dev, 0);
132 typhoon_setfreq_generic(dev, dev->mutefreq);
133 dev->muted = 1;
134}
135
136static void typhoon_unmute(struct typhoon_device *dev)
137{
138 if (dev->muted == 0)
139 return;
140 typhoon_setfreq_generic(dev, dev->curfreq);
141 typhoon_setvol_generic(dev, dev->curvol);
142 dev->muted = 0;
143}
144
145static int typhoon_setvol(struct typhoon_device *dev, int vol)
146{
147 if (dev->muted && vol != 0) { /* user is unmuting the card */
148 dev->curvol = vol;
149 typhoon_unmute(dev);
150 return 0;
151 }
152 if (vol == dev->curvol) /* requested volume == current */
153 return 0;
154
155 if (vol == 0) { /* volume == 0 means mute the card */
156 typhoon_mute(dev);
157 dev->curvol = vol;
158 return 0;
159 }
160 typhoon_setvol_generic(dev, vol);
161 dev->curvol = vol;
162 return 0;
163}
164
165
166static int typhoon_do_ioctl(struct inode *inode, struct file *file,
167 unsigned int cmd, void *arg)
168{
169 struct video_device *dev = video_devdata(file);
170 struct typhoon_device *typhoon = dev->priv;
171
172 switch (cmd) {
173 case VIDIOCGCAP:
174 {
175 struct video_capability *v = arg;
176 memset(v,0,sizeof(*v));
177 v->type = VID_TYPE_TUNER;
178 v->channels = 1;
179 v->audios = 1;
180 strcpy(v->name, "Typhoon Radio");
181 return 0;
182 }
183 case VIDIOCGTUNER:
184 {
185 struct video_tuner *v = arg;
186 if (v->tuner) /* Only 1 tuner */
187 return -EINVAL;
188 v->rangelow = 875 * 1600;
189 v->rangehigh = 1080 * 1600;
190 v->flags = VIDEO_TUNER_LOW;
191 v->mode = VIDEO_MODE_AUTO;
192 v->signal = 0xFFFF; /* We can't get the signal strength */
193 strcpy(v->name, "FM");
194 return 0;
195 }
196 case VIDIOCSTUNER:
197 {
198 struct video_tuner *v = arg;
199 if (v->tuner != 0)
200 return -EINVAL;
201 /* Only 1 tuner so no setting needed ! */
202 return 0;
203 }
204 case VIDIOCGFREQ:
205 {
206 unsigned long *freq = arg;
207 *freq = typhoon->curfreq;
208 return 0;
209 }
210 case VIDIOCSFREQ:
211 {
212 unsigned long *freq = arg;
213 typhoon->curfreq = *freq;
214 typhoon_setfreq(typhoon, typhoon->curfreq);
215 return 0;
216 }
217 case VIDIOCGAUDIO:
218 {
219 struct video_audio *v = arg;
220 memset(v, 0, sizeof(*v));
221 v->flags |= VIDEO_AUDIO_MUTABLE | VIDEO_AUDIO_VOLUME;
222 v->mode |= VIDEO_SOUND_MONO;
223 v->volume = typhoon->curvol;
224 v->step = 1 << 14;
225 strcpy(v->name, "Typhoon Radio");
226 return 0;
227 }
228 case VIDIOCSAUDIO:
229 {
230 struct video_audio *v = arg;
231 if (v->audio)
232 return -EINVAL;
233 if (v->flags & VIDEO_AUDIO_MUTE)
234 typhoon_mute(typhoon);
235 else
236 typhoon_unmute(typhoon);
237 if (v->flags & VIDEO_AUDIO_VOLUME)
238 typhoon_setvol(typhoon, v->volume);
239 return 0;
240 }
241 default:
242 return -ENOIOCTLCMD;
243 }
244}
245
246static int typhoon_ioctl(struct inode *inode, struct file *file,
247 unsigned int cmd, unsigned long arg)
248{
249 return video_usercopy(inode, file, cmd, arg, typhoon_do_ioctl);
250}
251
252static struct typhoon_device typhoon_unit =
253{
254 .iobase = CONFIG_RADIO_TYPHOON_PORT,
255 .curfreq = CONFIG_RADIO_TYPHOON_MUTEFREQ,
256 .mutefreq = CONFIG_RADIO_TYPHOON_MUTEFREQ,
257};
258
259static struct file_operations typhoon_fops = {
260 .owner = THIS_MODULE,
261 .open = video_exclusive_open,
262 .release = video_exclusive_release,
263 .ioctl = typhoon_ioctl,
264 .llseek = no_llseek,
265};
266
267static struct video_device typhoon_radio =
268{
269 .owner = THIS_MODULE,
270 .name = "Typhoon Radio",
271 .type = VID_TYPE_TUNER,
272 .hardware = VID_HARDWARE_TYPHOON,
273 .fops = &typhoon_fops,
274};
275
276#ifdef CONFIG_RADIO_TYPHOON_PROC_FS
277
278static int typhoon_get_info(char *buf, char **start, off_t offset, int len)
279{
280 char *out = buf;
281
282 #ifdef MODULE
283 #define MODULEPROCSTRING "Driver loaded as a module"
284 #else
285 #define MODULEPROCSTRING "Driver compiled into kernel"
286 #endif
287
288 /* output must be kept under PAGE_SIZE */
289 out += sprintf(out, BANNER);
290 out += sprintf(out, "Load type: " MODULEPROCSTRING "\n\n");
291 out += sprintf(out, "frequency = %lu kHz\n",
292 typhoon_unit.curfreq >> 4);
293 out += sprintf(out, "volume = %d\n", typhoon_unit.curvol);
294 out += sprintf(out, "mute = %s\n", typhoon_unit.muted ?
295 "on" : "off");
296 out += sprintf(out, "iobase = 0x%x\n", typhoon_unit.iobase);
297 out += sprintf(out, "mute frequency = %lu kHz\n",
298 typhoon_unit.mutefreq >> 4);
299 return out - buf;
300}
301
302#endif /* CONFIG_RADIO_TYPHOON_PROC_FS */
303
304MODULE_AUTHOR("Dr. Henrik Seidel");
305MODULE_DESCRIPTION("A driver for the Typhoon radio card (a.k.a. EcoRadio).");
306MODULE_LICENSE("GPL");
307
308static int io = -1;
309static int radio_nr = -1;
310
311module_param(io, int, 0);
312MODULE_PARM_DESC(io, "I/O address of the Typhoon card (0x316 or 0x336)");
313module_param(radio_nr, int, 0);
314
315#ifdef MODULE
316static unsigned long mutefreq = 0;
317module_param(mutefreq, ulong, 0);
318MODULE_PARM_DESC(mutefreq, "Frequency used when muting the card (in kHz)");
319#endif
320
321static int __init typhoon_init(void)
322{
323#ifdef MODULE
324 if (io == -1) {
325 printk(KERN_ERR "radio-typhoon: You must set an I/O address with io=0x316 or io=0x336\n");
326 return -EINVAL;
327 }
328 typhoon_unit.iobase = io;
329
330 if (mutefreq < 87000 || mutefreq > 108500) {
331 printk(KERN_ERR "radio-typhoon: You must set a frequency (in kHz) used when muting the card,\n");
332 printk(KERN_ERR "radio-typhoon: e.g. with \"mutefreq=87500\" (87000 <= mutefreq <= 108500)\n");
333 return -EINVAL;
334 }
335 typhoon_unit.mutefreq = mutefreq;
336#endif /* MODULE */
337
338 printk(KERN_INFO BANNER);
339 init_MUTEX(&typhoon_unit.lock);
340 io = typhoon_unit.iobase;
341 if (!request_region(io, 8, "typhoon")) {
342 printk(KERN_ERR "radio-typhoon: port 0x%x already in use\n",
343 typhoon_unit.iobase);
344 return -EBUSY;
345 }
346
347 typhoon_radio.priv = &typhoon_unit;
348 if (video_register_device(&typhoon_radio, VFL_TYPE_RADIO, radio_nr) == -1)
349 {
350 release_region(io, 8);
351 return -EINVAL;
352 }
353 printk(KERN_INFO "radio-typhoon: port 0x%x.\n", typhoon_unit.iobase);
354 printk(KERN_INFO "radio-typhoon: mute frequency is %lu kHz.\n",
355 typhoon_unit.mutefreq);
356 typhoon_unit.mutefreq <<= 4;
357
358 /* mute card - prevents noisy bootups */
359 typhoon_mute(&typhoon_unit);
360
361#ifdef CONFIG_RADIO_TYPHOON_PROC_FS
362 if (!create_proc_info_entry("driver/radio-typhoon", 0, NULL,
363 typhoon_get_info))
364 printk(KERN_ERR "radio-typhoon: registering /proc/driver/radio-typhoon failed\n");
365#endif
366
367 return 0;
368}
369
370static void __exit typhoon_cleanup_module(void)
371{
372
373#ifdef CONFIG_RADIO_TYPHOON_PROC_FS
374 remove_proc_entry("driver/radio-typhoon", NULL);
375#endif
376
377 video_unregister_device(&typhoon_radio);
378 release_region(io, 8);
379}
380
381module_init(typhoon_init);
382module_exit(typhoon_cleanup_module);
383
diff --git a/drivers/media/radio/radio-zoltrix.c b/drivers/media/radio/radio-zoltrix.c
new file mode 100644
index 000000000000..342f92df4aba
--- /dev/null
+++ b/drivers/media/radio/radio-zoltrix.c
@@ -0,0 +1,385 @@
1/* zoltrix radio plus driver for Linux radio support
2 * (c) 1998 C. van Schaik <carl@leg.uct.ac.za>
3 *
4 * BUGS
5 * Due to the inconsistency in reading from the signal flags
6 * it is difficult to get an accurate tuned signal.
7 *
8 * It seems that the card is not linear to 0 volume. It cuts off
9 * at a low volume, and it is not possible (at least I have not found)
10 * to get fine volume control over the low volume range.
11 *
12 * Some code derived from code by Romolo Manfredini
13 * romolo@bicnet.it
14 *
15 * 1999-05-06 - (C. van Schaik)
16 * - Make signal strength and stereo scans
17 * kinder to cpu while in delay
18 * 1999-01-05 - (C. van Schaik)
19 * - Changed tuning to 1/160Mhz accuracy
20 * - Added stereo support
21 * (card defaults to stereo)
22 * (can explicitly force mono on the card)
23 * (can detect if station is in stereo)
24 * - Added unmute function
25 * - Reworked ioctl functions
26 * 2002-07-15 - Fix Stereo typo
27 */
28
29#include <linux/module.h> /* Modules */
30#include <linux/init.h> /* Initdata */
31#include <linux/ioport.h> /* check_region, request_region */
32#include <linux/delay.h> /* udelay, msleep */
33#include <asm/io.h> /* outb, outb_p */
34#include <asm/uaccess.h> /* copy to/from user */
35#include <linux/videodev.h> /* kernel radio structs */
36#include <linux/config.h> /* CONFIG_RADIO_ZOLTRIX_PORT */
37
38#ifndef CONFIG_RADIO_ZOLTRIX_PORT
39#define CONFIG_RADIO_ZOLTRIX_PORT -1
40#endif
41
42static int io = CONFIG_RADIO_ZOLTRIX_PORT;
43static int radio_nr = -1;
44
45struct zol_device {
46 int port;
47 int curvol;
48 unsigned long curfreq;
49 int muted;
50 unsigned int stereo;
51 struct semaphore lock;
52};
53
54static int zol_setvol(struct zol_device *dev, int vol)
55{
56 dev->curvol = vol;
57 if (dev->muted)
58 return 0;
59
60 down(&dev->lock);
61 if (vol == 0) {
62 outb(0, io);
63 outb(0, io);
64 inb(io + 3); /* Zoltrix needs to be read to confirm */
65 up(&dev->lock);
66 return 0;
67 }
68
69 outb(dev->curvol-1, io);
70 msleep(10);
71 inb(io + 2);
72 up(&dev->lock);
73 return 0;
74}
75
76static void zol_mute(struct zol_device *dev)
77{
78 dev->muted = 1;
79 down(&dev->lock);
80 outb(0, io);
81 outb(0, io);
82 inb(io + 3); /* Zoltrix needs to be read to confirm */
83 up(&dev->lock);
84}
85
86static void zol_unmute(struct zol_device *dev)
87{
88 dev->muted = 0;
89 zol_setvol(dev, dev->curvol);
90}
91
92static int zol_setfreq(struct zol_device *dev, unsigned long freq)
93{
94 /* tunes the radio to the desired frequency */
95 unsigned long long bitmask, f, m;
96 unsigned int stereo = dev->stereo;
97 int i;
98
99 if (freq == 0)
100 return 1;
101 m = (freq / 160 - 8800) * 2;
102 f = (unsigned long long) m + 0x4d1c;
103
104 bitmask = 0xc480402c10080000ull;
105 i = 45;
106
107 down(&dev->lock);
108
109 outb(0, io);
110 outb(0, io);
111 inb(io + 3); /* Zoltrix needs to be read to confirm */
112
113 outb(0x40, io);
114 outb(0xc0, io);
115
116 bitmask = (bitmask ^ ((f & 0xff) << 47) ^ ((f & 0xff00) << 30) ^ ( stereo << 31));
117 while (i--) {
118 if ((bitmask & 0x8000000000000000ull) != 0) {
119 outb(0x80, io);
120 udelay(50);
121 outb(0x00, io);
122 udelay(50);
123 outb(0x80, io);
124 udelay(50);
125 } else {
126 outb(0xc0, io);
127 udelay(50);
128 outb(0x40, io);
129 udelay(50);
130 outb(0xc0, io);
131 udelay(50);
132 }
133 bitmask *= 2;
134 }
135 /* termination sequence */
136 outb(0x80, io);
137 outb(0xc0, io);
138 outb(0x40, io);
139 udelay(1000);
140 inb(io+2);
141
142 udelay(1000);
143
144 if (dev->muted)
145 {
146 outb(0, io);
147 outb(0, io);
148 inb(io + 3);
149 udelay(1000);
150 }
151
152 up(&dev->lock);
153
154 if(!dev->muted)
155 {
156 zol_setvol(dev, dev->curvol);
157 }
158 return 0;
159}
160
161/* Get signal strength */
162
163static int zol_getsigstr(struct zol_device *dev)
164{
165 int a, b;
166
167 down(&dev->lock);
168 outb(0x00, io); /* This stuff I found to do nothing */
169 outb(dev->curvol, io);
170 msleep(20);
171
172 a = inb(io);
173 msleep(10);
174 b = inb(io);
175
176 up(&dev->lock);
177
178 if (a != b)
179 return (0);
180
181 if ((a == 0xcf) || (a == 0xdf) /* I found this out by playing */
182 || (a == 0xef)) /* with a binary scanner on the card io */
183 return (1);
184 return (0);
185}
186
187static int zol_is_stereo (struct zol_device *dev)
188{
189 int x1, x2;
190
191 down(&dev->lock);
192
193 outb(0x00, io);
194 outb(dev->curvol, io);
195 msleep(20);
196
197 x1 = inb(io);
198 msleep(10);
199 x2 = inb(io);
200
201 up(&dev->lock);
202
203 if ((x1 == x2) && (x1 == 0xcf))
204 return 1;
205 return 0;
206}
207
208static int zol_do_ioctl(struct inode *inode, struct file *file,
209 unsigned int cmd, void *arg)
210{
211 struct video_device *dev = video_devdata(file);
212 struct zol_device *zol = dev->priv;
213
214 switch (cmd) {
215 case VIDIOCGCAP:
216 {
217 struct video_capability *v = arg;
218
219 memset(v,0,sizeof(*v));
220 v->type = VID_TYPE_TUNER;
221 v->channels = 1 + zol->stereo;
222 v->audios = 1;
223 strcpy(v->name, "Zoltrix Radio");
224 return 0;
225 }
226 case VIDIOCGTUNER:
227 {
228 struct video_tuner *v = arg;
229 if (v->tuner)
230 return -EINVAL;
231 strcpy(v->name, "FM");
232 v->rangelow = (int) (88.0 * 16000);
233 v->rangehigh = (int) (108.0 * 16000);
234 v->flags = zol_is_stereo(zol)
235 ? VIDEO_TUNER_STEREO_ON : 0;
236 v->flags |= VIDEO_TUNER_LOW;
237 v->mode = VIDEO_MODE_AUTO;
238 v->signal = 0xFFFF * zol_getsigstr(zol);
239 return 0;
240 }
241 case VIDIOCSTUNER:
242 {
243 struct video_tuner *v = arg;
244 if (v->tuner != 0)
245 return -EINVAL;
246 /* Only 1 tuner so no setting needed ! */
247 return 0;
248 }
249 case VIDIOCGFREQ:
250 {
251 unsigned long *freq = arg;
252 *freq = zol->curfreq;
253 return 0;
254 }
255 case VIDIOCSFREQ:
256 {
257 unsigned long *freq = arg;
258 zol->curfreq = *freq;
259 zol_setfreq(zol, zol->curfreq);
260 return 0;
261 }
262 case VIDIOCGAUDIO:
263 {
264 struct video_audio *v = arg;
265 memset(v, 0, sizeof(*v));
266 v->flags |= VIDEO_AUDIO_MUTABLE | VIDEO_AUDIO_VOLUME;
267 v->mode |= zol_is_stereo(zol)
268 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
269 v->volume = zol->curvol * 4096;
270 v->step = 4096;
271 strcpy(v->name, "Zoltrix Radio");
272 return 0;
273 }
274 case VIDIOCSAUDIO:
275 {
276 struct video_audio *v = arg;
277 if (v->audio)
278 return -EINVAL;
279
280 if (v->flags & VIDEO_AUDIO_MUTE)
281 zol_mute(zol);
282 else {
283 zol_unmute(zol);
284 zol_setvol(zol, v->volume / 4096);
285 }
286
287 if (v->mode & VIDEO_SOUND_STEREO) {
288 zol->stereo = 1;
289 zol_setfreq(zol, zol->curfreq);
290 }
291 if (v->mode & VIDEO_SOUND_MONO) {
292 zol->stereo = 0;
293 zol_setfreq(zol, zol->curfreq);
294 }
295 return 0;
296 }
297 default:
298 return -ENOIOCTLCMD;
299 }
300}
301
302static int zol_ioctl(struct inode *inode, struct file *file,
303 unsigned int cmd, unsigned long arg)
304{
305 return video_usercopy(inode, file, cmd, arg, zol_do_ioctl);
306}
307
308static struct zol_device zoltrix_unit;
309
310static struct file_operations zoltrix_fops =
311{
312 .owner = THIS_MODULE,
313 .open = video_exclusive_open,
314 .release = video_exclusive_release,
315 .ioctl = zol_ioctl,
316 .llseek = no_llseek,
317};
318
319static struct video_device zoltrix_radio =
320{
321 .owner = THIS_MODULE,
322 .name = "Zoltrix Radio Plus",
323 .type = VID_TYPE_TUNER,
324 .hardware = VID_HARDWARE_ZOLTRIX,
325 .fops = &zoltrix_fops,
326};
327
328static int __init zoltrix_init(void)
329{
330 if (io == -1) {
331 printk(KERN_ERR "You must set an I/O address with io=0x???\n");
332 return -EINVAL;
333 }
334 if ((io != 0x20c) && (io != 0x30c)) {
335 printk(KERN_ERR "zoltrix: invalid port, try 0x20c or 0x30c\n");
336 return -ENXIO;
337 }
338
339 zoltrix_radio.priv = &zoltrix_unit;
340 if (!request_region(io, 2, "zoltrix")) {
341 printk(KERN_ERR "zoltrix: port 0x%x already in use\n", io);
342 return -EBUSY;
343 }
344
345 if (video_register_device(&zoltrix_radio, VFL_TYPE_RADIO, radio_nr) == -1)
346 {
347 release_region(io, 2);
348 return -EINVAL;
349 }
350 printk(KERN_INFO "Zoltrix Radio Plus card driver.\n");
351
352 init_MUTEX(&zoltrix_unit.lock);
353
354 /* mute card - prevents noisy bootups */
355
356 /* this ensures that the volume is all the way down */
357
358 outb(0, io);
359 outb(0, io);
360 msleep(20);
361 inb(io + 3);
362
363 zoltrix_unit.curvol = 0;
364 zoltrix_unit.stereo = 1;
365
366 return 0;
367}
368
369MODULE_AUTHOR("C.van Schaik");
370MODULE_DESCRIPTION("A driver for the Zoltrix Radio Plus.");
371MODULE_LICENSE("GPL");
372
373module_param(io, int, 0);
374MODULE_PARM_DESC(io, "I/O address of the Zoltrix Radio Plus (0x20c or 0x30c)");
375module_param(radio_nr, int, 0);
376
377static void __exit zoltrix_cleanup_module(void)
378{
379 video_unregister_device(&zoltrix_radio);
380 release_region(io, 2);
381}
382
383module_init(zoltrix_init);
384module_exit(zoltrix_cleanup_module);
385