/*
* 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 <sound/driver.h>
#include <linux/slab.h>
#include <linux/vmalloc.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"
/*
* we use a vmalloc'ed (sg-)buffer
*/
/* get the physical page pointer on the given offset */
static struct page *snd_pcm_get_vmalloc_page(snd_pcm_substream_t *subs, unsigned long offset)
{
void *pageptr = subs->runtime->dma_area + offset;
return vmalloc_to_page(pageptr);
}
/*
* allocate a buffer via vmalloc_32().
* called from hw_params
* NOTE: this may be called not only once per pcm open!
*/
static int snd_pcm_alloc_vmalloc_buffer(snd_pcm_substream_t *subs, size_t size)
{
snd_pcm_runtime_t *runtime = subs->runtime;
if (runtime->dma_area) {
/* already allocated */
if (runtime->dma_bytes >= size)
return 0; /* already enough large */
vfree(runtime->dma_area);
}
runtime->dma_area = vmalloc_32(size);
if (! runtime->dma_area)
return -ENOMEM;
memset(runtime->dma_area, 0, size);
runtime->dma_bytes = size;
return 1; /* changed */
}
/*
* free the buffer.
* called from hw_free callback
* NOTE: this may be called not only once per pcm open!
*/
static int snd_pcm_free_vmalloc_buffer(snd_pcm_substream_t *subs)
{
snd_pcm_runtime_t *runtime = subs->runtime;
if (runtime->dma_area) {
vfree(runtime->dma_area);
runtime->dma_area = NULL;
}
return 0;
}
/*
* read three pending pcm bytes via inb()
*/
static void vx_pcm_read_per_bytes(vx_core_t *chip, snd_pcm_runtime_t *runtime, vx_pipe_t *pipe)
{
int