/*
* Driver for Digigram VX soundcards
*
* PCM part
*
* Copyright (c) 2002,2003 by Takashi Iwai <tiwai@suse.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*
* STRATEGY
* for playback, we send series of "chunks", which size is equal with the
* IBL size, typically 126 samples. at each end of chunk, the end-of-buffer
* interrupt is notified, and the interrupt handler will feed the next chunk.
*
* the current position is calculated from the sample count RMH.
* pipe->transferred is the counter of data which has been already transferred.
* if this counter reaches to the period size, snd_pcm_period_elapsed() will
* be issued.
*
* for capture, the situation is much easier.
* to get a low latency response, we'll check the capture streams at each
* interrupt (capture stream has no EOB notification). if the pending
* data is accumulated to the period size, snd_pcm_period_elapsed() is
* called and the pointer is updated.
*
* the current point of read buffer is kept in pipe->hw_ptr. note that
* this is in bytes.
*
*
* TODO
* - linked trigger for full-duplex mode.
* - scheduled action on the stream.
*/
#include <linux/slab.h>
#include <linux/delay.h>
#include <sound/core.h>
#include <sound/asoundef.h>
#include <sound/pcm.h>
#include <sound/vx_core.h>
#include "vx_cmd.h"
/*
* read three pending pcm bytes via inb()
*/
static void vx_pcm_read_per_bytes(struct vx_core *chip, struct snd_pcm_runtime *runtime,
struct vx_pipe *pipe)
{
int offset = pipe->hw_ptr;
unsigned char *buf = (unsigned char *)(runtime->dma_area + offset);
*buf++ = vx_inb(chip, RXH);
if (++offset >= pipe->buffer_bytes) {
offset = 0;
buf = (unsigned char *)runtime->dma_area;
}
*buf++ = vx_inb(chip, RXM);
if (++offset >= pipe->buffer_bytes) {
offset = 0;
buf = (unsigned char *)runtime->dma_area;
}
*buf++ = vx_inb(chip, RXL);
if (++offset >= pipe->buffer_bytes) {
offset = 0;
buf = (unsigned char *)runtime->dma_area;
}
pipe->hw_ptr = offset;
}
/*
* vx_set_pcx_time - convert from the PC time to the RMH status time.
* @pc_time: the pointer for the PC-time to set
* @dsp_time: the pointer for RMH status time array
*/
static void vx_set_pcx_time(struct vx_core *chip, pcx_time_t *pc_time,
unsigned int *dsp_time)
{
dsp_time[0] = (unsigned int)((*pc_time) >> 24) & PCX_TIME_HI_MASK;
dsp_time[1] = (unsigned int)(*pc_time) & MASK_DSP_WORD;
}
/*
* vx_set_differed_time - set the differed time if specified
* @rmh: the rmh record to modify
* @pipe: the pipe to be checked
*
* if the pipe is programmed with the differed time, set the DSP time
* o