1 /* zoltrix radio plus driver for Linux radio support
2 * (c) 1998 C. van Schaik <carl@leg.uct.ac.za>
5 * Due to the inconsistancy in reading from the signal flags
6 * it is difficult to get an accurate tuned signal.
8 * There seems to be a problem with the volume setting that I must still
10 * It seems that the card has is not linear to 0 volume. It cuts off
11 * at a low frequency, and it is not possible (at least I have not found)
12 * to get fine volume control over the low volume range.
14 * Some code derived from code by Frans Brinkman
17 #include <linux/module.h> /* Modules */
18 #include <linux/init.h> /* Initdata */
19 #include <linux/ioport.h> /* check_region, request_region */
20 #include <linux/delay.h> /* udelay */
21 #include <asm/io.h> /* outb, outb_p */
22 #include <asm/uaccess.h> /* copy to/from user */
23 #include <linux/videodev.h> /* kernel radio structs */
24 #include <linux/config.h> /* CONFIG_RADIO_ZOLTRIX_PORT */
26 #ifndef CONFIG_RADIO_ZOLTRIX_PORT
27 #define CONFIG_RADIO_ZOLTRIX_PORT -1
30 static int io
= CONFIG_RADIO_ZOLTRIX_PORT
;
36 unsigned long curfreq
;
43 static void sleep_delay(long n
)
45 /* Sleep nicely for 'n' uS */
46 int d
= n
/ (1000000 / HZ
);
51 unsigned long x
= jiffies
;
52 while ((jiffies
- x
) <= d
)
57 static void zol_mute(struct zol_device
*dev
)
62 inb(io
+ 3); /* Zoltrix needs to be read to confirm */
65 static void zol_on(int vol
)
73 static int zol_setvol(struct zol_device
*dev
, int vol
)
75 if (vol
== dev
->curvol
) { /* requested volume = current */
76 if (dev
->muted
) { /* user is unmuting the card */
82 if (vol
== 0) { /* volume = 0 means mute the card */
94 static int zol_setfreq(struct zol_device
*dev
, unsigned long freq
)
96 /* tunes the radio to the desired frequency */
97 unsigned long long bitmask
, f
, m
;
100 m
= (freq
* 25 / 4 - 8800) * 2;
101 f
= (unsigned long long) m
+ 0x4d1c;
103 bitmask
= 0xc480402c10080000ull
;
111 bitmask
= (bitmask
^ ((f
& 0xff) << 47) ^ ((f
& 0xff00) << 30) ^ ( /*stereo */ 0 << 31));
113 if ((bitmask
& 0x8000000000000000ull
) != 0) {
130 /* termination sequence */
138 /* Get signal strength */
140 int zol_getsigstr(struct zol_device
*dev
)
144 outb(0x00, io
); /* This stuff I found to do nothing */
145 outb(dev
->curvol
, io
);
152 if ((a
== b
) && (a
== 0xdf)) /* I found this out by playing */
153 /* with a binary scanner on the card io */
158 if (inb(io
) & 2) /* bit set = no signal present */
160 return 1; /* signal present */
163 static int zol_ioctl(struct video_device
*dev
, unsigned int cmd
, void *arg
)
165 struct zol_device
*rt
= dev
->priv
;
170 struct video_capability v
;
171 v
.type
= VID_TYPE_TUNER
;
174 /* No we don't do pictures */
179 strcpy(v
.name
, "Zoltrix Radio");
180 if (copy_to_user(arg
, &v
, sizeof(v
)))
186 struct video_tuner v
;
187 if (copy_from_user(&v
, arg
, sizeof(v
)) != 0)
189 if (v
.tuner
) /* Only 1 tuner */
191 v
.rangelow
= (int) (88.0 * 16);
192 v
.rangehigh
= (int) (108.0 * 16);
194 v
.mode
= VIDEO_MODE_AUTO
;
195 v
.signal
= 0xFFFF * zol_getsigstr(rt
);
196 if (copy_to_user(arg
, &v
, sizeof(v
)))
202 struct video_tuner v
;
203 if (copy_from_user(&v
, arg
, sizeof(v
)))
207 /* Only 1 tuner so no setting needed ! */
211 if (copy_to_user(arg
, &rt
->curfreq
, sizeof(rt
->curfreq
)))
215 if (copy_from_user(&rt
->curfreq
, arg
, sizeof(rt
->curfreq
)))
217 zol_setfreq(rt
, rt
->curfreq
);
221 struct video_audio v
;
222 memset(&v
, 0, sizeof(v
));
223 v
.flags
|= VIDEO_AUDIO_MUTABLE
| VIDEO_AUDIO_VOLUME
;
224 v
.volume
= rt
->curvol
* 4096;
226 strcpy(v
.name
, "Radio");
227 if (copy_to_user(arg
, &v
, sizeof(v
)))
233 struct video_audio v
;
234 if (copy_from_user(&v
, arg
, sizeof(v
)))
239 if (v
.flags
& VIDEO_AUDIO_MUTE
)
242 zol_setvol(rt
, v
.volume
/ 4096);
251 static int zol_open(struct video_device
*dev
, int flags
)
260 static void zol_close(struct video_device
*dev
)
266 static struct zol_device zoltrix_unit
;
268 static struct video_device zoltrix_radio
=
270 "Zoltrix Radio Plus",
272 VID_HARDWARE_ZOLTRIX
,
275 NULL
, /* Can't read (no capture ability) */
276 NULL
, /* Can't write */
283 __initfunc(int zoltrix_init(struct video_init
*v
))
285 if (check_region(io
, 2)) {
286 printk(KERN_ERR
"zoltrix: port 0x%x already in use\n", io
);
289 zoltrix_radio
.priv
= &zoltrix_unit
;
291 if (video_register_device(&zoltrix_radio
, VFL_TYPE_RADIO
) == -1)
294 request_region(io
, 2, "zoltrix");
295 printk(KERN_INFO
"Zoltrix Radio Plus card driver.\n");
297 /* mute card - prevents noisy bootups */
299 /* this ensures that the volume is all the way down */
306 zoltrix_unit
.curvol
= 0;
313 MODULE_AUTHOR("C.van Schaik");
314 MODULE_DESCRIPTION("A driver for the Zoltrix Radio Plus.");
315 MODULE_PARM(io
, "i");
316 MODULE_PARM_DESC(io
, "I/O address of the Zoltrix Radio Plus (0x20c or 0x30c)");
320 int init_module(void)
323 printk(KERN_ERR
"You must set an I/O address with io=0x???\n");
326 return zoltrix_init(NULL
);
329 void cleanup_module(void)
331 video_unregister_device(&zoltrix_radio
);
332 release_region(io
, 2);