Merge git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6
[sfrench/cifs-2.6.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
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_g_audio (struct file *file, void *priv,
230                            struct v4l2_audio *a)
231 {
232         if (a->index > 1)
233                 return -EINVAL;
234
235         strcpy(a->name, "Radio");
236         a->capability = V4L2_AUDCAP_STEREO;
237         return 0;
238 }
239
240 static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
241 {
242         *i = 0;
243         return 0;
244 }
245
246 static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
247 {
248         if (i != 0)
249                 return -EINVAL;
250         return 0;
251 }
252
253
254 static int vidioc_s_audio (struct file *file, void *priv,
255                            struct v4l2_audio *a)
256 {
257         if (a->index != 0)
258                 return -EINVAL;
259
260         return 0;
261 }
262
263 static int vidioc_s_frequency (struct file *file, void *priv,
264                                 struct v4l2_frequency *f)
265 {
266         struct video_device *dev = video_devdata(file);
267         struct az_device *az = dev->priv;
268
269         az->curfreq = f->frequency;
270         az_setfreq(az, az->curfreq);
271         return 0;
272 }
273
274 static int vidioc_g_frequency (struct file *file, void *priv,
275                                 struct v4l2_frequency *f)
276 {
277         struct video_device *dev = video_devdata(file);
278         struct az_device *az = dev->priv;
279
280         f->type = V4L2_TUNER_RADIO;
281         f->frequency = az->curfreq;
282
283         return 0;
284 }
285
286 static int vidioc_queryctrl (struct file *file, void *priv,
287                             struct v4l2_queryctrl *qc)
288 {
289         int i;
290
291         for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
292                 if (qc->id && qc->id == radio_qctrl[i].id) {
293                         memcpy(qc, &(radio_qctrl[i]),
294                                                 sizeof(*qc));
295                         return (0);
296                 }
297         }
298         return -EINVAL;
299 }
300
301 static int vidioc_g_ctrl (struct file *file, void *priv,
302                             struct v4l2_control *ctrl)
303 {
304         struct video_device *dev = video_devdata(file);
305         struct az_device *az = dev->priv;
306
307         switch (ctrl->id) {
308                 case V4L2_CID_AUDIO_MUTE:
309                         if (az->curvol==0)
310                                 ctrl->value=1;
311                         else
312                                 ctrl->value=0;
313                         return (0);
314                 case V4L2_CID_AUDIO_VOLUME:
315                         ctrl->value=az->curvol * 6554;
316                         return (0);
317         }
318         return -EINVAL;
319 }
320
321 static int vidioc_s_ctrl (struct file *file, void *priv,
322                             struct v4l2_control *ctrl)
323 {
324         struct video_device *dev = video_devdata(file);
325         struct az_device *az = dev->priv;
326
327         switch (ctrl->id) {
328                 case V4L2_CID_AUDIO_MUTE:
329                         if (ctrl->value) {
330                                 az_setvol(az,0);
331                         } else {
332                                 az_setvol(az,az->curvol);
333                         }
334                         return (0);
335                 case V4L2_CID_AUDIO_VOLUME:
336                         az_setvol(az,ctrl->value);
337                         return (0);
338         }
339         return -EINVAL;
340 }
341
342 static struct az_device aztech_unit;
343
344 static const struct file_operations aztech_fops = {
345         .owner          = THIS_MODULE,
346         .open           = video_exclusive_open,
347         .release        = video_exclusive_release,
348         .ioctl          = video_ioctl2,
349 #ifdef CONFIG_COMPAT
350         .compat_ioctl   = v4l_compat_ioctl32,
351 #endif
352         .llseek         = no_llseek,
353 };
354
355 static struct video_device aztech_radio=
356 {
357         .owner              = THIS_MODULE,
358         .name               = "Aztech radio",
359         .type               = VID_TYPE_TUNER,
360         .fops               = &aztech_fops,
361         .vidioc_querycap    = vidioc_querycap,
362         .vidioc_g_tuner     = vidioc_g_tuner,
363         .vidioc_s_tuner     = vidioc_s_tuner,
364         .vidioc_g_audio     = vidioc_g_audio,
365         .vidioc_s_audio     = vidioc_s_audio,
366         .vidioc_g_input     = vidioc_g_input,
367         .vidioc_s_input     = vidioc_s_input,
368         .vidioc_g_frequency = vidioc_g_frequency,
369         .vidioc_s_frequency = vidioc_s_frequency,
370         .vidioc_queryctrl   = vidioc_queryctrl,
371         .vidioc_g_ctrl      = vidioc_g_ctrl,
372         .vidioc_s_ctrl      = vidioc_s_ctrl,
373 };
374
375 module_param_named(debug,aztech_radio.debug, int, 0644);
376 MODULE_PARM_DESC(debug,"activates debug info");
377
378 static int __init aztech_init(void)
379 {
380         if(io==-1)
381         {
382                 printk(KERN_ERR "You must set an I/O address with io=0x???\n");
383                 return -EINVAL;
384         }
385
386         if (!request_region(io, 2, "aztech"))
387         {
388                 printk(KERN_ERR "aztech: port 0x%x already in use\n", io);
389                 return -EBUSY;
390         }
391
392         mutex_init(&lock);
393         aztech_radio.priv=&aztech_unit;
394
395         if(video_register_device(&aztech_radio, VFL_TYPE_RADIO, radio_nr)==-1)
396         {
397                 release_region(io,2);
398                 return -EINVAL;
399         }
400
401         printk(KERN_INFO "Aztech radio card driver v1.00/19990224 rkroll@exploits.org\n");
402         /* mute card - prevents noisy bootups */
403         outb (0, io);
404         return 0;
405 }
406
407 MODULE_AUTHOR("Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
408 MODULE_DESCRIPTION("A driver for the Aztech radio card.");
409 MODULE_LICENSE("GPL");
410
411 module_param(io, int, 0);
412 module_param(radio_nr, int, 0);
413 MODULE_PARM_DESC(io, "I/O address of the Aztech card (0x350 or 0x358)");
414
415 static void __exit aztech_cleanup(void)
416 {
417         video_unregister_device(&aztech_radio);
418         release_region(io,2);
419 }
420
421 module_init(aztech_init);
422 module_exit(aztech_cleanup);