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)
Previous article:ARM stack method
Next article:u-boot-2011.06 transplant
Recommended ReadingLatest update time:2024-11-23 06:26
- Popular Resources
- Popular amplifiers
- Non-invasive Blood Glucose Level Monitoring System for Diabetic Patients using Near-Infrared Spectro
- Accurate Level-crossing ADC Design for Biomedical Acquisition
- A portable ECG for recording and flexible development of algorithms and stress detection
- 12-Lead ECG Data Acquisition System Based on ADS1298
- Naxin Micro and Xinxian jointly launched the NS800RT series of real-time control MCUs
- How to learn embedded systems based on ARM platform
- Summary of jffs2_scan_eraseblock issues
- Application of SPCOMM Control in Serial Communication of Delphi7.0
- Using TComm component to realize serial communication in Delphi environment
- Bar chart code for embedded development practices
- Embedded Development Learning (10)
- Embedded Development Learning (8)
- Embedded Development Learning (6)
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- Intel promotes AI with multi-dimensional efforts in technology, application, and ecology
- ChinaJoy Qualcomm Snapdragon Theme Pavilion takes you to experience the new changes in digital entertainment in the 5G era
- Infineon's latest generation IGBT technology platform enables precise control of speed and position
- Two test methods for LED lighting life
- Don't Let Lightning Induced Surges Scare You
- Application of brushless motor controller ML4425/4426
- Easy identification of LED power supply quality
- World's first integrated photovoltaic solar system completed in Israel
- Sliding window mean filter for avr microcontroller AD conversion
- What does call mean in the detailed explanation of ABB robot programming instructions?
- STMicroelectronics discloses its 2027-2028 financial model and path to achieve its 2030 goals
- 2024 China Automotive Charging and Battery Swapping Ecosystem Conference held in Taiyuan
- State-owned enterprises team up to invest in solid-state battery giant
- The evolution of electronic and electrical architecture is accelerating
- The first! National Automotive Chip Quality Inspection Center established
- BYD releases self-developed automotive chip using 4nm process, with a running score of up to 1.15 million
- GEODNET launches GEO-PULSE, a car GPS navigation device
- Should Chinese car companies develop their own high-computing chips?
- Infineon and Siemens combine embedded automotive software platform with microcontrollers to provide the necessary functions for next-generation SDVs
- Continental launches invisible biometric sensor display to monitor passengers' vital signs
- Mobile station update development board, welcome to borrow~~
- [NUCLEO-L452RE Review] + Add a FREERTOS timer
- Where do you usually buy chips or components?
- Putty released version 0.71
- XDS510-USB2.0 driver installation
- Physical Explanation of Matched Filters
- [Evaluation of SGP40] 4. zynq + SVM40
- Toshiba photorelay TLP3547 small current conduction long time stability
- EEWORLD University ---- designing electrical systems vol 1
- Rice goes in and rice comes out. How is rice husked? You may not have seen it even if you have been eating it for decades!