]> err.no Git - linux-2.6/blob - drivers/media/radio/radio-aztech.c
V4L/DVB (5148): Convert radio-aztech to use video_ioctl2
[linux-2.6] / drivers / media / radio / radio-aztech.c
1 /* radio-aztech.c - Aztech radio card driver for Linux 2.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:
6  *
7  * Quay Ly
8  * Donald Song
9  * Jason Lewis      (jlewis@twilight.vtc.vsc.edu)
10  * Scott McGrath    (smcgrath@twilight.vtc.vsc.edu)
11  * William McGrath  (wmcgrath@twilight.vtc.vsc.edu)
12  *
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.
15  *
16  * History:
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)
20  *
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
26 */
27
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 <asm/io.h>             /* outb, outb_p                 */
33 #include <asm/uaccess.h>        /* copy to/from user            */
34 #include <linux/videodev2.h>    /* kernel radio structs         */
35 #include <media/v4l2-common.h>
36
37 #include <linux/version.h>      /* for KERNEL_VERSION MACRO     */
38 #define RADIO_VERSION KERNEL_VERSION(0,0,2)
39
40 static struct v4l2_queryctrl radio_qctrl[] = {
41         {
42                 .id            = V4L2_CID_AUDIO_MUTE,
43                 .name          = "Mute",
44                 .minimum       = 0,
45                 .maximum       = 1,
46                 .default_value = 1,
47                 .type          = V4L2_CTRL_TYPE_BOOLEAN,
48         },{
49                 .id            = V4L2_CID_AUDIO_VOLUME,
50                 .name          = "Volume",
51                 .minimum       = 0,
52                 .maximum       = 0xff,
53                 .step          = 1,
54                 .default_value = 0xff,
55                 .type          = V4L2_CTRL_TYPE_INTEGER,
56         }
57 };
58
59 /* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */
60
61 #ifndef CONFIG_RADIO_AZTECH_PORT
62 #define CONFIG_RADIO_AZTECH_PORT -1
63 #endif
64
65 static int io = CONFIG_RADIO_AZTECH_PORT;
66 static int radio_nr = -1;
67 static int radio_wait_time = 1000;
68 static struct mutex lock;
69
70 struct az_device
71 {
72         int curvol;
73         unsigned long curfreq;
74         int stereo;
75 };
76
77 static int volconvert(int level)
78 {
79         level>>=14;             /* Map 16bits down to 2 bit */
80         level&=3;
81
82         /* convert to card-friendly values */
83         switch (level)
84         {
85                 case 0:
86                         return 0;
87                 case 1:
88                         return 1;
89                 case 2:
90                         return 4;
91                 case 3:
92                         return 5;
93         }
94         return 0;       /* Quieten gcc */
95 }
96
97 static void send_0_byte (struct az_device *dev)
98 {
99         udelay(radio_wait_time);
100         outb_p(2+volconvert(dev->curvol), io);
101         outb_p(64+2+volconvert(dev->curvol), io);
102 }
103
104 static void send_1_byte (struct az_device *dev)
105 {
106         udelay (radio_wait_time);
107         outb_p(128+2+volconvert(dev->curvol), io);
108         outb_p(128+64+2+volconvert(dev->curvol), io);
109 }
110
111 static int az_setvol(struct az_device *dev, int vol)
112 {
113         mutex_lock(&lock);
114         outb (volconvert(vol), io);
115         mutex_unlock(&lock);
116         return 0;
117 }
118
119 /* thanks to Michael Dwyer for giving me a dose of clues in
120  * the signal strength department..
121  *
122  * This card has a stereo bit - bit 0 set = mono, not set = stereo
123  * It also has a "signal" bit - bit 1 set = bad signal, not set = good
124  *
125  */
126
127 static int az_getsigstr(struct az_device *dev)
128 {
129         if (inb(io) & 2)        /* bit set = no signal present */
130                 return 0;
131         return 1;               /* signal present */
132 }
133
134 static int az_getstereo(struct az_device *dev)
135 {
136         if (inb(io) & 1)        /* bit set = mono */
137                 return 0;
138         return 1;               /* stereo */
139 }
140
141 static int az_setfreq(struct az_device *dev, unsigned long frequency)
142 {
143         int  i;
144
145         frequency += 171200;            /* Add 10.7 MHz IF              */
146         frequency /= 800;               /* Convert to 50 kHz units      */
147
148         mutex_lock(&lock);
149
150         send_0_byte (dev);              /*  0: LSB of frequency       */
151
152         for (i = 0; i < 13; i++)        /*   : frequency bits (1-13)  */
153                 if (frequency & (1 << i))
154                         send_1_byte (dev);
155                 else
156                         send_0_byte (dev);
157
158         send_0_byte (dev);              /* 14: test bit - always 0    */
159         send_0_byte (dev);              /* 15: test bit - always 0    */
160         send_0_byte (dev);              /* 16: band data 0 - always 0 */
161         if (dev->stereo)                /* 17: stereo (1 to enable)   */
162                 send_1_byte (dev);
163         else
164                 send_0_byte (dev);
165
166         send_1_byte (dev);              /* 18: band data 1 - unknown  */
167         send_0_byte (dev);              /* 19: time base - always 0   */
168         send_0_byte (dev);              /* 20: spacing (0 = 25 kHz)   */
169         send_1_byte (dev);              /* 21: spacing (1 = 25 kHz)   */
170         send_0_byte (dev);              /* 22: spacing (0 = 25 kHz)   */
171         send_1_byte (dev);              /* 23: AM/FM (FM = 1, always) */
172
173         /* latch frequency */
174
175         udelay (radio_wait_time);
176         outb_p(128+64+volconvert(dev->curvol), io);
177
178         mutex_unlock(&lock);
179
180         return 0;
181 }
182
183 static int vidioc_querycap (struct file *file, void  *priv,
184                                         struct v4l2_capability *v)
185 {
186         strlcpy(v->driver, "radio-aztech", sizeof (v->driver));
187         strlcpy(v->card, "Aztech Radio", sizeof (v->card));
188         sprintf(v->bus_info,"ISA");
189         v->version = RADIO_VERSION;
190         v->capabilities = V4L2_CAP_TUNER;
191         return 0;
192 }
193
194 static int vidioc_g_tuner (struct file *file, void *priv,
195                                 struct v4l2_tuner *v)
196 {
197         struct video_device *dev = video_devdata(file);
198         struct az_device *az = dev->priv;
199
200         if (v->index > 0)
201                 return -EINVAL;
202
203         strcpy(v->name, "FM");
204         v->type = V4L2_TUNER_RADIO;
205
206         v->rangelow=(87*16000);
207         v->rangehigh=(108*16000);
208         v->rxsubchans =V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO;
209         v->capability=V4L2_TUNER_CAP_LOW;
210         if(az_getstereo(az))
211                 v->audmode = V4L2_TUNER_MODE_STEREO;
212         else
213                 v->audmode = V4L2_TUNER_MODE_MONO;
214         v->signal=0xFFFF*az_getsigstr(az);
215
216         return 0;
217 }
218
219
220 static int vidioc_s_tuner (struct file *file, void *priv,
221                                 struct v4l2_tuner *v)
222 {
223         if (v->index > 0)
224                 return -EINVAL;
225
226         return 0;
227 }
228
229 static int vidioc_s_frequency (struct file *file, void *priv,
230                                 struct v4l2_frequency *f)
231 {
232         struct video_device *dev = video_devdata(file);
233         struct az_device *az = dev->priv;
234
235         az->curfreq = f->frequency;
236         az_setfreq(az, az->curfreq);
237         return 0;
238 }
239
240 static int vidioc_g_frequency (struct file *file, void *priv,
241                                 struct v4l2_frequency *f)
242 {
243         struct video_device *dev = video_devdata(file);
244         struct az_device *az = dev->priv;
245
246         f->type = V4L2_TUNER_RADIO;
247         f->frequency = az->curfreq;
248
249         return 0;
250 }
251
252 static int vidioc_queryctrl (struct file *file, void *priv,
253                             struct v4l2_queryctrl *qc)
254 {
255         int i;
256
257         for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
258                 if (qc->id && qc->id == radio_qctrl[i].id) {
259                         memcpy(qc, &(radio_qctrl[i]),
260                                                 sizeof(*qc));
261                         return (0);
262                 }
263         }
264         return -EINVAL;
265 }
266
267 static int vidioc_g_ctrl (struct file *file, void *priv,
268                             struct v4l2_control *ctrl)
269 {
270         struct video_device *dev = video_devdata(file);
271         struct az_device *az = dev->priv;
272
273         switch (ctrl->id) {
274                 case V4L2_CID_AUDIO_MUTE:
275                         if (az->curvol==0)
276                                 ctrl->value=1;
277                         else
278                                 ctrl->value=0;
279                         return (0);
280                 case V4L2_CID_AUDIO_VOLUME:
281                         ctrl->value=az->curvol * 6554;
282                         return (0);
283         }
284         return -EINVAL;
285 }
286
287 static int vidioc_s_ctrl (struct file *file, void *priv,
288                             struct v4l2_control *ctrl)
289 {
290         struct video_device *dev = video_devdata(file);
291         struct az_device *az = dev->priv;
292
293         switch (ctrl->id) {
294                 case V4L2_CID_AUDIO_MUTE:
295                         if (ctrl->value) {
296                                 az_setvol(az,0);
297                         } else {
298                                 az_setvol(az,az->curvol);
299                         }
300                         return (0);
301                 case V4L2_CID_AUDIO_VOLUME:
302                         az_setvol(az,ctrl->value);
303                         return (0);
304         }
305         return -EINVAL;
306 }
307
308 static struct az_device aztech_unit;
309
310 static const struct file_operations aztech_fops = {
311         .owner          = THIS_MODULE,
312         .open           = video_exclusive_open,
313         .release        = video_exclusive_release,
314         .ioctl          = video_ioctl2,
315         .compat_ioctl   = v4l_compat_ioctl32,
316         .llseek         = no_llseek,
317 };
318
319 static struct video_device aztech_radio=
320 {
321         .owner              = THIS_MODULE,
322         .name               = "Aztech radio",
323         .type               = VID_TYPE_TUNER,
324         .hardware           = 0,
325         .fops               = &aztech_fops,
326         .vidioc_querycap    = vidioc_querycap,
327         .vidioc_g_tuner     = vidioc_g_tuner,
328         .vidioc_s_tuner     = vidioc_s_tuner,
329         .vidioc_g_frequency = vidioc_g_frequency,
330         .vidioc_s_frequency = vidioc_s_frequency,
331         .vidioc_queryctrl   = vidioc_queryctrl,
332         .vidioc_g_ctrl      = vidioc_g_ctrl,
333         .vidioc_s_ctrl      = vidioc_s_ctrl,
334 };
335
336 module_param_named(debug,aztech_radio.debug, int, 0644);
337 MODULE_PARM_DESC(debug,"activates debug info");
338
339 static int __init aztech_init(void)
340 {
341         if(io==-1)
342         {
343                 printk(KERN_ERR "You must set an I/O address with io=0x???\n");
344                 return -EINVAL;
345         }
346
347         if (!request_region(io, 2, "aztech"))
348         {
349                 printk(KERN_ERR "aztech: port 0x%x already in use\n", io);
350                 return -EBUSY;
351         }
352
353         mutex_init(&lock);
354         aztech_radio.priv=&aztech_unit;
355
356         if(video_register_device(&aztech_radio, VFL_TYPE_RADIO, radio_nr)==-1)
357         {
358                 release_region(io,2);
359                 return -EINVAL;
360         }
361
362         printk(KERN_INFO "Aztech radio card driver v1.00/19990224 rkroll@exploits.org\n");
363         /* mute card - prevents noisy bootups */
364         outb (0, io);
365         return 0;
366 }
367
368 MODULE_AUTHOR("Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
369 MODULE_DESCRIPTION("A driver for the Aztech radio card.");
370 MODULE_LICENSE("GPL");
371
372 module_param(io, int, 0);
373 module_param(radio_nr, int, 0);
374 MODULE_PARM_DESC(io, "I/O address of the Aztech card (0x350 or 0x358)");
375
376 static void __exit aztech_cleanup(void)
377 {
378         video_unregister_device(&aztech_radio);
379         release_region(io,2);
380 }
381
382 module_init(aztech_init);
383 module_exit(aztech_cleanup);