1 /* radio-aztech.c - Aztech radio card driver for Linux 2.2
3 * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
4 * Adapted to support the Video for Linux API by
5 * Russell Kroll <rkroll@exploits.org>. Based on original tuner code by:
9 * Jason Lewis (jlewis@twilight.vtc.vsc.edu)
10 * Scott McGrath (smcgrath@twilight.vtc.vsc.edu)
11 * William McGrath (wmcgrath@twilight.vtc.vsc.edu)
13 * The basis for this code may be found at http://bigbang.vtc.vsc.edu/fmradio/
14 * along with more information on the card itself.
17 * 1999-02-24 Russell Kroll <rkroll@exploits.org>
18 * Fine tuning/VIDEO_TUNER_LOW
19 * Range expanded to 87-108 MHz (from 87.9-107.8)
21 * Notable changes from the original source:
22 * - includes stripped down to the essentials
23 * - for loops used as delays replaced with udelay()
24 * - #defines removed, changed to static values
25 * - tuning structure changed - no more character arrays, other changes
28 #include <linux/module.h> /* Modules */
29 #include <linux/init.h> /* Initdata */
30 #include <linux/ioport.h> /* request_region */
31 #include <linux/delay.h> /* udelay */
32 #include <linux/videodev2.h> /* kernel radio structs */
33 #include <linux/version.h> /* for KERNEL_VERSION MACRO */
34 #include <linux/io.h> /* outb, outb_p */
35 #include <media/v4l2-device.h>
36 #include <media/v4l2-ioctl.h>
38 MODULE_AUTHOR("Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
39 MODULE_DESCRIPTION("A driver for the Aztech radio card.");
40 MODULE_LICENSE("GPL");
42 /* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */
44 #ifndef CONFIG_RADIO_AZTECH_PORT
45 #define CONFIG_RADIO_AZTECH_PORT -1
48 static int io
= CONFIG_RADIO_AZTECH_PORT
;
49 static int radio_nr
= -1;
50 static int radio_wait_time
= 1000;
52 module_param(io
, int, 0);
53 module_param(radio_nr
, int, 0);
54 MODULE_PARM_DESC(io
, "I/O address of the Aztech card (0x350 or 0x358)");
56 #define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
60 struct v4l2_device v4l2_dev
;
61 struct video_device vdev
;
64 unsigned long curfreq
;
69 static struct aztech aztech_card
;
71 static int volconvert(int level
)
73 level
>>= 14; /* Map 16bits down to 2 bit */
76 /* convert to card-friendly values */
87 return 0; /* Quieten gcc */
90 static void send_0_byte(struct aztech
*az
)
92 udelay(radio_wait_time
);
93 outb_p(2 + volconvert(az
->curvol
), az
->io
);
94 outb_p(64 + 2 + volconvert(az
->curvol
), az
->io
);
97 static void send_1_byte(struct aztech
*az
)
99 udelay (radio_wait_time
);
100 outb_p(128 + 2 + volconvert(az
->curvol
), az
->io
);
101 outb_p(128 + 64 + 2 + volconvert(az
->curvol
), az
->io
);
104 static int az_setvol(struct aztech
*az
, int vol
)
106 mutex_lock(&az
->lock
);
107 outb(volconvert(vol
), az
->io
);
108 mutex_unlock(&az
->lock
);
112 /* thanks to Michael Dwyer for giving me a dose of clues in
113 * the signal strength department..
115 * This card has a stereo bit - bit 0 set = mono, not set = stereo
116 * It also has a "signal" bit - bit 1 set = bad signal, not set = good
120 static int az_getsigstr(struct aztech
*az
)
124 mutex_lock(&az
->lock
);
125 if (inb(az
->io
) & 2) /* bit set = no signal present */
127 mutex_unlock(&az
->lock
);
131 static int az_getstereo(struct aztech
*az
)
135 mutex_lock(&az
->lock
);
136 if (inb(az
->io
) & 1) /* bit set = mono */
138 mutex_unlock(&az
->lock
);
142 static int az_setfreq(struct aztech
*az
, unsigned long frequency
)
146 mutex_lock(&az
->lock
);
148 az
->curfreq
= frequency
;
149 frequency
+= 171200; /* Add 10.7 MHz IF */
150 frequency
/= 800; /* Convert to 50 kHz units */
152 send_0_byte(az
); /* 0: LSB of frequency */
154 for (i
= 0; i
< 13; i
++) /* : frequency bits (1-13) */
155 if (frequency
& (1 << i
))
160 send_0_byte(az
); /* 14: test bit - always 0 */
161 send_0_byte(az
); /* 15: test bit - always 0 */
162 send_0_byte(az
); /* 16: band data 0 - always 0 */
163 if (az
->stereo
) /* 17: stereo (1 to enable) */
168 send_1_byte(az
); /* 18: band data 1 - unknown */
169 send_0_byte(az
); /* 19: time base - always 0 */
170 send_0_byte(az
); /* 20: spacing (0 = 25 kHz) */
171 send_1_byte(az
); /* 21: spacing (1 = 25 kHz) */
172 send_0_byte(az
); /* 22: spacing (0 = 25 kHz) */
173 send_1_byte(az
); /* 23: AM/FM (FM = 1, always) */
175 /* latch frequency */
177 udelay(radio_wait_time
);
178 outb_p(128 + 64 + volconvert(az
->curvol
), az
->io
);
180 mutex_unlock(&az
->lock
);
185 static int vidioc_querycap(struct file
*file
, void *priv
,
186 struct v4l2_capability
*v
)
188 strlcpy(v
->driver
, "radio-aztech", sizeof(v
->driver
));
189 strlcpy(v
->card
, "Aztech Radio", sizeof(v
->card
));
190 strlcpy(v
->bus_info
, "ISA", sizeof(v
->bus_info
));
191 v
->version
= RADIO_VERSION
;
192 v
->capabilities
= V4L2_CAP_TUNER
| V4L2_CAP_RADIO
;
196 static int vidioc_g_tuner(struct file
*file
, void *priv
,
197 struct v4l2_tuner
*v
)
199 struct aztech
*az
= video_drvdata(file
);
204 strlcpy(v
->name
, "FM", sizeof(v
->name
));
205 v
->type
= V4L2_TUNER_RADIO
;
207 v
->rangelow
= 87 * 16000;
208 v
->rangehigh
= 108 * 16000;
209 v
->rxsubchans
= V4L2_TUNER_SUB_MONO
| V4L2_TUNER_SUB_STEREO
;
210 v
->capability
= V4L2_TUNER_CAP_LOW
;
211 if (az_getstereo(az
))
212 v
->audmode
= V4L2_TUNER_MODE_STEREO
;
214 v
->audmode
= V4L2_TUNER_MODE_MONO
;
215 v
->signal
= 0xFFFF * az_getsigstr(az
);
220 static int vidioc_s_tuner(struct file
*file
, void *priv
,
221 struct v4l2_tuner
*v
)
223 return v
->index
? -EINVAL
: 0;
226 static int vidioc_g_input(struct file
*filp
, void *priv
, unsigned int *i
)
232 static int vidioc_s_input(struct file
*filp
, void *priv
, unsigned int i
)
234 return i
? -EINVAL
: 0;
237 static int vidioc_g_audio(struct file
*file
, void *priv
,
238 struct v4l2_audio
*a
)
241 strlcpy(a
->name
, "Radio", sizeof(a
->name
));
242 a
->capability
= V4L2_AUDCAP_STEREO
;
246 static int vidioc_s_audio(struct file
*file
, void *priv
,
247 struct v4l2_audio
*a
)
249 return a
->index
? -EINVAL
: 0;
252 static int vidioc_s_frequency(struct file
*file
, void *priv
,
253 struct v4l2_frequency
*f
)
255 struct aztech
*az
= video_drvdata(file
);
257 az_setfreq(az
, f
->frequency
);
261 static int vidioc_g_frequency(struct file
*file
, void *priv
,
262 struct v4l2_frequency
*f
)
264 struct aztech
*az
= video_drvdata(file
);
266 f
->type
= V4L2_TUNER_RADIO
;
267 f
->frequency
= az
->curfreq
;
271 static int vidioc_queryctrl(struct file
*file
, void *priv
,
272 struct v4l2_queryctrl
*qc
)
275 case V4L2_CID_AUDIO_MUTE
:
276 return v4l2_ctrl_query_fill(qc
, 0, 1, 1, 1);
277 case V4L2_CID_AUDIO_VOLUME
:
278 return v4l2_ctrl_query_fill(qc
, 0, 0xff, 1, 0xff);
283 static int vidioc_g_ctrl(struct file
*file
, void *priv
,
284 struct v4l2_control
*ctrl
)
286 struct aztech
*az
= video_drvdata(file
);
289 case V4L2_CID_AUDIO_MUTE
:
295 case V4L2_CID_AUDIO_VOLUME
:
296 ctrl
->value
= az
->curvol
* 6554;
302 static int vidioc_s_ctrl(struct file
*file
, void *priv
,
303 struct v4l2_control
*ctrl
)
305 struct aztech
*az
= video_drvdata(file
);
308 case V4L2_CID_AUDIO_MUTE
:
312 az_setvol(az
, az
->curvol
);
314 case V4L2_CID_AUDIO_VOLUME
:
315 az_setvol(az
, ctrl
->value
);
321 static const struct v4l2_file_operations aztech_fops
= {
322 .owner
= THIS_MODULE
,
323 .ioctl
= video_ioctl2
,
326 static const struct v4l2_ioctl_ops aztech_ioctl_ops
= {
327 .vidioc_querycap
= vidioc_querycap
,
328 .vidioc_g_tuner
= vidioc_g_tuner
,
329 .vidioc_s_tuner
= vidioc_s_tuner
,
330 .vidioc_g_audio
= vidioc_g_audio
,
331 .vidioc_s_audio
= vidioc_s_audio
,
332 .vidioc_g_input
= vidioc_g_input
,
333 .vidioc_s_input
= vidioc_s_input
,
334 .vidioc_g_frequency
= vidioc_g_frequency
,
335 .vidioc_s_frequency
= vidioc_s_frequency
,
336 .vidioc_queryctrl
= vidioc_queryctrl
,
337 .vidioc_g_ctrl
= vidioc_g_ctrl
,
338 .vidioc_s_ctrl
= vidioc_s_ctrl
,
341 static int __init
aztech_init(void)
343 struct aztech
*az
= &aztech_card
;
344 struct v4l2_device
*v4l2_dev
= &az
->v4l2_dev
;
347 strlcpy(v4l2_dev
->name
, "aztech", sizeof(v4l2_dev
->name
));
351 v4l2_err(v4l2_dev
, "you must set an I/O address with io=0x350 or 0x358\n");
355 if (!request_region(az
->io
, 2, "aztech")) {
356 v4l2_err(v4l2_dev
, "port 0x%x already in use\n", az
->io
);
360 res
= v4l2_device_register(NULL
, v4l2_dev
);
362 release_region(az
->io
, 2);
363 v4l2_err(v4l2_dev
, "Could not register v4l2_device\n");
367 mutex_init(&az
->lock
);
368 strlcpy(az
->vdev
.name
, v4l2_dev
->name
, sizeof(az
->vdev
.name
));
369 az
->vdev
.v4l2_dev
= v4l2_dev
;
370 az
->vdev
.fops
= &aztech_fops
;
371 az
->vdev
.ioctl_ops
= &aztech_ioctl_ops
;
372 az
->vdev
.release
= video_device_release_empty
;
373 video_set_drvdata(&az
->vdev
, az
);
375 if (video_register_device(&az
->vdev
, VFL_TYPE_RADIO
, radio_nr
) < 0) {
376 v4l2_device_unregister(v4l2_dev
);
377 release_region(az
->io
, 2);
381 v4l2_info(v4l2_dev
, "Aztech radio card driver v1.00/19990224 rkroll@exploits.org\n");
382 /* mute card - prevents noisy bootups */
387 static void __exit
aztech_exit(void)
389 struct aztech
*az
= &aztech_card
;
391 video_unregister_device(&az
->vdev
);
392 v4l2_device_unregister(&az
->v4l2_dev
);
393 release_region(az
->io
, 2);
396 module_init(aztech_init
);
397 module_exit(aztech_exit
);