ignore the bootloader's commandline on orion
[oweals/openwrt.git] / package / fonera-mp3-drv / src / mp3_drv.c
1 /*
2 * a.lp_mp3 - VS1011B driver for Fonera 
3 * Copyright (c) 2007 phrozen.org - John Crispin <john@phrozen.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA
18 *
19 * Feedback, Bugs...  john@phrozen.org 
20 *
21 */
22
23
24 #include <linux/module.h>
25 #include <linux/errno.h>
26 #include <linux/ioport.h>
27 #include <linux/init.h>
28 #include <asm/uaccess.h>
29 #include <asm/io.h>
30 #include <linux/timer.h>
31 #include <linux/init.h>
32 #include <linux/genhd.h>
33 #include <linux/device.h> 
34 #include <asm-mips/mach-atheros/reset.h>
35
36 // do we want debuging info ?
37 #if 0
38 #define DBG(x) x
39 #else
40 #define DBG(x) 
41 #endif
42
43 #define MP3_CHUNK_SIZE                  4096
44 #define MP3_BUFFERING                   0
45 #define MP3_PLAYING                     1
46 #define MP3_BUFFER_FINISHED             2
47 #define MP3_PLAY_FINISHED               3
48 typedef struct _MP3_DATA{
49         unsigned char   mp3[MP3_CHUNK_SIZE];
50         unsigned char state;
51 } MP3_DATA;
52
53 #define IOCTL_MP3_INIT                  0x01
54 #define IOCTL_MP3_RESET                 0x02
55 #define IOCTL_MP3_SETVOLUME             0x03
56 #define IOCTL_MP3_GETVOLUME             0x04
57
58 typedef struct _AUDIO_DATA{
59         unsigned int bitrate;
60         unsigned int sample_rate;
61         unsigned char is_stereo;
62 }AUDIO_DATA;
63 #define IOCTL_MP3_GETAUDIODATA          0x05
64 #define IOCTL_MP3_CLEARBUFFER           0x06
65 #define IOCTL_MP3_PLAY                  0x07
66
67 typedef struct _MP3_BEEP{
68         unsigned char   freq;
69         unsigned int    ms;
70 } MP3_BEEP;
71 #define IOCTL_MP3_BEEP                  0x08                    
72 #define IOCTL_MP3_END_REACHED           0x09
73 #define IOCTL_MP3_BASS                  0x10
74
75 #define CRYSTAL12288    0x9800
76 #define CRYSTAL24576    0x0
77
78 #define DEV_NAME                        "mp3"
79 #define DEV_MAJOR                       196
80 #define MAX_MP3_COUNT   1
81
82 typedef struct _mp3_inf{
83         unsigned char is_open;
84 } mp3_inf;
85 static mp3_inf mp3_info[MAX_MP3_COUNT];
86
87 #define MP3_BUFFER_SIZE         (128 * 1024)
88 unsigned char mp3_buffer[MP3_BUFFER_SIZE];
89
90 static unsigned long int mp3_buffer_offset_write = 0;
91 static unsigned long int mp3_buffer_offset_read = 0;
92 static unsigned char mp3_buffering_status = MP3_BUFFERING;
93 static unsigned long int mp3_data_in_buffer = 0;
94 static int mp3_thread = 0;
95 unsigned int crystal_freq;
96
97 #include "vs10xx.c"
98
99 static wait_queue_head_t wq;
100 static DECLARE_COMPLETION(mp3_exit);
101
102 static int mp3_playback_thread(void *data){
103         int j;
104         unsigned long timeout;
105         unsigned char empty = 0;
106         printk("started kthread\n");
107         daemonize("kmp3");
108         while(mp3_buffering_status != MP3_PLAY_FINISHED){
109                 if((mp3_buffering_status == MP3_PLAYING) || (mp3_buffering_status == MP3_BUFFER_FINISHED)){
110                         while((VS1011_NEEDS_DATA) && (!empty)){
111                                 if(mp3_buffer_offset_read == MP3_BUFFER_SIZE){
112                                         mp3_buffer_offset_read = 0;
113                                 }
114                                 
115                                 if(mp3_data_in_buffer == 0){
116                                         if(mp3_buffering_status == MP3_BUFFER_FINISHED){
117                                                 printk("mp3_drv.ko : finished playing\n");
118                                                 mp3_buffering_status = MP3_PLAY_FINISHED;
119                                         } else {
120                                                 empty = 1;
121                                                 printk("mp3_drv.ko : buffer empty ?\n");
122                                                 if(mp3_buffering_status != MP3_PLAY_FINISHED){
123                                                 }
124                                         }
125                                 } else {
126                                         for(j = 0; j < 32; j++){
127                                                 VS1011_send_SDI(mp3_buffer[mp3_buffer_offset_read + j]);
128                                         }
129                                         mp3_buffer_offset_read += 32;
130                                         mp3_data_in_buffer -= 32;
131                                 }
132                         }
133                 }
134                 empty = 0;
135                 timeout = 1;    
136         timeout = wait_event_interruptible_timeout(wq, (timeout==0), timeout);  
137         }
138         complete_and_exit(&mp3_exit, 0); 
139 }
140
141 static ssize_t module_write(struct file * file, const char * buffer, size_t count, loff_t *offset){
142         MP3_DATA mp3_data;
143         
144         copy_from_user((char*) &mp3_data, buffer, sizeof(MP3_DATA));
145         
146         if(mp3_data.state == MP3_BUFFER_FINISHED){
147                 mp3_buffering_status = MP3_BUFFER_FINISHED;
148                 DBG(printk("mp3_drv.ko : file end reached\n"));
149                 return 1;
150         }
151         
152         if(mp3_data.state == MP3_PLAY_FINISHED){
153                 mp3_buffering_status = MP3_PLAY_FINISHED;
154                 mp3_data_in_buffer = 0;
155                 DBG(printk("mp3_drv.ko : stop playing\n"));
156                 return 1;
157         }
158         
159         if(mp3_data_in_buffer + MP3_CHUNK_SIZE >= MP3_BUFFER_SIZE){
160                 DBG(printk("mp3_drv.ko : buffer is full? %ld\n", mp3_data_in_buffer);)
161                 return 0;
162         }
163         
164         if(mp3_buffer_offset_write == MP3_BUFFER_SIZE){
165                 mp3_buffer_offset_write = 0;
166         }
167         
168         memcpy(&mp3_buffer[mp3_buffer_offset_write], mp3_data.mp3, MP3_CHUNK_SIZE);
169         mp3_buffer_offset_write += MP3_CHUNK_SIZE;
170         mp3_buffering_status = mp3_data.state;
171         mp3_data_in_buffer += MP3_CHUNK_SIZE;
172         return 1;
173 }
174
175 static int module_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg){
176         unsigned int    retval = 0;
177         AUDIO_DATA      audio_data;
178         MP3_BEEP        mp3_beep;
179         DBG(printk("mp3_drv.ko : Ioctl Called (cmd=%d)\n", cmd );)
180         switch (cmd) {
181                 case IOCTL_MP3_INIT:
182                         crystal_freq = arg;
183                         VS1011_init(crystal_freq, 1);
184                         VS1011_print_registers();
185                         break;
186
187                 case IOCTL_MP3_RESET:
188                         DBG(printk("mp3_drv.ko : doing a sw reset\n");)
189                         VS1011_init(crystal_freq, 0);
190                         VS1011_print_registers();
191                         VS1011_send_zeros(0x20);
192                         break;
193
194                 case IOCTL_MP3_SETVOLUME:
195                         DBG(printk("mp3_drv.ko : setting volume to : %lu\n", arg&0xffff);)
196                         VS1011_set_volume(arg);
197                         break;
198
199                 case IOCTL_MP3_GETVOLUME:
200                         retval = VS1011_get_volume();
201                         DBG(printk("mp3_drv.ko : read volume : %d\n", retval);)
202                         break;
203
204                 case IOCTL_MP3_GETAUDIODATA:
205                         DBG(printk("mp3_drv.ko : read audio data\n");)
206                         VS1011_get_audio_data(&audio_data);
207                         copy_to_user((char*)arg, (char*)&audio_data, sizeof(AUDIO_DATA));
208                         break;
209
210                 case IOCTL_MP3_CLEARBUFFER:
211                         DBG(printk("mp3_drv.ko : clearing buffer\n");)
212                         mp3_buffer_offset_read = 0;
213                         mp3_buffer_offset_write = 0;
214                         mp3_buffering_status = MP3_PLAY_FINISHED;
215                         mp3_data_in_buffer = 0;
216                         break;
217
218                 case IOCTL_MP3_PLAY:
219                         mp3_thread = kernel_thread(mp3_playback_thread, NULL, CLONE_KERNEL);
220                         break;
221
222                 case IOCTL_MP3_BEEP:
223                         copy_from_user((char*)&mp3_beep, (char*)arg, sizeof(MP3_BEEP));
224                         VS1011_sine(1,mp3_beep.freq);
225                         msDelay(mp3_beep.ms);
226                         VS1011_sine(0,0);
227                         break;
228
229                 case IOCTL_MP3_END_REACHED:
230                         if(mp3_buffering_status == MP3_PLAY_FINISHED){
231                                 retval = 1;
232                         }
233                         break;
234
235                 case IOCTL_MP3_BASS:
236                         VS1011_set_bass(arg);
237                         break;
238
239                 default:
240                         printk("mp3_drv.ko : unknown ioctl\n");
241                         break;
242
243         }
244         return retval;
245 }
246
247 static int module_open(struct inode *inode, struct file *file){
248         unsigned int dev_minor = MINOR(inode->i_rdev);
249         if(dev_minor !=  0){
250                 printk("mp3_drv.ko : trying to access unknown minor device -> %d\n", dev_minor);
251                 return -ENODEV;
252         }
253         if(mp3_info[dev_minor].is_open) {
254                 printk("mp3_drv.ko : Device with minor ID %d already in use\n", dev_minor);
255                 return -EBUSY;
256         }
257         mp3_info[dev_minor].is_open = 1;
258         
259         mp3_buffering_status = MP3_PLAY_FINISHED;
260         printk("mp3_drv.ko : Minor %d has been opened\n", dev_minor);
261         return 0;
262 }
263
264 static int module_close(struct inode * inode, struct file * file){
265         unsigned int dev_minor = MINOR(inode->i_rdev);
266         mp3_info[dev_minor].is_open = 0;
267         printk("mp3_drv.ko : Minor %d has been closed\n", dev_minor);
268         mp3_buffering_status = MP3_PLAY_FINISHED;
269         return 0;
270 }
271
272 struct file_operations modulemp3_fops = {
273         write:         module_write,
274         ioctl:         module_ioctl,
275         open:          module_open,
276         release:       module_close
277 };
278
279 static struct class *mp3_class; 
280
281 static int __init mod_init(void){
282         printk("mp3_drv.ko : VS1011b Driver\n");
283         printk("mp3_drv.ko : Made by John '2B|!2B' Crispin (john@phrozen.org)\n");
284         printk("mp3_drv.ko : Starting ...\n");
285         
286         printk("disabling atheros reset button irq\n");
287
288         ar531x_disable_reset_button();
289
290         if(register_chrdev(DEV_MAJOR, DEV_NAME, &modulemp3_fops)) {
291                 printk( "mp3_drv.ko : Error whilst opening %s (%d)\n", DEV_NAME, DEV_MAJOR);
292                 return( -ENODEV );
293         }
294
295         printk("mp3_drv.ko : using sysfs to create device nodes\n");
296         mp3_class = class_create(THIS_MODULE, DEV_NAME); 
297         class_device_create(mp3_class, NULL, 
298                 MKDEV(DEV_MAJOR, 0), 
299                 NULL, DEV_NAME); 
300
301         mp3_info[0].is_open = 0;
302         printk("mp3_drv.ko : Device %s registered for major ID %d\n", DEV_NAME, DEV_MAJOR);
303         crystal_freq = CRYSTAL12288;
304         VS1011_init(crystal_freq, 1);
305         VS1011_print_registers();
306         printk("end of init\n");
307         init_waitqueue_head(&wq);
308         printk("wait queue started\n");
309         return 0;
310 }
311
312 static void __exit mod_exit(void){
313         printk( "mp3_drv.ko : Cleanup\n" );
314         unregister_chrdev(DEV_MAJOR, DEV_NAME);
315 }
316
317 module_init (mod_init);
318 module_exit (mod_exit);
319
320 MODULE_LICENSE("GPL");
321 MODULE_AUTHOR("K. John '2B|!2B' Crispin");
322 MODULE_DESCRIPTION("vs1011 Driver for Fox Board");
323
324
325