linux应用层i2c api函数,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通信,则需要提供自定义的回调函数。

时间: 01-25

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值