rk音頻驅動分析之codec


Es8323.c (sound\soc\codecs) 
1.入口函數
dts里面
 status = "okay";
        es8323: es8323@11 {
                status = "okay";
                compatible = "es8323";
                reg = <0x11>;
                pa-en1 = <&gpio1 GPIO_A0 GPIO_ACTIVE_HIGH>;
                pa-en2 = <&gpio1 GPIO_A1 GPIO_ACTIVE_HIGH>;
        };

static const struct i2c_device_id es8323_i2c_id[] = {
 { "es8323", 0 },{ }};
static struct i2c_driver es8323_i2c_driver = {
 .driver = {
  .name = "ES8323",
  .owner = THIS_MODULE,
 },
 .shutdown = es8323_i2c_shutdown,
 .probe = es8323_i2c_probe,
 .remove = es8323_i2c_remove,
 .id_table = es8323_i2c_id,
};

匹配之后就進入probe函數
I2C適配器的能力的相關代碼
/* declare our i2c functionality */
static u32 rockchip_i2c_func(struct i2c_adapter *adap)
{
        return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING;
}
/* i2c bus registration info */
static const struct i2c_algorithm rockchip_i2c_algorithm = {
        .master_xfer = rockchip_i2c_xfer,
        .functionality = rockchip_i2c_func,
};

es8323_i2c_probe
    es8323 = devm_kzalloc(&i2c->dev,sizeof(struct es8323_priv), GFP_KERNEL); //分配es8323_priv結構體
    //用來判定設配器的能力,這一點非常重要。你也可以直接查看對應適配器的能力,開頭有相關函數聲明
    if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) 
    es8323->control_type = SND_SOC_I2C; //設置控制接口
    es8323->spk_ctl_gpio = of_get_named_gpio_flags(i2c->dev.of_node, "pa-en1", 0, &flags); //獲取dts的相關控制腳
        es8323->spk_gpio_level = (flags & OF_GPIO_ACTIVE_LOW)? 0:1; //獲取默認狀態
        //一般gpio_request封裝了mem_request(),起保護作用,最后要調用mem_free之類的。主要是告訴內核這地址被占用了。當其它地方調用同一地址的gpio_request就會報告錯誤,該地址已被申請。在/proc/mem應該會有地址占用表描述。
        ret = gpio_request(es8323->spk_ctl_gpio, NULL); 
        gpio_direction_output(es8323->spk_ctl_gpio,es8323->spk_gpio_level); //設置狀態
    。。。。獲取另外兩個端口。。。。
    ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_es8323, &es8323_dai, 1); //注冊codec,單獨分析


2.注冊codec,單獨分析
codec相關結構體,主要是codec的相關操作函數
static struct snd_soc_codec_driver soc_codec_dev_es8323 = {
 .probe = es8323_probe,
 .remove = es8323_remove,
 .suspend = es8323_suspend,
 .resume = es8323_resume,
 .set_bias_level = es8323_set_bias_level,
 .reg_cache_size = ARRAY_SIZE(es8323_reg),
 .reg_word_size = sizeof(u16),
 .reg_cache_default = es8323_reg,
 .reg_cache_step = 1,
}

codec dai相關的函數
static struct snd_soc_dai_ops es8323_ops = {
 .startup = es8323_pcm_startup,
 .hw_params = es8323_pcm_hw_params,
 .set_fmt = es8323_set_dai_fmt,
 .set_sysclk = es8323_set_dai_sysclk,
 .digital_mute = es8323_mute,
};
static struct snd_soc_dai_driver es8323_dai = {
 .name = "ES8323 HiFi",
 .playback = {
  .stream_name = "Playback",
  .channels_min = 1,
  .channels_max = 2,
  .rates = es8323_RATES,// SNDRV_PCM_RATE_48000,
  .formats = es8323_FORMATS,
 },
 .capture = {
  .stream_name = "Capture",
  .channels_min = 1,
  .channels_max = 8,
  .rates = es8323_RATES,// SNDRV_PCM_RATE_48000,
  .formats = es8323_FORMATS,
  },
 .ops = &es8323_ops,
 .symmetric_rates = 1,
};

函數分析
snd_soc_register_codec(&i2c->dev, &soc_codec_dev_es8323, &es8323_dai, 1);
    codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); //分配snd_soc_codec結構體
    /* create CODEC component name */
    codec->name = fmt_single_name(dev, &codec->id); //通過一定規則獲取名字
    codec->compress_type = SND_SOC_FLAT_COMPRESSION; //因為沒有指定,所有默認是這個模式
    codec->write = codec_drv->write; //這里沒有
    codec->read = codec_drv->read; //這里沒有
    codec->volatile_register = codec_drv->volatile_register; //這里沒有
    codec->readable_register = codec_drv->readable_register; //這里沒有
    codec->writable_register = codec_drv->writable_register; //這里沒有
    codec->ignore_pmdown_time = codec_drv->ignore_pmdown_time; //這里沒有
    codec->dapm.bias_level = SND_SOC_BIAS_OFF;
    codec->dapm.dev = dev;
     codec->dapm.codec = codec;
    codec->dapm.seq_notifier = codec_drv->seq_notifier; //這里沒有
    codec->dapm.stream_event = codec_drv->stream_event; //這里沒有
    codec->dev = dev;
    codec->driver = codec_drv;
    codec->num_dai = num_dai; //這里是1
    /* allocate CODEC register cache */
    if (codec_drv->reg_cache_size && codec_drv->reg_word_size)
        //reg_cache_size這里是52個,word是2 byte,reg_size =52*2=104
        reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size;
        codec->reg_size = reg_size; //104
        /* it is necessary to make a copy of the default register cache
        * because in the case of using a compression type that requires
        * the default register cache to be marked as the
        * kernel might have freed the array by the time we initialize the cache. */
        有必要復制默認寄存器緩存的副本,因為在需要使用壓縮類型的情況下,在初始化緩存時,內核可能釋放了數組,需要默認的寄存器緩存被標記
        if (codec_drv->reg_cache_default) //一組默認寄存器
            在kernel中可以通過kmemdup將一種類型的數據賦值給另外同一個類型的數據,把reg_cache_default賦值給reg_def_copy 
            codec->reg_def_copy = kmemdup(codec_drv->reg_cache_default, reg_size, GFP_KERNEL);
    if (codec_drv->reg_access_size && codec_drv->reg_access_default) //我們這里沒有
    for (i = 0; i < num_dai; i++) { //這里num_dai=1
        //讓DAI的數據格式同時擁有大小字節序
        fixup_codec_formats(&dai_drv[i].playback);
            stream->formats |= codec_format_map[i]; //比如SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE,
        fixup_codec_formats(&dai_drv[i].capture);
    list_add(&codec->list, &codec_list); //把codec->list加到codec_list的最前面
    /* register any DAIs */
     ret = snd_soc_register_dais(dev, dai_drv, num_dai); //注冊dai
    for (i = 0; i < count; i++)  //這里count=1
        dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL); //分配struct snd_soc_dai *dai;
        /* create DAI component name */
        dai->name = fmt_multiple_name(dev, &dai_drv[i]); //這里名字是ES8323 HiFi
        dai->dev = dev;
        dai->driver = &dai_drv[i];
        dai->dapm.dev = dev;
        list_for_each_entry(codec, &codec_list, list) //從codec_list找出對應的codec
            if (codec->dev == dev)
                dai->codec = codec; //賦值
        list_add(&dai->list, &dai_list); //把 dai->list加入到dai_list頭部


3.es8323_probe
    codec->read = es8323_read_reg_cache; //es8323 register cache函數,cache是一個數組
    codec->write = es8323_write;
        ret = codec->hw_write(codec->control_data, data, 2);
    //issue a single I2C message in master transmit mode
    codec->hw_write = (hw_write_t)i2c_master_send;  //i2c的寫函數
    codec->control_data = container_of(codec->dev, struct i2c_client, dev); //得到i2c_client
    es8323_codec = codec;
    ret = es8323_reset(codec); //寫寄存器復位
        snd_soc_write(codec, ES8323_CONTROL1, 0x80);
        return snd_soc_write(codec, ES8323_CONTROL1, 0x00);
    .........
    snd_soc_write(codec, 0x02,0xf3); //一些初始化寄存器設置
    .............
    es8323_set_bias_level(codec, SND_SOC_BIAS_STANDBY); //設置偏壓為待機



4.es8323_set_dai_sysclk
/* The set of rates we can generate from the above for each SYSCLK */
static unsigned int rates_12288[] = {
 8000, 12000, 16000, 24000, 24000, 32000, 48000, 96000,
};
static struct snd_pcm_hw_constraint_list constraints_12288 = {
 .count = ARRAY_SIZE(rates_12288),
 .list = rates_12288,
};

    switch (freq) //我們這里設置12288000 
    case 12288000:
        es8323->sysclk_constraints = &constraints_12288; //我們可以從上面為每一個SYSCLK生成一組速率
        es8323->sysclk = freq;


5.es8323_pcm_hw_params //caodec的硬件參數設置
/* codec hifi mclk clock divider coefficients */
static const struct _coeff_div coeff_div[] = {
 /* 8k */
    mclk         rate    fs        sr    usb
 {12288000, 8000, 1536, 0xa, 0x0},
 {11289600, 8000, 1408, 0x9, 0x0},
 {18432000, 8000, 2304, 0xc, 0x0},
 {16934400, 8000, 2112, 0xb, 0x0},
 {12000000, 8000, 1500, 0xb, 0x1},
 /* 11.025k */
 /* 16k */
 /* 22.05k */
 /* 32k */
 /* 44.1k */
。。。。。。。。。。。
 /* 48k */
 /* 88.2k */
 /* 96k */
}

static int es8323_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params,
    struct snd_soc_dai *dai)
    coeff = get_coeff(es8323->sysclk, params_rate(params));
        for (i = 0; i < ARRAY_SIZE(coeff_div); i++)  //獲得對於的rate和mclk
          if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
    switch (params_format(params)) //設置格式,找到對於的codec寄存器數值
    case SNDRV_PCM_FORMAT_S16_LE:
        adciface |= 0x000C;
        daciface |= 0x0018;
    ........................
    /* set iface & srate*/
    snd_soc_write(codec, ES8323_DAC_IFACE, daciface); //設置寄存器DACWL
    snd_soc_write(codec, ES8323_ADC_IFACE, adciface); //ADCWL
    if (coeff >= 0) {
        snd_soc_write(codec, ES8323_IFACE, srate); //主機串行模式,然后采樣率,應該是錄音
        snd_soc_write(codec, ES8323_ADCCONTROL5, coeff_div[coeff].sr | (coeff_div[coeff].usb) << 4); //采樣率的主頻
        snd_soc_write(codec, ES8323_DACCONTROL2, coeff_div[coeff].sr | (coeff_div[coeff].usb) << 4); //采樣率的主頻


6.es8323_set_dai_fmt設置dai的格式
static int es8323_set_dai_fmt(struct snd_soc_dai *codec_dai,  unsigned int fmt)
    iface = snd_soc_read(codec, ES8323_IFACE); //讀取接口相關的寄存器
    adciface = snd_soc_read(codec, ES8323_ADC_IFACE);
    daciface = snd_soc_read(codec, ES8323_DAC_IFACE);
    /* set master/slave audio interface */
    switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) //看是主還是從模式
    case SND_SOC_DAIFMT_CBM_CFM: // MASTER MODE
        iface |= 0x80;
    case SND_SOC_DAIFMT_CBS_CFS: // SLAVE MODE
        iface &= 0x7F;
    /* interface format */ADC數據接口的模式
    switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) 
    case SND_SOC_DAIFMT_I2S: //我們這里是I2S
        adciface &= 0xFC;
        daciface &= 0xF9;
    。。。。。。。
    /* clock inversion */ 時鍾翻轉
    switch (fmt & SND_SOC_DAIFMT_INV_MASK)
    case SND_SOC_DAIFMT_NB_NF:
    。。。。。。。
    //設置對應的寄存器
    snd_soc_write(codec, ES8323_IFACE, iface);
    snd_soc_write(codec, ES8323_ADC_IFACE, adciface);
    snd_soc_write(codec, ES8323_DAC_IFACE, daciface);


7.static int es8323_mute(struct snd_soc_dai *dai, int mute)
靜音函數,靜音和播放
    if (mute) //這里soc_pcm_prepare傳入0
        //拉低ES8323_CODEC_SET_SPK和ES8323_CODEC_SET_HP控制腳
        es8323_set_gpio(ES8323_CODEC_SET_SPK,!es8323->spk_gpio_level);
        es8323_set_gpio(ES8323_CODEC_SET_HP,!es8323->hp_gpio_level);
        snd_soc_write(codec, ES8323_DACCONTROL3, 0x06);  //靜音
    else
        snd_soc_write(codec, ES8323_DACCONTROL3, 0x02); 
        snd_soc_write(codec, 0x30,es8323_DEF_VOL); //設置音量
        snd_soc_write(codec, 0x31,es8323_DEF_VOL); //設置音量
    if(hp_irq_flag == 0) //沒有耳機
        es8323_set_gpio(ES8323_CODEC_SET_SPK,es8323->spk_gpio_level);
    else //耳機
        es8323_set_gpio(ES8323_CODEC_SET_HP,es8323->hp_gpio_level);


8.rockchip_i2s_trigger
啟動傳輸
static int rockchip_i2s_trigger(struct snd_pcm_substream *substream, int cmd,  struct snd_soc_dai *dai)
    switch (cmd)
    case SNDRV_PCM_TRIGGER_START: //開始傳輸
    case SNDRV_PCM_TRIGGER_RESUME:
    case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
        if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
            rockchip_snd_rxctrl(i2s, 1); //從codec接收
        else
            rockchip_snd_txctrl(i2s, 1);//傳輸給codec
    case SNDRV_PCM_TRIGGER_SUSPEND:
    case SNDRV_PCM_TRIGGER_STOP:
    case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
            if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
                rockchip_snd_rxctrl(i2s, 0); //停止
            else
                 rockchip_snd_txctrl(i2s, 0); //停止
    

9.es8323_set_bias_level //設置codec偏壓
    switch (level)
    case SND_SOC_BIAS_ON:這里什么都沒有做
    case SND_SOC_BIAS_PREPARE: //進入low power模式,這些寄存器都是這樣
        snd_soc_write(codec, ES8323_ANAVOLMANAG, 0x7C);
        snd_soc_write(codec, ES8323_CHIPLOPOW1, 0x00);
        snd_soc_write(codec, ES8323_CHIPLOPOW2, 0x00);
        snd_soc_write(codec, ES8323_CHIPPOWER, 0x00);       
        snd_soc_write(codec, ES8323_ADCPOWER, 0x00);
    case SND_SOC_BIAS_STANDBY: //這個和SND_SOC_BIAS_PREPARE一樣
    case SND_SOC_BIAS_OFF: //關閉
        snd_soc_write(codec, ES8323_ADCPOWER, 0xFF);
        snd_soc_write(codec, ES8323_DACPOWER, 0xC0);
        snd_soc_write(codec, ES8323_CHIPLOPOW1, 0xFF);
        snd_soc_write(codec, ES8323_CHIPLOPOW2, 0xFF);
        snd_soc_write(codec, ES8323_CHIPPOWER, 0xFF);
        snd_soc_write(codec, ES8323_ANAVOLMANAG, 0x7B);
    codec->dapm.bias_level = level;

     

    
    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM