sh: hp6xx mach-type cleanups.
[pandora-kernel.git] / sound / oss / sh_dac_audio.c
1 #include <linux/module.h>
2 #include <linux/init.h>
3 #include <linux/sched.h>
4 #include <linux/linkage.h>
5 #include <linux/slab.h>
6 #include <linux/fs.h>
7 #include <linux/sound.h>
8 #include <linux/soundcard.h>
9 #include <linux/interrupt.h>
10 #include <asm/io.h>
11 #include <asm/uaccess.h>
12 #include <asm/irq.h>
13 #include <asm/delay.h>
14 #include <asm/cpu/dac.h>
15 #include <asm/machvec.h>
16 #include <asm/hp6xx/hp6xx.h>
17 #include <asm/hd64461/hd64461.h>
18
19 #define MODNAME "sh_dac_audio"
20
21 #define TMU_TOCR_INIT   0x00
22
23 #define TMU1_TCR_INIT   0x0020  /* Clock/4, rising edge; interrupt on */
24 #define TMU1_TSTR_INIT  0x02    /* Bit to turn on TMU1 */
25
26 #define TMU_TSTR        0xfffffe92
27 #define TMU1_TCOR       0xfffffea0
28 #define TMU1_TCNT       0xfffffea4
29 #define TMU1_TCR        0xfffffea8
30
31 #define BUFFER_SIZE 48000
32
33 static int rate;
34 static int empty;
35 static char *data_buffer, *buffer_begin, *buffer_end;
36 static int in_use, device_major;
37
38 static void dac_audio_start_timer(void)
39 {
40         u8 tstr;
41
42         tstr = ctrl_inb(TMU_TSTR);
43         tstr |= TMU1_TSTR_INIT;
44         ctrl_outb(tstr, TMU_TSTR);
45 }
46
47 static void dac_audio_stop_timer(void)
48 {
49         u8 tstr;
50
51         tstr = ctrl_inb(TMU_TSTR);
52         tstr &= ~TMU1_TSTR_INIT;
53         ctrl_outb(tstr, TMU_TSTR);
54 }
55
56 static void dac_audio_reset(void)
57 {
58         dac_audio_stop_timer();
59         buffer_begin = buffer_end = data_buffer;
60         empty = 1;
61 }
62
63 static void dac_audio_sync(void)
64 {
65         while (!empty)
66                 schedule();
67 }
68
69 static void dac_audio_start(void)
70 {
71         if (mach_is_hp6xx()) {
72                 u16 v = inw(HD64461_GPADR);
73                 v &= ~HD64461_GPADR_SPEAKER;
74                 outw(v, HD64461_GPADR);
75         }
76
77         sh_dac_enable(CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL);
78         ctrl_outw(TMU1_TCR_INIT, TMU1_TCR);
79 }
80 static void dac_audio_stop(void)
81 {
82         dac_audio_stop_timer();
83
84         if (mach_is_hp6xx()) {
85                 u16 v = inw(HD64461_GPADR);
86                 v |= HD64461_GPADR_SPEAKER;
87                 outw(v, HD64461_GPADR);
88         }
89
90         sh_dac_disable(CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL);
91 }
92
93 static void dac_audio_set_rate(void)
94 {
95         unsigned long interval;
96
97         interval = (current_cpu_data.module_clock / 4) / rate;
98         ctrl_outl(interval, TMU1_TCOR);
99         ctrl_outl(interval, TMU1_TCNT);
100 }
101
102 static int dac_audio_ioctl(struct inode *inode, struct file *file,
103                            unsigned int cmd, unsigned long arg)
104 {
105         int val;
106
107         switch (cmd) {
108         case OSS_GETVERSION:
109                 return put_user(SOUND_VERSION, (int *)arg);
110
111         case SNDCTL_DSP_SYNC:
112                 dac_audio_sync();
113                 return 0;
114
115         case SNDCTL_DSP_RESET:
116                 dac_audio_reset();
117                 return 0;
118
119         case SNDCTL_DSP_GETFMTS:
120                 return put_user(AFMT_U8, (int *)arg);
121
122         case SNDCTL_DSP_SETFMT:
123                 return put_user(AFMT_U8, (int *)arg);
124
125         case SNDCTL_DSP_NONBLOCK:
126                 file->f_flags |= O_NONBLOCK;
127                 return 0;
128
129         case SNDCTL_DSP_GETCAPS:
130                 return 0;
131
132         case SOUND_PCM_WRITE_RATE:
133                 val = *(int *)arg;
134                 if (val > 0) {
135                         rate = val;
136                         dac_audio_set_rate();
137                 }
138                 return put_user(rate, (int *)arg);
139
140         case SNDCTL_DSP_STEREO:
141                 return put_user(0, (int *)arg);
142
143         case SOUND_PCM_WRITE_CHANNELS:
144                 return put_user(1, (int *)arg);
145
146         case SNDCTL_DSP_SETDUPLEX:
147                 return -EINVAL;
148
149         case SNDCTL_DSP_PROFILE:
150                 return -EINVAL;
151
152         case SNDCTL_DSP_GETBLKSIZE:
153                 return put_user(BUFFER_SIZE, (int *)arg);
154
155         case SNDCTL_DSP_SETFRAGMENT:
156                 return 0;
157
158         default:
159                 printk(KERN_ERR "sh_dac_audio: unimplemented ioctl=0x%x\n",
160                        cmd);
161                 return -EINVAL;
162         }
163         return -EINVAL;
164 }
165
166 static ssize_t dac_audio_write(struct file *file, const char *buf, size_t count,
167                                loff_t * ppos)
168 {
169         int free;
170         int nbytes;
171
172         if (count < 0)
173                 return -EINVAL;
174
175         if (!count) {
176                 dac_audio_sync();
177                 return 0;
178         }
179
180         free = buffer_begin - buffer_end;
181
182         if (free < 0)
183                 free += BUFFER_SIZE;
184         if ((free == 0) && (empty))
185                 free = BUFFER_SIZE;
186         if (count > free)
187                 count = free;
188         if (buffer_begin > buffer_end) {
189                 if (copy_from_user((void *)buffer_end, buf, count))
190                         return -EFAULT;
191
192                 buffer_end += count;
193         } else {
194                 nbytes = data_buffer + BUFFER_SIZE - buffer_end;
195                 if (nbytes > count) {
196                         if (copy_from_user((void *)buffer_end, buf, count))
197                                 return -EFAULT;
198                         buffer_end += count;
199                 } else {
200                         if (copy_from_user((void *)buffer_end, buf, nbytes))
201                                 return -EFAULT;
202                         if (copy_from_user
203                             ((void *)data_buffer, buf + nbytes, count - nbytes))
204                                 return -EFAULT;
205                         buffer_end = data_buffer + count - nbytes;
206                 }
207         }
208
209         if (empty) {
210                 empty = 0;
211                 dac_audio_start_timer();
212         }
213
214         return count;
215 }
216
217 static ssize_t dac_audio_read(struct file *file, char *buf, size_t count,
218                               loff_t * ppos)
219 {
220         return -EINVAL;
221 }
222
223 static int dac_audio_open(struct inode *inode, struct file *file)
224 {
225         if (file->f_mode & FMODE_READ)
226                 return -ENODEV;
227         if (in_use)
228                 return -EBUSY;
229
230         in_use = 1;
231
232         dac_audio_start();
233
234         return 0;
235 }
236
237 static int dac_audio_release(struct inode *inode, struct file *file)
238 {
239         dac_audio_sync();
240         dac_audio_stop();
241         in_use = 0;
242
243         return 0;
244 }
245
246 struct file_operations dac_audio_fops = {
247       .read =           dac_audio_read,
248       .write =  dac_audio_write,
249       .ioctl =  dac_audio_ioctl,
250       .open =           dac_audio_open,
251       .release =        dac_audio_release,
252 };
253
254 static irqreturn_t timer1_interrupt(int irq, void *dev, struct pt_regs *regs)
255 {
256         unsigned long timer_status;
257
258         timer_status = ctrl_inw(TMU1_TCR);
259         timer_status &= ~0x100;
260         ctrl_outw(timer_status, TMU1_TCR);
261
262         if (!empty) {
263                 sh_dac_output(*buffer_begin, CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL);
264                 buffer_begin++;
265
266                 if (buffer_begin == data_buffer + BUFFER_SIZE)
267                         buffer_begin = data_buffer;
268                 if (buffer_begin == buffer_end) {
269                         empty = 1;
270                         dac_audio_stop_timer();
271                 }
272         }
273         return IRQ_HANDLED;
274 }
275
276 static int __init dac_audio_init(void)
277 {
278         int retval;
279
280         if ((device_major = register_sound_dsp(&dac_audio_fops, -1)) < 0) {
281                 printk(KERN_ERR "Cannot register dsp device");
282                 return device_major;
283         }
284
285         in_use = 0;
286
287         data_buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL);
288         if (data_buffer == NULL)
289                 return -ENOMEM;
290
291         dac_audio_reset();
292         rate = 8000;
293         dac_audio_set_rate();
294
295         retval =
296             request_irq(TIMER1_IRQ, timer1_interrupt, IRQF_DISABLED, MODNAME, 0);
297         if (retval < 0) {
298                 printk(KERN_ERR "sh_dac_audio: IRQ %d request failed\n",
299                        TIMER1_IRQ);
300                 return retval;
301         }
302
303         return 0;
304 }
305
306 static void __exit dac_audio_exit(void)
307 {
308         free_irq(TIMER1_IRQ, 0);
309
310         unregister_sound_dsp(device_major);
311         kfree((void *)data_buffer);
312 }
313
314 module_init(dac_audio_init);
315 module_exit(dac_audio_exit);
316
317 MODULE_AUTHOR("Andriy Skulysh, askulysh@image.kiev.ua");
318 MODULE_DESCRIPTION("SH DAC sound driver");
319 MODULE_LICENSE("GPL");