Alsa驅動snd_soc_read的底層實現


在分析snd_soc_codec_driver的結構體時,發現有些芯片的驅動中定義了字段reg_word_size, reg_cache_size, reg_cache_default,但沒有定義read/write,如wm8993:

static struct snd_soc_codec_driver soc_codec_dev_wm8993 = {
    .probe =     wm8993_probe,
    .remove =     wm8993_remove,
    .suspend =    wm8993_suspend,
    .resume =    wm8993_resume,
    .set_bias_level = wm8993_set_bias_level,
    .reg_cache_size = ARRAY_SIZE(wm8993_reg_defaults),
    .reg_word_size = sizeof(u16),
    .reg_cache_default = wm8993_reg_defaults,
    .volatile_register = wm8993_volatile,
};

而另外的一些芯片驅動中,則定義了字段read, write,如wm8400和cx20442:

static struct snd_soc_codec_driver soc_codec_dev_wm8400 = {
    .probe =    wm8400_codec_probe,
    .remove =    wm8400_codec_remove,
    .suspend =    wm8400_suspend,
    .resume =    wm8400_resume,
    .read = wm8400_read,
    .write = wm8400_write,
    .set_bias_level = wm8400_set_bias_level,
};
static struct snd_soc_codec_driver cx20442_codec_dev = {
    .probe =     cx20442_codec_probe,
    .remove =     cx20442_codec_remove,
    .reg_cache_default = &cx20442_reg,
    .reg_cache_size = 1,
    .reg_word_size = sizeof(u8),
    .read = cx20442_read_reg_cache,
    .write = cx20442_write,
    .dapm_widgets = cx20442_dapm_widgets,
    .num_dapm_widgets = ARRAY_SIZE(cx20442_dapm_widgets),
    .dapm_routes = cx20442_audio_map,
    .num_dapm_routes = ARRAY_SIZE(cx20442_audio_map),
};

猜測read/write應該和snd_soc_read/write有關,在soc_core.c中注意到snd_soc_read的源碼:

unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg)
{
    unsigned int ret;

    ret = codec->read(codec, reg);
    dev_dbg(codec->dev, "read %x => %x\n", reg, ret);
    trace_snd_soc_reg_read(codec, reg, ret);

    return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_read);

因此,要想使用snd_soc_read,必須要設置codec->read回調函數,當我們提供了read/write函數時,在snd_soc_register_codec函數中會設置codec->read

int snd_soc_register_codec(struct device *dev,
               const struct snd_soc_codec_driver *codec_drv,
               struct snd_soc_dai_driver *dai_drv,
               int num_dai)
{
    ...
codec
->write = codec_drv->write; codec->read = codec_drv->read; codec->volatile_register = codec_drv->volatile_register;

OK,這里和我們soc_codec_dev_wm8400以及cx20442_codec_dev都對應的上,snd_soc_read最終會調用我們提供的回調函數。

問題來了,soc_codec_dev_wm8993中並沒有提供回調函數,snd_soc_read是如何工作的呢?剛開始百思不得其解,肯定會有什么地方設置了codec->read!繼續找代碼,終於在soc_cache.c中找到了一個神奇的函數:snd_soc_codec_set_cache_io,看看代碼片段:

int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
                   int addr_bits, int data_bits,
                   enum snd_soc_control_type control)
{
    ...

    codec->write = io_types[i].write;
    codec->read = io_types[i].read;
    codec->bulk_write_raw = snd_soc_hw_bulk_write_raw;

果然,它設置了codec->read!而在wm8993的probe函數中,有如下的調用:

ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C);

它設置了I2C的地址寬度為8位,寄存器寬度為16位,I2C通信方式,如果根據這些參數繼續追蹤io_types[i].read,則會發現它最終調用了I2C的標准讀函數:

 

static unsigned int do_i2c_read(struct snd_soc_codec *codec,
                void *reg, int reglen,
                void *data, int datalen)
{
    struct i2c_msg xfer[2];
    int ret;
    struct i2c_client *client = codec->control_data;

    /* Write register */
    xfer[0].addr = client->addr;
    xfer[0].flags = 0;
    xfer[0].len = reglen;
    xfer[0].buf = reg;
    xfer[0].scl_rate = 100 * 1000;

    /* Read data */
    xfer[1].addr = client->addr;
    xfer[1].flags = I2C_M_RD;
    xfer[1].len = datalen;
    xfer[1].buf = data;

    ret = i2c_transfer(client->adapter, xfer, 2);
    if (ret == 2)
        return 0;
    else if (ret < 0)
        return ret;
    else
        return -EIO;
}

 

至此,想起之前在調試WM8741的時候,有一次不小心把snd_soc_codec_set_cache_io給注釋掉了,結果導致snd_soc_read/write完全失效,就是這個原因。

分析至此,結論就很明確了,如果我們使用的是標准的I2C通信,則可以不提供read/write回調,讓soc使用默認的do_i2c_read/write。如果我們使用了非標准的通信方式,如GPIO模擬串口,或者其它非標准的I2C通信,則需要提供自定義的回調函數。


免責聲明!

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



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