S3c24xx-pcm.c of ALSA Sound Card Driver Based on uda34x

Source: Internet
Author: User

Although ac97 is currently debugging audio, the idea is the same. Transfer to another person's article record

 

Original article address:

 

Http://chxxxyg.blog.163.com/blog/static/150281193201033105123937/

 

 

# Include <Linux/module. h>
# Include <Linux/init. h>
# Include <Linux/IO. h>
# Include <Linux/platform_device.h>
# Include <Linux/slab. h>
# Include <Linux/dma-mapping.h>

# Include <sound/CORE. h>
# Include <sound/PCM. h>
# Include <sound/pcm_params.h>
# Include <sound/soc. h>

# Include <ASM/DMA. h>
# Include <Mach/hardware. h>
# Include <Mach/DMA. h>
# Include <Mach/audio. h>

# Include "s3c24xx-pcm.h"

# Define s3c24xx_pcm_debug 0
# If s3c24xx_pcm_debug
# Define dbg (X...) printk (kern_debug "s3c24xx-pcm:" X)
# Else
# Define dbg (X ...)
# Endif
// Defines some buffer information. It is used in the function s3c24xx_pcm_open to initialize the struct substream-> runtime-> HW
Static const struct snd_pcm_hardware s3c24xx_pcm_hardware = {
. Info = sndrv_pcm_info_interleaved |
Sndrv_pcm_info_block_transfer |
Sndrv_pcm_info_mmap |
Sndrv_pcm_info_mmap_valid |
Sndrv_pcm_info_pause |
Sndrv_pcm_info_resume,
. Formats = sndrv_pcm_fmtbit_s16_le |
Sndrv_pcm_fmtbit_u16_le |
Sndrv_pcm_fmtbit_u8 |
Sndrv_pcm_fmtbit_s8,
. Channels_min = 2,
. Channels_max = 2,
. Buffer_bytes_max = 128*1024,
. Period_bytes_min = page_size,
. Period_bytes_max = page_size * 2,
. Periods_min = 2,
. Periods_max= 128,
. Required o_size = 32,
};
// The Cache Management struct is mounted to runtime> private_data in the function s3c24xx_pcm_open.
// The struct is initialized in the function s3c24xx_pcm_hw_params.
// The managed cache region is allocated in the function s3c24xx_pcm_preallocate_dma_buffer.
Struct s3c24xx_runtime_data {
Spinlock_t lock;
Int state;
Unsigned int dma_loaded; // number of segments inserted into the DMA backup storage chain
Unsigned int dma_limit; // The maximum number of the cache segment.
Unsigned int dma_period; // maximum amount of data stored in each segment of Cache
Dma_addr_t dma_start; // The start address of the cache area.
Dma_addr_t dma_pos; // The first segment address that is not inserted with the DMA standby cache chain
Dma_addr_t dma_end; // The end address of the cache area.
// Structure Params is initialized in the function s3c24xx_pcm_hw_params prtd-> Params = DMA;
Struct s3c24xx_pcm_dma_params * Params;
};

/* S3c24xx_pcm_enqueue
*
* Place a DMA buffer onto the queue for the DMA system
* To handle.
*/
Static void s3c24xx_pcm_enqueue (struct snd_pcm_substream * substream)
{
Struct s3c24xx_runtime_data * prtd = substream-> runtime-> private_data;
Dma_addr_t Pos = prtd-> dma_pos;
Int ret;

Dbg ("entered % s/n", _ FUNC __);

While (prtd-> dma_loaded <prtd-> dma_limit ){
Unsigned long Len = prtd-> dma_period;

Dbg ("dma_loaded: % d/N", prtd-> dma_loaded );

If (Pos + Len)> prtd-> dma_end ){
Len = prtd-> dma_end-Pos;
Dbg (kern_debug "% s: Corrected DMA Len % LD/N ",
_ FUNC __, Len );
}
// Insert the cache into the DMA standby cache chain. The POS must be a physical address, which will be written to the DMA initialization destination or source register.
Ret = s3c2410_dma_enqueue (prtd-> Params-> channel,
Substream, POs, Len );

If (ret = 0 ){
Prtd-> dma_loaded ++;
Pos + = prtd-> dma_period;
If (Pos> = prtd-> dma_end)
Pos = prtd-> dma_start;
} Else
Break;
}

Prtd-> dma_pos = Pos;
}
// When a cache is used up, the function will be called in the interrupt handler.
Static void s3c24xx_audio_buffdone (struct s3c2410_dma_chan * channel,
Void * dev_id, int size,
Enum s3c2410_dma_buffresult result)
{
Struct snd_pcm_substream * substream = dev_id;
Struct s3c24xx_runtime_data * prtd;

Dbg ("entered % s/n", _ FUNC __);

If (result = s3c2410_res_abort | result = s3c2410_res_err)
Return;

Prtd = substream-> runtime-> private_data;

If (substream)
Snd_pcm_period_elapsed (substream );

Spin_lock (& prtd-> lock );
If (prtd-> State & st_running ){
Prtd-> dma_loaded --; // when a cache is used up, insert the cache to the end of the DMA alternate linked list. The entire cache area is a circular cache area.
S3c24xx_pcm_enqueue (substream );
}

Spin_unlock (& prtd-> lock );
}

Static int s3c24xx_pcm_hw_params (struct snd_pcm_substream * substream,
Struct snd_pcm_hw_params * Params)
{
Struct snd_pcm_runtime * runtime = substream-> runtime;
Struct s3c24xx_runtime_data * prtd = runtime-> private_data;
Struct snd_soc_pcm_runtime * RTD = substream-> private_data;
Struct s3c24xx_pcm_dma_params * DMA = RTD-> Dai-> cpu_dai-> dma_data;
// Obtain the buffer size from the structure Params-> intervals
Unsigned long totbytes = params_buffer_bytes (Params );
Int ret = 0;

Dbg ("entered % s/n", _ FUNC __);

/* Return if this is a bufferless transfer e.g.
* Codec <--> BT codec or GSM Modem -- LG fixme */
If (! DMA)
Return 0;

/* This may get called several times by OSS emulation
* With Different Params-HW */
If (prtd-> Params = NULL ){
/* Prepare DMA */
// The structure DMA is s3c24xx_i2s_pcm_stereo_out or s3c24xx_i2s_pcm_stereo_in.
// Define in file s3c24xx-i2s.c
// The structure Params is s3c24xx_runtime_data.
Prtd-> Params = DMA;

Dbg ("Params % P, client % P, channel % d/N", prtd-> Params,
Prtd-> Params-> client, prtd-> Params-> channel );
// Apply for a DMA Channel
Ret = s3c2410_dma_request (prtd-> Params-> channel,
Prtd-> Params-> client, null );

If (Ret <0 ){
Dbg (kern_err "failed to get DMA channel/N ");
Return ret;
}
}
// This function mainly implements Chan-> callback_fn = RTN; that is, it directs Chan-> callback_fn to the function s3c24xx_audio_buffdone.
S3c2410_dma_set_buffdone_fn (prtd-> Params-> channel,
S3c24xx_audio_buffdone );
// Use the allocated cache substream-> dma_buffer to initialize some variables of substream-> Runtime
Snd_pcm_set_runtime_buffer (substream, & substream-> dma_buffer );

Runtime-> dma_bytes = totbytes;

Spin_lock_irq (& prtd-> lock );
Prtd-> dma_loaded = 0;
Prtd-> dma_limit = runtime-> HW. periods_min;
Prtd-> dma_period = params_period_bytes (Params); // obtain the cache size.
Prtd-> dma_start = runtime-> dma_addr;
Prtd-> dma_pos = prtd-> dma_start;
Prtd-> dma_end = prtd-> dma_start + totbytes;
Spin_unlock_irq (& prtd-> lock );

Return 0;
}

Static int s3c24xx_pcm_hw_free (struct snd_pcm_substream * substream)
{
Struct s3c24xx_runtime_data * prtd = substream-> runtime-> private_data;

Dbg ("entered % s/n", _ FUNC __);

/* Todo-Do We Need To ensure DMA flushed */
Snd_pcm_set_runtime_buffer (substream, null );

If (prtd-> Params ){
S3c2410_dma_free (prtd-> Params-> channel, prtd-> Params-> client );
Prtd-> Params = NULL;
}

Return 0;
}

Static int s3c24xx_pcm_prepare (struct snd_pcm_substream * substream)
{
Struct s3c24xx_runtime_data * prtd = substream-> runtime-> private_data;
Int ret = 0;

Dbg ("entered % s/n", _ FUNC __);

/* Return if this is a bufferless transfer e.g.
* Codec <--> BT codec or GSM Modem -- LG fixme */
If (! Prtd-> Params)
Return 0;

/* Channel needs locking ing for mem => device, increment memory ADDR,
* Sync to pclk, half-word transfers to the IIS-FIFO .*/
If (substream-> stream = sndrv_pcm_stream_playback) {// if it is playing
S3c2410_dma_devconfig (prtd-> Params-> channel, // configure the initialization destination register and corresponding control register
S3c2410_dmasrc_mem, s3c2410_disrcc_inc |
S3c2410_disrcc_apb, prtd-> Params-> dma_addr );

S3c2410_dma_config (prtd-> Params-> channel,
Prtd-> Params-> dma_size, // configure the DMA control register and save the configuration value to Chan-> dcon = dcon;
S3c2410_dcon_sync_pclk |
S3c2410_dcon_handshake );
} Else {// recording,
S3c2410_dma_config (prtd-> Params-> channel,
Prtd-> Params-> dma_size, // configure the initialization source register and corresponding control register
S3c2410_dcon_handshake |
S3c2410_dcon_sync_pclk );

S3c2410_dma_devconfig (prtd-> Params-> channel,
S3c2410_dmasrc_hw, 0x3,
Prtd-> Params-> dma_addr );
}

/* Flush the DMA channel */
S3c2410_dma_ctrl (prtd-> Params-> channel, s3c2410_dmaop_flush );
Prtd-> dma_loaded = 0;
Prtd-> dma_pos = prtd-> dma_start;

/* Enqueue DMA buffers */
S3c24xx_pcm_enqueue (substream); // Insert the cache chain into the DMA standby cache chain

Return ret;
}

Static int s3c24xx_pcm_trigger (struct snd_pcm_substream * substream, int cmd)
{
Struct s3c24xx_runtime_data * prtd = substream-> runtime-> private_data;
Int ret = 0;

Dbg ("entered % s/n", _ FUNC __);

Spin_lock (& prtd-> lock );

Switch (CMD ){
Case sndrv_pcm_trigger_start:
Case sndrv_pcm_trigger_resume:
Case sndrv_pcm_trigger_pause_release:
Prtd-> state | = st_running; // load the DMA cache to start DMA data transmission
S3c2410_dma_ctrl (prtd-> Params-> channel, s3c2410_dmaop_start );
S3c2410_dma_ctrl (prtd-> Params-> channel, s3c2410_dmaop_started );
Break;

Case sndrv_pcm_trigger_stop:
Case sndrv_pcm_trigger_suspend:
Case sndrv_pcm_trigger_pause_push:
Prtd-> State & = ~ St_running; // stop DMA Transmission
S3c2410_dma_ctrl (prtd-> Params-> channel, s3c2410_dmaop_stop );
Break;

Default:
Ret =-einval;
Break;
}

Spin_unlock (& prtd-> lock );

Return ret;
}

Static snd_pcm_uframes_t
S3c24xx_pcm_pointer (struct snd_pcm_substream * substream)
{
Struct snd_pcm_runtime * runtime = substream-> runtime;
Struct s3c24xx_runtime_data * prtd = runtime-> private_data;
Unsigned long res;
Dma_addr_t SRC, DST;

Dbg ("entered % s/n", _ FUNC __);

Spin_lock (& prtd-> lock); // read the current destination register and current source register of DMA.
S3c2410_dma_getposition (prtd-> Params-> channel, & SRC, & DST );

If (substream-> stream = sndrv_pcm_stream_capture)
Res = DST-prtd-> dma_start; // calculate the transmitted data
Else
Res = Src-prtd-> dma_start;

Spin_unlock (& prtd-> lock );

Dbg ("pointer % x/N", SRC, DST );

/* We seem to be getting the odd error from the PCM library due
* To out-of-bounds pointers. This is maybe due to the DMA Engine
* Not Having loaded the new values for the channel before being
* Callled... (todo-fix)
*/

If (RES> = snd_pcm_lib_buffer_bytes (substream )){
If (RES = snd_pcm_lib_buffer_bytes (substream ))
Res = 0;
}

Return bytes_to_frames (substream-> runtime, Res); // converts cached data into frames.
}

Static int s3c24xx_pcm_open (struct snd_pcm_substream * substream)
{
Struct snd_pcm_runtime * runtime = substream-> runtime;
Struct s3c24xx_runtime_data * prtd;

Dbg ("entered % s/n", _ FUNC __);
// Use s3c24xx_pcm_hardware to initialize the struct substream-> runtime-> HW
Snd_soc_set_runtime_hwparams (substream, & s3c24xx_pcm_hardware );
// Allocate memory for the structure s3c24xx_runtime_data
Prtd = kzarloc (sizeof (struct s3c24xx_runtime_data), gfp_kernel );
If (prtd = NULL)
Return-enomem;

Spin_lock_init (& prtd-> lock );

Runtime-> private_data = prtd ;//
Return 0;
}

Static int s3c24xx_pcm_close (struct snd_pcm_substream * substream)
{
Struct snd_pcm_runtime * runtime = substream-> runtime;
Struct s3c24xx_runtime_data * prtd = runtime-> private_data;

Dbg ("entered % s/n", _ FUNC __);

If (! Prtd)
Dbg ("s3c24xx_pcm_close called with prtd = NULL/N ");

Kfree (prtd );

Return 0;
}

Static int s3c24xx_pcm_mmap (struct snd_pcm_substream * substream,
Struct vm_area_struct * VMA)
{
Struct snd_pcm_runtime * runtime = substream-> runtime;

Dbg ("entered % s/n", _ FUNC __);
// Associate some user space addresses to the device memory. No matter when the program reads or writes within the specified range, it is actually accessing the device
Return dma_mmap_writecombine (substream-> PCM-> card-> Dev, VMA,
Runtime-> dma_area,
Runtime-> dma_addr,
Runtime-> dma_bytes );
}
// Stream operation function for playing and recording
Static struct snd_pcm_ops s3c24xx_pcm_ops = {
. Open = s3c24xx_pcm_open,
. Close = s3c24xx_pcm_close,
. IOCTL = snd_pcm_lib_ioctl,
. Hw_params = s3c24xx_pcm_hw_params,
. Hw_free = s3c24xx_pcm_hw_free,
. Prepare = s3c24xx_pcm_prepare,
. Trigger = s3c24xx_pcm_trigger,
. Pointer = s3c24xx_pcm_pointer,
. MMAP = s3c24xx_pcm_mmap,
};
// Allocate a memory segment for the data cache area
Static int s3c24xx_pcm_preallocate_dma_buffer (struct snd_pcm * PCM, int Stream)
{
Struct snd_pcm_substream * substream = PCM-> streams [stream]. substream;
Struct snd_dma_buffer * Buf = & substream-> dma_buffer;
Size_t size = s3c24xx_pcm_hardware.buffer_bytes_max;

Dbg ("entered % s/n", _ FUNC __);

Buf-> Dev. type = sndrv_dma_type_dev;
Buf-> Dev. Dev = PCM-> card-> dev;
Buf-> private_data = NULL;
Buf-> area = dma_alloc_writecombine (PCM-> card-> Dev, size,
& Buf-> ADDR, gfp_kernel );
If (! Buf-> Area)
Return-enomem;
Buf-> bytes = size;
Return 0;
}

Static void s3c24xx_pcm_free_dma_buffers (struct snd_pcm * PCM)
{
Struct snd_pcm_substream * substream;
Struct snd_dma_buffer * Buf;
Int stream;

Dbg ("entered % s/n", _ FUNC __);

For (Stream = 0; stream <2; stream ++ ){
Substream = PCM-> streams [stream]. substream;
If (! Substream)
Continue;

Buf = & substream-> dma_buffer;
If (! Buf-> Area)
Continue;

Dma_free_writecombine (PCM-> card-> Dev, Buf-> bytes,
Buf-> area, Buf-> ADDR );
Buf-> area = NULL;
}
}

Static u64 s3c24xx_pcm_dmamask = dma_32bit_mask;
// Allocate the playback and recording cache Areas
Static int s3c24xx_pcm_new (struct snd_card * card,
Struct snd_soc_dai * DAI, struct snd_pcm * PCM)
{
Int ret = 0;

Dbg ("entered % s/n", _ FUNC __);

If (! Card-> Dev-> dma_mask)
Card-> Dev-> dma_mask = & s3c24xx_pcm_dmamask;
If (! Card-> Dev-> coherent_dma_mask)
Card-> Dev-> coherent_dma_mask = 0 xffffffff;

If (Dai-> playback. channels_min ){
Ret = s3c24xx_pcm_preallocate_dma_buffer (PCM,
Sndrv_pcm_stream_playback );
If (RET)
Goto out;
}

If (Dai-> capture. channels_min ){
Ret = s3c24xx_pcm_preallocate_dma_buffer (PCM,
Sndrv_pcm_stream_capture );
If (RET)
Goto out;
}
Out:
Return ret;
}
// PCM instance initialization function and data flow operation function
Struct snd_soc_platform s3c24xx_soc_platform = {
. Name = "s3c24xx-audio ",
. Pcm_ops = & s3c24xx_pcm_ops,
. Pcm_new = s3c24xx_pcm_new,
. Pcm_free = s3c24xx_pcm_free_dma_buffers,
};
Export_symbol_gpl (s3c24xx_soc_platform );

Static int _ init s3c24xx_soc_platform_init (void)
{// Mount s3c24xx_soc_platform to the linked list platform_list, and call the snd_soc_instantiate_card function,
// If the sound card is not initialized, remove the corresponding struct from the linked list and call the initialization function of each struct,
Return snd_soc_register_platform (& s3c24xx_soc_platform );
}
Module_init (s3c24xx_soc_platform_init );

Static void _ exit s3c24xx_soc_platform_exit (void)
{
Snd_soc_unregister_platform (& s3c24xx_soc_platform );
}
Module_exit (s3c24xx_soc_platform_exit );

Module_author ("Ben dooks, <ben@simtec.co.uk> ");
Module_description ("Samsung s3c24xx pcm dma module ");
Module_license ("GPL ");

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.