import of ftp.dlink.com/GPL/DSMG-600_reB/ppclinux.tar.gz
[linux-2.4.21-pre4.git] / drivers / media / radio / radio-maxiradio.c
1 /* 
2  * Guillemot Maxi Radio FM 2000 PCI radio card driver for Linux 
3  * (C) 2001 Dimitromanolakis Apostolos <apdim@grecian.net>
4  *
5  * Based in the radio Maestro PCI driver. Actually it uses the same chip
6  * for radio but different pci controller.
7  *
8  * I didn't have any specs I reversed engineered the protocol from
9  * the windows driver (radio.dll). 
10  *
11  * The card uses the TEA5757 chip that includes a search function but it
12  * is useless as I haven't found any way to read back the frequency. If 
13  * anybody does please mail me.
14  *
15  * For the pdf file see:
16  * http://www.semiconductors.philips.com/pip/TEA5757H/V1
17  *
18  *
19  * CHANGES:
20  *   0.75b
21  *     - better pci interface thanks to Francois Romieu <romieu@cogenit.fr>
22  *
23  *   0.75
24  *     - tiding up
25  *     - removed support for multiple devices as it didn't work anyway
26  *
27  * BUGS: 
28  *   - card unmutes if you change frequency
29  *
30  */
31
32
33 #include <linux/module.h>
34 #include <linux/init.h>
35 #include <linux/ioport.h>
36 #include <linux/delay.h>
37 #include <linux/sched.h>
38 #include <asm/io.h>
39 #include <asm/uaccess.h>
40 #include <asm/semaphore.h>
41 #include <linux/pci.h>
42 #include <linux/videodev.h>
43
44 /* version 0.75      Sun Feb  4 22:51:27 EET 2001 */
45 #define DRIVER_VERSION  "0.75"
46
47 #ifndef PCI_VENDOR_ID_GUILLEMOT
48 #define PCI_VENDOR_ID_GUILLEMOT 0x5046
49 #endif
50
51 #ifndef PCI_DEVICE_ID_GUILLEMOT
52 #define PCI_DEVICE_ID_GUILLEMOT_MAXIRADIO 0x1001
53 #endif
54
55
56 /* TEA5757 pin mappings */
57 const int clk = 1, data = 2, wren = 4, mo_st = 8, power = 16 ;
58
59 static int radio_nr = -1;
60 MODULE_PARM(radio_nr, "i");
61
62
63 #define FREQ_LO          50*16000
64 #define FREQ_HI         150*16000
65
66 #define FREQ_IF         171200 /* 10.7*16000   */
67 #define FREQ_STEP       200    /* 12.5*16      */
68
69 #define FREQ2BITS(x)    ((( (unsigned int)(x)+FREQ_IF+(FREQ_STEP<<1))\
70                         /(FREQ_STEP<<2))<<2) /* (x==fmhz*16*1000) -> bits */
71
72 #define BITS2FREQ(x)    ((x) * FREQ_STEP - FREQ_IF)
73
74
75 static int radio_open(struct video_device *, int);
76 static int radio_ioctl(struct video_device *, unsigned int, void *);
77 static void radio_close(struct video_device *);
78
79 static struct video_device maxiradio_radio=
80 {
81         owner:          THIS_MODULE,
82         name:           "Maxi Radio FM2000 radio",
83         type:           VID_TYPE_TUNER,
84         hardware:       VID_HARDWARE_SF16MI,
85         open:           radio_open,
86         close:          radio_close,
87         ioctl:          radio_ioctl,
88 };
89
90 static struct radio_device
91 {
92         __u16   io,     /* base of radio io */
93                 muted,  /* VIDEO_AUDIO_MUTE */
94                 stereo, /* VIDEO_TUNER_STEREO_ON */     
95                 tuned;  /* signal strength (0 or 0xffff) */
96                 
97         unsigned long freq;
98         
99         struct  semaphore lock;
100 } radio_unit = {0, 0, 0, 0, };
101
102
103 static void sleep_125ms(void)
104 {
105         current->state = TASK_INTERRUPTIBLE;
106         schedule_timeout(HZ >> 3);
107 }
108
109
110 static void outbit(unsigned long bit, __u16 io)
111 {
112         if(bit != 0)
113                 {
114                         outb(  power|wren|data     ,io); udelay(4);
115                         outb(  power|wren|data|clk ,io); udelay(4);
116                         outb(  power|wren|data     ,io); udelay(4);
117                 }
118         else    
119                 {
120                         outb(  power|wren          ,io); udelay(4);
121                         outb(  power|wren|clk      ,io); udelay(4);
122                         outb(  power|wren          ,io); udelay(4);
123                 }
124 }
125
126 static void turn_power(__u16 io, int p)
127 {
128         if(p != 0) outb(power, io); else outb(0,io);
129 }
130
131
132 static void set_freq(__u16 io, __u32 data)
133 {
134         unsigned long int si;
135         int bl;
136         
137         /* TEA5757 shift register bits (see pdf) */
138
139         outbit(0,io); // 24  search 
140         outbit(1,io); // 23  search up/down
141         
142         outbit(0,io); // 22  stereo/mono
143
144         outbit(0,io); // 21  band
145         outbit(0,io); // 20  band (only 00=FM works I think)
146
147         outbit(0,io); // 19  port ?
148         outbit(0,io); // 18  port ?
149         
150         outbit(0,io); // 17  search level
151         outbit(0,io); // 16  search level
152  
153         si = 0x8000;
154         for(bl = 1; bl <= 16 ; bl++) { outbit(data & si,io); si >>=1; }
155         
156         outb(power,io);
157 }
158
159 static int get_stereo(__u16 io)
160 {       
161         outb(power,io); udelay(4);
162         return !(inb(io) & mo_st);
163 }
164
165 static int get_tune(__u16 io)
166 {       
167         outb(power+clk,io); udelay(4);
168         return !(inb(io) & mo_st);
169 }
170
171
172 inline static int radio_function(struct video_device *dev, 
173                                  unsigned int cmd, void *arg)
174 {
175         struct radio_device *card=dev->priv;
176         switch(cmd) {
177                 case VIDIOCGCAP: {
178                         struct video_capability v;
179                         
180                         strcpy(v.name, "Maxi Radio FM2000 radio");
181                         v.type=VID_TYPE_TUNER;
182                         v.channels=v.audios=1;
183                         v.maxwidth=v.maxheight=v.minwidth=v.minheight=0;
184                         
185                         if(copy_to_user(arg,&v,sizeof(v)))
186                                 return -EFAULT;
187                                 
188                         return 0;
189                 }
190                 case VIDIOCGTUNER: {
191                         struct video_tuner v;
192                         
193                         if(copy_from_user(&v, arg,sizeof(v))!=0)
194                                 return -EFAULT;
195                                 
196                         if(v.tuner)
197                                 return -EINVAL;
198                                 
199                         card->stereo = 0xffff * get_stereo(card->io);
200                         card->tuned = 0xffff * get_tune(card->io);
201                         
202                         v.flags = VIDEO_TUNER_LOW | card->stereo;
203                         v.signal = card->tuned;
204                         
205                         strcpy(v.name, "FM");
206                         
207                         v.rangelow = FREQ_LO;
208                         v.rangehigh = FREQ_HI;
209                         v.mode = VIDEO_MODE_AUTO;
210                         
211                         if(copy_to_user(arg,&v, sizeof(v)))
212                                 return -EFAULT;
213                                 
214                   return 0;
215                 }
216                 case VIDIOCSTUNER: {
217                         struct video_tuner v;
218                         
219                         if(copy_from_user(&v, arg, sizeof(v)))
220                                 return -EFAULT;
221                                 
222                         if(v.tuner!=0)
223                                 return -EINVAL;
224                                 
225                         return 0;
226                 }
227                 case VIDIOCGFREQ: {
228                         unsigned long tmp=card->freq;
229                         
230                         if(copy_to_user(arg, &tmp, sizeof(tmp)))
231                                 return -EFAULT;
232                                 
233                         return 0;
234                 }
235                 
236                 case VIDIOCSFREQ: {
237                         unsigned long tmp;
238                         
239                         if(copy_from_user(&tmp, arg, sizeof(tmp)))
240                                 return -EFAULT;
241                                 
242                         if ( tmp<FREQ_LO || tmp>FREQ_HI )
243                                 return -EINVAL;
244                                 
245                         card->freq = tmp;
246                         
247                         set_freq(card->io, FREQ2BITS(card->freq));
248                         sleep_125ms();
249
250                         return 0;
251                 }
252                 case VIDIOCGAUDIO: {    
253                         struct video_audio v;
254                         strcpy(v.name, "Radio");
255                         v.audio=v.volume=v.bass=v.treble=v.balance=v.step=0;
256                         v.flags=VIDEO_AUDIO_MUTABLE | card->muted;
257                         v.mode=VIDEO_SOUND_STEREO;
258                         if(copy_to_user(arg,&v, sizeof(v)))
259                                 return -EFAULT;
260                         return 0;               
261                 }
262                 
263                 case VIDIOCSAUDIO: {
264                         struct video_audio v;
265                         
266                         if(copy_from_user(&v, arg, sizeof(v)))
267                                 return -EFAULT;
268                                 
269                         if(v.audio)
270                                 return -EINVAL;
271                                 
272                         
273                         card->muted = v.flags & VIDEO_AUDIO_MUTE;
274                                 
275                         if(card->muted)
276                                 turn_power(card->io, 0);
277                         else
278                                 set_freq(card->io, FREQ2BITS(card->freq));
279                                 
280                         return 0;
281                 }
282                 
283                 case VIDIOCGUNIT: {
284                         struct video_unit v;
285                         v.video=VIDEO_NO_UNIT;
286                         v.vbi=VIDEO_NO_UNIT;
287                         v.radio=dev->minor;
288                         v.audio=0;
289                         v.teletext=VIDEO_NO_UNIT;
290                         if(copy_to_user(arg, &v, sizeof(v)))
291                                 return -EFAULT;
292                         return 0;               
293                 }
294                 default: return -ENOIOCTLCMD;
295         }
296 }
297
298 static int radio_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
299 {
300         struct radio_device *card=dev->priv;
301         int ret;
302         down(&card->lock);
303         ret = radio_function(dev, cmd, arg);
304         up(&card->lock);
305         return ret;
306 }
307
308 static int radio_open(struct video_device *dev, int flags)
309 {
310         return 0;
311 }
312
313 static void radio_close(struct video_device *dev)
314 {
315 }
316
317 MODULE_AUTHOR("Dimitromanolakis Apostolos, apdim@grecian.net");
318 MODULE_DESCRIPTION("Radio driver for the Guillemot Maxi Radio FM2000 radio.");
319 MODULE_LICENSE("GPL");
320
321
322 EXPORT_NO_SYMBOLS;
323
324 static int __devinit maxiradio_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
325 {
326         if(!request_region(pci_resource_start(pdev, 0),
327                            pci_resource_len(pdev, 0), "Maxi Radio FM 2000")) {
328                 printk(KERN_ERR "radio-maxiradio: can't reserve I/O ports\n");
329                 goto err_out;
330         }
331
332         if (pci_enable_device(pdev))
333                 goto err_out_free_region;
334
335         radio_unit.io = pci_resource_start(pdev, 0);
336         init_MUTEX(&radio_unit.lock);
337         maxiradio_radio.priv = &radio_unit;
338
339         if(video_register_device(&maxiradio_radio, VFL_TYPE_RADIO, radio_nr)==-1) {
340                 printk("radio-maxiradio: can't register device!");
341                 goto err_out_free_region;
342         }
343
344         printk(KERN_INFO "radio-maxiradio: version "
345                DRIVER_VERSION
346                " time "
347                __TIME__ "  "
348                __DATE__
349                "\n");
350
351         printk(KERN_INFO "radio-maxiradio: found Guillemot MAXI Radio device (io = 0x%x)\n",
352                radio_unit.io);
353         return 0;
354
355 err_out_free_region:
356         release_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
357 err_out:
358         return -ENODEV;
359 }
360
361 static void __devexit maxiradio_remove_one(struct pci_dev *pdev)
362 {
363         video_unregister_device(&maxiradio_radio);
364         release_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
365 }
366
367 static struct pci_device_id maxiradio_pci_tbl[] __devinitdata = {
368         { PCI_VENDOR_ID_GUILLEMOT, PCI_DEVICE_ID_GUILLEMOT_MAXIRADIO,
369                 PCI_ANY_ID, PCI_ANY_ID, },
370         { 0,}
371 };
372
373 MODULE_DEVICE_TABLE(pci, maxiradio_pci_tbl);
374
375 static struct pci_driver maxiradio_driver = {
376         name:           "radio-maxiradio",
377         id_table:       maxiradio_pci_tbl,
378         probe:          maxiradio_init_one,
379         remove:         __devexit_p(maxiradio_remove_one),
380 };
381
382 int __init maxiradio_radio_init(void)
383 {
384         return pci_module_init(&maxiradio_driver);
385 }
386
387 void __exit maxiradio_radio_exit(void)
388 {
389         pci_unregister_driver(&maxiradio_driver);
390 }
391
392 module_init(maxiradio_radio_init);
393 module_exit(maxiradio_radio_exit);