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-15 01:57

S3C2440 MMU driver code template (RealView MDK)
A good memory is not as good as a bad pen. In order to facilitate the code review and code reuse in the future, here is the S3C2440 MMU code library I wrote. I used the friendly MINI2440 development board and the development environment was RealView MDK 4.22. The source code structure is simple and clear. The original
[Microcontroller]
s3c2440 bare metal - LCD programming - 5 - drawing dots, lines and circles on LCD
1. Draw a dot No matter what kind of graphics it is, they are all based on points, so we need to draw points first. The rest are some data processing of the upper layer. Various graphics and even colorful pictures are nothing more than data constructed by points. We implement point drawing in farmebu
[Microcontroller]
Interface circuit and driver design of nRF2401 based on S3C2440
    The communication frequency is 2.4GHz ISM band, which is suitable for short-distance wireless communication due to its advantages such as license-free, short wavelength, small antenna size, and few peripheral devices. Combining ARM9 with nRF24E1 can reduce the size of the device, reduce system power consumption, a
[Microcontroller]
Interface circuit and driver design of nRF2401 based on S3C2440
Understand the principle and application of S3C2440 touch screen driver
1. Development environment Host: VMWare--Fedora 9 Development board: Mini2440--64MB Nand, Kernel:2.6.30.4 Compiler: arm-linux-gcc-4.3.2 2. Prerequisite knowledge 1. Linux input subsystem (Input Subsystem): In Linux, the input subsystem is composed of the input subsystem device driver layer, the input subsystem core
[Microcontroller]
Understand the principle and application of S3C2440 touch screen driver
S3C2440 Development Board Practice (4): External Interrupts
I have recently learned a series of interrupt programs, so I will review them and write an article to share my learning. Take external interrupt as an example (the most commonly used in microcontrollers before) 1. ARM working mode The ARM architecture (supports 7 operating modes except Cortex) depends on the value o
[Microcontroller]
S3C2440 Development Board Practice (4): External Interrupts
S3C2440 timer 4 interrupt test program
__irq is an identifier used to indicate whether a function is an interrupt function. For different compilers, the position of __irq in the function name is different, for example: In the ADS compiler: void __irq IRQ_Eint0(void); In the Keil compiler: void IRQ_Eint0(void) __irq; But its meaning is the same, it complete
[Microcontroller]
Guo Tianxiang-S3C2440 Development Board Linux2.6.31 Porting Tutorial
The development board I use is the TQ2440 development board from Guangzhou Tianqian Recently, I used Guo Tianxiang's tutorial to learn the S3C2440 embedded linux2.6.31 system migration. After successfully migrating the yaffs2 file system, I found that the ping command of the system migrated by t
[Microcontroller]
Design of Ethernet interface circuit based on S3C2440 embedded system
    This paper mainly introduces an Ethernet interface circuit design scheme based on Samsung ARM9 chip S3C2440 embedded system, and successfully realizes the network data exchange of embedded system by using industrial Ethernet controller DM9000AEP. Based on the network interface circuit, the paper analyzes the drive
[Microcontroller]
Design of Ethernet interface circuit based on S3C2440 embedded system
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号