extractedLnx/linux-2.4.37/drivers/sound/ali5455.c_ali_ioctl.c
static int ali_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
struct ali_state *state = (struct ali_state *) file->private_data;
struct ali_channel *c = NULL;
struct dmabuf *dmabuf = &state->dmabuf;
unsigned long flags;
audio_buf_info abinfo;
count_info cinfo;
unsigned int i_scr;
int val = 0, ret;
struct ac97_codec *codec = state->card->ac97_codec[0];
#ifdef DEBUG
printk("ali_audio: ali_ioctl, arg=0x%x, cmd=",
arg ? *(int *) arg : 0);
#endif
switch (cmd) {
case OSS_GETVERSION:
#ifdef DEBUG
printk("OSS_GETVERSION\n");
#endif
return put_user(SOUND_VERSION, (int *) arg);
case SNDCTL_DSP_RESET:
#ifdef DEBUG
printk("SNDCTL_DSP_RESET\n");
#endif
spin_lock_irqsave(&state->card->lock, flags);
if (dmabuf->enable == DAC_RUNNING) {
c = dmabuf->write_channel;
__stop_dac(state);
}
if (dmabuf->enable == ADC_RUNNING) {
c = dmabuf->read_channel;
__stop_adc(state);
}
if (dmabuf->enable == CODEC_SPDIFOUT_RUNNING) {
c = dmabuf->codec_spdifout_channel;
__stop_spdifout(state);
}
if (dmabuf->enable == CONTROLLER_SPDIFOUT_RUNNING) {
c = dmabuf->controller_spdifout_channel;
__stop_spdifout(state);
}
if (c != NULL) {
outb(2, state->card->iobase + c->port + OFF_CR); /* reset DMA machine */
outl(virt_to_bus(&c->sg[0]), state->card->iobase + c->port + OFF_BDBAR);
outb(0, state->card->iobase + c->port + OFF_CIV);
outb(0, state->card->iobase + c->port + OFF_LVI);
}
spin_unlock_irqrestore(&state->card->lock, flags);
synchronize_irq();
dmabuf->ready = 0;
dmabuf->swptr = dmabuf->hwptr = 0;
dmabuf->count = dmabuf->total_bytes = 0;
return 0;
case SNDCTL_DSP_SYNC:
#ifdef DEBUG
printk("SNDCTL_DSP_SYNC\n");
#endif
if (codec_independent_spdif_locked > 0) {
if (dmabuf->enable != CODEC_SPDIFOUT_RUNNING
|| file->f_flags & O_NONBLOCK)
return 0;
if ((val = drain_spdifout(state, 1)))
return val;
} else {
if (controller_independent_spdif_locked > 0) {
if (dmabuf->enable !=
CONTROLLER_SPDIFOUT_RUNNING
|| file->f_flags & O_NONBLOCK)
return 0;
if ((val = drain_spdifout(state, 1)))
return val;
} else {
if (dmabuf->enable != DAC_RUNNING
|| file->f_flags & O_NONBLOCK)
return 0;
if ((val = drain_dac(state, 1)))
return val;
}
}
dmabuf->total_bytes = 0;
return 0;
case SNDCTL_DSP_SPEED: /* set smaple rate */
#ifdef DEBUG
printk("SNDCTL_DSP_SPEED\n");
#endif
if (get_user(val, (int *) arg))
return -EFAULT;
if (val >= 0) {
if (file->f_mode & FMODE_WRITE) {
if ((state->card->ac97_status & SPDIF_ON)) { /* S/PDIF Enabled */
/* RELTEK ALC650 only support 48000, need to check that */
if (ali_valid_spdif_rate(codec, val)) {
if (codec_independent_spdif_locked > 0) {
ali_set_spdif_output(state, -1, 0);
stop_spdifout(state);
dmabuf->ready = 0;
/* I add test codec independent spdif out */
spin_lock_irqsave(&state->card->lock, flags);
ali_set_codecspdifout_rate(state, val); // I modified
spin_unlock_irqrestore(&state->card->lock, flags);
/* Set S/PDIF transmitter rate. */
i_scr = inl(state->card->iobase + ALI_SCR);
if ((i_scr & 0x00300000) == 0x00100000) {
ali_set_spdif_output(state, AC97_EA_SPSA_7_8, codec_independent_spdif_locked);
} else {
if ((i_scr&0x00300000) == 0x00200000)
{
ali_set_spdif_output(state, AC97_EA_SPSA_6_9, codec_independent_spdif_locked);
} else {
if ((i_scr & 0x00300000) == 0x00300000) {
ali_set_spdif_output(state, AC97_EA_SPSA_10_11, codec_independent_spdif_locked);
} else {
ali_set_spdif_output(state, AC97_EA_SPSA_7_8, codec_independent_spdif_locked);
}
}
}
if (!(state->card->ac97_status & SPDIF_ON)) {
val = dmabuf->rate;
}
} else {
if (controller_independent_spdif_locked > 0)
{
stop_spdifout(state);
dmabuf->ready = 0;
spin_lock_irqsave(&state->card->lock, flags);
ali_set_spdifout_rate(state, controller_independent_spdif_locked);
spin_unlock_irqrestore(&state->card->lock, flags);
} else {
/* Set DAC rate */
ali_set_spdif_output(state, -1, 0);
stop_dac(state);
dmabuf->ready = 0;
spin_lock_irqsave(&state->card->lock, flags);
ali_set_dac_rate(state, val);
spin_unlock_irqrestore(&state->card->lock, flags);
/* Set S/PDIF transmitter rate. */
ali_set_spdif_output(state, AC97_EA_SPSA_3_4, val);
if (!(state->card->ac97_status & SPDIF_ON))
{
val = dmabuf->rate;
}
}
}
} else { /* Not a valid rate for S/PDIF, ignore it */
val = dmabuf->rate;
}
} else {
stop_dac(state);
dmabuf->ready = 0;
spin_lock_irqsave(&state->card->lock, flags);
ali_set_dac_rate(state, val);
spin_unlock_irqrestore(&state->card->lock, flags);
}
}
if (file->f_mode & FMODE_READ) {
stop_adc(state);
dmabuf->ready = 0;
spin_lock_irqsave(&state->card->lock, flags);
ali_set_adc_rate(state, val);
spin_unlock_irqrestore(&state->card->lock, flags);
}
}
return put_user(dmabuf->rate, (int *) arg);
case SNDCTL_DSP_STEREO: /* set stereo or mono channel */
#ifdef DEBUG
printk("SNDCTL_DSP_STEREO\n");
#endif
if (dmabuf->enable & DAC_RUNNING) {
stop_dac(state);
}
if (dmabuf->enable & ADC_RUNNING) {
stop_adc(state);
}
if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) {
stop_spdifout(state);
}
if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) {
stop_spdifout(state);
}
return put_user(1, (int *) arg);
case SNDCTL_DSP_GETBLKSIZE:
if (file->f_mode & FMODE_WRITE) {
if (codec_independent_spdif_locked > 0) {
if (!dmabuf->ready && (val = prog_dmabuf(state, 2)))
return val;
} else {
if (controller_independent_spdif_locked > 0) {
if (!dmabuf->ready && (val = prog_dmabuf(state, 3)))
return val;
} else {
if (!dmabuf->ready && (val = prog_dmabuf(state, 0)))
return val;
}
}
}
if (file->f_mode & FMODE_READ) {
if (!dmabuf->ready && (val = prog_dmabuf(state, 1)))
return val;
}
#ifdef DEBUG
printk("SNDCTL_DSP_GETBLKSIZE %d\n", dmabuf->userfragsize);
#endif
return put_user(dmabuf->userfragsize, (int *) arg);
case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format */
#ifdef DEBUG
printk("SNDCTL_DSP_GETFMTS\n");
#endif
return put_user(AFMT_S16_LE, (int *) arg);
case SNDCTL_DSP_SETFMT: /* Select sample format */
#ifdef DEBUG
printk("SNDCTL_DSP_SETFMT\n");
#endif
return put_user(AFMT_S16_LE, (int *) arg);
case SNDCTL_DSP_CHANNELS: // add support 4,6 channel
#ifdef DEBUG
printk("SNDCTL_DSP_CHANNELS\n");
#endif
if (get_user(val, (int *) arg))
return -EFAULT;
if (val > 0) {
if (dmabuf->enable & DAC_RUNNING) {
stop_dac(state);
}
if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) {
stop_spdifout(state);
}
if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) {
stop_spdifout(state);
}
if (dmabuf->enable & ADC_RUNNING) {
stop_adc(state);
}
} else {
return put_user(state->card->channels, (int *) arg);
}
i_scr = inl(state->card->iobase + ALI_SCR);
/* Current # of channels enabled */
if (i_scr & 0x00000100)
ret = 4;
else if (i_scr & 0x00000200)
ret = 6;
else
ret = 2;
switch (val) {
case 2: /* 2 channels is always supported */
if (codec_independent_spdif_locked > 0) {
outl(((i_scr & 0xfffffcff) | 0x00100000), (state->card->iobase + ALI_SCR));
} else
outl((i_scr & 0xfffffcff), (state->card->iobase + ALI_SCR));
/* Do we need to change mixer settings???? */
break;
case 4: /* Supported on some chipsets, better check first */
if (codec_independent_spdif_locked > 0) {
outl(((i_scr & 0xfffffcff) | 0x00000100 | 0x00200000), (state->card->iobase + ALI_SCR));
} else
outl(((i_scr & 0xfffffcff) | 0x00000100), (state->card->iobase + ALI_SCR));
break;
case 6: /* Supported on some chipsets, better check first */
if (codec_independent_spdif_locked > 0) {
outl(((i_scr & 0xfffffcff) | 0x00000200 | 0x00008000 | 0x00300000), (state->card->iobase + ALI_SCR));
} else
outl(((i_scr & 0xfffffcff) | 0x00000200 | 0x00008000), (state->card->iobase + ALI_SCR));
break;
default: /* nothing else is ever supported by the chipset */
val = ret;
break;
}
return put_user(val, (int *) arg);
case SNDCTL_DSP_POST: /* the user has sent all data and is notifying us */
/* we update the swptr to the end of the last sg segment then return */
#ifdef DEBUG
printk("SNDCTL_DSP_POST\n");
#endif
if (codec_independent_spdif_locked > 0) {
if (!dmabuf->ready || (dmabuf->enable != CODEC_SPDIFOUT_RUNNING))
return 0;
} else {
if (controller_independent_spdif_locked > 0) {
if (!dmabuf->ready || (dmabuf->enable != CONTROLLER_SPDIFOUT_RUNNING))
return 0;
} else {
if (!dmabuf->ready || (dmabuf->enable != DAC_RUNNING))
return 0;
}
}
if ((dmabuf->swptr % dmabuf->fragsize) != 0) {
val = dmabuf->fragsize - (dmabuf->swptr % dmabuf->fragsize);
dmabuf->swptr += val;
dmabuf->count += val;
}
return 0;
case SNDCTL_DSP_SUBDIVIDE:
if (dmabuf->subdivision)
return -EINVAL;
if (get_user(val, (int *) arg))
return -EFAULT;
if (val != 1 && val != 2 && val != 4)
return -EINVAL;
#ifdef DEBUG
printk("SNDCTL_DSP_SUBDIVIDE %d\n", val);
#endif
dmabuf->subdivision = val;
dmabuf->ready = 0;
return 0;
case SNDCTL_DSP_SETFRAGMENT:
if (get_user(val, (int *) arg))
return -EFAULT;
dmabuf->ossfragsize = 1 << (val & 0xffff);
dmabuf->ossmaxfrags = (val >> 16) & 0xffff;
if (!dmabuf->ossfragsize || !dmabuf->ossmaxfrags)
return -EINVAL;
/*
* Bound the frag size into our allowed range of 256 - 4096
*/
if (dmabuf->ossfragsize < 256)
dmabuf->ossfragsize = 256;
else if (dmabuf->ossfragsize > 4096)
dmabuf->ossfragsize = 4096;
/*
* The numfrags could be something reasonable, or it could
* be 0xffff meaning "Give me as much as possible". So,
* we check the numfrags * fragsize doesn't exceed our
* 64k buffer limit, nor is it less than our 8k minimum.
* If it fails either one of these checks, then adjust the
* number of fragments, not the size of them. It's OK if
* our number of fragments doesn't equal 32 or anything
* like our hardware based number now since we are using
* a different frag count for the hardware. Before we get
* into this though, bound the maxfrags to avoid overflow
* issues. A reasonable bound would be 64k / 256 since our
* maximum buffer size is 64k and our minimum frag size is
* 256. On the other end, our minimum buffer size is 8k and
* our maximum frag size is 4k, so the lower bound should
* be 2.
*/
if (dmabuf->ossmaxfrags > 256)
dmabuf->ossmaxfrags = 256;
else if (dmabuf->ossmaxfrags < 2)
dmabuf->ossmaxfrags = 2;
val = dmabuf->ossfragsize * dmabuf->ossmaxfrags;
while (val < 8192) {
val <<= 1;
dmabuf->ossmaxfrags <<= 1;
}
while (val > 65536) {
val >>= 1;
dmabuf->ossmaxfrags >>= 1;
}
dmabuf->ready = 0;
#ifdef DEBUG
printk("SNDCTL_DSP_SETFRAGMENT 0x%x, %d, %d\n", val,
dmabuf->ossfragsize, dmabuf->ossmaxfrags);
#endif
return 0;
case SNDCTL_DSP_GETOSPACE:
if (!(file->f_mode & FMODE_WRITE))
return -EINVAL;
if (codec_independent_spdif_locked > 0) {
if (!dmabuf->ready && (val = prog_dmabuf(state, 2)) != 0)
return val;
} else {
if (controller_independent_spdif_locked > 0) {
if (!dmabuf->ready && (val = prog_dmabuf(state, 3)) != 0)
return val;
} else {
if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
return val;
}
}
spin_lock_irqsave(&state->card->lock, flags);
ali_update_ptr(state);
abinfo.fragsize = dmabuf->userfragsize;
abinfo.fragstotal = dmabuf->userfrags;
if (dmabuf->mapped)
abinfo.bytes = dmabuf->dmasize;
else
abinfo.bytes = ali_get_free_write_space(state);
abinfo.fragments = abinfo.bytes / dmabuf->userfragsize;
spin_unlock_irqrestore(&state->card->lock, flags);
#if defined(DEBUG) || defined(DEBUG_MMAP)
printk("SNDCTL_DSP_GETOSPACE %d, %d, %d, %d\n",
abinfo.bytes, abinfo.fragsize, abinfo.fragments,
abinfo.fragstotal);
#endif
return copy_to_user((void *) arg, &abinfo,
sizeof(abinfo)) ? -EFAULT : 0;
case SNDCTL_DSP_GETOPTR:
if (!(file->f_mode & FMODE_WRITE))
return -EINVAL;
if (codec_independent_spdif_locked > 0) {
if (!dmabuf->ready && (val = prog_dmabuf(state, 2)) != 0)
return val;
} else {
if (controller_independent_spdif_locked > 0) {
if (!dmabuf->ready && (val = prog_dmabuf(state, 3)) != 0)
return val;
} else {
if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
return val;
}
}
spin_lock_irqsave(&state->card->lock, flags);
val = ali_get_free_write_space(state);
cinfo.bytes = dmabuf->total_bytes;
cinfo.ptr = dmabuf->hwptr;
cinfo.blocks = val / dmabuf->userfragsize;
if (codec_independent_spdif_locked > 0) {
if (dmabuf->mapped && (dmabuf->trigger & SPDIF_ENABLE_OUTPUT)) {
dmabuf->count += val;
dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize;
__ali_update_lvi(state, 2);
}
} else {
if (controller_independent_spdif_locked > 0) {
if (dmabuf->mapped && (dmabuf->trigger & SPDIF_ENABLE_OUTPUT)) {
dmabuf->count += val;
dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize;
__ali_update_lvi(state, 3);
}
} else {
if (dmabuf->mapped && (dmabuf->trigger & PCM_ENABLE_OUTPUT)) {
dmabuf->count += val;
dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize;
__ali_update_lvi(state, 0);
}
}
}
spin_unlock_irqrestore(&state->card->lock, flags);
#if defined(DEBUG) || defined(DEBUG_MMAP)
printk("SNDCTL_DSP_GETOPTR %d, %d, %d, %d\n", cinfo.bytes,
cinfo.blocks, cinfo.ptr, dmabuf->count);
#endif
return copy_to_user((void *) arg, &cinfo, sizeof(cinfo));
case SNDCTL_DSP_GETISPACE:
if (!(file->f_mode & FMODE_READ))
return -EINVAL;
if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0)
return val;
spin_lock_irqsave(&state->card->lock, flags);
abinfo.bytes = ali_get_available_read_data(state);
abinfo.fragsize = dmabuf->userfragsize;
abinfo.fragstotal = dmabuf->userfrags;
abinfo.fragments = abinfo.bytes / dmabuf->userfragsize;
spin_unlock_irqrestore(&state->card->lock, flags);
#if defined(DEBUG) || defined(DEBUG_MMAP)
printk("SNDCTL_DSP_GETISPACE %d, %d, %d, %d\n",
abinfo.bytes, abinfo.fragsize, abinfo.fragments,
abinfo.fragstotal);
#endif
return copy_to_user((void *) arg, &abinfo,
sizeof(abinfo)) ? -EFAULT : 0;
case SNDCTL_DSP_GETIPTR:
if (!(file->f_mode & FMODE_READ))
return -EINVAL;
if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
return val;
spin_lock_irqsave(&state->card->lock, flags);
val = ali_get_available_read_data(state);
cinfo.bytes = dmabuf->total_bytes;
cinfo.blocks = val / dmabuf->userfragsize;
cinfo.ptr = dmabuf->hwptr;
if (dmabuf->mapped && (dmabuf->trigger & PCM_ENABLE_INPUT)) {
dmabuf->count -= val;
dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize;
__ali_update_lvi(state, 1);
}
spin_unlock_irqrestore(&state->card->lock, flags);
#if defined(DEBUG) || defined(DEBUG_MMAP)
printk("SNDCTL_DSP_GETIPTR %d, %d, %d, %d\n", cinfo.bytes,
cinfo.blocks, cinfo.ptr, dmabuf->count);
#endif
return copy_to_user((void *) arg, &cinfo, sizeof(cinfo));
case SNDCTL_DSP_NONBLOCK:
#ifdef DEBUG
printk("SNDCTL_DSP_NONBLOCK\n");
#endif
file->f_flags |= O_NONBLOCK;
return 0;
case SNDCTL_DSP_GETCAPS:
#ifdef DEBUG
printk("SNDCTL_DSP_GETCAPS\n");
#endif
return put_user(DSP_CAP_REALTIME | DSP_CAP_TRIGGER |
DSP_CAP_MMAP | DSP_CAP_BIND, (int *) arg);
case SNDCTL_DSP_GETTRIGGER:
val = 0;
#ifdef DEBUG
printk("SNDCTL_DSP_GETTRIGGER 0x%x\n", dmabuf->trigger);
#endif
return put_user(dmabuf->trigger, (int *) arg);
case SNDCTL_DSP_SETTRIGGER:
if (get_user(val, (int *) arg))
return -EFAULT;
#if defined(DEBUG) || defined(DEBUG_MMAP)
printk("SNDCTL_DSP_SETTRIGGER 0x%x\n", val);
#endif
if (!(val & PCM_ENABLE_INPUT) && dmabuf->enable == ADC_RUNNING) {
stop_adc(state);
}
if (!(val & PCM_ENABLE_OUTPUT) && dmabuf->enable == DAC_RUNNING) {
stop_dac(state);
}
if (!(val & SPDIF_ENABLE_OUTPUT) && dmabuf->enable == CODEC_SPDIFOUT_RUNNING) {
stop_spdifout(state);
}
if (!(val & SPDIF_ENABLE_OUTPUT) && dmabuf->enable == CONTROLLER_SPDIFOUT_RUNNING) {
stop_spdifout(state);
}
dmabuf->trigger = val;
if (val & PCM_ENABLE_OUTPUT && !(dmabuf->enable & DAC_RUNNING)) {
if (!dmabuf->write_channel) {
dmabuf->ready = 0;
dmabuf->write_channel = state->card->alloc_pcm_channel(state->card);
if (!dmabuf->write_channel)
return -EBUSY;
}
if (!dmabuf->ready && (ret = prog_dmabuf(state, 0)))
return ret;
if (dmabuf->mapped) {
spin_lock_irqsave(&state->card->lock, flags);
ali_update_ptr(state);
dmabuf->count = 0;
dmabuf->swptr = dmabuf->hwptr;
dmabuf->count = ali_get_free_write_space(state);
dmabuf->swptr = (dmabuf->swptr + dmabuf->count) % dmabuf->dmasize;
__ali_update_lvi(state, 0);
spin_unlock_irqrestore(&state->card->lock,
flags);
} else
start_dac(state);
}
if (val & SPDIF_ENABLE_OUTPUT && !(dmabuf->enable & CODEC_SPDIFOUT_RUNNING)) {
if (!dmabuf->codec_spdifout_channel) {
dmabuf->ready = 0;
dmabuf->codec_spdifout_channel = state->card->alloc_codec_spdifout_channel(state->card);
if (!dmabuf->codec_spdifout_channel)
return -EBUSY;
}
if (!dmabuf->ready && (ret = prog_dmabuf(state, 2)))
return ret;
if (dmabuf->mapped) {
spin_lock_irqsave(&state->card->lock, flags);
ali_update_ptr(state);
dmabuf->count = 0;
dmabuf->swptr = dmabuf->hwptr;
dmabuf->count = ali_get_free_write_space(state);
dmabuf->swptr = (dmabuf->swptr + dmabuf->count) % dmabuf->dmasize;
__ali_update_lvi(state, 2);
spin_unlock_irqrestore(&state->card->lock,
flags);
} else
start_spdifout(state);
}
if (val & SPDIF_ENABLE_OUTPUT && !(dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING)) {
if (!dmabuf->controller_spdifout_channel) {
dmabuf->ready = 0;
dmabuf->controller_spdifout_channel = state->card->alloc_controller_spdifout_channel(state->card);
if (!dmabuf->controller_spdifout_channel)
return -EBUSY;
}
if (!dmabuf->ready && (ret = prog_dmabuf(state, 3)))
return ret;
if (dmabuf->mapped) {
spin_lock_irqsave(&state->card->lock, flags);
ali_update_ptr(state);
dmabuf->count = 0;
dmabuf->swptr = dmabuf->hwptr;
dmabuf->count = ali_get_free_write_space(state);
dmabuf->swptr = (dmabuf->swptr + dmabuf->count) % dmabuf->dmasize;
__ali_update_lvi(state, 3);
spin_unlock_irqrestore(&state->card->lock, flags);
} else
start_spdifout(state);
}
if (val & PCM_ENABLE_INPUT && !(dmabuf->enable & ADC_RUNNING)) {
if (!dmabuf->read_channel) {
dmabuf->ready = 0;
dmabuf->read_channel = state->card->alloc_rec_pcm_channel(state->card);
if (!dmabuf->read_channel)
return -EBUSY;
}
if (!dmabuf->ready && (ret = prog_dmabuf(state, 1)))
return ret;
if (dmabuf->mapped) {
spin_lock_irqsave(&state->card->lock,
flags);
ali_update_ptr(state);
dmabuf->swptr = dmabuf->hwptr;
dmabuf->count = 0;
spin_unlock_irqrestore(&state->card->lock, flags);
}
ali_update_lvi(state, 1);
start_adc(state);
}
return 0;
case SNDCTL_DSP_SETDUPLEX:
#ifdef DEBUG
printk("SNDCTL_DSP_SETDUPLEX\n");
#endif
return -EINVAL;
case SNDCTL_DSP_GETODELAY:
if (!(file->f_mode & FMODE_WRITE))
return -EINVAL;
spin_lock_irqsave(&state->card->lock, flags);
ali_update_ptr(state);
val = dmabuf->count;
spin_unlock_irqrestore(&state->card->lock, flags);
#ifdef DEBUG
printk("SNDCTL_DSP_GETODELAY %d\n", dmabuf->count);
#endif
return put_user(val, (int *) arg);
case SOUND_PCM_READ_RATE:
#ifdef DEBUG
printk("SOUND_PCM_READ_RATE %d\n", dmabuf->rate);
#endif
return put_user(dmabuf->rate, (int *) arg);
case SOUND_PCM_READ_CHANNELS:
#ifdef DEBUG
printk("SOUND_PCM_READ_CHANNELS\n");
#endif
return put_user(2, (int *) arg);
case SOUND_PCM_READ_BITS:
#ifdef DEBUG
printk("SOUND_PCM_READ_BITS\n");
#endif
return put_user(AFMT_S16_LE, (int *) arg);
case SNDCTL_DSP_SETSPDIF: /* Set S/PDIF Control register */
#ifdef DEBUG
printk("SNDCTL_DSP_SETSPDIF\n");
#endif
if (get_user(val, (int *) arg))
return -EFAULT;
/* Check to make sure the codec supports S/PDIF transmitter */
if ((state->card->ac97_features & 4)) {
/* mask out the transmitter speed bits so the user can't set them */
val &= ~0x3000;
/* Add the current transmitter speed bits to the passed value */
ret = ali_ac97_get(codec, AC97_SPDIF_CONTROL);
val |= (ret & 0x3000);
ali_ac97_set(codec, AC97_SPDIF_CONTROL, val);
if (ali_ac97_get(codec, AC97_SPDIF_CONTROL) != val) {
printk(KERN_ERR "ali_audio: Unable to set S/PDIF configuration to 0x%04x.\n", val);
return -EFAULT;
}
}
#ifdef DEBUG
else
printk(KERN_WARNING "ali_audio: S/PDIF transmitter not avalible.\n");
#endif
return put_user(val, (int *) arg);
case SNDCTL_DSP_GETSPDIF: /* Get S/PDIF Control register */
#ifdef DEBUG
printk("SNDCTL_DSP_GETSPDIF\n");
#endif
if (get_user(val, (int *) arg))
return -EFAULT;
/* Check to make sure the codec supports S/PDIF transmitter */
if (!(state->card->ac97_features & 4)) {
#ifdef DEBUG
printk(KERN_WARNING "ali_audio: S/PDIF transmitter not avalible.\n");
#endif
val = 0;
} else {
val = ali_ac97_get(codec, AC97_SPDIF_CONTROL);
}
return put_user(val, (int *) arg);
//end add support spdif out
//add support 4,6 channel
case SNDCTL_DSP_GETCHANNELMASK:
#ifdef DEBUG
printk("SNDCTL_DSP_GETCHANNELMASK\n");
#endif
if (get_user(val, (int *) arg))
return -EFAULT;
/* Based on AC'97 DAC support, not ICH hardware */
val = DSP_BIND_FRONT;
if (state->card->ac97_features & 0x0004)
val |= DSP_BIND_SPDIF;
if (state->card->ac97_features & 0x0080)
val |= DSP_BIND_SURR;
if (state->card->ac97_features & 0x0140)
val |= DSP_BIND_CENTER_LFE;
return put_user(val, (int *) arg);
case SNDCTL_DSP_BIND_CHANNEL:
#ifdef DEBUG
printk("SNDCTL_DSP_BIND_CHANNEL\n");
#endif
if (get_user(val, (int *) arg))
return -EFAULT;
if (val == DSP_BIND_QUERY) {
val = DSP_BIND_FRONT; /* Always report this as being enabled */
if (state->card->ac97_status & SPDIF_ON)
val |= DSP_BIND_SPDIF;
else {
if (state->card->ac97_status & SURR_ON)
val |= DSP_BIND_SURR;
if (state->card->
ac97_status & CENTER_LFE_ON)
val |= DSP_BIND_CENTER_LFE;
}
} else { /* Not a query, set it */
if (!(file->f_mode & FMODE_WRITE))
return -EINVAL;
if (dmabuf->enable == DAC_RUNNING) {
stop_dac(state);
}
if (val & DSP_BIND_SPDIF) { /* Turn on SPDIF */
/* Ok, this should probably define what slots
* to use. For now, we'll only set it to the
* defaults:
*
* non multichannel codec maps to slots 3&4
* 2 channel codec maps to slots 7&8
* 4 channel codec maps to slots 6&9
* 6 channel codec maps to slots 10&11
*
* there should be some way for the app to
* select the slot assignment.
*/
i_scr = inl(state->card->iobase + ALI_SCR);
if (codec_independent_spdif_locked > 0) {
if ((i_scr & 0x00300000) == 0x00100000) {
ali_set_spdif_output(state, AC97_EA_SPSA_7_8, codec_independent_spdif_locked);
} else {
if ((i_scr & 0x00300000) == 0x00200000) {
ali_set_spdif_output(state, AC97_EA_SPSA_6_9, codec_independent_spdif_locked);
} else {
if ((i_scr & 0x00300000) == 0x00300000) {
ali_set_spdif_output(state, AC97_EA_SPSA_10_11, codec_independent_spdif_locked);
}
}
}
} else { /* codec spdif out (pcm out share ) */
ali_set_spdif_output(state, AC97_EA_SPSA_3_4, dmabuf->rate); //I do not modify
}
if (!(state->card->ac97_status & SPDIF_ON))
val &= ~DSP_BIND_SPDIF;
} else {
int mask;
int channels;
/* Turn off S/PDIF if it was on */
if (state->card->ac97_status & SPDIF_ON)
ali_set_spdif_output(state, -1, 0);
mask =
val & (DSP_BIND_FRONT | DSP_BIND_SURR |
DSP_BIND_CENTER_LFE);
switch (mask) {
case DSP_BIND_FRONT:
channels = 2;
break;
case DSP_BIND_FRONT | DSP_BIND_SURR:
channels = 4;
break;
case DSP_BIND_FRONT | DSP_BIND_SURR | DSP_BIND_CENTER_LFE:
channels = 6;
break;
default:
val = DSP_BIND_FRONT;
channels = 2;
break;
}
ali_set_dac_channels(state, channels);
/* check that they really got turned on */
if (!state->card->ac97_status & SURR_ON)
val &= ~DSP_BIND_SURR;
if (!state->card->
ac97_status & CENTER_LFE_ON)
val &= ~DSP_BIND_CENTER_LFE;
}
}
return put_user(val, (int *) arg);
case SNDCTL_DSP_MAPINBUF:
case SNDCTL_DSP_MAPOUTBUF:
case SNDCTL_DSP_SETSYNCRO:
case SOUND_PCM_WRITE_FILTER:
case SOUND_PCM_READ_FILTER:
return -EINVAL;
}
return -EINVAL;
}
Generated by GNU enscript 1.6.4.