From: Paul Mundt SuperH DAC OSS driver. --- 25-akpm/sound/oss/Kconfig | 8 25-akpm/sound/oss/Makefile | 1 25-akpm/sound/oss/sh_dac_audio.c | 325 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 334 insertions(+) diff -puN sound/oss/Kconfig~sh-03-dac-oss-driver sound/oss/Kconfig --- 25/sound/oss/Kconfig~sh-03-dac-oss-driver 2004-03-23 02:05:26.045144032 -0800 +++ 25-akpm/sound/oss/Kconfig 2004-03-23 02:05:26.049143424 -0800 @@ -1153,3 +1153,11 @@ config SOUND_AD1980 tristate "AD1980 front/back switch plugin" depends on SOUND_PRIME!=n +config SOUND_SH_DAC_AUDIO + tristate "SuperH DAC audio support" + depends on SOUND_PRIME!=n && SOUND && CPU_SH3 + +config SOUND_SH_DAC_AUDIO_CHANNEL + int " DAC channel" + default "1" + depends on SOUND_SH_DAC_AUDIO diff -puN sound/oss/Makefile~sh-03-dac-oss-driver sound/oss/Makefile --- 25/sound/oss/Makefile~sh-03-dac-oss-driver 2004-03-23 02:05:26.046143880 -0800 +++ 25-akpm/sound/oss/Makefile 2004-03-23 02:05:26.050143272 -0800 @@ -10,6 +10,7 @@ obj-$(CONFIG_SOUND_CS4232) += cs4232.o a # Please leave it as is, cause the link order is significant ! +obj-$(CONFIG_SOUND_SH_DAC_AUDIO) += sh_dac_audio.o obj-$(CONFIG_SOUND_HAL2) += hal2.o obj-$(CONFIG_SOUND_AEDSP16) += aedsp16.o obj-$(CONFIG_SOUND_PSS) += pss.o ad1848.o mpu401.o diff -puN /dev/null sound/oss/sh_dac_audio.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/sound/oss/sh_dac_audio.c 2004-03-23 02:05:26.051143120 -0800 @@ -0,0 +1,325 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef MACH_HP600 +#include +#include +#endif + +#define MODNAME "sh_dac_audio" + +#define TMU_TOCR_INIT 0x00 + +#define TMU1_TCR_INIT 0x0020 /* Clock/4, rising edge; interrupt on */ +#define TMU1_TSTR_INIT 0x02 /* Bit to turn on TMU1 */ + +#define TMU_TSTR 0xfffffe92 +#define TMU1_TCOR 0xfffffea0 +#define TMU1_TCNT 0xfffffea4 +#define TMU1_TCR 0xfffffea8 + +#define BUFFER_SIZE 48000 + +static int rate; +static int empty; +static char *data_buffer, *buffer_begin, *buffer_end; +static int in_use, device_major; + +static void dac_audio_start_timer(void) +{ + u8 tstr; + + tstr = ctrl_inb(TMU_TSTR); + tstr |= TMU1_TSTR_INIT; + ctrl_outb(tstr, TMU_TSTR); +} + +static void dac_audio_stop_timer(void) +{ + u8 tstr; + + tstr = ctrl_inb(TMU_TSTR); + tstr &= ~TMU1_TSTR_INIT; + ctrl_outb(tstr, TMU_TSTR); +} + +static void dac_audio_reset(void) +{ + dac_audio_stop_timer(); + buffer_begin = buffer_end = data_buffer; + empty = 1; +} + +static void dac_audio_sync(void) +{ + while (!empty) + schedule(); +} + +static void dac_audio_start(void) +{ +#ifdef MACH_HP600 + u16 v; + v = inw(HD64461_GPADR); + v &= ~HD64461_GPADR_SPEAKER; + outw(v, HD64461_GPADR); +#endif + sh_dac_enable(CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL); + ctrl_outw(TMU1_TCR_INIT, TMU1_TCR); +} +static void dac_audio_stop(void) +{ +#ifdef MACH_HP600 + u16 v; +#endif + dac_audio_stop_timer(); +#ifdef MACH_HP600 + v = inw(HD64461_GPADR); + v |= HD64461_GPADR_SPEAKER; + outw(v, HD64461_GPADR); +#endif + sh_dac_disable(CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL); +} + +static void dac_audio_set_rate(void) +{ + unsigned long interval; + + interval = (current_cpu_data.module_clock / 4) / rate; + ctrl_outl(interval, TMU1_TCOR); + ctrl_outl(interval, TMU1_TCNT); +} + +static int dac_audio_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int val; + + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_SYNC: + dac_audio_sync(); + return 0; + + case SNDCTL_DSP_RESET: + dac_audio_reset(); + return 0; + + case SNDCTL_DSP_GETFMTS: + return put_user(AFMT_U8, (int *)arg); + + case SNDCTL_DSP_SETFMT: + return put_user(AFMT_U8, (int *)arg); + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETCAPS: + return 0; + + case SOUND_PCM_WRITE_RATE: + val = *(int *)arg; + if (val > 0) { + rate = val; + dac_audio_set_rate(); + } + return put_user(rate, (int *)arg); + + case SNDCTL_DSP_STEREO: + return put_user(0, (int *)arg); + + case SOUND_PCM_WRITE_CHANNELS: + return put_user(1, (int *)arg); + + case SNDCTL_DSP_SETDUPLEX: + return -EINVAL; + + case SNDCTL_DSP_PROFILE: + return -EINVAL; + + case SNDCTL_DSP_GETBLKSIZE: + return put_user(BUFFER_SIZE, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + return 0; + + default: + printk(KERN_ERR "sh_dac_audio: unimplemented ioctl=0x%x\n", + cmd); + return -EINVAL; + } + return -EINVAL; +} + +static ssize_t dac_audio_write(struct file *file, const char *buf, size_t count, + loff_t * ppos) +{ + int free; + int nbytes; + + if (count < 0) + return -EINVAL; + + if (!count) { + dac_audio_sync(); + return 0; + } + + free = buffer_begin - buffer_end; + + if (free < 0) + free += BUFFER_SIZE; + if ((free == 0) && (empty)) + free = BUFFER_SIZE; + if (count > free) + count = free; + if (buffer_begin > buffer_end) { + if (copy_from_user((void *)buffer_end, buf, count)) + return -EFAULT; + + buffer_end += count; + } else { + nbytes = data_buffer + BUFFER_SIZE - buffer_end; + if (nbytes > count) { + if (copy_from_user((void *)buffer_end, buf, count)) + return -EFAULT; + buffer_end += count; + } else { + if (copy_from_user((void *)buffer_end, buf, nbytes)) + return -EFAULT; + if (copy_from_user + ((void *)data_buffer, buf + nbytes, count - nbytes)) + return -EFAULT; + buffer_end = data_buffer + count - nbytes; + } + } + + if (empty) { + empty = 0; + dac_audio_start_timer(); + } + + return count; +} + +static ssize_t dac_audio_read(struct file *file, char *buf, size_t count, + loff_t * ppos) +{ + return -EINVAL; +} + +static int dac_audio_open(struct inode *inode, struct file *file) +{ + if (file->f_mode & FMODE_READ) + return -ENODEV; + if (in_use) + return -EBUSY; + + in_use = 1; + + dac_audio_start(); + + return 0; +} + +static int dac_audio_release(struct inode *inode, struct file *file) +{ + dac_audio_sync(); + dac_audio_stop(); + in_use = 0; + + return 0; +} + +struct file_operations dac_audio_fops = { + .read = dac_audio_read, + .write = dac_audio_write, + .ioctl = dac_audio_ioctl, + .open = dac_audio_open, + .release = dac_audio_release, +}; + +static irqreturn_t timer1_interrupt(int irq, void *dev, struct pt_regs *regs) +{ + unsigned long timer_status; + + timer_status = ctrl_inw(TMU1_TCR); + timer_status &= ~0x100; + ctrl_outw(timer_status, TMU1_TCR); + + if (!empty) { + sh_dac_output(*buffer_begin, CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL); + buffer_begin++; + + if (buffer_begin == data_buffer + BUFFER_SIZE) + buffer_begin = data_buffer; + if (buffer_begin == buffer_end) { + empty = 1; + dac_audio_stop_timer(); + } + } + return IRQ_HANDLED; +} + +static int __init dac_audio_init(void) +{ + int retval; + + if ((device_major = register_sound_dsp(&dac_audio_fops, -1)) < 0) { + printk(KERN_ERR "Cannot register dsp device"); + return device_major; + } + + in_use = 0; + + data_buffer = (char *)kmalloc(BUFFER_SIZE, GFP_KERNEL); + if (data_buffer == NULL) + return -ENOMEM; + + dac_audio_reset(); + rate = 8000; + dac_audio_set_rate(); + + retval = + request_irq(TIMER1_IRQ, timer1_interrupt, SA_INTERRUPT, MODNAME, 0); + if (retval < 0) { + printk(KERN_ERR "sh_dac_audio: IRQ %d request failed\n", + TIMER1_IRQ); + return retval; + } + + return 0; +} + +static void __exit dac_audio_exit(void) +{ + free_irq(TIMER1_IRQ, 0); + + unregister_sound_dsp(device_major); + kfree((void *)data_buffer); +} + +module_init(dac_audio_init); +module_exit(dac_audio_exit); + +MODULE_AUTHOR("Andriy Skulysh, askulysh@image.kiev.ua"); +MODULE_DESCRIPTION("SH DAC sound driver"); +MODULE_LICENSE("GPL"); _