diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/char/Config.in | 4 | ||||
-rw-r--r-- | drivers/sgi/Config.in | 1 | ||||
-rw-r--r-- | drivers/sgi/Makefile | 8 | ||||
-rw-r--r-- | drivers/sgi/audio/Makefile | 34 | ||||
-rw-r--r-- | drivers/sgi/audio/hal2.c | 1296 | ||||
-rw-r--r-- | drivers/sgi/audio/hal2.h | 259 | ||||
-rw-r--r-- | drivers/sound/Config.in | 3 | ||||
-rw-r--r-- | drivers/sound/Makefile | 1 | ||||
-rw-r--r-- | drivers/sound/hal2.c | 1529 | ||||
-rw-r--r-- | drivers/sound/hal2.h | 259 |
10 files changed, 1799 insertions, 1595 deletions
diff --git a/drivers/char/Config.in b/drivers/char/Config.in index 89da63706..a81a95013 100644 --- a/drivers/char/Config.in +++ b/drivers/char/Config.in @@ -113,7 +113,9 @@ if [ "$CONFIG_VIDEO_DEV" != "n" ]; then dep_tristate 'Colour QuickCam Video For Linux (EXPERIMENTAL)' CONFIG_VIDEO_CQCAM $CONFIG_VIDEO_DEV fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - dep_tristate 'SGI Vino Video For Linux (EXPERIMENTAL)' CONFIG_VIDEO_VINO $CONFIG_VIDEO_DEV + if [ "$CONFIG_SGI" = "y" ]; then + dep_tristate 'SGI Vino Video For Linux (EXPERIMENTAL)' CONFIG_VIDEO_VINO $CONFIG_VIDEO_DEV + fi fi dep_tristate 'Mediavision Pro Movie Studio Video For Linux' CONFIG_VIDEO_PMS $CONFIG_VIDEO_DEV dep_tristate 'SAA5249 Teletext processor' CONFIG_VIDEO_SAA5249 $CONFIG_VIDEO_DEV diff --git a/drivers/sgi/Config.in b/drivers/sgi/Config.in index c399abeef..2f05afc6d 100644 --- a/drivers/sgi/Config.in +++ b/drivers/sgi/Config.in @@ -12,7 +12,6 @@ fi bool 'SGI DS1286 RTC support' CONFIG_SGI_DS1286 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - tristate 'SGI HAL2 Audio support' CONFIG_SGI_HAL2 tristate 'SGI Newport Graphics support' CONFIG_SGI_NEWPORT_GFX tristate 'SGI Newport Console support' CONFIG_SGI_NEWPORT_CONSOLE if [ "$CONFIG_SGI_NEWPORT_CONSOLE" = "m" ]; then diff --git a/drivers/sgi/Makefile b/drivers/sgi/Makefile index 675e9554f..3de37311c 100644 --- a/drivers/sgi/Makefile +++ b/drivers/sgi/Makefile @@ -8,8 +8,8 @@ # Note 2! The CFLAGS definitions are now in the main makefile... SUB_DIRS := -MOD_SUB_DIRS := $(SUB_DIRS) char audio -ALL_SUB_DIRS := $(SUB_DIRS) char audio +MOD_SUB_DIRS := $(SUB_DIRS) char +ALL_SUB_DIRS := $(SUB_DIRS) char L_OBJS := @@ -18,7 +18,7 @@ MOD_LIST_NAME := SGI_MODULES # Character and Audio devices for SGI machines. # -SUB_DIRS += char audio -L_OBJS += char/sgichar.o audio/sgiaudio.o +SUB_DIRS += char +L_OBJS += char/sgichar.o include $(TOPDIR)/Rules.make diff --git a/drivers/sgi/audio/Makefile b/drivers/sgi/audio/Makefile deleted file mode 100644 index 2b0a4a3d4..000000000 --- a/drivers/sgi/audio/Makefile +++ /dev/null @@ -1,34 +0,0 @@ -# -# Makefile for the linux kernel. -# -# Note! Dependencies are done automagically by 'make dep', which also -# removes any old dependencies. DON'T put your own dependencies here -# unless it's something special (ie not a .c file). -# -# Note 2! The CFLAGS definitions are now in the main makefile... - -# -# sgi audio drivers -# - -O_TARGET := sgiaudio.o -O_OBJS := -M_OBJS := -M := -MM := - - - - - -ifeq ($(CONFIG_SGI_HAL2),y) -M=y -OX_OBJS += hal2.o -else - ifeq ($(CONFIG_SGI_HAL2),m) - MM=y - MX_OBJS += hal2.o - endif -endif - -include $(TOPDIR)/Rules.make diff --git a/drivers/sgi/audio/hal2.c b/drivers/sgi/audio/hal2.c deleted file mode 100644 index 8a61dab50..000000000 --- a/drivers/sgi/audio/hal2.c +++ /dev/null @@ -1,1296 +0,0 @@ -/* $Id: hal2.c,v 1.13 1999/02/07 22:18:37 ulfc Exp $ - * - * drivers/sgi/audio/hal2.c - * - * Copyright (C) 1998-1999 Ulf Carlsson (ulfc@bun.falkenberg.se) - * - */ - -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/errno.h> -#include <linux/types.h> -#include <linux/interrupt.h> -#include <linux/malloc.h> -#include <linux/string.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/delay.h> -#include <linux/soundcard.h> - -#include <asm/uaccess.h> -#include <asm/system.h> -#include <asm/page.h> -#include <asm/io.h> -#include <asm/sgihpc.h> - -#include "hal2.h" - -#define DEBUG - -struct hal2_buffer { - struct hpc_dma_desc desc; - - struct hal2_buffer *next; - unsigned long buf; /* pointer in KSEG1 address space */ -}; - -/* As you might know, dma descriptors must be 8 byte aligned, that's why we have - * them in this special way. - */ - -struct hal2_channel { - struct hal2_buffer *ring; - - int pbus; - - int bres_mod; - int bres_master; - - int bufs; - int free_bufs; - - unsigned long flags; -#define H2_CH_LITTLE_END (1<<0) -#define H2_CH_DAC (1<<1) -#define H2_CH_STEREO (1<<2) -#define H2_CH_MASTER (1<<3) /* 0=48.0k 1=44.1k */ -#define H2_CH_PORT_ENBL (1<<4) -#define H2_CH_PBUS_SETUP (1<<5) -#define H2_CH_PBUS_ENBL (1<<6) - -}; - -struct hal2_private { - struct hal2_buffer *dac_head; - struct hal2_buffer *adc_tail; - - int dac_underrun; - - unsigned long empty_buf; /* in case of underrun */ - - struct hal2_channel dac_chan; - struct hal2_channel adc_chan; -}; - -struct sgiaudio_chan { - int started; - int rp; - int wp; - void *buf; - int bufsz; /* multiple of PAGE_SIZE */ - - struct sgiaudio_chan_ops *ops; - - struct wait_queue *queue; -}; - -struct sgiaudio { - struct sgiaudio_chan dac; - struct sgiaudio_chan adc; - - void *private; /* for internal hal2 usage .. */ -}; - -struct sgiaudio_chan_ops { - int (*init) (struct sgiaudio *); - int (*stereo) (struct sgiaudio *, int); - int (*endian) (struct sgiaudio *, int); - int (*freq) (struct sgiaudio *, double); - int (*configure) (struct sgiaudio *); - void (*start) (struct sgiaudio *); - void (*stop) (struct sgiaudio *); - void (*free) (struct sgiaudio *); - void *priv; -}; - -#if 0 -#define INDIRECT_WAIT(regs) while(regs->isr & H2_ISR_TSTATUS); -#endif - -#define INDIRECT_WAIT(regs) \ -{ \ - int cnt = 1000; \ - printk("hal2: waiting isr:%04hx ", regs->isr); \ - printk("idr0:%04hx idr1:%04hx idr2:%04hx idr3:%04hx\n", \ - regs->idr0, regs->idr1, regs->idr2, regs->idr3); \ - \ - while(regs->isr & H2_ISR_TSTATUS && --cnt) \ - udelay(5); \ - if (!cnt) \ - printk("hal2: failed waiting for indirect trans.\n"); \ - \ - printk("hal2: finished waiting at cnt:%d isr:%04hx ", \ - cnt, regs->isr); \ - printk("idr0:%04hx idr1:%04hx idr2:%04hx idr3:%04hx\n", \ - regs->idr0, regs->idr1, regs->idr2, regs->idr3); \ -} \ - -static unsigned short ireg_read(unsigned short address) -{ - unsigned short tmp; - - h2_ctrl->iar = address; - INDIRECT_WAIT(h2_ctrl) - tmp = h2_ctrl->idr0; - return tmp; -} - -static void ireg_write(unsigned short address, unsigned short val) -{ - h2_ctrl->idr0 = val; - h2_ctrl->iar = address; - INDIRECT_WAIT(h2_ctrl) -} - -static void ireg_write2(unsigned short address, unsigned short val0, unsigned - short val1) -{ - h2_ctrl->idr0 = val0; - h2_ctrl->idr1 = val1; - h2_ctrl->iar = address; - INDIRECT_WAIT(h2_ctrl) -} - -static void ireg_write4(unsigned short address, unsigned short val0, unsigned - short val1, unsigned short val2, unsigned short val3) -{ - h2_ctrl->idr0 = val0; - h2_ctrl->idr1 = val1; - h2_ctrl->idr2 = val2; - h2_ctrl->idr3 = val3; - h2_ctrl->iar = address; - INDIRECT_WAIT(h2_ctrl) -} - -static void ireg_setbit(unsigned short write_address, unsigned short - read_address, int bit) -{ - int tmp; - - h2_ctrl->iar = read_address; - INDIRECT_WAIT(h2_ctrl); - tmp = h2_ctrl->idr0; - h2_ctrl->idr0 = tmp | bit; - h2_ctrl->iar = write_address; - INDIRECT_WAIT(h2_ctrl); -} - -static void ireg_clearbit(unsigned short write_address, unsigned short - read_address, int bit) -{ - unsigned short tmp; - - h2_ctrl->iar = read_address; - INDIRECT_WAIT(h2_ctrl); - tmp = h2_ctrl->idr0; - h2_ctrl->idr0 = tmp & ~bit; - h2_ctrl->iar = write_address; - INDIRECT_WAIT(h2_ctrl); -} - -static void hal2_reset(void) -{ - printk("resetting global isr:%04hx\n", h2_ctrl->isr); - h2_ctrl->isr = 0; /* reset the card */ - printk("reset done isr:%04hx\n", h2_ctrl->isr); - h2_ctrl->isr = H2_ISR_GLOBAL_RESET_N | H2_ISR_CODEC_RESET_N; - printk("reactivation done isr:%04hx\n", h2_ctrl->isr); -} - -/* enable/disable a specific PBUS dma channel */ -static __inline__ void sgipbus_enable(unsigned int channel, unsigned long desc) -{ - struct hpc3_pbus_dmacregs *pbus = &hpc3c0->pbdma0; - - pbus[channel].pbdma_dptr = desc; - pbus[channel].pbdma_ctrl |= HPC3_PDMACTRL_ACT; -} - -static __inline__ void sgipbus_disable(unsigned int channel) -{ - struct hpc3_pbus_dmacregs *pbus = &hpc3c0->pbdma0; - - pbus[channel].pbdma_ctrl &= ~HPC3_PDMACTRL_ACT; -} - -static __inline__ int sgipbus_interrupted(unsigned int channel) -{ - struct hpc3_pbus_dmacregs *pbus = &hpc3c0->pbdma0; - - /* When we read pbdma_ctrl, bit 0 indicates interrupt signal. - * The interrupt signal is also cleared after read - */ - - return (pbus[channel].pbdma_ctrl & 0x01); -} - -static int hal2_probe(void) -{ - unsigned short board, major, minor; - - unsigned short tmp; - - hal2_reset(); - - if (h2_ctrl->rev & H2_REV_AUDIO_PRESENT) { - - printk("hal2: there was no device?\n"); - return -ENODEV; - } - - board = (h2_ctrl->rev & H2_REV_BOARD_M) >> 12; - major = (h2_ctrl->rev & H2_REV_MAJOR_CHIP_M) >> 4; - minor = (h2_ctrl->rev & H2_REV_MINOR_CHIP_M); - - printk("SGI H2 Processor, Revision %i.%i.%i\n", - board, major, minor); - - if (board != 4 || major != 1 || minor != 0) { - printk("hal2: Other revision than 4.1.0 detected\n"); - printk("hal2: Your card is probably not supported\n"); - } - -#ifdef DEBUG - printk("hal2: checking registers\n"); -#endif - - /* check that the indirect registers are working by writing some bogus - * stuff and then reading it back */ - - ireg_write(H2IW_DAC_C1, 0x123); /* 16 bit register */ - printk("hal2: wrote #1\n"); - ireg_write2(H2IW_BRES2_C2, 0x132, 0x231); /* 32 bit register */ - printk("hal2: wrote #2\n"); - - if ((tmp = ireg_read(H2IR_DAC_C1)) != 0x123) { - printk("hal2: Didn't pass register check #1 (%04hx)\n", tmp); - return -ENODEV; - } - printk("hal2: read #1\n"); - if ((tmp = ireg_read(H2IR_BRES2_C2_0)) != 0x132) { - printk("hal2: Didn't pass register check #2 (%04hx)\n", tmp); - return -ENODEV; - } - printk("hal2: read #2\n"); - if ((tmp = ireg_read(H2IR_BRES2_C2_1)) != 0x231) { - printk("hal2: Didn't pass register check #3 (%04hx)\n", tmp); - return -ENODEV; - } - printk("hal2: read #3\n"); - -#ifdef DEBUG - printk("hal2: card found\n"); -#endif - - return 0; -} - -static int hal2_init(struct sgiaudio *sa) -{ - static int initialized = 0; - - if (initialized) { - printk("hal2_init: already initialized?\n"); - return 0; - } - - if (!sa->private) { -#ifdef DEBUG - printk("hal2: allocating private hal2 pointer\n"); -#endif - sa->private = kmalloc(sizeof(struct hal2_private), GFP_KERNEL); - if (!sa->private) - return -ENOMEM; - memset(sa->private, 0, sizeof(struct hal2_private)); - } - -#ifdef DEBUG - printk("hal2: resetting chip\n"); -#endif - - hal2_reset(); - - /* setup indigo codec mode */ - h2_ctrl->isr &= ~H2_ISR_CODEC_MODE; - printk("hal2 init done..\n"); - - initialized = 1; - - return 0; -} - -static int hal2_setup_ring(struct hal2_private *hp, struct hal2_channel *chan) -{ - struct hal2_buffer *hbuf; - unsigned long buffer; - int j = 0; - - /* FIXME: Clear up the memory leaks here */ - - chan->ring = kmalloc(sizeof (struct hal2_buffer), GFP_KERNEL); - if (!chan->ring) - return -ENOMEM; - - hbuf = chan->ring; - buffer = get_free_page(GFP_KERNEL); - if (!buffer) - return -ENOMEM; - - hbuf->desc.pbuf = PHYSADDR(buffer); - hbuf->buf = KSEG1ADDR(buffer); - - while (j++ < chan->bufs - 1) { - hbuf->next = kmalloc(sizeof (struct hal2_buffer), GFP_KERNEL); - if (!chan->ring) - return -ENOMEM; - - buffer = get_free_page(GFP_KERNEL); - if (!buffer) - return -ENOMEM; - - /* we have to link the physical DMA buffers with phyical - * addresses first so that the HPC3 knows what to do */ - - hbuf->desc.pnext = PHYSADDR(buffer); - - hbuf = hbuf->next; - hbuf->buf = KSEG1ADDR(buffer); - hbuf->desc.pbuf = PHYSADDR(buffer); - } - - hbuf->next = chan->ring; - hbuf->desc.pnext = chan->ring->desc.pbuf; - - return 0; -} - -static int hal2_init_dac(struct sgiaudio *sa) -{ - struct hal2_private *hp; - struct hal2_channel *chan; - int err; - - printk("initializing dac\n"); - err = hal2_init(sa); - if (err) - return err; - - hp = (struct hal2_private *) sa->private; - chan = &hp->dac_chan; - - err = hal2_setup_ring(hp, chan); - if (err) - return err; - - hp->adc_tail = chan->ring; - - return 0; -} - -static int hal2_init_adc(struct sgiaudio *sa) -{ - struct hal2_private *hp; - struct hal2_channel *chan; - int err; - - printk("initializing adc\n"); - - err = hal2_init(sa); - if (err) - return err; - - hp = (struct hal2_private *) sa->private; - chan = &hp->dac_chan; - - err = hal2_setup_ring(hp, chan); - if (err) - return err; - - hp->dac_head = chan->ring; - - return 0; -} - -static __inline__ int hal2_sample_size(struct hal2_channel *chan) -{ - return (chan->flags & H2_CH_STEREO ? 4 : 2); -} - -static int hal2_setup_pbus(struct hal2_private *hp, struct hal2_channel *chan) -{ - struct hpc3_pbus_dmacregs *pbus = &hpc3c0->pbdma0; - int sample_sz, highwater, fifosize; - unsigned long flags; - unsigned long fifobeg, fifoend; - - chan->pbus = (chan->flags & H2_CH_DAC) ? 0 : 1; - - sample_sz = hal2_sample_size(chan); /* bytes/sample */ - - /* an audio fifo should be 4 samples long and doubleword aligned, - highwater should be half the fifo size */ - - fifosize = (sample_sz * 4) >> 3; /* doublewords */ - highwater = (sample_sz * 2) >> 1; /* halfwords */ - - /* DAC is always before ADC in our PBUS DMA FIFO. Reserve space for the - * maximum fifo length, we might change to stereo later if we're in mono - * now - */ - - if (chan->flags & H2_CH_DAC) - fifobeg = 0; - else - fifobeg = (4 * 4) >> 3; - - fifoend = fifobeg + fifosize; - - flags = HPC3_PDMACTRL_RT | - (chan->flags & H2_CH_DAC ? 0 : HPC3_PDMACTRL_RCV) | - (chan->flags & H2_CH_LITTLE_END ? HPC3_PDMACTRL_SEL : 0); - - pbus[chan->pbus].pbdma_ctrl = (highwater << 8) | (fifobeg << 16) | - (fifoend << 24) | flags; - - /* Realtime, 16-bit, cycles to spend and more default settings for - * soundcards, taken directly from the spec. */ - hpc3c0->pbus_dmacfgs[chan->pbus][0] = 0x8248844; - - return 0; -} - -static void hal2_update_dac_buf(struct sgiaudio *sa, struct hal2_private *hp) -{ - int rp = sa->dac.rp; - int wp = sa->dac.wp; - int bufsz = sa->dac.bufsz; - struct hal2_buffer *hbuf = hp->dac_head; - struct hal2_channel *chan = &hp->dac_chan; - int left; - int count = 0; - - printk("updating dac buffer!\n"); - - if (!hbuf) { - printk("Oops, ADC head wasn't set..\n"); - return; - } - - /* bytes left to read in the fifo */ - left = (wp - rp + bufsz) % bufsz; - - if (hp->dac_underrun) { - if (left < hal2_sample_size(&hp->dac_chan)) { - /* we're still in underrun... */ - return; - } - else { - /* new enforcements have arrived, go back to normal - * ring buffer behavior */ - hbuf->desc.pbuf = PHYSADDR(hbuf->buf); - hbuf->desc.pnext = PHYSADDR(hbuf->next->buf); - - hbuf = hbuf->next; - } - } - - /* oops, a newly discovered underrun .. can this happen? */ - if (left < hal2_sample_size(&hp->dac_chan)) - goto out; - - if (chan->free_bufs < 1) { - printk("Oops, lost DAC buffer count!\n"); - return; - } else - chan->free_bufs--; - - count = (left>PAGE_SIZE)?PAGE_SIZE:left; - - if (rp + count < bufsz) - memcpy((void *) hbuf->buf, sa->dac.buf + rp, count); - else { - /* crosses the buffer boundary */ - int first = bufsz - rp - 1; - - memcpy((void *) hbuf->buf, sa->dac.buf + rp, first); - memcpy((void *) hbuf->buf + first, sa->dac.buf, count - first); - } - - sa->dac.rp = (rp + count) % bufsz; - hbuf->desc.cntinfo = HPCDMA_XIE | (count & HPCDMA_BCNT); - - hbuf = hbuf->next; - - wake_up_interruptible(&sa->dac.queue); -out: - - /* was this the last buffer before underrun? */ - if (left - count < hal2_sample_size(&hp->dac_chan)) { - /* ok, loop the empty buf.. */ - hbuf = hbuf->next; - hbuf->desc.pnext = hbuf->desc.pbuf = PHYSADDR(hp->empty_buf); - hbuf->desc.cntinfo = (PAGE_SIZE & HPCDMA_BCNT); - - hp->dac_underrun = 1; - } - hp->dac_head = hbuf; -} - -static void hal2_update_adc_buf(struct sgiaudio *sa, struct hal2_private *hp) -{ - int rp = sa->adc.rp; - int wp = sa->adc.wp; - int bufsz = sa->adc.bufsz; - struct hal2_buffer *hbuf = hp->dac_head; - struct hal2_channel *chan = &hp->dac_chan; - int left; - int count; - - if (!hbuf) { - printk("Oops, ADC head wasn't set..\n"); - return; - } - - /* bytes left to write in the fifo */ - left = bufsz - (wp - rp + bufsz) % bufsz; - - if (chan->free_bufs >= chan->bufs) { - printk("Oops, lost ADC buffer count!\n"); - return; - } else - chan->free_bufs++; - - /* check overrun */ - if (left < PAGE_SIZE) - return; /* XXX some better way? */ - - count = PAGE_SIZE; - - memcpy(sa->adc.buf + wp, (void *) hbuf->buf, count); - sa->adc.wp = (wp + count) % bufsz; - - wake_up_interruptible(&sa->adc.queue); -} - -static void hal2_reset_adc_ring(struct hal2_private *hp, struct hal2_channel - *chan) -{ - struct hal2_buffer *first, *hbuf; - - first = chan->ring; - - for (hbuf = first; hbuf->next != first; hbuf = hbuf->next) - hbuf->desc.cntinfo = HPCDMA_XIE; -} - -static void hal2_enable_port(struct hal2_private *hp, struct hal2_channel *chan) -{ - if (!(chan->flags & H2_CH_PORT_ENBL)) { - /* pick a dma port */ - ireg_setbit(H2IW_DMA_DRV, H2IR_DMA_DRV, (1 << chan->pbus)); - - /* activate it! */ - if (chan->flags & H2_CH_DAC) - ireg_setbit(H2IW_DMA_PORT_EN, H2IR_DMA_PORT_EN, - H2I_DMA_PORT_EN_CODECTX); - else - ireg_setbit(H2IW_DMA_PORT_EN, H2IR_DMA_PORT_EN, - H2I_DMA_PORT_EN_CODECR); - - chan->flags |= H2_CH_PORT_ENBL; - } -} - -static void hal2_disable_port(struct hal2_private *hp, - struct hal2_channel *chan) -{ - if (chan->flags & H2_CH_PORT_ENBL) { - if (chan->flags & H2_CH_DAC) - ireg_clearbit(H2IW_DMA_PORT_EN, H2IR_DMA_PORT_EN, - H2I_DMA_PORT_EN_CODECTX); - else - ireg_clearbit(H2IW_DMA_PORT_EN, H2IR_DMA_PORT_EN, - H2I_DMA_PORT_EN_CODECR); - - chan->flags &= ~H2_CH_PORT_ENBL; - } -} - -/* We have three bresenham clock generators, which we can use independantly. - * - * There is one 44.1k and one 48.0k master clock for each of them. We can - * adjust the inc and the mod values for those clocks, and thus reduce the - * frequency. - * - * freq = master_clock * (inc / mod); where (inc / mod) is a positive fraction - * in range [0,1]. Inc should always be set to 4 for codecs - */ -static __inline__ int hal2_calc_mod(int *mod, int master, double freq) -{ - double master_freq = (master == 0 ? 44800 : 44100); - int tmp; - - tmp = (4.0 * master_freq) / freq; - if (tmp > 65536 || tmp < 4) - return -EINVAL; - - *mod = tmp; - - return 0; -} - -static __inline__ int hal2_calc_freq(double *freq, int master, int mod) -{ - double master_freq = (master == 0 ? 44800 : 44100); - double tmp; - - tmp = (4.0 * master_freq) / mod; - if (tmp < 0 || tmp > 48000) - return -EINVAL; - - *freq = tmp; - - return 0; -} - -static int hal2_set_freq(struct hal2_channel *chan, double freq) -{ - int mod; - int err; - - /* Try both 44.1k master and 48.0k master .. */ - - err = hal2_calc_mod(&mod, 0, freq); - if (!err) { - chan->bres_master = 0; - chan->bres_mod = mod; - return 0; - } - - err = hal2_calc_mod(&mod, 1, freq); - if (!err) { - chan->bres_master = 1; - chan->bres_mod = mod; - return 0; - } - - /* We failed to find a matching mod value for either 44.1 master or - * 48.0k master, try something else.. - */ - return -EINVAL; -} - - -/* Exploit this little neat gcc extension to build our three functions :) */ - -#define __BUILD_CONF_BRESN_CLOCK(clock) \ -static void hal2_configure_bres##clock(struct hal2_private *hp, \ - struct hal2_channel *chan) \ -{ \ - int master = chan->bres_master; \ - int mod = chan->bres_mod; \ - int inc = 4; \ - \ - ireg_write(H2IW_BRES##clock##_C1 , master); \ - ireg_write2(H2IW_BRES##clock##_C2 , inc, mod); \ -} \ - -__BUILD_CONF_BRESN_CLOCK(1) -__BUILD_CONF_BRESN_CLOCK(2) -__BUILD_CONF_BRESN_CLOCK(3) - -static int hal2_configure_dac(struct sgiaudio *sa) -{ - struct hal2_private *hp = (struct hal2_private *) sa->private; - struct hal2_channel *chan = &hp->dac_chan; - int datatype = ((chan->flags & H2_CH_STEREO)?2:0) << 8; - int clock = 1 << 3; - - /* Let's be sure that the dma port is disabled */ - hal2_disable_port(hp, chan); - - hal2_configure_bres1(hp, chan); - - if (chan->flags & H2_CH_LITTLE_END) - ireg_setbit(H2IW_DMA_END, H2IR_DMA_END, - H2I_DMA_PORT_EN_CODECTX); - else - ireg_clearbit(H2IW_DMA_END, H2IR_DMA_END, - H2I_DMA_PORT_EN_CODECTX); - - ireg_write(H2IW_DAC_C1, chan->pbus | clock | datatype); - - hal2_setup_pbus(hp, chan); - - /* dac ctrl 2? */ - - /* Enable the dma port (note: we're not starting the PBUS yet) */ - hal2_enable_port(hp, chan); - - return 0; -} - -static int hal2_configure_adc(struct sgiaudio *sa) -{ - struct hal2_private *hp = (struct hal2_private *) sa->private; - struct hal2_channel *chan = &hp->dac_chan; - int datatype = ((chan->flags & H2_CH_STEREO)?2:0) << 8; - int clock = 2 << 3; - - hal2_disable_port(hp, chan); - - hal2_configure_bres2(hp, chan); - - if (chan->flags & H2_CH_LITTLE_END) - ireg_setbit(H2IW_DMA_END, H2IR_DMA_END, H2I_DMA_PORT_EN_CODECR); - else - ireg_clearbit(H2IW_DMA_END, H2IR_DMA_END, - H2I_DMA_PORT_EN_CODECR); - - ireg_write(H2IW_ADC_C1, chan->pbus | clock | datatype); - - hal2_setup_pbus(hp, chan); - - /* adc ctrl 2? */ - - /* Enable the dma port (note: we're not starting the PBUS yet) */ - hal2_enable_port(hp, chan); - - return 0; -} - -static int hal2_set_dac_stereo(struct sgiaudio *sa, int stereo) -{ - struct hal2_private *hp = (struct hal2_private *) sa->private; - struct hal2_channel *chan = &hp->dac_chan; - - chan->flags |= H2_CH_STEREO; - - return 0; -} - -static int hal2_set_adc_stereo(struct sgiaudio *sa, int stereo) -{ - struct hal2_private *hp = (struct hal2_private *) sa->private; - struct hal2_channel *chan = &hp->dac_chan; - - chan->flags |= H2_CH_STEREO; - - return 0; -} - -static int hal2_set_dac_endian(struct sgiaudio *sa, int little_end) -{ - struct hal2_private *hp = (struct hal2_private *) sa->private; - struct hal2_channel *chan = &hp->dac_chan; - - if (little_end) - chan->flags |= H2_CH_LITTLE_END; - else - chan->flags &= ~H2_CH_LITTLE_END; - - return 0; -} - -static int hal2_set_adc_endian(struct sgiaudio *sa, int little_end) -{ - struct hal2_private *hp = (struct hal2_private *) sa->private; - struct hal2_channel *chan = &hp->adc_chan; - - if (little_end) - chan->flags |= H2_CH_LITTLE_END; - else - chan->flags &= ~H2_CH_LITTLE_END; - - return 0; -} - -static int hal2_set_dac_freq(struct sgiaudio *sa, double freq) -{ - struct hal2_private *hp = (struct hal2_private *) sa->private; - struct hal2_channel *chan = &hp->dac_chan; - int err; - - err = hal2_set_freq(chan, freq); - if (err) - return err; - - return 0; -} - -static int hal2_set_adc_freq(struct sgiaudio *sa, double freq) -{ - struct hal2_private *hp = (struct hal2_private *) sa->private; - struct hal2_channel *chan = &hp->adc_chan; - int err; - - err = hal2_set_freq(chan, freq); - if (err) - return err; - return 0; -} - -static void hal2_go(struct hal2_channel *chan) -{ - /* The hal2 *has* to be enabled before we enable PBUS DMA transfers, - * atleast I say so.. (we need some kind of "order") */ - if (!(chan->flags & H2_CH_PORT_ENBL)) - return; - - if (!(chan->flags & H2_CH_PBUS_ENBL)) { - sgipbus_enable(chan->pbus, PHYSADDR(&chan->ring->desc)); - chan->flags |= H2_CH_PBUS_ENBL; - } -#ifdef DEBUG - else - printk("Attempt to enable the H2 DMA channel twice\n"); -#endif -} - -static void hal2_stop_pbus(struct hal2_channel *chan) -{ - if (chan->flags & H2_CH_PBUS_ENBL) { - sgipbus_enable(chan->pbus, PHYSADDR(&chan->ring->desc)); - chan->flags &= ~H2_CH_PBUS_ENBL; - } -#ifdef DEBUG - else - printk("Trying to stop already stopped DMA channel\n"); -#endif -} - -static void hal2_start_adc(struct sgiaudio *sa) -{ - struct hal2_private *hp = (struct hal2_private *) sa->private; - struct hal2_channel *chan = &hp->adc_chan; - - hal2_reset_adc_ring(hp, chan); - - hal2_go(chan); -} - -static void hal2_start_dac(struct sgiaudio *sa) -{ - struct hal2_private *hp = (struct hal2_private *) sa->private; - struct hal2_channel *chan = &hp->dac_chan; - - /* fill da funky ring buffer */ - while (chan->free_bufs && !hp->dac_underrun) - hal2_update_dac_buf(sa, hp); - - hal2_go(chan); -} - -static void hal2_stop_dac(struct sgiaudio *sa) -{ - struct hal2_private *hp = (struct hal2_private *) sa->private; - struct hal2_channel *chan = &hp->dac_chan; - - hal2_disable_port(hp, chan); - - hal2_stop_pbus(chan); -} - -static void hal2_stop_adc(struct sgiaudio *sa) -{ - struct hal2_private *hp = (struct hal2_private *) sa->private; - struct hal2_channel *chan = &hp->adc_chan; - - hal2_disable_port(hp, chan); - - hal2_stop_pbus(chan); -} - -static void hal2_free_dac(struct sgiaudio *sa) -{ - struct hal2_private *hp = (struct hal2_private *) sa->private; - struct hal2_channel *chan = &hp->dac_chan; - - if (!hp) - return; - - kfree(hp); -} - -static void hal2_free_adc(struct sgiaudio *sa) -{ - struct hal2_private *hp = (struct hal2_private *) sa->private; - struct hal2_channel *chan = &hp->adc_chan; - - if (!hp) - return; - - kfree(hp); -} - -void hal2_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct sgiaudio *sa = (struct sgiaudio *) dev_id; - struct hal2_private *hp = (struct hal2_private *) sa->private; - - struct hal2_channel *chan; - - chan = &hp->dac_chan; - if ((chan->flags & H2_CH_PBUS_ENBL) && sgipbus_interrupted(chan->pbus)) - while (chan->free_bufs && !hp->dac_underrun) - hal2_update_dac_buf(sa, hp); - - chan = &hp->adc_chan; - if ((chan->flags & H2_CH_PBUS_ENBL) && sgipbus_interrupted(chan->pbus)) - ; -} - -/* - * My bogus OSS interface, please excuse my sins.. - */ - -static struct sgiaudio_chan_ops dac_ops = { - hal2_init_dac, - hal2_set_dac_stereo, - hal2_set_dac_endian, - hal2_set_dac_freq, - hal2_configure_dac, - hal2_start_dac, - hal2_stop_dac, - hal2_free_dac, -}; - -static struct sgiaudio_chan_ops adc_ops = { - hal2_init_adc, - hal2_set_adc_stereo, - hal2_set_adc_endian, - hal2_set_adc_freq, - hal2_configure_adc, - hal2_start_adc, - hal2_stop_adc, - hal2_free_adc, -}; - -static ssize_t sgiaudio_dsp_read(struct file *filp, - char *buf, size_t count, loff_t *ppos) -{ - return count; -} - -static ssize_t sgiaudio_dsp_write(struct file *filp, - const char *buf, size_t count, loff_t *ppos) -{ - struct sgiaudio *sa = (struct sgiaudio *) filp->private_data; - struct sgiaudio_chan *chan = &sa->dac; - - int wp = chan->wp; - int rp = chan->rp; - int bufsz = chan->bufsz; - int left; - - unsigned long flags; - - printk("attempt to write..\n"); - - if (count >= bufsz) { - printk("hal: short write?\n"); - return -EINVAL; /* XXX we should support this, but not yet */ - } - - if (!chan->started) { - printk("configuring dac.\n"); - chan->ops->configure(sa); - printk("dac configured\n"); - } - - save_and_cli(flags); - - left = (rp - wp + bufsz) % bufsz - 1; - - if (filp->f_flags | O_NONBLOCK) { - count = count > left ? left : count; - } else { - /* assume that the channel is started since we're here */ - while (left < count) { - printk("oups, overflow, let's sleep..\n"); - interruptible_sleep_on(&chan->queue); - left = (rp - wp + bufsz) % bufsz - 1; - } - } - - restore_flags(flags); - - printk("copying buffer\n"); - - if (!access_ok(VERIFY_READ, buf, count)) - return -EFAULT; - - if (wp + count < bufsz) - __copy_from_user(chan->buf + wp, buf, count); - else { - /* crosses the buffer boundary */ - int first = bufsz - wp - 1; - - __copy_from_user(chan->buf + wp, buf, first); - __copy_from_user(chan->buf + first, buf, count - first); - } - - chan->wp = (wp + count) % bufsz; - - if (!chan->started) { - printk("starting channel\n"); - chan->ops->start(sa); - printk("channel started\n"); - sa->dac.started = 1; - } - - return count; -} - -static int sgiaudio_dsp_ioctl(struct inode *inode, struct file *filp, - unsigned int cmd, unsigned long arg) -{ - return 0; -} - -static int sgiaudio_init_dsp(struct sgiaudio *sa, struct sgiaudio_chan *chan, - struct sgiaudio_chan_ops *ops) -{ - int err; - - chan->ops = ops; - - chan->bufsz = 4 * PAGE_SIZE; - chan->wp = 0; - chan->rp = 1; - - chan->buf = kmalloc(chan->bufsz, GFP_KERNEL); - if (!chan->buf) - return -ENOMEM; - - err = chan->ops->init(sa); - if (err) - goto cleanup; - - err = chan->ops->stereo(sa, 0); - if (err) - goto cleanup; - - err = chan->ops->endian(sa, 0); - if (err) - goto cleanup; - - err = chan->ops->freq(sa, 8000); - if (err) - goto cleanup; - - return 0; - -cleanup: - kfree(chan->buf); - return err; -} - -static int sgiaudio_dsp_open(struct inode *inode, struct file *filp) -{ - struct sgiaudio *sa = (struct sgiaudio *) filp->private_data; - struct sgiaudio_chan *dac, *adc; - int err; - - dac = &sa->dac; - adc = &sa->adc; - - if (filp->f_flags & O_RDONLY || filp->f_flags & O_RDWR) { - printk("opened with read perm\n"); - err = sgiaudio_init_dsp(sa, &sa->adc, &adc_ops); - if (err) - return err; - } - if (filp->f_flags & O_WRONLY || filp->f_flags & O_RDWR) { - printk("opened with write perm\n"); - err = sgiaudio_init_dsp(sa, &sa->dac, &dac_ops); - if (err) - return err; - } - - MOD_INC_USE_COUNT; -#ifdef DEBUG - printk("DSP opened successfully\n"); -#endif - return 0; - -} - -static int sgiaudio_dsp_release(struct inode *inode, struct file *filp) -{ - struct sgiaudio *sa = (struct sgiaudio *) filp->private_data; - struct sgiaudio_chan *dac, *adc; - - printk("in dsp release..\n"); - - dac = &sa->dac; - adc = &sa->adc; - - if (filp->f_flags & O_RDONLY || filp->f_flags & O_RDWR) - adc->ops->free(sa); - - if (filp->f_flags & O_WRONLY || filp->f_flags & O_RDWR) - dac->ops->free(sa); - - /* we have to call the default release file operation, to free up - * structures and decrease usage, this is a little workaround .. */ - printk("dispatching release.."); - inode->i_op->default_file_ops->release(inode, filp); - - return 0; -} - -struct file_operations sgiaudio_dsp_ops = { - NULL, /* sgiaudio_dsp_lseek */ - sgiaudio_dsp_read, - sgiaudio_dsp_write, - NULL, /* sgiaudio_dsp_readdir */ - NULL, /* sgiaudio_dsp_select */ - sgiaudio_dsp_ioctl, - NULL, /* sgiaudio_dsp_mmap */ - sgiaudio_dsp_open, - NULL, /* sgiaudio_dsp_flush */ - sgiaudio_dsp_release, -}; - -static int sgiaudio_mixer_ioctl(struct inode *inode, struct file *filp, - unsigned int cmd, unsigned long arg) -{ - return 0; -} - -static int sgiaudio_mixer_open(struct inode *inode, struct file *filp) -{ - return 0; -} - -static int sgiaudio_mixer_release(struct inode *inode, struct file *filp) -{ - return 0; -} - -struct file_operations sgiaudio_mixer_ops = { - NULL, /* sgiaudio_mixer_lseek */ - NULL, /* sgiaudio_mixer_read */ - NULL, /* sgiaudio_mixer_write */ - NULL, /* sgiaudio_mixer_readdir */ - NULL, /* sgiaudio_mixer_select */ - sgiaudio_mixer_ioctl, - NULL, /* sgiaudio_mixer_mmap */ - sgiaudio_mixer_open, - NULL, /* sgiaudio_mixer_flush */ - sgiaudio_mixer_release, -}; - -static int sgiaudio_open(struct inode *inode, struct file *filp) -{ - static struct sgiaudio *sa; - int minor = MINOR(inode->i_rdev); - - static int initialized = 0; - - if (!initialized) { - sa = kmalloc(sizeof(struct sgiaudio), GFP_KERNEL); - if (!sa) - return -ENOMEM; - - memset(sa, 0, sizeof(struct sgiaudio)); - - filp->private_data = (void *) sa; - - initialized++; - } - - switch (minor) { - case 3: - filp->f_op = &sgiaudio_dsp_ops; - - /* dispatch the call to the specific open routine .. */ - return filp->f_op->open(inode, filp); - - /* not reached */ - break; - default: - return -ENODEV; - - /* not reached */ - break; - } - return 0; -} - -static int sgiaudio_release(struct inode *inode, struct file *filp) -{ - struct sgiaudio *sa = (struct sgiaudio *) filp->private_data; - - printk("release dispatched!\n"); - - if (sa) - kfree(sa); - - MOD_DEC_USE_COUNT; - - return 0; -} - -static struct file_operations sgiaudio_ops = { - NULL, /* sgiaudio_lseek */ - NULL, /* sgiaudio_read */ - NULL, /* sgiaudio_write */ - NULL, /* sgiaudio_readdir */ - NULL, /* sgiaudio_select */ - NULL, /* sgiaudio_ioctl */ - NULL, /* sgiaudio_mmap */ - sgiaudio_open, - NULL, /* sgiaudio_flush */ - sgiaudio_release, -}; - -__initfunc(void sgiaudio_init(void)) -{ - int result; - int err; - - - err = hal2_probe(); - if (err) { - printk("sgiaudio: unable to find hal2 subsystem\n"); - return; - } - - printk("sgiaudio: initializing\n"); - result = register_chrdev(SOUND_MAJOR, "sound", &sgiaudio_ops); - if (result < 0) { - printk("sgiaudio: unable to get major %d", SOUND_MAJOR); - } -} - -#ifdef MODULE - -int init_module(void) -{ - int err; - int result; - - err = hal2_probe(); - if (err) { - printk("sgiaudio: unable to find hal2 subsystem\n"); - return -ENODEV; - } - - result = register_chrdev(SOUND_MAJOR, "sound", &sgiaudio_ops); - if (result < 0) { - printk("sgiaudio: unable to get major %d", SOUND_MAJOR); - return result; - } - - return 0; -} - -void cleanup_module(void) -{ - unregister_chrdev(SOUND_MAJOR, "sound"); -} - -#endif diff --git a/drivers/sgi/audio/hal2.h b/drivers/sgi/audio/hal2.h deleted file mode 100644 index 64a72a5c2..000000000 --- a/drivers/sgi/audio/hal2.h +++ /dev/null @@ -1,259 +0,0 @@ -/* - * drivers/sgi/audio/hal2.h - * - * Copyright (C) 1998 Ulf Carlsson (ulfc@bun.falkenberg.se) - * - */ - -#define H2_HAL2_BASE (HPC3_CHIP0_PBASE + 0x58000) -#define H2_CTRL_PIO (H2_HAL2_BASE + 0 * 0x200) -#define H2_AES_PIO (H2_HAL2_BASE + 1 * 0x200) -#define H2_VOLUME_PIO (H2_HAL2_BASE + 2 * 0x200) -#define H2_SYNTH_PIO (H2_HAL2_BASE + 3 * 0x200) - -typedef volatile unsigned int hal_reg; - -struct hal2_ctrl_regs { - hal_reg _unused0[4]; - hal_reg isr; /* 0x10 Status Register */ - hal_reg _unused1[3]; - hal_reg rev; /* 0x20 Revision Register */ - hal_reg _unused2[3]; - hal_reg iar; /* 0x30 Indirect Address Register */ - hal_reg _unused3[3]; - hal_reg idr0; /* 0x40 Indirect Data Register 0 */ - hal_reg _unused4[3]; - hal_reg idr1; /* 0x50 Indirect Data Register 1 */ - hal_reg _unused5[3]; - hal_reg idr2; /* 0x60 Indirect Data Register 2 */ - hal_reg _unused6[3]; - hal_reg idr3; /* 0x70 Indirect Data Register 3 */ -} volatile *h2_ctrl = (struct hal2_ctrl_regs *) KSEG1ADDR(H2_CTRL_PIO); - -struct hal2_vol_regs { - hal_reg right; /* 0x00 Right volume */ - hal_reg left; /* 0x04 Left volume */ -} volatile *h2_vol = (struct hal2_vol_regs *) KSEG1ADDR(H2_VOLUME_PIO); - -/* AES and synth regs should end up here if we ever support them */ - -/* Indirect status register */ - -#define H2_ISR_TSTATUS 0x01 /* RO: transaction status 1=busy */ -#define H2_ISR_USTATUS 0x02 /* RO: utime status bit 1=armed */ -#define H2_ISR_CODEC_MODE 0x04 /* codec mode 0=indigo 1=quad */ -#define H2_ISR_GLOBAL_RESET_N 0x08 /* chip global reset 0=reset */ -#define H2_ISR_CODEC_RESET_N 0x10 /* codec/synth reset 0=reset */ - - /* Revision register */ - -#define H2_REV_AUDIO_PRESENT 0x8000 /* RO: audio present 0=present */ -#define H2_REV_BOARD_M 0x7000 /* RO: bits 14:12, board revision */ -#define H2_REV_MAJOR_CHIP_M 0x00F0 /* RO: bits 7:4, major chip revision */ -#define H2_REV_MINOR_CHIP_M 0x000F /* RO: bits 3:0, minor chip revision */ - -/* Indirect address register */ - -/* - * Address of indirect internal register to be accessed. A write to this - * register initiates read or write access to the indirect registers in the - * HAL2. Note that there af four indirect data registers for write access to - * registers larger than 16 byte. - */ - -#define H2_IAR_TYPE_M 0xF000 /* bits 15:12, type of functional */ - /* block the register resides in */ - /* 1=DMA Port */ - /* 9=Global DMA Control */ - /* 2=Bresenham */ - /* 3=Unix Timer */ -#define H2_IAR_NUM_M 0x0F00 /* bits 11:8 instance of the */ - /* blockin which the indirect */ - /* register resides */ - /* If IAR_TYPE_M=DMA Port: */ - /* 1=Synth In */ - /* 2=AES In */ - /* 3=AES Out */ - /* 4=DAC Out */ - /* 5=ADC Out */ - /* 6=Synth Control */ - /* If IAR_TYPE_M=Global DMA Control: */ - /* 1=Control */ - /* If IAR_TYPE_M=Bresenham: */ - /* 1=Bresenham Clock Gen 1 */ - /* 2=Bresenham Clock Gen 2 */ - /* 3=Bresenham Clock Gen 3 */ - /* If IAR_TYPE_M=Unix Timer: */ - /* 1=Unix Timer */ -#define H2_IAR_ACCESS_SELECT 0x0080 /* 1=read 0=write */ -#define H2_IAR_PARAM 0x000C /* Parameter Select */ -#define H2_IAR_RB_INDEX_M 0x0003 /* Read Back Index */ - /* 00:word0 */ - /* 01:word1 */ - /* 10:word2 */ - /* 11:word3 */ -/* - * HAL2 internal addressing - * - * The HAL2 has "indirect registers" (idr) which are accessed by writing to the - * Indirect Data registers. Write the address to the Indirect Address register - * to transfer the data. - * - * We define the H2IR_* to the read address and H2IW_* to the write address and - * H2I_* to be fields in whatever register is referred to. - * - * When we write to indirect registers which are larger than one word (16 bit) - * we have to fill more than one indirect register before writing. When we read - * back however we have to read several times, each time with different Read - * Back Indexes (there are defs for doing this easily). - */ - -/* - * Relay Control - */ -#define H2IW_RELAY_C 0x9100 /* state of RELAY pin signal */ -#define H2IR_RELAY_C 0x9180 /* state of RELAY pin signal */ -#define H2I_RELAY_C_STATE 0x01 /* state of RELAY pin signal */ - -#define H2IW_DMA_PORT_EN 0x09104 /* dma port enable */ -#define H2IR_DMA_PORT_EN 0x9184 /* dma port enable */ -#define H2I_DMA_PORT_EN_SY_IN 0x01 /* synth_in dma port */ -#define H2I_DMA_PORT_EN_AESRX 0x02 /* aes receiver dma port */ -#define H2I_DMA_PORT_EN_AESTX 0x04 /* aes transmitter dma port */ -#define H2I_DMA_PORT_EN_CODECTX 0x08 /* codec transmit dma port */ -#define H2I_DMA_PORT_EN_CODECR 0x10 /* codec receive dma port */ - /* 0=disable 1=enable */ - -#define H2IW_DMA_END 0x9108 /* global dma endian select */ -#define H2IR_DMA_END 0x9188 /* global dma endian select */ -#define H2I_DMA_END_SY_IN 0x01 /* synth_in dma port */ -#define H2I_DMA_END_AESRX 0x02 /* aes receiver dma port */ -#define H2I_DMA_END_AESTX 0x04 /* aes transmitter dma port */ -#define H2I_DMA_END_CODECTX 0x08 /* codec transmit dma port */ -#define H2I_DMA_END_CODECR 0x10 /* codec receive dma port */ - /* 0=big endian 1=little endian */ - -#define H2IW_DMA_DRV 0x910C /* global dma bus drive enable */ -#define H2IR_DMA_DRV 0x918C /* global dma bus drive enable */ - -#define H2IW_SYNTH_C 0x1104 /* synth dma control write */ -#define H2IR_SYNTH_C 0x1184 /* synth dma control read */ - -#define H2IW_AESRX_C 0x1204 /* aes rx dma control write */ -#define H2IR_AESRX_C 0x1284 /* aes rx dma control read */ -#define H2I_AESRX_C_TS_EN 0x20 /* timestamp enable 0=no 1=yes */ -#define H2I_AESRX_C_TS_FMT 0x40 /* timestamp format */ -#define H2I_AESRX_C_NAUDIO 0x80 /* pbus dma data format */ - /* 0=sign_ext 1=pass_non_audio */ - -#define H2IW_AESTX_C 0x1304 /* aes tx dma control write */ -#define H2IR_AESTX_C 0x1384 /* aes tx dma control read */ -#define H2I_AESTX_C_CLKID_M 0x18 /* bits 4:3, clockid */ - /* 1=Bresenham Clock Gen 1 */ - /* 2=Bresenham Clock Gen 2 */ - /* 3=Bresenham Clock Gen 3 */ -#define H2I_AESTX_C_DTYPE 0x300 /* bits 9:8, datatype */ - /* 1=mono 2=stereo 3=quad */ - -/* DAC */ - -#define H2IW_DAC_C1 0x1404 /* dac tx dma control 1 write */ -#define H2IR_DAC_C1 0x1484 /* dac tx dma control 1 read */ -#define H2I_DAC_C1_CLKID 0x18 /* bits 4:3, clockid */ - /* 1=Bresenham Clock Gen 1 */ - /* 2=Bresenham Clock Gen 2 */ - /* 3=Bresenham Clock Gen 3 */ -#define H2I_DAC_C1_DTYPE 0x300 /* bits 9:8 datatype */ - /* 1=mono 2=stereo 3=quad */ - -#define H2IW_DAC_C2 0x1408 /* dac control 2 write word 0 */ -#define H2IR_DAC_C2_0 0x1488 /* dac control 2 read word 0 */ -#define H2IR_DAC_C2_1 0x1489 /* dac control 2 read word 1 */ - /* XXX: The spec says 0x1488 */ -#define H2I_DAC_C2_0_R_GAIN 0x0f /* right a/d input gain bit 0-3 */ -#define H2I_DAC_C2_0_L_GAIN 0xf0 /* left a/d input gain bit 0-3 */ -#define H2I_DAC_C2_0_R_SEL 0x100 /* right a/d input select */ -#define H2I_DAC_C2_0_L_SEL 0x200 /* left a/d input select */ -#define H2I_DAC_C2_0_MUTE 0x400 /* 1=mute */ -#define H2I_DAC_C2_1_DO1 0x01 /* digital output port bit 1 */ -#define H2I_DAC_C2_1_DO2 0x02 /* digital output port bit 0 */ -#define H2I_DAC_C2_1_R_ATTEN 0x7c /* bits 6:2 right a/d output */ - /* attenuation, bit 0-4 (4=msb) */ -#define H2I_DAC_C2_1_L_ATTEN 0xf80 /* bits 11:7 left a/d output */ - /* attenuation, bit 0-4 (4=msb) */ -/* ADC */ - -#define H2IW_ADC_C1 0x1504 /* adc tx dma control 1 write */ -#define H2IR_ADC_C1 0x1584 /* adc tx dma control 1 read */ -#define H2I_ADC_C1_CLKID 0x18 /* bits 4:3, clockid */ - /* 1=Bresenham Clock Gen 1 */ - /* 2=Bresenham Clock Gen 2 */ - /* 3=Bresenham Clock Gen 3 */ -#define H2I_ADC_C1_DTYPE 0x300 /* bits 9:8, datatype */ - /* 1=mono 2=stereo 3=quad */ - -#define H2IW_ADC_C2 0x1508 /* adc control 2 write word 0-1 */ - /* both words have to be written at */ - /* the same time, fill idr[0-1] */ -#define H2IR_ADC_C2_0 0x1588 /* adc control 2 read word 0 */ -#define H2IR_ADC_C2_1 0x1589 /* adc control 2 read word 1 */ - /* XXX: The spec says 0x1588 */ -#define H2I_ADC_C2_0_R_GAIN 0x0f /* right a/d input gain bit 0-3 */ -#define H2I_ADC_C2_0_L_GAIN 0xf0 /* left a/d input gain bit 0-3 */ -#define H2I_ADC_C2_0_R_SEL 0x100 /* right a/d input select */ -#define H2I_ADC_C2_0_L_SEL 0x200 /* left a/d input select */ -#define H2I_ADC_C2_0_MUTE 0x400 /* 1=mute */ -#define H2I_ADC_C2_1_DO1 0x01 /* digital output port bit 1 */ -#define H2I_ADC_C2_1_DO2 0x02 /* digital output port bit 0 */ -#define H2I_ADC_C2_1_R_ATTEN 0x7c /* bits 6:2, right a/d output */ - /* attenuation, bit 0-4 (4=msb) */ -#define H2I_ADC_C2_1_L_ATTEN 0xf80 /* bits 11:7, left a/d output */ - /* attenuation, bit 0-4 (4=msb) */ - -#define H2IW_SYNTH_MAP_C 0x1104 /* synth dma handshake ctrl write */ -#define H2IR_SYNTH_MAP_C 0x1184 /* synth dma handshake ctrl read */ - -#define H2IW_BRES1_C1 0x2104 /* clock gen 1 ctrl 1 write */ -#define H2IR_BRES1_C1 0x2184 /* clock gen 1 ctrl 1 read */ -#define H2I_BRES1_C1_FSRSEL 0x03 /* master clock source */ - /* 0=48.0 1=44.1 2=aes_rx */ - -#define H2IW_BRES1_C2 0x2108 /* clock gen 1 ctrl 2 write word 0-1 */ -#define H2IR_BRES1_C2_0 0x2188 /* clock gen 1 ctrl 2 read word 0 */ -#define H2IR_BRES1_C2_1 0x2189 /* clock gen 1 ctrl 2 read word 1 */ - /* XXX: The spec says 0x2188 */ -#define H2I_BRES1_C2_0_INC 0xffff /* increment value, inc <= mod */ -#define H2I_BRES1_C2_1_INC 0xffff /* modcontrol value, */ - /* modctrl=0x00ffff & (modinc-1) */ - -#define H2IW_BRES2_C1 0x2204 /* clock gen 2 ctrl 1 write */ -#define H2IR_BRES2_C1 0x2284 /* clock gen 2 ctrl 1 read */ -#define H2I_BRES2_C1_FSRSEL 0x03 /* master clock source */ - /* 0=48.0 1=44.1 2=aes_rx */ - -#define H2IW_BRES2_C2 0x2208 /* clock gen 2 ctrl 2 write word 0-1 */ -#define H2IR_BRES2_C2_0 0x2288 /* clock gen 2 ctrl 2 read word 0 */ -#define H2IR_BRES2_C2_1 0x2289 /* clock gen 2 ctrl 2 read word 1 */ -#define H2I_BRES2_C2_0_INC 0xffff /* increment value, inc <= mod */ -#define H2I_BRES2_C2_1_INC 0xffff /* modcontrol value, */ - /* modctrl=0x00ffff & (modinc-1) */ - -#define H2IW_BRES3_C1 0x2304 /* clock gen 3 ctrl 1 write */ -#define H2IR_BRES3_C1 0x2384 /* clock gen 3 ctrl 1 read */ -#define H2I_BRES3_C1_FSRSEL 0x03 /* master clock source */ - /* 0=48.0 1=44.1 2=aes_rx */ - -#define H2IW_BRES3_C2 0x2308 /* clock gen 3 ctrl 2 write word 0-1 */ -#define H2IR_BRES3_C2_0 0x2388 /* clock gen 3 ctrl 2 read word 0 */ -#define H2IR_BRES3_C2_1 0x2389 /* clock gen 3 ctrl 2 read word 1 */ -#define H2I_BRES3_C2_0_INC 0xffff /* increment value, inc <= mod */ -#define H2I_BRES3_C2_1_INC 0xffff /* modcontrol value, */ - /* modctrl=0x00ffff & (modinc-1) */ - -#define H2IW_UTIME 0x3104 /* unix timer write (preload time) */ -#define H2IR_UTIME 0x3184 /* unix timer read */ -#define H2I_UTIME_0_LD 0xffff /* microseconds, LSB's */ -#define H2I_UTIME_1_LD0 0x0f /* microseconds, MSB's */ -#define H2I_UTIME_1_LD1 0xf0 /* tenths of microseconds */ -#define H2I_UTIME_2_LD 0xffff /* seconds, LSB's */ -#define H2I_UTIME_3_LD 0xffff /* seconds, MSB's */ diff --git a/drivers/sound/Config.in b/drivers/sound/Config.in index b13122e4e..c7de93a50 100644 --- a/drivers/sound/Config.in +++ b/drivers/sound/Config.in @@ -16,6 +16,9 @@ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then fi dep_tristate 'Creative Ensoniq AudioPCI 97 (ES1371)' CONFIG_SOUND_ES1371 $CONFIG_SOUND dep_tristate 'S3 SonicVibes' CONFIG_SOUND_SONICVIBES $CONFIG_SOUND + if [ "$CONFIG_SGI" = "y" ]; then + dep_tristate 'SGI Indy HAL2' CONFIG_SOUND_HAL2 $CONFIG_SOUND + fi fi dep_tristate 'Support for Turtle Beach MultiSound Classic, Tahiti, Monterey' CONFIG_SOUND_MSNDCLAS $CONFIG_SOUND diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile index c4fb47dce..b76e03ce3 100644 --- a/drivers/sound/Makefile +++ b/drivers/sound/Makefile @@ -76,6 +76,7 @@ obj-$(CONFIG_SOUND_WAVEFRONT) += wavefront.o #jnx obj-$(CONFIG_SOUND_ES1370) += es1370.o obj-$(CONFIG_SOUND_ES1371) += es1371.o +obj-$(CONFIG_SOUND_HAL2) += hal2.o obj-$(CONFIG_SOUND_SONICVIBES) += sonicvibes.o # Declare multi-part drivers. diff --git a/drivers/sound/hal2.c b/drivers/sound/hal2.c new file mode 100644 index 000000000..6a70267cc --- /dev/null +++ b/drivers/sound/hal2.c @@ -0,0 +1,1529 @@ +/* $Id: hal2.c,v 1.13 1999/02/07 22:18:37 ulfc Exp $ + * + * drivers/sgi/audio/hal2.c + * + * Copyright (C) 1998-1999 Ulf Carlsson (ulfc@bun.falkenberg.se) + * + */ + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/malloc.h> +#include <linux/string.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/poll.h> +#include <linux/soundcard.h> +#include <linux/sound.h> + +#include <asm/uaccess.h> +#include <asm/system.h> +#include <asm/page.h> +#include <asm/io.h> +#include <asm/sgihpc.h> + +#include "hal2.h" + +#define DEBUG + +static const unsigned sample_size[] = { 2, 4 }; +static const unsigned sample_shift[] = { 1, 2 }; + +struct hal2_ring_buf { + struct hpc_dma_desc desc; + + struct hal2_ring_buf *next; + unsigned long buf; /* pointer in KSEG1 address space */ +}; + +/* As you might know, dma descriptors must be 8 byte aligned, that's why we have + * them in this special way. + */ + +struct hal2_state { + /* soundcore stuff */ + int dev_audio; + int dev_mixer; + int dev_dac; + + int irq; + + unsigned int dacrate, adcrate; + + spinlock_t lock; + struct semaphore open_sem; + mode_t open_mode; + struct wait_queue *open_wait; + + struct dmabuf { + void *rawbuf; + unsigned int buforder; + struct hal2_ring_buf *ringbuf; + + struct hpc3_pbus_dmacregs *pbus; + int pbusnr; + + unsigned int numfrag; + unsigned int fragshift; + int swptr; + int hwptr; + + unsigned int total_bytes; + + int count; + unsigned int error; /* over/underrun */ + struct wait_queue *wait; + + + /* Internal flags */ + unsigned little_end:1; + unsigned stereo:1; + unsigned receive:1; + unsigned pbus_setup:1; + unsigned pbus_en:1; + unsigned port_en:1; + + /* redundant, but makes calculations easier */ + unsigned fragsize; + unsigned dmasize; + unsigned fragsamples; + + /* OSS stuff */ + unsigned mapped:1; + unsigned ready:1; + unsigned endcleared:1; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; + } dma_dac, dma_adc; +}; + +struct hal2_state *dev; + +/* ------------------------------------------------------------------------ */ + +extern inline unsigned ld2(unsigned int x) +{ + unsigned r = 0; + + if (x >= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 4) { + x >>= 2; + r += 2; + } + if (x >= 2) + r++; + return r; +} + +/* ------------------------------------------------------------------ */ + +#define INDIRECT_WAIT(regs) while(regs->isr & H2_ISR_TSTATUS); + +#if 0 +#define INDIRECT_WAIT(regs) \ +{ \ + int cnt = 1000; \ + printk("hal2: waiting isr:%04hx ", regs->isr); \ + printk("idr0:%04hx idr1:%04hx idr2:%04hx idr3:%04hx\n", \ + regs->idr0, regs->idr1, regs->idr2, regs->idr3); \ + \ + while(regs->isr & H2_ISR_TSTATUS && --cnt) \ + udelay(5); \ + if (!cnt) \ + printk("hal2: failed waiting for indirect trans.\n"); \ + \ + printk("hal2: finished waiting at cnt:%d isr:%04hx ", \ + cnt, regs->isr); \ + printk("idr0:%04hx idr1:%04hx idr2:%04hx idr3:%04hx\n", \ + regs->idr0, regs->idr1, regs->idr2, regs->idr3); \ +} +#endif + +#define READ_ADDR(addr) (addr | (1<<7)) +#define WRITE_ADDR(addr) (addr) + +static unsigned short ireg_read16(unsigned short address) +{ + + h2_ctrl->iar = READ_ADDR(address); + INDIRECT_WAIT(h2_ctrl); + return h2_ctrl->idr0; +} + +static unsigned long ireg_read32(unsigned short address) +{ + unsigned long ret; + + h2_ctrl->iar = READ_ADDR(address); + INDIRECT_WAIT(h2_ctrl); + ret = h2_ctrl->idr0; + h2_ctrl->iar = READ_ADDR(address | 0x1); + INDIRECT_WAIT(h2_ctrl); + ret |= h2_ctrl->idr0 << 16; + return ret; +} + +static void ireg_write16(unsigned short address, unsigned short val) +{ + h2_ctrl->idr0 = val; + h2_ctrl->iar = WRITE_ADDR(address); + INDIRECT_WAIT(h2_ctrl) +} + +static void ireg_write32(unsigned short address, unsigned long val) +{ + h2_ctrl->idr0 = val & 0xffff; + h2_ctrl->idr1 = val >> 16; + h2_ctrl->iar = WRITE_ADDR(address); + INDIRECT_WAIT(h2_ctrl) +} + +static void ireg_write64(unsigned short address, unsigned long long val) +{ + h2_ctrl->idr0 = val & 0xffff; + h2_ctrl->idr1 = (val >> 16) & 0xffff; + h2_ctrl->idr2 = (val >> 32) & 0xffff; + h2_ctrl->idr3 = (val >> 48); + h2_ctrl->iar = WRITE_ADDR(address); + INDIRECT_WAIT(h2_ctrl) +} + +static void ireg_setbit16(unsigned short address, unsigned short bit) +{ + h2_ctrl->iar = READ_ADDR(address); + INDIRECT_WAIT(h2_ctrl); + h2_ctrl->idr0 = h2_ctrl->idr0 | bit; + h2_ctrl->iar = WRITE_ADDR(address); + INDIRECT_WAIT(h2_ctrl); +} + +static void ireg_setbit32(unsigned short address, unsigned long bit) +{ + unsigned long tmp; + + h2_ctrl->iar = READ_ADDR(address); + INDIRECT_WAIT(h2_ctrl); + tmp = h2_ctrl->idr0 | (h2_ctrl->idr1 << 16) | bit; + h2_ctrl->idr0 = tmp & 0xffff; + h2_ctrl->idr1 = (tmp >> 16) & 0xffff; + h2_ctrl->iar = WRITE_ADDR(address); + INDIRECT_WAIT(h2_ctrl); +} + +static void ireg_clearbit16(unsigned long address, unsigned short bit) +{ + h2_ctrl->iar = READ_ADDR(address); + INDIRECT_WAIT(h2_ctrl); + h2_ctrl->idr0 = h2_ctrl->idr0 & ~bit; + h2_ctrl->iar = address; + INDIRECT_WAIT(h2_ctrl); +} + +static void ireg_clearbit32(unsigned short address, unsigned long bit) +{ + unsigned long tmp; + + h2_ctrl->iar = READ_ADDR(address); + INDIRECT_WAIT(h2_ctrl); + tmp = (h2_ctrl->idr0 | (h2_ctrl->idr1 << 16)) & ~bit; + h2_ctrl->idr0 = tmp & 0xffff; + h2_ctrl->idr1 = (tmp >> 16) & 0xffff; + h2_ctrl->iar = WRITE_ADDR(address); + INDIRECT_WAIT(h2_ctrl); +} + +static void ireg_setmask16(unsigned short address, int shift, + unsigned short mask, unsigned short bits) +{ + h2_ctrl->iar = READ_ADDR(address); + INDIRECT_WAIT(h2_ctrl); + h2_ctrl->idr0 = (h2_ctrl->idr0 & ~mask) | (bits << shift); + h2_ctrl->iar = WRITE_ADDR(address); + INDIRECT_WAIT(h2_ctrl); +} + +static void ireg_setmask32(unsigned long address, int shift, + unsigned long mask, unsigned long bits) +{ + unsigned long tmp; + + h2_ctrl->iar = READ_ADDR(address); + INDIRECT_WAIT(h2_ctrl); + tmp = ((h2_ctrl->idr1 | h2_ctrl->idr0) & ~mask) | (bits << shift); + h2_ctrl->idr0 = tmp & 0xffff; + h2_ctrl->idr1 = tmp >> 16; + h2_ctrl->iar = WRITE_ADDR(address); + INDIRECT_WAIT(h2_ctrl); +} + +/* -----------------------------------------------------------------------*/ + +/* enable/disable a specific PBUS dma channel */ +static __inline__ void hpcpbus_enable(struct hpc3_pbus_dmacregs *pbus, + struct hpc_dma_desc *desc) +{ + pbus->pbdma_dptr = PHYSADDR(desc); + pbus->pbdma_ctrl |= HPC3_PDMACTRL_ACT; +} + +static __inline__ void hpcpbus_disable(struct hpc3_pbus_dmacregs *pbus) +{ + pbus->pbdma_ctrl &= ~HPC3_PDMACTRL_ACT; +} + +static __inline__ int hpcpbus_int(struct hpc3_pbus_dmacregs *pbus) +{ + /* When we read pbdma_ctrl, bit 0 indicates interrupt signal. + * The interrupt signal is also cleared after read + */ + + return (pbus->pbdma_ctrl & 0x01); +} + +/* ---------------------------------------------------------------------- */ + +/* We have three bresenham clock generators, which we can use independantly. + * + * There is one 44.1k and one 48.0k master clock for each of them. We can + * adjust the inc and the mod values for those clocks, and thus reduce the + * frequency. + * + * freq = master * (inc / mod) + * freq * mod = master * inc + * mod = (master * inc) / freq + * + * where (inc / mod) is a positive fraction in range [0,1]. Inc should always be + * set to 4 for codecs + * + */ + +#define __BUILD_SET_BRESN_CLOCK(clock) \ +static __inline__ void hal2_set_bres##clock(unsigned short master, \ + unsigned long mod) \ +{ \ + unsigned long incmod = (4 << 16) | mod; \ + ireg_write16(H2I_BRES##clock##_C1 , master); \ + ireg_write32(H2I_BRES##clock##_C2 , incmod); \ +} + +__BUILD_SET_BRESN_CLOCK(1) +__BUILD_SET_BRESN_CLOCK(2) +__BUILD_SET_BRESN_CLOCK(3) + +static int hal2_set_adc_rate(struct hal2_state *s, unsigned int rate) +{ + /* XXX how do i set this one? */ + + /* Try both 44.1k master and 48.0k master .. */ + + if ((48000 << 2) % rate == 0) + hal2_set_bres1(0, (48000 << 2) % rate); + else if ((44100 << 2) % rate == 0) + hal2_set_bres1(1, (44100 << 2) % rate); + else + /* this was no valid rate */ + return -EINVAL; + + return 0; +} + +static int hal2_set_dac_rate(struct hal2_state *s, unsigned int rate) +{ + /* Try both 44.1k master and 48.0k master .. */ + + if ((48000 << 2) % rate == 0) { + hal2_set_bres1(0, (48000 << 2) % rate); + s->dacrate = rate; + } else if ((44100 << 2) % rate == 0) { + hal2_set_bres1(1, (44100 << 2) % rate); + s->dacrate = rate; + } else + /* this was no valid rate */ + return -EINVAL; + + return 0; +} + +/* --------------------------------------------------------------------- */ + +static void hal2_stop(struct dmabuf *db) +{ + if (db->pbus_en) { + hpcpbus_disable(db->pbus); + db->pbus_en = 0; + } +#ifdef DEBUG + else + printk("Trying to stop already stopped DMA channel\n"); +#endif +} + +static void hal2_stop_adc(struct hal2_state *s) +{ + struct dmabuf *db = &s->dma_adc; + + hal2_stop(db); +} + +static void hal2_stop_dac(struct hal2_state *s) +{ + struct dmabuf *db = &s->dma_dac; + + hal2_stop(db); +} + + +static void hal2_start_adc(struct hal2_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + if (!s->dma_adc.port_en && (s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) && s->dma_adc.ready) { + hpcpbus_enable(s->dma_adc.pbus, &s->dma_adc.ringbuf->desc); + s->dma_adc.port_en = 1; + } + spin_unlock_irqrestore(&s->lock, flags); +} + +static void hal2_start_dac(struct hal2_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + if (!s->dma_dac.port_en && (s->dma_dac.mapped || s->dma_dac.count > 0) + && s->dma_dac.ready) { + hpcpbus_enable(s->dma_dac.pbus, &s->dma_dac.ringbuf->desc); + s->dma_dac.port_en = 1; + } + spin_unlock_irqrestore(&s->lock, flags); + +} + +/* ----------------------------------------------------------------------- */ + +#define DMABUF_DEFAULTORDER (17-PAGE_SHIFT) +#define DMABUF_MINORDER 1 + +static void hal2_dealloc_dmabuf(struct dmabuf *db) +{ + unsigned long map, mapend; + + if (db->rawbuf) { + /* undo marking the pages as reserved */ + mapend = MAP_NR(db->rawbuf + (PAGE_SIZE << db->buforder) -1); + for (map = MAP_NR(db->rawbuf); map <= mapend; map++) + clear_bit(PG_reserved, &mem_map[map].flags); + free_pages((unsigned long)db->rawbuf, db->buforder); + } + db->rawbuf = NULL; + db->mapped = db->ready = 0; +} + +static int hal2_setup_ring(struct dmabuf *db) +{ + int order; + unsigned long buffer; + unsigned long map, mapend; + struct hal2_ring_buf *rbuf; + int i = 0; + + /* FIXME: Clear up the memory leaks here */ + + for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) { + buffer = __get_free_pages(GFP_KERNEL, db->buforder); + if (buffer) + break; + } + + if (!buffer) + return -ENOMEM; + + db->buforder = order; + db->rawbuf = (void *) buffer; + + /* we have to mark the pages as reserver, otherwise remap_page_range + * doesn't do what we want for some */ + + mapend = MAP_NR(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); + for (map = MAP_NR(db->rawbuf); map <= mapend; map++) + set_bit(PG_reserved, &mem_map[map].flags); + + db->ringbuf = kmalloc(sizeof (struct hal2_ring_buf), GFP_KERNEL); + if (!db->ringbuf) + return -ENOMEM; + + rbuf = db->ringbuf; + rbuf->desc.pbuf = PHYSADDR(buffer + i * PAGE_SIZE); + rbuf->buf = KSEG1ADDR(buffer + i * PAGE_SIZE); + i++; + + while (i++ < (1 << db->buforder)) { + rbuf->next = kmalloc(sizeof (struct hal2_ring_buf), GFP_KERNEL); + if (!db->ringbuf) + return -ENOMEM; + + buffer = get_free_page(GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + /* we have to link the physical DMA buffers with phyical + * addresses first so that the HPC3 knows what to do */ + + rbuf->desc.pnext = PHYSADDR(buffer + i * PAGE_SIZE); + + rbuf = rbuf->next; + rbuf->buf = KSEG1ADDR(buffer + i * PAGE_SIZE); + rbuf->desc.pbuf = PHYSADDR(buffer + i * PAGE_SIZE); + } + + rbuf->next = db->ringbuf; + rbuf->desc.pnext = db->ringbuf->desc.pbuf; /* make it a ring! */ + + return 0; +} + +static void hal2_reset_adc_ring(struct hal2_state *s) + +{ + struct hal2_ring_buf *first, *rbuf; + + first = s->dma_adc.ringbuf; + + for (rbuf = first; rbuf->next != first; rbuf = rbuf->next) + rbuf->desc.cntinfo = HPCDMA_XIE | PAGE_SIZE; + + rbuf->desc.cntinfo = HPCDMA_XIE | PAGE_SIZE; +} + +static void hal2_reset_dac_ring(struct hal2_state *s) + +{ + struct hal2_ring_buf *first, *rbuf; + + first = s->dma_dac.ringbuf; + + for (rbuf = first; rbuf->next != first; rbuf = rbuf->next) + rbuf->desc.cntinfo = HPCDMA_XIE | PAGE_SIZE; + + rbuf->desc.cntinfo = HPCDMA_XIE | PAGE_SIZE; +} + +static int hal2_setup_pbus(struct hal2_state *s, struct dmabuf *db, int pbusnr) +{ + struct hpc3_pbus_dmacregs *pbus = &hpc3c0->pbdma0; + int sampsz, highwater, fifosize; + unsigned long ctrl; + unsigned long fifobeg, fifoend; + + db->pbusnr = pbusnr; + db->pbus = &pbus[pbusnr]; + + sampsz = (db->stereo ? 4 : 2); + + /* an audio fifo should be 4 samples long and doubleword aligned, + highwater should be half the fifo size */ + + fifosize = (sampsz * 4) >> 3; /* doublewords */ + highwater = (sampsz * 2) >> 1; /* halfwords */ + + /* DAC is always before ADC in our PBUS DMA FIFO. Reserve space for the + * maximum fifo length, we might change to stereo later if we're in mono + * now + */ + + fifobeg = (db->pbusnr == 1) ? 0 : (4 * 4) >> 3; + + fifoend = fifobeg + fifosize; + + ctrl = HPC3_PDMACTRL_RT | (db->receive ? HPC3_PDMACTRL_RCV : 0) | + (db->little_end ? HPC3_PDMACTRL_SEL : 0); + + pbus->pbdma_ctrl = (highwater << 8) | (fifobeg << 16) | + (fifoend << 24) | ctrl; + + /* Realtime, 16-bit, cycles to spend and more default settings for + * soundcards, taken directly from the spec. */ + hpc3c0->pbus_dmacfgs[db->pbusnr][0] = 0x8248844; + + return 0; +} + +static int hal2_prog_dmabuf(struct hal2_state *s, struct dmabuf *db, + unsigned rate) +{ + unsigned bytespersec; + unsigned bufs; + int err; + + db->hwptr = db->swptr = db->total_bytes = db->count = db->error = + db->endcleared = 0; + + if (!db->rawbuf) { + db->ready = db->mapped = 0; + err = hal2_setup_ring(db); + if (err) + return err; + } + + bytespersec = rate << sample_shift[db->stereo]; + bufs = PAGE_SIZE << db->buforder; + if (db->ossfragshift) { + if ((1000 << db->ossfragshift) < bytespersec) + db->fragshift = ld2(bytespersec/1000); + else + db->fragshift = db->ossfragshift; + } else { + db->fragshift = ld2(bytespersec/100/(db->subdivision ? db->subdivision : 1)); + if (db->fragshift < 3) + db->fragshift = 3; + } + db->numfrag = bufs >> db->fragshift; + while (db->numfrag < 4 && db->fragshift > 3) { + db->fragshift--; + db->numfrag = bufs >> db->fragshift; + } + db->fragsize = 1 << db->fragshift; + if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) + db->numfrag = db->ossmaxfrags; + db->fragsamples = db->fragsize >> sample_shift[db->stereo]; + db->dmasize = db->numfrag << db->fragshift; + memset(db->rawbuf, 0, db->dmasize); + return 0; +} + +static __inline__ int hal2_prog_dmabuf_adc(struct hal2_state *s) +{ + int err; + + hal2_stop_adc(s); + + err = hal2_setup_pbus(s, &s->dma_adc, 1); + if (err) + return err; + + err = hal2_prog_dmabuf(s, &s->dma_adc, s->adcrate); + if (err) + return err; + + hal2_reset_adc_ring(s); + + hal2_set_adc_rate(s, s->adcrate); + + ireg_write16(H2I_ADC_C1, + (0 << H2I_ADC_C1_DMA_SHIFT) | + (1 << H2I_ADC_C1_CLKID_SHIFT) | + (s->dma_adc.stereo << H2I_ADC_C1_DATAT_SHIFT)); + ireg_setbit16(H2I_DMA_PORT_EN, H2I_DMA_PORT_EN_CODECR); + + ireg_setbit16(H2I_DMA_DRV, (1 << 0)); + + s->dma_adc.ready = 1; + + return 0; +} + +static __inline__ int hal2_prog_dmabuf_dac(struct hal2_state *s) +{ + int err; + + hal2_stop_adc(s); + + err = hal2_setup_pbus(s, &s->dma_dac, 2); + if (err) + return err; + + err = hal2_prog_dmabuf(s, &s->dma_dac, s->dacrate); + if (err) + return err; + + hal2_reset_dac_ring(s); + + hal2_set_dac_rate(s, s->dacrate); + + ireg_write32(H2I_DAC_C1, + (1 << H2I_DAC_C1_DMA_SHIFT) | + (2 << H2I_DAC_C1_CLKID_SHIFT) | + (s->dma_dac.stereo << H2I_ADC_C1_DATAT_SHIFT)); + + ireg_setbit16(H2I_DMA_PORT_EN, H2I_DMA_PORT_EN_CODECR); + + ireg_setbit16(H2I_DMA_DRV, (1 << 1)); + + s->dma_dac.ready = 1; + + return 0; +} + +static __inline__ unsigned int hal2_get_hwptr(struct hal2_state *s, + struct dmabuf *db) +{ + return db->pbus->pbdma_bptr; +} + +extern __inline__ void clear_advance(void *buf, unsigned bsize, unsigned bptr, + unsigned len, unsigned char c) +{ + if (bptr + len > bsize) { + unsigned x = bsize - bptr; + memset(((char *)buf) + bptr, c, x); + bptr = 0; + len -= x; + } + memset(((char *)buf) + bptr, c, len); +} + +static void hal2_update_ptr(struct hal2_state *s) +{ + int diff; + + /* update ADC pointer */ + if (s->dma_adc.port_en) { + diff = hal2_get_hwptr(s, &s->dma_adc); + s->dma_adc.total_bytes += diff; + s->dma_adc.count += diff; + if (s->dma_adc.mapped) { + if (s->dma_adc.count >= s->dma_adc.fragsize) + wake_up(&s->dma_adc.wait); + } else { + if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { + hal2_stop_adc(s); + s->dma_adc.error++; + } + if (s->dma_adc.count > 0) + wake_up(&s->dma_adc.wait); + } + } + + /* update DAC1 pointer */ + if (s->dma_dac.port_en) { + diff = hal2_get_hwptr(s, &s->dma_dac); + s->dma_dac.total_bytes += diff; + if (s->dma_dac.mapped) { + s->dma_dac.count += diff; + if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) + wake_up(&s->dma_dac.wait); + } else { + s->dma_dac.count -= diff; + if (s->dma_dac.count <= 0) { + hal2_stop_dac(s); + s->dma_dac.error++; + } else if (s->dma_dac.count <= + (signed)s->dma_dac.fragsize && + !s->dma_dac.endcleared) { + clear_advance(s->dma_dac.rawbuf, + s->dma_dac.dmasize, + s->dma_dac.swptr, + s->dma_dac.fragsize, 0); + s->dma_dac.endcleared = 1; + } + if (s->dma_dac.count < (signed)s->dma_dac.dmasize) + wake_up(&s->dma_dac.wait); + } + } +} + +void hal2_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct hal2_state *s = (struct hal2_state *) dev_id; + + spin_lock(&s->lock); + + if ((s->dma_adc.pbus_en && hpcpbus_int(s->dma_adc.pbus))|| + (s->dma_dac.pbus_en && hpcpbus_int(s->dma_dac.pbus))) + hal2_update_ptr(s); + + spin_unlock(&s->lock); +} + + +static __inline__ void hal2_clear_advance(void *buf, unsigned long bsize, + unsigned long bptr, unsigned int len, + unsigned char c) +{ + if (bptr + len > bsize) { + unsigned long x = bsize - bptr; + memset(((char *)buf) + bptr, c, x); + bptr = 0; + len -= x; + } + memset(((char *)buf) + bptr, c, len); +} + + +/* ----------------------------------------------------------------------- */ + +static int hal2_drain_dac(struct hal2_state *s, int nonblock) +{ + struct wait_queue wait = { current, NULL }; + unsigned long flags; + int count, tmo; + + if (s->dma_dac.mapped) + return 0; + current->state = TASK_INTERRUPTIBLE; + add_wait_queue(&s->dma_dac.wait, &wait); + for (;;) { + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&s->dma_dac.wait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + tmo = (count * HZ) / s->dacrate; + tmo >>= sample_shift[(s->dma_dac.stereo)?0:1]; + + if (!schedule_timeout(tmo ? : 1) && tmo) + printk(KERN_DEBUG ": dma timed out??\n"); + } + remove_wait_queue(&s->dma_dac.wait, &wait); + current->state = TASK_RUNNING; + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +static ssize_t hal2_read(struct file *file, char *buffer, size_t count, + loff_t *ppos) +{ + struct hal2_state *s = (struct hal2_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + if (!s) + return -ENXIO; + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_adc.mapped) + return -ENXIO; + if (!s->dma_adc.ready && (ret = hal2_prog_dmabuf_adc(s))) + return ret; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + swptr = s->dma_adc.swptr; + cnt = s->dma_adc.dmasize-swptr; + if (s->dma_adc.count < cnt) + cnt = s->dma_adc.count; + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + hal2_start_adc(s); + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EBUSY; + interruptible_sleep_on(&s->dma_adc.wait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) + return ret ? ret : -EFAULT; + swptr = (swptr + cnt) % s->dma_adc.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_adc.swptr = swptr; + s->dma_adc.count -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + hal2_start_adc(s); + } + return ret; +} + +static ssize_t hal2_write(struct file *file, const char *buffer, size_t count, + loff_t *ppos) +{ + struct hal2_state *s = (struct hal2_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + if (!s) + return -ENXIO; + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_dac.mapped) + return -ENXIO; + if (!s->dma_dac.ready && (ret = hal2_prog_dmabuf_dac(s))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + if (s->dma_dac.count < 0) { + s->dma_dac.count = 0; + s->dma_dac.swptr = s->dma_dac.hwptr; + } + swptr = s->dma_dac.swptr; + cnt = s->dma_dac.dmasize-swptr; + if (s->dma_dac.count + cnt > s->dma_dac.dmasize) + cnt = s->dma_dac.dmasize - s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + hal2_start_dac(s); + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EBUSY; + interruptible_sleep_on(&s->dma_dac.wait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) + return ret ? ret : -EFAULT; + swptr = (swptr + cnt) % s->dma_dac.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_dac.swptr = swptr; + s->dma_dac.count += cnt; + s->dma_dac.endcleared = 0; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + hal2_start_dac(s); + } + return ret; +} + + +static unsigned int hal2_poll(struct file *file, struct poll_table_struct *wait) +{ + struct hal2_state *s = (struct hal2_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + if (!s) + return -EINVAL; + if (file->f_flags & FMODE_WRITE) + poll_wait(file, &s->dma_dac.wait, wait); + if (file->f_flags & FMODE_READ) + poll_wait(file, &s->dma_adc.wait, wait); + spin_lock_irqsave(&s->lock, flags); + hal2_update_ptr(s); + if (file->f_flags & FMODE_READ) { + if (s->dma_adc.mapped) { + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + mask |= POLLIN | POLLRDNORM; + } else { + if (s->dma_adc.count > 0) + mask |= POLLIN | POLLRDNORM; + } + } + if (file->f_flags & FMODE_WRITE) { + if (s->dma_dac.mapped) { + if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((signed)s->dma_dac.dmasize > s->dma_dac.count) + mask |= POLLOUT | POLLWRNORM; + } + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int hal2_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct hal2_state *s = (struct hal2_state *)file->private_data; + struct dmabuf *db; + int ret; + unsigned long size; + + if (!s) + return -EINVAL; + if (vma->vm_flags & VM_WRITE) { + if ((ret = hal2_prog_dmabuf_dac(s)) != 0) + return ret; + db = &s->dma_dac; + } else if (vma->vm_flags & VM_READ) { + if ((ret = hal2_prog_dmabuf_adc(s)) != 0) + return ret; + db = &s->dma_adc; + } else + return -EINVAL; + if (vma->vm_offset != 0) + return -EINVAL; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << db->buforder)) + return -EINVAL; + if (remap_page_range(vma->vm_start, PHYSADDR(db->rawbuf), size, vma->vm_page_prot)) + return -EAGAIN; + db->mapped = 1; + vma->vm_file = file; + file->f_count++; + return 0; +} + +static int hal2_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct hal2_state *s = (struct hal2_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int val, mapped, ret; + + if (!s) + return -EINVAL; + mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) || + ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + /* XXX check what drain really should do */ + return hal2_drain_dac(s, 0/*file->f_flags & O_NONBLOCK*/); + return 0; + + case SNDCTL_DSP_SETDUPLEX: + return 0; + + case SNDCTL_DSP_GETCAPS: + /* XXX do we really support trigger? */ + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | + DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg); + + case SNDCTL_DSP_RESET: + if (file->f_mode & FMODE_WRITE) { + hal2_stop_dac(s); + synchronize_irq(); + s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0; + } + if (file->f_mode & FMODE_READ) { + hal2_stop_adc(s); + synchronize_irq(); + s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; + } + return 0; + + case SNDCTL_DSP_SPEED: + get_user_ret(val, (int *)arg, -EFAULT); + if (val >= 0) { + if (file->f_mode & FMODE_READ) { + hal2_stop_adc(s); + s->dma_adc.ready = 0; + hal2_set_adc_rate(s, val); + } + if (file->f_mode & FMODE_WRITE) { + hal2_stop_dac(s); + s->dma_dac.ready = 0; + hal2_set_dac_rate(s, val); + } + } + return put_user((file->f_mode & FMODE_READ) ? s->adcrate : s->dacrate, (int *)arg); + + case SNDCTL_DSP_STEREO: + get_user_ret(val, (int *)arg, -EFAULT); + if (file->f_mode & FMODE_READ) { + hal2_stop_adc(s); + s->dma_adc.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val) { + ireg_setmask16(H2I_ADC_C1, + H2I_ADC_C1_DATAT_SHIFT, + H2I_ADC_C1_DATAT_M, 2); + s->dma_adc.stereo = 1; + } else { + ireg_setmask16(H2I_ADC_C1, + H2I_ADC_C1_DATAT_SHIFT, + H2I_ADC_C1_DATAT_M, 1); + s->dma_adc.stereo = 0; + } + spin_unlock_irqrestore(&s->lock, flags); + } + if (file->f_mode & FMODE_WRITE) { + hal2_stop_dac(s); + s->dma_dac.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val) { + ireg_setmask16(H2I_DAC_C1, + H2I_DAC_C1_DATAT_SHIFT, + H2I_DAC_C1_DATAT_M, 2); + s->dma_dac.stereo = 1; + } else { + ireg_setmask16(H2I_DAC_C1, + H2I_DAC_C1_DATAT_SHIFT, + H2I_DAC_C1_DATAT_M, 1); + s->dma_dac.stereo = 0; + } + spin_unlock_irqrestore(&s->lock, flags); + } + return 0; + + case SNDCTL_DSP_CHANNELS: + get_user_ret(val, (int *)arg, -EFAULT); + if (val != 0) { + if (file->f_mode & FMODE_READ) { + hal2_stop_adc(s); + s->dma_adc.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val >= 2) { + ireg_setmask16(H2I_ADC_C1, + H2I_ADC_C1_DATAT_SHIFT, + H2I_ADC_C1_DATAT_M, 2); + s->dma_dac.stereo = 1; + } else { + ireg_setmask16(H2I_ADC_C1, + H2I_ADC_C1_DATAT_SHIFT, + H2I_ADC_C1_DATAT_M, 1); + s->dma_adc.stereo = 0; + } + spin_unlock_irqrestore(&s->lock, flags); + } + if (file->f_mode & FMODE_WRITE) { + hal2_stop_dac(s); + s->dma_dac.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val >= 2) { + ireg_setmask16(H2I_DAC_C1, + H2I_DAC_C1_DATAT_SHIFT, + H2I_DAC_C1_DATAT_M, 2); + s->dma_dac.stereo = 1; + } else { + ireg_setmask16(H2I_DAC_C1, + H2I_DAC_C1_DATAT_SHIFT, + H2I_DAC_C1_DATAT_M, 1); + s->dma_dac.stereo = 0; + } + spin_unlock_irqrestore(&s->lock, flags); + } + } + return put_user(s->dma_dac.stereo ? 2 : 1, (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_S16_BE, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Maybe Little endian too */ + return put_user(AFMT_S16_BE, (int *)arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if (file->f_mode & FMODE_READ && s->dma_adc.port_en) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && s->dma_dac.port_en) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + get_user_ret(val, (int *)arg, -EFAULT); + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + if (!s->dma_adc.ready && (ret = hal2_prog_dmabuf_adc(s))) + return ret; + hal2_start_adc(s); + } else + hal2_stop_adc(s); + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!s->dma_dac.ready && (ret = hal2_prog_dmabuf_dac(s))) + return ret; + hal2_start_dac(s); + } else + hal2_stop_dac(s); + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!s->dma_dac.port_en && (val = hal2_prog_dmabuf_dac(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + hal2_update_ptr(s); + abinfo.fragsize = s->dma_dac.fragsize; + abinfo.bytes = s->dma_dac.dmasize - s->dma_dac.count; + abinfo.fragstotal = s->dma_dac.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!(s->dma_adc.port_en) && (val = hal2_prog_dmabuf_adc(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + hal2_update_ptr(s); + abinfo.fragsize = s->dma_adc.fragsize; + abinfo.bytes = s->dma_adc.count; + abinfo.fragstotal = s->dma_adc.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + hal2_update_ptr(s); + val = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + return put_user(val, (int *)arg); + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + hal2_update_ptr(s); + cinfo.bytes = s->dma_adc.total_bytes; + cinfo.blocks = s->dma_adc.total_bytes >> s->dma_adc.fragshift; + cinfo.ptr = s->dma_adc.hwptr; + if (s->dma_adc.mapped) + s->dma_adc.count &= s->dma_adc.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + hal2_update_ptr(s); + cinfo.bytes = s->dma_dac.total_bytes; + cinfo.blocks = s->dma_dac.total_bytes >> s->dma_dac.fragshift; + cinfo.ptr = s->dma_dac.hwptr; + if (s->dma_dac.mapped) + s->dma_dac.count &= s->dma_dac.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + if ((val = hal2_prog_dmabuf_dac(s))) + return val; + return put_user(s->dma_dac.fragsize, (int *)arg); + } + if ((val = hal2_prog_dmabuf_adc(s))) + return val; + return put_user(s->dma_adc.fragsize, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + get_user_ret(val, (int *)arg, -EFAULT); + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = val & 0xffff; + s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_adc.ossfragshift < 4) + s->dma_adc.ossfragshift = 4; + if (s->dma_adc.ossfragshift > 15) + s->dma_adc.ossfragshift = 15; + if (s->dma_adc.ossmaxfrags < 4) + s->dma_adc.ossmaxfrags = 4; + } + if (file->f_mode & FMODE_WRITE) { + s->dma_dac.ossfragshift = val & 0xffff; + s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_dac.ossfragshift < 4) + s->dma_dac.ossfragshift = 4; + if (s->dma_dac.ossfragshift > 15) + s->dma_dac.ossfragshift = 15; + if (s->dma_dac.ossmaxfrags < 4) + s->dma_dac.ossmaxfrags = 4; + } + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || + (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision)) + return -EINVAL; + get_user_ret(val, (int *)arg, -EFAULT); + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + if (file->f_mode & FMODE_READ) + s->dma_adc.subdivision = val; + if (file->f_mode & FMODE_WRITE) + s->dma_dac.subdivision = val; + return 0; + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_RATE: + case SOUND_PCM_READ_CHANNELS: + case SOUND_PCM_READ_BITS: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + + } + return -EINVAL; +} + +static int hal2_open(struct inode *inode, struct file *file) +{ + struct hal2_state *s = dev; + + if (!s) + return -ENODEV; + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & file->f_mode) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + up(&s->open_sem); + interruptible_sleep_on(&s->open_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; + hal2_set_adc_rate(s, 8000); + } + if (file->f_mode & FMODE_WRITE) { + s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0; + hal2_set_dac_rate(s, 8000); + } + s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + up(&s->open_sem); + MOD_INC_USE_COUNT; + return 0; +} + +static int hal2_release(struct inode *inode, struct file *file) +{ + struct hal2_state *s = (struct hal2_state *)file->private_data; + + if (!s) + return -ENODEV; + if (file->f_mode & FMODE_WRITE) + hal2_drain_dac(s, file->f_flags & O_NONBLOCK); + down(&s->open_sem); + if (file->f_flags & FMODE_WRITE) { + hal2_stop_dac(s); + hal2_dealloc_dmabuf(&s->dma_dac); + } + if (file->f_flags & FMODE_READ) { + hal2_stop_adc(s); + hal2_dealloc_dmabuf(&s->dma_adc); + } + s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); + up(&s->open_sem); + wake_up(&s->open_wait); + MOD_DEC_USE_COUNT; + return 0; +} + +static struct file_operations hal2_audio_fops = { + NULL, /* hal2_llseek */ + hal2_read, + hal2_write, + NULL, /* hal2_readdir */ + hal2_poll, + hal2_ioctl, + hal2_mmap, + hal2_open, + NULL, /* hal2_flush */ + hal2_release, + NULL, /* hal2_fsync */ + NULL, /* hal2_fasync */ + NULL, /* hal2_check_media_change */ + NULL, /* hal2_revalidate */ + NULL, /* hal2_lock */ +}; + +/* ----------------------------------------------------------------------- */ + +static void hal2_reset(void) +{ + printk("resetting global isr:%04hx\n", h2_ctrl->isr); +#if 0 + /* try to leave the card in the bootup state so that the mixer stuff + * isn't gone */ + h2_ctrl->isr = 0; /* reset the card */ +#endif + printk("reset done isr:%04hx\n", h2_ctrl->isr); + h2_ctrl->isr = H2_ISR_GLOBAL_RESET_N | H2_ISR_CODEC_RESET_N; + printk("reactivation done isr:%04hx\n", h2_ctrl->isr); +} + +static int hal2_probe(void) +{ + unsigned short board, major, minor; + + unsigned long tmp; + + hal2_reset(); + + if (h2_ctrl->rev & H2_REV_AUDIO_PRESENT) { + + printk("hal2: there was no device?\n"); + return -ENODEV; + } + + board = (h2_ctrl->rev & H2_REV_BOARD_M) >> 12; + major = (h2_ctrl->rev & H2_REV_MAJOR_CHIP_M) >> 4; + minor = (h2_ctrl->rev & H2_REV_MINOR_CHIP_M); + + printk("SGI H2 Processor, Revision %i.%i.%i\n", + board, major, minor); + + if (board != 4 || major != 1 || minor != 0) { + printk("hal2: Other revision than 4.1.0 detected\n"); + printk("hal2: Your card is probably not supported\n"); + } + +#ifdef DEBUG + printk("hal2: probing card\n"); +#endif + + /* check that the indirect registers are working by writing some bogus + * stuff and then reading it back */ + + ireg_write16(H2I_DAC_C1, 0x4084); /* 16 bit register */ + ireg_write32(H2I_BRES2_C2, 0x20C0084); /* 32 bit register */ + +#ifdef DEBUG + printk("hal2: write done\n"); +#endif + + if ((tmp = ireg_read16(H2I_DAC_C1)) != 0x4084) { + printk("hal2: Didn't pass register check #1 (%04lx)\n", tmp); + return -ENODEV; + } + if ((tmp = ireg_read32(H2I_BRES2_C2)) != 0x20C0084) { + printk("hal2: Didn't pass register check #2 (%08lx)\n", tmp); + return -ENODEV; + } + +#ifdef DEBUG + printk("hal2: read done\n"); +#endif + printk("hal2: card found\n"); + + return 0; +} + +static int hal2_init(void) +{ + static int initialized = 0; + + if (initialized) { + printk("hal2: already initialized?\n"); + return 0; + } + + if (!dev) { +#ifdef DEBUG + printk("hal2: allocating hal2 device pointer\n"); +#endif + dev = kmalloc(sizeof(struct hal2_state), GFP_KERNEL); + if (!dev) + return -ENOMEM; + memset(dev, 0, sizeof(struct hal2_state)); + } + +#ifdef DEBUG + printk("hal2: resetting chip\n"); +#endif + hal2_reset(); + + /* setup indigo codec mode */ + +#ifdef DEBUG + printk("hal2: init done\n"); +#endif + + initialized = 1; + + return 0; +} + + +#ifdef MODULE +__initfunc(int init_module(void)) +#else +__initfunc(int init_hal2(void)) +#endif +{ + struct hal2_state *s; + int err; + + err = hal2_probe(); + if (err) + return err; + + err = hal2_init(); + if (err) + return err; + + s = dev; + + memset(s, 0, sizeof(struct hal2_state)); + init_waitqueue(&s->dma_adc.wait); + init_waitqueue(&s->dma_dac.wait); + init_waitqueue(&s->open_wait); + s->open_sem = MUTEX; + + /* Indigo CODEC mode (!QUAD) */ + h2_ctrl->isr &= ~H2_ISR_QUAD_MODE; + + s->irq = 12; /* wild guess! */ + err = request_irq(s->irq, hal2_interrupt, SA_INTERRUPT, "hal2", s); + if (err) { + printk(KERN_ERR "hal2: irq %u in use\n", s->irq); + return err; + } + s->dev_audio = register_sound_dsp(&hal2_audio_fops); + if (s->dev_audio < 0) + printk(KERN_ERR "hal2: cannot register misc device\n"); + + hal2_set_adc_rate(s, 22050); + hal2_set_dac_rate(s, 22050); + + return 0; +} + +#ifdef MODULE + +void cleanup_module(void) +{ + struct hal2_state *s = dev; + + synchronize_irq(); + free_irq(s->irq, s); + unregister_sound_dsp(s->dev_audio); + kfree_s(s, sizeof(struct hal2_state)); + printk(KERN_INFO "hal2: unloading\n"); +} + +#endif diff --git a/drivers/sound/hal2.h b/drivers/sound/hal2.h new file mode 100644 index 000000000..1749534ce --- /dev/null +++ b/drivers/sound/hal2.h @@ -0,0 +1,259 @@ +/* + * drivers/sgi/audio/hal2.h + * + * Copyright (C) 1998 Ulf Carlsson (ulfc@bun.falkenberg.se) + * + */ + +#define H2_HAL2_BASE (HPC3_CHIP0_PBASE + 0x58000) +#define H2_CTRL_PIO (H2_HAL2_BASE + 0 * 0x200) +#define H2_AES_PIO (H2_HAL2_BASE + 1 * 0x200) +#define H2_VOLUME_PIO (H2_HAL2_BASE + 2 * 0x200) +#define H2_SYNTH_PIO (H2_HAL2_BASE + 3 * 0x200) + +typedef volatile unsigned int hal_reg; + +struct hal2_ctrl_regs { + hal_reg _unused0[4]; + hal_reg isr; /* 0x10 Status Register */ + hal_reg _unused1[3]; + hal_reg rev; /* 0x20 Revision Register */ + hal_reg _unused2[3]; + hal_reg iar; /* 0x30 Indirect Address Register */ + hal_reg _unused3[3]; + hal_reg idr0; /* 0x40 Indirect Data Register 0 */ + hal_reg _unused4[3]; + hal_reg idr1; /* 0x50 Indirect Data Register 1 */ + hal_reg _unused5[3]; + hal_reg idr2; /* 0x60 Indirect Data Register 2 */ + hal_reg _unused6[3]; + hal_reg idr3; /* 0x70 Indirect Data Register 3 */ +} volatile *h2_ctrl = (struct hal2_ctrl_regs *) KSEG1ADDR(H2_CTRL_PIO); + +struct hal2_vol_regs { + hal_reg right; /* 0x00 Right volume */ + hal_reg left; /* 0x04 Left volume */ +} volatile *h2_vol = (struct hal2_vol_regs *) KSEG1ADDR(H2_VOLUME_PIO); + +/* AES and synth regs should end up here if we ever support them */ + +/* Indirect status register */ + +#define H2_ISR_TSTATUS 0x01 /* RO: transaction status 1=busy */ +#define H2_ISR_USTATUS 0x02 /* RO: utime status bit 1=armed */ +#define H2_ISR_QUAD_MODE 0x04 /* codec mode 0=indigo 1=quad */ +#define H2_ISR_GLOBAL_RESET_N 0x08 /* chip global reset 0=reset */ +#define H2_ISR_CODEC_RESET_N 0x10 /* codec/synth reset 0=reset */ + + /* Revision register */ + +#define H2_REV_AUDIO_PRESENT 0x8000 /* RO: audio present 0=present */ +#define H2_REV_BOARD_M 0x7000 /* RO: bits 14:12, board revision */ +#define H2_REV_MAJOR_CHIP_M 0x00F0 /* RO: bits 7:4, major chip revision */ +#define H2_REV_MINOR_CHIP_M 0x000F /* RO: bits 3:0, minor chip revision */ + +/* Indirect address register */ + +/* + * Address of indirect internal register to be accessed. A write to this + * register initiates read or write access to the indirect registers in the + * HAL2. Note that there af four indirect data registers for write access to + * registers larger than 16 byte. + */ + +#define H2_IAR_TYPE_M 0xF000 /* bits 15:12, type of functional */ + /* block the register resides in */ + /* 1=DMA Port */ + /* 9=Global DMA Control */ + /* 2=Bresenham */ + /* 3=Unix Timer */ +#define H2_IAR_NUM_M 0x0F00 /* bits 11:8 instance of the */ + /* blockin which the indirect */ + /* register resides */ + /* If IAR_TYPE_M=DMA Port: */ + /* 1=Synth In */ + /* 2=AES In */ + /* 3=AES Out */ + /* 4=DAC Out */ + /* 5=ADC Out */ + /* 6=Synth Control */ + /* If IAR_TYPE_M=Global DMA Control: */ + /* 1=Control */ + /* If IAR_TYPE_M=Bresenham: */ + /* 1=Bresenham Clock Gen 1 */ + /* 2=Bresenham Clock Gen 2 */ + /* 3=Bresenham Clock Gen 3 */ + /* If IAR_TYPE_M=Unix Timer: */ + /* 1=Unix Timer */ +#define H2_IAR_ACCESS_SELECT 0x0080 /* 1=read 0=write */ +#define H2_IAR_PARAM 0x000C /* Parameter Select */ +#define H2_IAR_RB_INDEX_M 0x0003 /* Read Back Index */ + /* 00:word0 */ + /* 01:word1 */ + /* 10:word2 */ + /* 11:word3 */ +/* + * HAL2 internal addressing + * + * The HAL2 has "indirect registers" (idr) which are accessed by writing to the + * Indirect Data registers. Write the address to the Indirect Address register + * to transfer the data. + * + * We define the H2IR_* to the read address and H2IW_* to the write address and + * H2I_* to be fields in whatever register is referred to. + * + * When we write to indirect registers which are larger than one word (16 bit) + * we have to fill more than one indirect register before writing. When we read + * back however we have to read several times, each time with different Read + * Back Indexes (there are defs for doing this easily). + */ + +/* + * Relay Control + */ +#define H2I_RELAY_C 0x9100 +#define H2I_RELAY_C_STATE 0x01 /* state of RELAY pin signal */ + +/* DMA port enable */ + +#define H2I_DMA_PORT_EN 0x9104 +#define H2I_DMA_PORT_EN_SY_IN 0x01 /* Synth_in DMA port */ +#define H2I_DMA_PORT_EN_AESRX 0x02 /* AES receiver DMA port */ +#define H2I_DMA_PORT_EN_AESTX 0x04 /* AES transmitter DMA port */ +#define H2I_DMA_PORT_EN_CODECTX 0x08 /* CODEC transmit DMA port */ +#define H2I_DMA_PORT_EN_CODECR 0x10 /* CODEC receive DMA port */ + +#define H2I_DMA_END 0x9108 /* global dma endian select */ +#define H2I_DMA_END_SY_IN 0x01 /* Synth_in DMA port */ +#define H2I_DMA_END_AESRX 0x02 /* AES receiver DMA port */ +#define H2I_DMA_END_AESTX 0x04 /* AES transmitter DMA port */ +#define H2I_DMA_END_CODECTX 0x08 /* CODEC transmit DMA port */ +#define H2I_DMA_END_CODECR 0x10 /* CODEC receive DMA port */ + /* 0=b_end 1=l_end */ + +#define H2I_DMA_DRV 0x910C /* global PBUS DMA enable */ + +#define H2I_SYNTH_C 0x1104 /* Synth DMA control */ + +#define H2I_AESRX_C 0x1204 /* AES RX dma control */ +#define H2I_AESRX_C_TS_EN 0x20 /* timestamp enable */ +#define H2I_AESRX_C_TS_FMT 0x40 /* timestamp format */ +#define H2I_AESRX_C_NAUDIO 0x80 /* PBUS DMA data format */ + +/* AESRX CTRL, 16 bit */ + +#define H2I_AESTX_C 0x1304 /* AES TX DMA control */ +#define H2I_AESTX_C_CLKID_SHIFT 3 /* Bresenham Clock Gen 1-3 */ +#define H2I_AESTX_C_CLKID_M 0x18 +#define H2I_AESTX_C_DATAT_SHIFT 8 /* 1=mono 2=stereo (3=quad) */ +#define H2I_AESTX_C_DATAT_M 0x300 + +/* DAC CTRL1, 16 bit */ + +#define H2I_DAC_C1 0x1404 /* DAC dma control */ +#define H2I_DAC_C1_DMA_SHIFT 0 /* DMA channel */ +#define H2I_DAC_C1_DMA_M 0x7 +#define H2I_DAC_C1_CLKID_SHIFT 3 /* Bresenham Clock Gen 1-3 */ +#define H2I_DAC_C1_CLKID_M 0x18 +#define H2I_DAC_C1_DATAT_SHIFT 8 /* 1=mono 2=stereo (3=quad) */ +#define H2I_DAC_C1_DATAT_M 0x300 + +/* DAC CTRL2, 32 bit */ + +#define H2I_DAC_C2 0x1408 +#define H2I_DAC_C2_RGAIN_SHIFT 0 /* right a/d input gain */ +#define H2I_DAC_C2_R_GAIN_M 0xf +#define H2I_DAC_C2_L_GAIN_SHIFT 4 /* left a/d input gain */ +#define H2I_DAC_C2_L_GAIN_M 0xf0 +#define H2I_DAC_C2_R_SEL 0x100 /* right input select */ +#define H2I_DAC_C2_L_SEL 0x200 /* left input select */ +#define H2I_DAC_C2_MUTE 0x400 /* mute */ +#define H2I_DAC_C2_DO1 0x10000 /* digital output port bit 0 */ +#define H2I_DAC_C2_DO2 0x20000 /* digital output port bit 1 */ +#define H2I_DAC_C2_R_ATT_SHIFT 18 /* right a/d output - */ +#define H2I_DAC_C2_R_ATT_M 0x7c0000 /* attenuation */ +#define H2I_DAC_C2_L_ATT_SHIFT 23 /* left a/d output - */ +#define H2I_DAC_C2_L_ATT_M 0x0000f80 /* attenuation */ + +/* ADC CTRL1, 16 bit */ + +#define H2I_ADC_C1 0x1504 /* DAC dma control */ +#define H2I_ADC_C1_DMA_SHIFT 0 /* DMA channel */ +#define H2I_ADC_C1_DMA_M 0x7 +#define H2I_ADC_C1_CLKID_SHIFT 3 /* Bresenham Clock Gen 1-3 */ +#define H2I_ADC_C1_CLKID_M 0x18 +#define H2I_ADC_C1_DATAT_SHIFT 8 /* 1=mono 2=stereo (3=quad) */ +#define H2I_ADC_C1_DATAT_M 0x300 + +/* ADC CTRL2, 32 bit */ + +#define H2I_ADC_C2 0x1508 +#define H2I_ADC_C2_RGAIN_SHIFT 0 /* right a/d input gain */ +#define H2I_ADC_C2_R_GAIN_M 0xf +#define H2I_ADC_C2_L_GAIN_SHIFT 4 /* left a/d input gain */ +#define H2I_ADC_C2_L_GAIN_M 0xf0 +#define H2I_ADC_C2_R_SEL 0x100 /* right input select */ +#define H2I_ADC_C2_L_SEL 0x200 /* left input select */ +#define H2I_ADC_C2_MUTE 0x400 /* mute */ +#define H2I_ADC_C2_DO1 0x10000 /* digital output port bit 0 */ +#define H2I_ADC_C2_DO2 0x20000 /* digital output port bit 1 */ +#define H2I_ADC_C2_R_ATT_SHIFT 18 /* right a/d output - */ +#define H2I_ADC_C2_R_ATT_M 0x7c0000 /* attenuation */ +#define H2I_ADC_C2_L_ATT_SHIFT 23 /* left a/d output - */ +#define H2I_ADC_C2_L_ATT_M 0x0000f80 /* attenuation */ + + +#define H2I_SYNTH_MAP_C 0x1104 /* synth dma handshake ctrl */ + +/* Clock generator 1 CTRL 1, 16 bit */ + +#define H2I_BRES1_C1 0x2104 +#define H2I_BRES1_C1_SHIFT 0 /* 0=48.0 1=44.1 2=aes_rx */ +#define H2I_BRES1_C1_M 0x03 + +/* Clock generator 1 CTRL 2, 32 bit */ + +#define H2I_BRES1_C2 0x2108 +#define H2I_BRES1_C2_INC_SHIFT 0 /* increment value */ +#define H2I_BRES1_C2_INC_M 0xffff +#define H2I_BRES1_C2_MOD_SHIFT 16 /* modcontrol value */ +#define H2I_BRES1_C2_MOD_M 0xffff0000 /* modctrl=0xffff&(modinc-1) */ + +/* Clock generator 2 CTRL 1, 16 bit */ + +#define H2I_BRES2_C1 0x2204 +#define H2I_BRES2_C1_SHIFT 0 /* 0=48.0 1=44.1 2=aes_rx */ +#define H2I_BRES2_C1_M 0x03 + +/* Clock generator 2 CTRL 2, 32 bit */ + +#define H2I_BRES2_C2 0x2208 +#define H2I_BRES2_C2_INC_SHIFT 0 /* increment value */ +#define H2I_BRES2_C2_INC_M 0xffff +#define H2I_BRES2_C2_MOD_SHIFT 16 /* modcontrol value */ +#define H2I_BRES2_C2_MOD_M 0xffff0000 /* modctrl=0xffff&(modinc-1) */ + +/* Clock generator 3 CTRL 1, 16 bit */ + +#define H2I_BRES3_C1 0x2304 +#define H2I_BRES3_C1_SHIFT 0 /* 0=48.0 1=44.1 2=aes_rx */ +#define H2I_BRES3_C1_M 0x03 + +/* Clock generator 3 CTRL 2, 32 bit */ + +#define H2I_BRES3_C2 0x2308 +#define H2I_BRES3_C2_INC_SHIFT 0 /* increment value */ +#define H2I_BRES3_C2_INC_M 0xffff +#define H2I_BRES3_C2_MOD_SHIFT 16 /* modcontrol value */ +#define H2I_BRES3_C2_MOD_M 0xffff0000 /* modctrl=0xffff&(modinc-1) */ + +/* Unix timer, 64 bit */ + +#if 0 +#define H2I_UTIME 0x3104 +#define H2I_UTIME_0_LD 0xffff /* microseconds, LSB's */ +#define H2I_UTIME_1_LD0 0x0f /* microseconds, MSB's */ +#define H2I_UTIME_1_LD1 0xf0 /* tenths of microseconds */ +#define H2I_UTIME_2_LD 0xffff /* seconds, LSB's */ +#define H2I_UTIME_3_LD 0xffff /* seconds, MSB's */ +#endif |