linux/sound/drivers/vx/vx_pcm.c

// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Driver for Digigram VX soundcards
 *
 * PCM part
 *
 * Copyright (c) 2002,2003 by Takashi Iwai <[email protected]>
 *
 * 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)
{}

/*
 * 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)
{}

/*
 * 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
 * on the rmh and changes its command length.
 *
 * returns the increase of the command length.
 */
static int vx_set_differed_time(struct vx_core *chip, struct vx_rmh *rmh,
				struct vx_pipe *pipe)
{}

/*
 * vx_set_stream_format - send the stream format command
 * @pipe: the affected pipe
 * @data: format bitmask
 */
static int vx_set_stream_format(struct vx_core *chip, struct vx_pipe *pipe,
				unsigned int data)
{}


/*
 * vx_set_format - set the format of a pipe
 * @pipe: the affected pipe
 * @runtime: pcm runtime instance to be referred
 *
 * returns 0 if successful, or a negative error code.
 */
static int vx_set_format(struct vx_core *chip, struct vx_pipe *pipe,
			 struct snd_pcm_runtime *runtime)
{}

/*
 * set / query the IBL size
 */
static int vx_set_ibl(struct vx_core *chip, struct vx_ibl_info *info)
{}


/*
 * vx_get_pipe_state - get the state of a pipe
 * @pipe: the pipe to be checked
 * @state: the pointer for the returned state
 *
 * checks the state of a given pipe, and stores the state (1 = running,
 * 0 = paused) on the given pointer.
 *
 * called from trigger callback only
 */
static int vx_get_pipe_state(struct vx_core *chip, struct vx_pipe *pipe, int *state)
{}


/*
 * vx_query_hbuffer_size - query available h-buffer size in bytes
 * @pipe: the pipe to be checked
 *
 * return the available size on h-buffer in bytes,
 * or a negative error code.
 *
 * NOTE: calling this function always switches to the stream mode.
 *       you'll need to disconnect the host to get back to the
 *       normal mode.
 */
static int vx_query_hbuffer_size(struct vx_core *chip, struct vx_pipe *pipe)
{}


/*
 * vx_pipe_can_start - query whether a pipe is ready for start
 * @pipe: the pipe to be checked
 *
 * return 1 if ready, 0 if not ready, and negative value on error.
 *
 * called from trigger callback only
 */
static int vx_pipe_can_start(struct vx_core *chip, struct vx_pipe *pipe)
{}

/*
 * vx_conf_pipe - tell the pipe to stand by and wait for IRQA.
 * @pipe: the pipe to be configured
 */
static int vx_conf_pipe(struct vx_core *chip, struct vx_pipe *pipe)
{}

/*
 * vx_send_irqa - trigger IRQA
 */
static int vx_send_irqa(struct vx_core *chip)
{}


#define MAX_WAIT_FOR_DSP
/*
 * vx boards do not support inter-card sync, besides
 * only 126 samples require to be prepared before a pipe can start
 */
#define CAN_START_DELAY
#define WAIT_STATE_DELAY

/*
 * vx_toggle_pipe - start / pause a pipe
 * @pipe: the pipe to be triggered
 * @state: start = 1, pause = 0
 *
 * called from trigger callback only
 *
 */
static int vx_toggle_pipe(struct vx_core *chip, struct vx_pipe *pipe, int state)
{}

    
/*
 * vx_stop_pipe - stop a pipe
 * @pipe: the pipe to be stopped
 *
 * called from trigger callback only
 */
static int vx_stop_pipe(struct vx_core *chip, struct vx_pipe *pipe)
{}


/*
 * vx_alloc_pipe - allocate a pipe and initialize the pipe instance
 * @capture: 0 = playback, 1 = capture operation
 * @audioid: the audio id to be assigned
 * @num_audio: number of audio channels
 * @pipep: the returned pipe instance
 *
 * return 0 on success, or a negative error code.
 */
static int vx_alloc_pipe(struct vx_core *chip, int capture,
			 int audioid, int num_audio,
			 struct vx_pipe **pipep)
{}


/*
 * vx_free_pipe - release a pipe
 * @pipe: pipe to be released
 */
static int vx_free_pipe(struct vx_core *chip, struct vx_pipe *pipe)
{}


/*
 * vx_start_stream - start the stream
 *
 * called from trigger callback only
 */
static int vx_start_stream(struct vx_core *chip, struct vx_pipe *pipe)
{}


/*
 * vx_stop_stream - stop the stream
 *
 * called from trigger callback only
 */
static int vx_stop_stream(struct vx_core *chip, struct vx_pipe *pipe)
{}


/*
 * playback hw information
 */

static const struct snd_pcm_hardware vx_pcm_playback_hw =;


/*
 * vx_pcm_playback_open - open callback for playback
 */
static int vx_pcm_playback_open(struct snd_pcm_substream *subs)
{}

/*
 * vx_pcm_playback_close - close callback for playback
 */
static int vx_pcm_playback_close(struct snd_pcm_substream *subs)
{}


/*
 * vx_notify_end_of_buffer - send "end-of-buffer" notifier at the given pipe
 * @pipe: the pipe to notify
 *
 * NB: call with a certain lock.
 */
static int vx_notify_end_of_buffer(struct vx_core *chip, struct vx_pipe *pipe)
{}

/*
 * vx_pcm_playback_transfer_chunk - transfer a single chunk
 * @subs: substream
 * @pipe: the pipe to transfer
 * @size: chunk size in bytes
 *
 * transfer a single buffer chunk.  EOB notificaton is added after that.
 * called from the interrupt handler, too.
 *
 * return 0 if ok.
 */
static int vx_pcm_playback_transfer_chunk(struct vx_core *chip,
					  struct snd_pcm_runtime *runtime,
					  struct vx_pipe *pipe, int size)
{}

/*
 * update the position of the given pipe.
 * pipe->position is updated and wrapped within the buffer size.
 * pipe->transferred is updated, too, but the size is not wrapped,
 * so that the caller can check the total transferred size later
 * (to call snd_pcm_period_elapsed).
 */
static int vx_update_pipe_position(struct vx_core *chip,
				   struct snd_pcm_runtime *runtime,
				   struct vx_pipe *pipe)
{}

/*
 * transfer the pending playback buffer data to DSP
 * called from interrupt handler
 */
static void vx_pcm_playback_transfer(struct vx_core *chip,
				     struct snd_pcm_substream *subs,
				     struct vx_pipe *pipe, int nchunks)
{}

/*
 * update the playback position and call snd_pcm_period_elapsed() if necessary
 * called from interrupt handler
 */
static void vx_pcm_playback_update(struct vx_core *chip,
				   struct snd_pcm_substream *subs,
				   struct vx_pipe *pipe)
{}

/*
 * vx_pcm_playback_trigger - trigger callback for playback
 */
static int vx_pcm_trigger(struct snd_pcm_substream *subs, int cmd)
{}

/*
 * vx_pcm_playback_pointer - pointer callback for playback
 */
static snd_pcm_uframes_t vx_pcm_playback_pointer(struct snd_pcm_substream *subs)
{}

/*
 * vx_pcm_prepare - prepare callback for playback and capture
 */
static int vx_pcm_prepare(struct snd_pcm_substream *subs)
{}


/*
 * operators for PCM playback
 */
static const struct snd_pcm_ops vx_pcm_playback_ops =;


/*
 * playback hw information
 */

static const struct snd_pcm_hardware vx_pcm_capture_hw =;


/*
 * vx_pcm_capture_open - open callback for capture
 */
static int vx_pcm_capture_open(struct snd_pcm_substream *subs)
{}

/*
 * vx_pcm_capture_close - close callback for capture
 */
static int vx_pcm_capture_close(struct snd_pcm_substream *subs)
{}



#define DMA_READ_ALIGN

/*
 * vx_pcm_capture_update - update the capture buffer
 */
static void vx_pcm_capture_update(struct vx_core *chip, struct snd_pcm_substream *subs,
				  struct vx_pipe *pipe)
{}

/*
 * vx_pcm_capture_pointer - pointer callback for capture
 */
static snd_pcm_uframes_t vx_pcm_capture_pointer(struct snd_pcm_substream *subs)
{}

/*
 * operators for PCM capture
 */
static const struct snd_pcm_ops vx_pcm_capture_ops =;


/*
 * interrupt handler for pcm streams
 */
void vx_pcm_update_intr(struct vx_core *chip, unsigned int events)
{}


/*
 * vx_init_audio_io - check the available audio i/o and allocate pipe arrays
 */
static int vx_init_audio_io(struct vx_core *chip)
{}


/*
 * free callback for pcm
 */
static void snd_vx_pcm_free(struct snd_pcm *pcm)
{}

/*
 * snd_vx_pcm_new - create and initialize a pcm
 */
int snd_vx_pcm_new(struct vx_core *chip)
{}