1. Parameter setting analysis
(1) open: soc_pcm_open calls the open or startup functions of cpu_dai, dma, codec_dai, and machine in sequence.
Only add parameter-related code in the open function of dma.
(2) SNDRV_PCM_IOCTL_HW_PARAMS: soc_pcm_hw_params calls the hw_params functions of machine, codec_dai, cpu_dai, and platform (dma) in sequence.
Implement the hw_params function in uda1341.c and s3c2440-iis.c (move the relevant code in the bare board program over)
(s3c2440-dma.c mainly involves data transmission, and the hw_params function will be implemented in the next section)
(3) When the sound card is turned on, the dma_open function of snd_pcm_ops in dma.c of the machine part will be called)
2. open function
(1) s3c2440_dma.c(Platform)
static int s3c2440_dma_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
int ret;
/* Set attributes*/
snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
snd_soc_set_runtime_hwparams(sub stream, &s3c2440_dma_hardware);
}
return 0;
}
(2) snd_pcm_hardware structure
static const struct snd_pcm_hardware s3c2440_dma_hardware = {
.info= SNDRV_PCM_INFO_INTERLEAVED | //Data arrangement (left-right-left-right or left-left-left-right-right)
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 | //Supported audio data formats
SNDRV_PCM_FMTBIT_U16_LE |
SNDRV_PCM_FMTBIT_U8 |
SNDRV_PCM_FMTBIT_S8,
.channels_min= 2, //Number of channels
.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,
.fifo_size= 32,
};
3. hw_params function (hardware parameters)
(1) uda1341.c (codec)
static int uda1341_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
/* According to the value of params, set the registers of UDA1341
* For example, clock setting, format
*/
/* For simplicity, set the clock, format and other parameters in uda1341_init_regs */
return 0;
}
Initialization of UDA1341
static void uda1341_init_regs(struct snd_soc_codec *codec)
{
/* GPB 4: L3CLOCK */
/* GPB 3: L3DATA */
/* GPB 2: L3MODE */
*gpbcon &= ~((3<<4) | (3< <6) | (3<<8));
*gpbcon |= ((1<<4) | (1<<6) | (1<<8));
uda1341_write_reg(codec, UDA1341_STATUS0, 0x40 | STAT0_SC_384FS | STAT0_DC_FILTER ); // reset uda1341
uda1341_write_reg(codec, UDA1341_STATUS1, STAT1_ADC_ON | STAT1_DAC_ON);
uda1341_write_reg(codec, UDA1341_DATA00, DATA0_VOLUME(0x0)); // maximum volume
uda1341_write_reg(codec, UDA1341_DATA01, DATA1_BASS(0)| DATA1_TREBLE(0));
uda1341_write_reg(codec, UDA1341_DATA10, 0); // not mute
}
The driver function uda1341_soc_probe calls the uda1341_init_regs function
/* default values for all registers */
static const char uda1341_reg[UDA1341_REG_NUM] = {
/* DATA0 */
0x00, 0x40, 0x80,
/* Extended address registers */
0x04, 0x04, 0x04, 0x00, 0x00, 0x00,
/* data1 */
0x00,
/* status regs */
0x00, 0x83,
};
probe function
static int uda1341_soc_probe(struct snd_soc_codec *codec)
{
int ret;
uda1341_init_regs(codec);
return ret;
}
(2) s3c2440_iis.c(Platform)
static int s3c2440_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
/* Set up IIS controller according to params*/
int tmp_fs;
int i;
int min = 0xffff;
int pre = 0;
unsigned int fs;
struct clk *clk = clk_get(NULL, "pclk");
Entry function maps registers (exit function unmaps)
/*gpecon = ioremap(0x56000040, 4);//0x56000040 is the physical address, size is 4
bytesiis_regs = ioremap(0x55000000, sizeof(struct s3c2440_iis_regs));*/
/* Configure GPIO for IIS */
*gpecon &= ~((3<<0) | (3<<2) | (3<<4) | (3<<6) | (3<<8));
*gpecon |= ((2<<0) | (2<<2) | (2<<4) | (2<<6) | (2<<8));
/* bit[9] : Master clock select, 0-PCLK
* bit[8] : 0 = Master mode
* bit[7:6] : 10 = Transmit mode
* bit[4] : 0-IIS compatible format
* bit[2] : 384fs, after determining MASTER CLOCK, fs = MASTER CLOCK/384
* bit[1:0] : Serial bit clock frequency select, 32fs
*/
Register mapping (write a structure and then map)
static volatile struct s3c2440_iis_regs *iis_regs;
struct s3c2440_iis_regs {
unsigned int iiscon;
unsigned int iismod;
unsigned int iispsr;
unsigned int iisfcon;
unsigned int iisfifo;
};
iis_regs = ioremap(0x55000000, sizeof(struct s3c2440_iis_regs));//Register mapping
if (params_format(params) == SNDRV_PCM_FORMAT_S16_LE)//The number of bits occupied by each sample value (8 bits or 16 bits is determined by params)
iis_regs->iismod = (2<<6) | (0<<4) | (1<<3) | (1<<2) | (1);
else if (params_format(params) == SNDRV_PCM_FORMAT_S8)
iis_regs->iismod = (2<<6) | (0<<4) | (0<<3) | (1<<2) | (1);
else
return -EINVAL;//Other values are wrong
struct clk *clk = clk_get(NULL, "pclk");
PCLK = clk_get_rate(clk); finally use clk_put(clk);
/* Master clock = PCLK/(n+1)
* fs = Master clock / 384//fs is the sampling rate
* fs = PCLK / (n+1) / 384
*/
fs = params_rate(params);//The sampling frequency is obtained according to paramsfor
(i = 0; i <= 31; i++)
{
tmp_fs = clk_get_rate(clk)/384/(i+1);
if (ABS(tmp_fs, fs) < min)
{
min = ABS(tmp_fs, fs);
pre = i;
}
}
iis_regs->iispsr = (pre << 5) | (pre);
/*
* bit15 : Transmit FIFO access mode select, 1-DMA
* bit13 : Transmit FIFO, 1-enable
*/
iis_regs->iisfcon = (1<<15) | (1<<13);
/*
* bit[5] : Transmit DMA service request, 1-enable
* bit[1] : IIS prescaler, 1-enable
*/
iis_regs->iiscon = (1<<5) | (1<<1) ;
clk_put(clk);
return 0;
}
(3) s3c2440_dma.c (Platform)
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);
/* Set DMA according to params */
snd_pcm_set_runtime_buffer(sub stream, &substream->dma_buffer ;
Codec
static struct snd_soc_codec_driver soc_codec_dev_uda1341 = {
.probe = uda1341_soc_probe,
/* UDA1341 registers do not support read operations, only write operations
. * To know the current value of a register,
* it can only be saved when writing (cache)
*/
.reg_cache_size = sizeof(uda1341_reg), // stores the value of the register (how big the cache is, look at the number of registers)
.reg_word_size = sizeof(u8), //The number of data bits occupied by each
register.reg_cache_default = uda1341_reg, //Default value.reg_cache_step
= 1,
.read = uda1341_read_reg_cache, //Function to read registers.write
= uda1341_write_reg, /* Write registers*/
};
(1) Read registers (codec chips that support register read operations can use the register read function)
/*
* The codec has no support for reading its registers except for peak level...
*/
For uda1341, read it from cache intelligently
static inline unsigned int uda1341_read_reg_cache(struct snd_soc_codec *codec,
unsigned int reg)
{
u8 *cache = codec->reg_cache; //For uda1341, read it from cache intelligently
if (reg >= UDA1341_REG_NUM) //The number of registers is greater than a certain value, return -1
return -1;
return cache[reg];
}
(2) Write register
static int uda1341_write_reg(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
u8 *cache = codec->reg_cache;//Write the value of the write register to the cache (back it up because uda1341 does not allow register read operations)
/* Save first*/
if (reg >= UDA1341_REG_NUM)//The number of registers is 12
return -1;
cache[reg] = value;//Save the value
/* Then write to the hardware*/
/* For EA (extended register), you need to call l3_write twice */First send the EA address value as data, and then send ED as data value
if ((reg >= UDA1341_EA000) && (reg <= UDA1341_EA110))
{ //The parameter on the left is the address (data0), and the parameter on the right is the data (uda1341_reg_addr[reg] corresponds to the extended address), the highest two bits or 1
l3_write(UDA1341_DATA0_ADDR, uda1341_reg_addr[reg] | UDA1341_EXTADDR_PREFIX);
l3_write(UDA1341_DATA0_ADDR, value | UDA1341_EXTDATA_PREFIX);
}
else
{
l3_write(uda1341_reg_addr[reg], value | uda1341_data_bit[reg]); //When we access a register, the data value must be or some bits to locate a register under a certain category of uda1341
}
return 0;
}
Dai part
static struct snd_soc_dai_driver uda1341_dai = {
.ops = &uda1341_dai_ops,
};
static const struct snd_soc_dai_ops uda1341_dai_ops = {
.hw_params = uda1341_hw_params,
};
5. Uda1341 Hardware Analysis
(1) 2440 is connected to uda1341 through three lines. To write to the register of uda1341, the address and data are required. When L3MODE is equal to 0, it means that the address is transmitted on the line L3DATA, and when it is 1, it is data. L3CLOCK means that 1 bit is transmitted per clock.
(2) Check the chip manual (UDA1341TS.pdf) to see how many registers there are
Previous article:ALSA Sound Card 08_Framework Written from Scratch_Study Notes
Next article:ALSA sound card 12_Add volume control from scratch_Study notes
- Popular Resources
- Popular amplifiers
- 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
- Huawei RF Basic Knowledge Training Full Solution PDF
- TI's AXC level translator
- I would like to ask the experts to help explain the filtering principle of this picture, as well as the calculation of inductance and capacitance parameters.
- Disassembly preview, does anyone know what this is?
- 【TGF4042 Signal Generator】+ Unboxing Review
- 【Smart Sports Watch】Work Submission
- TMS320F2812 complete program to implement SVPWM
- Making friends requires capital.
- Reading Notes of the Good Book "Operational Amplifier Parameter Analysis and LTspice Application Simulation" 03-Operational Amplifier Guard Ring...
- The Secret of Odometer Data