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/io.h> /* outb, outb_p */
34 #include <media/v4l2-device.h>
35 #include <media/v4l2-ioctl.h>
37 MODULE_AUTHOR("Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
38 MODULE_DESCRIPTION("A driver for the Aztech radio card.");
39 MODULE_LICENSE("GPL");
40 MODULE_VERSION("0.0.3");
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)");
58 struct v4l2_device v4l2_dev
;
59 struct video_device vdev
;
62 unsigned long curfreq
;
67 static struct aztech aztech_card
;
69 static int volconvert(int level
)
71 level
>>= 14; /* Map 16bits down to 2 bit */
74 /* convert to card-friendly values */
85 return 0; /* Quieten gcc */
88 static void send_0_byte(struct aztech
*az
)
90 udelay(radio_wait_time
);
91 outb_p(2 + volconvert(az
->curvol
), az
->io
);
92 outb_p(64 + 2 + volconvert(az
->curvol
), az
->io
);
95 static void send_1_byte(struct aztech
*az
)
97 udelay (radio_wait_time
);
98 outb_p(128 + 2 + volconvert(az
->curvol
), az
->io
);
99 outb_p(128 + 64 + 2 + volconvert(az
->curvol
), az
->io
);
102 static int az_setvol(struct aztech
*az
, int vol
)
104 mutex_lock(&az
->lock
);
105 outb(volconvert(vol
), az
->io
);
106 mutex_unlock(&az
->lock
);
110 /* thanks to Michael Dwyer for giving me a dose of clues in
111 * the signal strength department..
113 * This card has a stereo bit - bit 0 set = mono, not set = stereo
114 * It also has a "signal" bit - bit 1 set = bad signal, not set = good
118 static int az_getsigstr(struct aztech
*az
)
122 mutex_lock(&az
->lock
);
123 if (inb(az
->io
) & 2) /* bit set = no signal present */
125 mutex_unlock(&az
->lock
);
129 static int az_getstereo(struct aztech
*az
)
133 mutex_lock(&az
->lock
);
134 if (inb(az
->io
) & 1) /* bit set = mono */
136 mutex_unlock(&az
->lock
);
140 static int az_setfreq(struct aztech
*az
, unsigned long frequency
)
144 mutex_lock(&az
->lock
);
146 az
->curfreq
= frequency
;
147 frequency
+= 171200; /* Add 10.7 MHz IF */
148 frequency
/= 800; /* Convert to 50 kHz units */
150 send_0_byte(az
); /* 0: LSB of frequency */
152 for (i
= 0; i
< 13; i
++) /* : frequency bits (1-13) */
153 if (frequency
& (1 << i
))
158 send_0_byte(az
); /* 14: test bit - always 0 */
159 send_0_byte(az
); /* 15: test bit - always 0 */
160 send_0_byte(az
); /* 16: band data 0 - always 0 */
161 if (az
->stereo
) /* 17: stereo (1 to enable) */
166 send_1_byte(az
); /* 18: band data 1 - unknown */
167 send_0_byte(az
); /* 19: time base - always 0 */
168 send_0_byte(az
); /* 20: spacing (0 = 25 kHz) */
169 send_1_byte(az
); /* 21: spacing (1 = 25 kHz) */
170 send_0_byte(az
); /* 22: spacing (0 = 25 kHz) */
171 send_1_byte(az
); /* 23: AM/FM (FM = 1, always) */
173 /* latch frequency */
175 udelay(radio_wait_time
);
176 outb_p(128 + 64 + volconvert(az
->curvol
), az
->io
);
178 mutex_unlock(&az
->lock
);
183 static int vidioc_querycap(struct file
*file
, void *priv
,
184 struct v4l2_capability
*v
)
186 strlcpy(v
->driver
, "radio-aztech", sizeof(v
->driver
));
187 strlcpy(v
->card
, "Aztech Radio", sizeof(v
->card
));
188 strlcpy(v
->bus_info
, "ISA", sizeof(v
->bus_info
));
189 v
->capabilities
= V4L2_CAP_TUNER
| V4L2_CAP_RADIO
;
193 static int vidioc_g_tuner(struct file
*file
, void *priv
,
194 struct v4l2_tuner
*v
)
196 struct aztech
*az
= video_drvdata(file
);
201 strlcpy(v
->name
, "FM", sizeof(v
->name
));
202 v
->type
= V4L2_TUNER_RADIO
;
204 v
->rangelow
= 87 * 16000;
205 v
->rangehigh
= 108 * 16000;
206 v
->rxsubchans
= V4L2_TUNER_SUB_MONO
| V4L2_TUNER_SUB_STEREO
;
207 v
->capability
= V4L2_TUNER_CAP_LOW
;
208 if (az_getstereo(az
))
209 v
->audmode
= V4L2_TUNER_MODE_STEREO
;
211 v
->audmode
= V4L2_TUNER_MODE_MONO
;
212 v
->signal
= 0xFFFF * az_getsigstr(az
);
217 static int vidioc_s_tuner(struct file
*file
, void *priv
,
218 struct v4l2_tuner
*v
)
220 return v
->index
? -EINVAL
: 0;
223 static int vidioc_g_input(struct file
*filp
, void *priv
, unsigned int *i
)
229 static int vidioc_s_input(struct file
*filp
, void *priv
, unsigned int i
)
231 return i
? -EINVAL
: 0;
234 static int vidioc_g_audio(struct file
*file
, void *priv
,
235 struct v4l2_audio
*a
)
238 strlcpy(a
->name
, "Radio", sizeof(a
->name
));
239 a
->capability
= V4L2_AUDCAP_STEREO
;
243 static int vidioc_s_audio(struct file
*file
, void *priv
,
244 struct v4l2_audio
*a
)
246 return a
->index
? -EINVAL
: 0;
249 static int vidioc_s_frequency(struct file
*file
, void *priv
,
250 struct v4l2_frequency
*f
)
252 struct aztech
*az
= video_drvdata(file
);
254 if (f
->tuner
!= 0 || f
->type
!= V4L2_TUNER_RADIO
)
256 az_setfreq(az
, f
->frequency
);
260 static int vidioc_g_frequency(struct file
*file
, void *priv
,
261 struct v4l2_frequency
*f
)
263 struct aztech
*az
= video_drvdata(file
);
267 f
->type
= V4L2_TUNER_RADIO
;
268 f
->frequency
= az
->curfreq
;
272 static int vidioc_queryctrl(struct file
*file
, void *priv
,
273 struct v4l2_queryctrl
*qc
)
276 case V4L2_CID_AUDIO_MUTE
:
277 return v4l2_ctrl_query_fill(qc
, 0, 1, 1, 1);
278 case V4L2_CID_AUDIO_VOLUME
:
279 return v4l2_ctrl_query_fill(qc
, 0, 0xff, 1, 0xff);
284 static int vidioc_g_ctrl(struct file
*file
, void *priv
,
285 struct v4l2_control
*ctrl
)
287 struct aztech
*az
= video_drvdata(file
);
290 case V4L2_CID_AUDIO_MUTE
:
296 case V4L2_CID_AUDIO_VOLUME
:
297 ctrl
->value
= az
->curvol
* 6554;
303 static int vidioc_s_ctrl(struct file
*file
, void *priv
,
304 struct v4l2_control
*ctrl
)
306 struct aztech
*az
= video_drvdata(file
);
309 case V4L2_CID_AUDIO_MUTE
:
313 az_setvol(az
, az
->curvol
);
315 case V4L2_CID_AUDIO_VOLUME
:
316 az_setvol(az
, ctrl
->value
);
322 static const struct v4l2_file_operations aztech_fops
= {
323 .owner
= THIS_MODULE
,
324 .unlocked_ioctl
= video_ioctl2
,
327 static const struct v4l2_ioctl_ops aztech_ioctl_ops
= {
328 .vidioc_querycap
= vidioc_querycap
,
329 .vidioc_g_tuner
= vidioc_g_tuner
,
330 .vidioc_s_tuner
= vidioc_s_tuner
,
331 .vidioc_g_audio
= vidioc_g_audio
,
332 .vidioc_s_audio
= vidioc_s_audio
,
333 .vidioc_g_input
= vidioc_g_input
,
334 .vidioc_s_input
= vidioc_s_input
,
335 .vidioc_g_frequency
= vidioc_g_frequency
,
336 .vidioc_s_frequency
= vidioc_s_frequency
,
337 .vidioc_queryctrl
= vidioc_queryctrl
,
338 .vidioc_g_ctrl
= vidioc_g_ctrl
,
339 .vidioc_s_ctrl
= vidioc_s_ctrl
,
342 static int __init
aztech_init(void)
344 struct aztech
*az
= &aztech_card
;
345 struct v4l2_device
*v4l2_dev
= &az
->v4l2_dev
;
348 strlcpy(v4l2_dev
->name
, "aztech", sizeof(v4l2_dev
->name
));
352 v4l2_err(v4l2_dev
, "you must set an I/O address with io=0x350 or 0x358\n");
356 if (!request_region(az
->io
, 2, "aztech")) {
357 v4l2_err(v4l2_dev
, "port 0x%x already in use\n", az
->io
);
361 res
= v4l2_device_register(NULL
, v4l2_dev
);
363 release_region(az
->io
, 2);
364 v4l2_err(v4l2_dev
, "Could not register v4l2_device\n");
368 mutex_init(&az
->lock
);
369 strlcpy(az
->vdev
.name
, v4l2_dev
->name
, sizeof(az
->vdev
.name
));
370 az
->vdev
.v4l2_dev
= v4l2_dev
;
371 az
->vdev
.fops
= &aztech_fops
;
372 az
->vdev
.ioctl_ops
= &aztech_ioctl_ops
;
373 az
->vdev
.release
= video_device_release_empty
;
374 video_set_drvdata(&az
->vdev
, az
);
375 /* mute card - prevents noisy bootups */
378 if (video_register_device(&az
->vdev
, VFL_TYPE_RADIO
, radio_nr
) < 0) {
379 v4l2_device_unregister(v4l2_dev
);
380 release_region(az
->io
, 2);
384 v4l2_info(v4l2_dev
, "Aztech radio card driver v1.00/19990224 rkroll@exploits.org\n");
388 static void __exit
aztech_exit(void)
390 struct aztech
*az
= &aztech_card
;
392 video_unregister_device(&az
->vdev
);
393 v4l2_device_unregister(&az
->v4l2_dev
);
394 release_region(az
->io
, 2);
397 module_init(aztech_init
);
398 module_exit(aztech_exit
);