ALSA sound card 10_Data transmission from scratch_Study notes

Publisher:CelestialSoulLatest update time:2024-07-10 Source: elecfans Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere

1. Introduction

(1) When an application uses a sound card, the data flow is as follows: the application sends data to the driver, the driver sends the data to the hardware sound card, and the sound card converts the data into sound data and plays it out.

(2) There are two ways to send data

The first method: the app sends data, and then sends the next segment after the driver has processed it (sending the next segment after processing will cause the sound to be intermittent)

The second method: the application continuously sends data, the driver continuously retrieves data and continuously sends it to the hardware. This solves the problem of intermittent sound, but requires the creation of a very large buffer (requested in the driver, called a buffer)



The data of a sampling point includes left channel data and right channel data


Here hw_ptr is a pointer (update means the pointer moves backward)


2. How to write a driver (s3c2440_dma.c (platform))

(1) The DMA file in the platform part is responsible for data transmission. Modify s3c2440_dma.c



3. Allocate/release buffer

When creating a sound card, the s3c2440_dma_new function is called, and the dma buffer is allocated in the function. When destroying the sound card, the dma buffer is released


(1) Allocate DMA BUFFER

static int s3c2440_dma_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_pcm *pcm = rtd->pcm;
struct snd_pcm_substream *substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
struct snd_dma_buffer *buf = &substream->dma_buffer;


int ret = 0;


/* 1. Allocate DMA BUFFER */
if (!card->dev->dma_mask)
card->dev->dma_mask = &dma_mask;
if (!card->dev ->coherent_dma_mask)
card->dev->coherent_dma_mask = DMA_BIT_MASK(32);


if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { //Playback function
playback_dma_info.virt_addr = (unsigned int)dma_alloc_writecombine(pcm->card->dev, s3c2440_dma_hardware.buffer_bytes_max,
&playback_dma_info.phy_addr, GFP_KERNEL); //Allocate DMA BUFFER //virt_addr is the virtual address of buffer, buffer_bytes_max is the size of the allocated buffer, phy_addr is the physical address of the allocated buffer
if (!playback_dma_info.virt_addr)//If the virtual address is empty, return an error
{
return -ENOMEM; // Returns an error flag indicating that there is no memory
}
playback_dma_info.buf_max_size = s3c2440_dma_hardware.buffer_bytes_max; //Allocation successful, record the size of the allocated buffer. Although 128K is allocated, the amount used is determined by the app.

//Tell the alsa core layer the buffer information
buf->dev.type = SNDRV_DMA_TYPE_DEV;
buf->dev .dev = pcm->card->dev;
buf->private_data = NULL;
buf->area = playback_dma_info.virt_addr; //Structure of playback-related dma buffer information
buf->bytes = playback_dma_info.buf_max_size; // The size of buf
}//


return ret;
}

(2) Release DMA BUFFER

static void s3c2440_dma_free(struct snd_pcm *pcm)
{
dma_free_writecombine(pcm->card->dev, playback_dma_info.buf_max_size,
(void *)playback_dma_info.virt_addr, playback_dma_info.phy_addr); //Release the dma buffer of the playback part (buffer size, buffer virtual address, buffer physical address)
}



4. request_irq

(1) open function

static int s3c2440_dma_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
int ret;
/* Register interrupt*/
ret = request_irq(IRQ_DMA2,s3c2440_dma2_irq, IRQF_DISABLED, "myalsa for playback",substream);
if (ret) //IRQ_DMA2 is the interrupt number, s3c2440_dma2_irq is the interrupt processing function, IRQF_DISABLED is the flag (when an interrupt occurs, the interrupt remains masked during the interrupt processing), "myalsa for playback" is the interrupt name, substream is the device ID
{
printk("request_irq error!n");
return -EIO;
}


return 0;
}

(2) Interrupt request function (update hw_ptr and other information after successful data transmission)

static irqreturn_t s3c2440_dma2_irq(int irq, void *devid) //The left parameter is the interrupt number, the right is the device ID
{
struct snd_pcm_substream *substream = devid; //The parameter substream given to the interrupt processing function s3c2440_dma2_irq in the request interrupt function called by the open function request_irq

/* Update status information*/ When a DMA is transmitted, an interrupt occurs
playback_dma_info.dma_ofs += playback_dma_info.period_size; //The offset address moves back one period
if (playback_dma_info.dma_ofs >= playback_dma_info.buffer_size) //If the offset address exceeds the buffer range
playback_dma_info.dma_ofs = 0; //Point to the beginning of the buffer

/* Update hw_ptr and other information, (driver part)
* And judge: if there is no data in the buffer, call trigger to stop DMA
*/

After transferring a DMA,
snd_pcm_period_elapsed(substream);


if (playback_dma_info.be_running)
{
/* If there is still data, be_running is 1 to indicate it is running
* 1. Load the next period
* 2. Start DMA transfer again
*/
load_dma_period();
s3c2440_dma_start();
}


return IRQ_HANDLED;
}



5. s3c2440_dma_hw_params (prepare parameters for DMA transfer)

static int s3c2440_dma_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned long totbytes = params_buffer_bytes(params);//The buffer size used is determined by the parameter params passed in by the application./*

Set DMA according to params */
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);


/* s3c2440_dma_new (inside the driver) allocates a large DMA BUFFER. How much the application will use is determined by the application. The parameter params
* params passed in determines how much to use
*/
runtime->dma_bytes = totbytes;//Record the size used, which will be used later. The application will return when it writes to the tail. Where the tail is is determined by the tail.
playback_dma_info.buffer_size = totbytes; //buffer_size
playback_dma_info.period_size = params_period_bytes(params); //period_size, in buffer, one DMA transfer is transferred in period


return 0;
}


6. prepare function (prepare data for DMA transfer)


static int s3c2440_dma_prepare(struct snd_pcm_substream *substream)
{
/* Prepare for DMA transfer */

/* Reset various status information (clear) */
playback_dma_info.dma_ofs = 0; //The offset value is set to 0, and the DMA transfer will start from the beginning
playback_dma_info.be_running = 0; //Indicates that DMA has not been started and is not running yet

/* Load the first period */
load_dma_period();


return 0;
}





return ret;
}


7. Load the data to be transferred

/* Data transfer: source, destination, length*/
static void load_dma_period(void)
{
/* Tell DMA the source, destination and length */
dma_regs->disrc = playback_dma_info.phy_addr + playback_dma_info.dma_ofs; /* Physical address of source*/physical address + offset address
dma_regs->disrcc = (0<<1) | (0<<0); /* Source is on AHB bus, source address increments*/
dma_regs->didst = 0x55000010; /* Physical address of destination*/IIC controller
dma_regs->didstc = (0<<2) | (1<<1) | (1<<0); /* Destination is on APB bus, destination address remains unchanged*/


/* bit22: 1-noreload *Indicates that after a piece of data is transmitted, some values ​​will be reloaded and DMA will be started by itself. In our interrupt program, DMA is started by itself, so it is not allowed to download automatically. Therefore, it is set to 1. If it is automatically loaded, data will be automatically transmitted before the interrupt function is processed. /Solve the problem of noise in playback
dma_regs->dcon = (1<<31)|(0<<30)|(1<<29)|(0<<28)|(0<<27)|(0<<24)|(1<<23)|(1<<22)|(1<<20)|(playback_dma_info.period_size/2); /* Enable interrupt, single transmission, hardware trigger*/period_size length, each transmission is not the entire buffer, but a period in it, period_size size. buffer_size is the size of the buffer used by the application, buf_max_size is the size of the buffer allocated by the driver, and the application can only use part of it. The reason for dividing by 2 is that (1<<20) is 2 bytes, so it is divided by 2 because it is in bytes. That is, how many times it is transmitted, 2 bytes each time.
}



8. dma_trigger function (trigger DMA transfer, after the transfer is completed, an interrupt is generated and the interrupt processing function s3c2440_dma2_irq is entered)



static int s3c2440_dma_trigger(struct snd_pcm_substream *substream, int cmd)
{
int ret = 0;


/* Start or stop DMA transfer according to cmd*/
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
/* Start DMA transfer*/
playback_dma_info.be_running = 1;//There is also data status information
s3c2440_dma_start();
break;


case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
/* Stop DMA transfer*/
playback_dma_info.be_running = 0; //Status information after data transmission
s3c2440_dma_stop();
break;


default:
ret = -EINVAL;
break;
}

9. dma_pointer function (indicates the location of the next DMA transfer (each DMA transfer is a period), the hw_ptr pointer is determined according to the DMA position)

[1] [2]
Reference address:ALSA sound card 10_Data transmission from scratch_Study notes

Previous article:ARM stack method
Next article:u-boot-2011.06 transplant

Recommended ReadingLatest update time:2024-11-23 06:26

s3c2440 learning path-004 distinguish nand/nor startup
Hardware platform: jz2440 Software platform: Ubuntu16.04 arm-linux-gcc-3.4.5 Source code location: https://github.com/lian494362816/C/tree/master/2440/005_leds/002 1. Principle analysis 1.1 Origin of the problem The previous article https://blog.csdn.net/lian494362816/article/details/84642425 mentioned the SP settin
[Microcontroller]
s3c2440 learning path-004 distinguish nand/nor startup
How to declare the clock source frequency of S3C2440 in ADS
It is important to note that if you do not apply the following at the top of the program, you will not be able to use embedded frequency variables such as PCLK and UCLK: #define GLOBAL_CLK 1 //The last 1 needs to be studied #include "def.h" ...... void Main() {    int pinlv;    pinlv=PCLK; //If GLOBAL_CLK is not d
[Microcontroller]
Analysis of S3C2440 camera interface CamInit() function initialization
The most common problem encountered when debugging the 2440 camera interface is the CamInit() function. The code is attached below for analysis. /* Description of Parameters CoDstWidth: Destination Width of Codec Path CoDstHeight: Destination Height of Codec Path PrDstWidth: Destination Width of Preview Path PrDst
[Microcontroller]
S3C2440 SPI driver framework
Detailed interpretation of S3C2440 SPI driver code: https://www.linuxidc.com/Linux/2012-08/68402p4.htm 一、platform device and board_info /* /arch/arm/plat-s3c24xx*/ static struct resource s3c_spi0_resource = { = { .start = S3C24XX_
[Microcontroller]
Linux kernel transplantation based on S3C2440 and yaffs2 file system production--starting system
Chapter 3 Starting the System Download the kernel image file and root file system image file generated in the previous two chapters to the mini2440 development board and check the startup information. I successfully transplanted the startup information as follows: VIVI version 0.1.4 ( root@capcross ) (gcc version 2.9
[Microcontroller]
Detailed explanation of Uboot transplantation on S3C2440 (VI)
1. Transplantation Environment Host: VMWare--Fedora 9 Development board: Mini2440--64MB Nand, Kernel: 2.6.30.4 Compiler: arm-linux-gcc-4.3.2.tgz u-boot:u-boot-2009.08.tar.bz2 2. Transplantation Steps 10) u-boot uses tftp service to download the kernel and uses nfs service to mount nfs fil
[Microcontroller]
Detailed explanation of Uboot transplantation on S3C2440 (VI)
S3C2440 DMA driver example
Abstract DMA as a character device and call it in the initialization function void *dma_alloc_writecombine(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp) The function is used to allocate two consecutive physical address spaces, one as the source space and the other as t
[Microcontroller]
VGA Display Technology Based on ARM Processor S3C2440
At present, many SOC manufacturers' microprocessor chips have integrated LCD controllers, such as Samsung's S3C2410. S3C2440, Intel's Xscale series, etc. Most embedded systems also use popular LCD display technology. However, in places where large-screen display is required and resolution is not high, such as workshops
[Microcontroller]
VGA Display Technology Based on ARM Processor S3C2440
Latest Microcontroller Articles
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号