blob: ae7fccc510a1cdb08cbf2773ff7ffb2692ddca8a [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001#include <linux/module.h>
2#include <linux/init.h>
3#include <linux/sched.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -07004#include <linux/linkage.h>
5#include <linux/slab.h>
6#include <linux/fs.h>
7#include <linux/sound.h>
8#include <linux/soundcard.h>
Andriy Skulysh4bcac202006-09-27 13:07:38 +09009#include <linux/interrupt.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070010#include <asm/io.h>
11#include <asm/uaccess.h>
12#include <asm/irq.h>
13#include <asm/delay.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070014#include <asm/cpu/dac.h>
Andriy Skulysh4bcac202006-09-27 13:07:38 +090015#include <asm/machvec.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070016#include <asm/hp6xx/hp6xx.h>
17#include <asm/hd64461/hd64461.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070018
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
33static int rate;
34static int empty;
35static char *data_buffer, *buffer_begin, *buffer_end;
36static int in_use, device_major;
37
38static 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
47static 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
56static void dac_audio_reset(void)
57{
58 dac_audio_stop_timer();
59 buffer_begin = buffer_end = data_buffer;
60 empty = 1;
61}
62
63static void dac_audio_sync(void)
64{
65 while (!empty)
66 schedule();
67}
68
69static void dac_audio_start(void)
70{
Andriy Skulysh4bcac202006-09-27 13:07:38 +090071 if (mach_is_hp6xx()) {
72 u16 v = inw(HD64461_GPADR);
73 v &= ~HD64461_GPADR_SPEAKER;
74 outw(v, HD64461_GPADR);
75 }
76
Linus Torvalds1da177e2005-04-16 15:20:36 -070077 sh_dac_enable(CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL);
78 ctrl_outw(TMU1_TCR_INIT, TMU1_TCR);
79}
80static void dac_audio_stop(void)
81{
Linus Torvalds1da177e2005-04-16 15:20:36 -070082 dac_audio_stop_timer();
Andriy Skulysh4bcac202006-09-27 13:07:38 +090083
84 if (mach_is_hp6xx()) {
85 u16 v = inw(HD64461_GPADR);
86 v |= HD64461_GPADR_SPEAKER;
87 outw(v, HD64461_GPADR);
88 }
89
Linus Torvalds1da177e2005-04-16 15:20:36 -070090 sh_dac_disable(CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL);
91}
92
93static 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
102static 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
166static 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
217static ssize_t dac_audio_read(struct file *file, char *buf, size_t count,
218 loff_t * ppos)
219{
220 return -EINVAL;
221}
222
223static 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
237static 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
246struct 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
254static 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
276static 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
Jesper Juhl4fa95ef2006-03-28 01:56:54 -0800287 data_buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288 if (data_buffer == NULL)
289 return -ENOMEM;
290
291 dac_audio_reset();
292 rate = 8000;
293 dac_audio_set_rate();
294
295 retval =
Thomas Gleixner65ca68b2006-07-01 19:29:46 -0700296 request_irq(TIMER1_IRQ, timer1_interrupt, IRQF_DISABLED, MODNAME, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297 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
306static 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
314module_init(dac_audio_init);
315module_exit(dac_audio_exit);
316
317MODULE_AUTHOR("Andriy Skulysh, askulysh@image.kiev.ua");
318MODULE_DESCRIPTION("SH DAC sound driver");
319MODULE_LICENSE("GPL");