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

时间: 2024-10-05 05:31:57

Alsa驱动snd_soc_read的底层实现的相关文章

Android 开发之 ---- 底层驱动开发(一)

驱动概述 说到 Android 驱动是离不开 Linux 驱动的.Android 内核采用的是 Linux2.6 内核 (最近Linux 3.3 已经包含了一些 Android 代码).但 Android 并没有完全照搬 Linux 系统内核,除了对Linux 进行部分修正,还增加了不少内容.android 驱动 主要分两种类型:Android 专用驱动 和 Android 使用的设备驱动(linux). Android 专有驱动程序: 1)Android Ashmem 匿名共享内存: 为用户空

Linux ALSA声卡驱动之一:ALSA架构简介【转】

本文转载自:http://blog.csdn.net/droidphone/article/details/6271122 声明:本博内容均由http://blog.csdn.net/droidphone原创,转载请注明出处,谢谢! 一.  概述 ALSA是Advanced Linux Sound Architecture 的缩写,目前已经成为了linux的主流音频体系结构,想了解更多的关于ALSA的这一开源项目的信息和知识,请查看以下网址:http://www.alsa-project.org

Linux ALSA声卡驱动之五:移动设备中的ALSA(ASoC)

转自http://blog.csdn.net/droidphone/article/details/7165482 1.  ASoC的由来 ASoC--ALSA System on Chip ,是建立在标准ALSA驱动层上,为了更好地支持嵌入式处理器和移动设备中的音频Codec的一套软件体系.在ASoc出现之前,内核对于SoC中的音频已经有部分的支持,不过会有一些局限性: Codec驱动与SoC CPU的底层耦合过于紧密,这种不理想会导致代码的重复,例如,仅是wm8731的驱动,当时Linux中

Ubuntu下声卡驱动解决方法alsa

一.首先介绍一下什么是ALSA : Advanced Linux Sound Architecture 的简称为 ALSA ,译成中文的意思是先进的Linux声音架构(这是google翻译的):一谈到架构就有点范围太大了,所以ALSA不仅仅是包括对声卡的支持和驱动: ALSA具有如下特征: 1.对所有音频接口的高效支持,从普通用户的声卡到专业级别多路音频设备: 2.声卡驱动完全模块化设计: 3.SMP and thread-safe design. 4.开发库(alsa-lib) 为程序设计提供

Linux ALSA 音频系统:物理链路篇

1. Overview 硬件平台及软件版本: Kernel - 3.4.5 SoC - Samsung exynos CODEC - WM8994 Machine - goni_wm8994 Userspace - tinyalsa Linux ALSA 音频系统架构大致如下: +--------+ +--------+ +--------+ |tinyplay| |tinycap | |tinymix | +--------+ +--------+ +--------+ | ^ ^ V | V

Linux音频驱动学习之:(1)ASOC分析

一.音频架构概述 (1)ALSA是Advanced Linux Sound Architecture 的缩写,目前已经成为了linux的主流音频体系结构,想了解更多的关于ALSA的这一开源项目的信息和知识,请查看以下网址:http://www.alsa-project.org/.在内核设备驱动层,ALSA提供了alsa-driver,同时在应用层,ALSA为我们提供了alsa-lib,应用程序只要调用alsa-lib提供的API,即可以完成对底层音频硬件的控制. (2)PCM是英文Pulse-c

Linux ALSA 音频系统:逻辑设备篇

6. 声卡和 PCM 设备的建立过程 前面几章分析了 Codec.Platform.Machine 驱动的组成部分及其注册过程,这三者都是物理设备相关的,大家应该对音频物理链路有了一定的认知.接着分析音频驱动的中间层,由于这些并不是真正的物理设备,故我们称之为逻辑设备. PCM 逻辑设备,我们又习惯称之为 PCM 中间层或 pcm native,起着承上启下的作用:往上是与用户态接口的交互,实现音频数据在用户态和内核态之间的拷贝:往下是触发 codec.platform.machine 的操作函

linux音频alsa-uda134x驱动文档阅读之一转自http://blog.csdn.net/wantianpei/article/details/7817293

前言 目前,linux系统常用的音频驱动有两种形式:alsa oss alsa:现在是linux下音频驱动的主要形式,与简单的oss兼容.oss:过去的形式而我们板子上的uda1341用的就是alsa驱动.alsa概述:因为我们用的是板上系统,用的也是alsa 的一个soc子系统.所以我们直接讲解alsa soc子系统. ALSA SoC LayerALSA板上系统层============== The overall project goal of the ALSA System on Chi

linux驱动之I2C

include/linux/i2c.h struct i2c_msg;struct i2c_algorithm;struct i2c_adapter;struct i2c_client;struct i2c_driver;union i2c_smbus_data; I2C驱动主要包含三部分:I2C核心.I2C总线驱动.I2C设备驱动,它们主要的数据结构在目录:/include/linux/i2c.h struct i2c_driver 1 /* 2 * A driver is capable o