Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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