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