ALSA sound card 09_parameter setting written from scratch_study notes

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

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

[1] [2]
Reference address:ALSA sound card 09_parameter setting written from scratch_study notes

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

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号