Linux Audio DRV(音频驱动)-基于mini2440

前言

Aduio驱动和网络路由的功能很相似,从一个入口最终到一个出口,这之间的路就是我们DRV最核心的地方了。

这里写图片描述

这里分析的Uda134X的驱动要比现在任何一款Android手机上的Codec都要简单的多。凡事由简入难,万事不离其中。

此仅仅分析Codec驱动。

基本信息展示

Uda1434x框图:
这里写图片描述

amixer命令显示结果

[Gavin@Gavin /]# amixer
Simple mixer control 'Master',0
  Capabilities: pvolume pvolume-joined
  Playback channels: Mono
  Limits: Playback 0 - 63
  Mono: Playback 63 [100%]
Simple mixer control 'Bass',0
  Capabilities: volume volume-joined
  Playback channels: Mono
  Capture channels: Mono
  Limits: 0 - 15
  Mono: 0 [0%]
Simple mixer control 'Treble',0
  Capabilities: volume volume-joined
  Playback channels: Mono
  Capture channels: Mono
  Limits: 0 - 3
  Mono: 0 [0%]
Simple mixer control 'PCM Playback De-emphasis',0
  Capabilities: enum
  Items: 'None' '32Khz' '44.1Khz' '48Khz'
  Item0: 'None'
Simple mixer control 'Mic',0
  Capabilities: volume volume-joined
  Playback channels: Mono
  Capture channels: Mono
  Limits: 0 - 31
  Mono: 0 [0%]
Simple mixer control 'Mic Sensitivity',0
  Capabilities: volume volume-joined
  Playback channels: Mono
  Capture channels: Mono
  Limits: 0 - 7
  Mono: 5 [71%]
Simple mixer control 'Capture',0
  Capabilities: cvolume
  Capture channels: Mono
  Limits: Capture 0 - 7
  Mono: Capture 5 [71%]
Simple mixer control 'ADC +6dB',0
  Capabilities: pswitch pswitch-joined
  Playback channels: Mono
  Mono: Playback [off]
Simple mixer control 'ADC Polarity',0
  Capabilities: pswitch pswitch-joined
  Playback channels: Mono
  Mono: Playback [off]
Simple mixer control 'AGC',0
  Capabilities: pswitch pswitch-joined
  Playback channels: Mono
  Mono: Playback [off]
Simple mixer control 'AGC Target',0
  Capabilities: volume volume-joined
  Playback channels: Mono
  Capture channels: Mono
  Limits: 0 - 3
  Mono: 3 [100%]
Simple mixer control 'AGC Timing',0
  Capabilities: volume volume-joined
  Playback channels: Mono
  Capture channels: Mono
  Limits: 0 - 7
  Mono: 0 [0%]
Simple mixer control 'Analog1',0
  Capabilities: volume volume-joined
  Playback channels: Mono
  Capture channels: Mono
  Limits: 0 - 31
  Mono: 27 [87%]
Simple mixer control 'Analog2',0
  Capabilities: volume volume-joined
  Playback channels: Mono
  Capture channels: Mono
  Limits: 0 - 31
  Mono: 27 [87%]
Simple mixer control 'DAC +6dB',0
  Capabilities: pswitch pswitch-joined
  Playback channels: Mono
  Mono: Playback [off]
Simple mixer control 'DAC Polarity',0
  Capabilities: pswitch pswitch-joined
  Playback channels: Mono
  Mono: Playback [off]
Simple mixer control 'DC Filter Enable',0
  Capabilities: pswitch pswitch-joined
  Playback channels: Mono
  Mono: Playback [off]
Simple mixer control 'Double Speed',0
  Capabilities: pswitch pswitch-joined
  Playback channels: Mono
  Mono: Playback [off]
Simple mixer control 'Input Mux',0
  Capabilities: enum
  Items: 'Differential' 'Analog1' 'Analog2' 'Both'
  Item0: 'Analog2'
Simple mixer control 'Sound Processing Filter',0
  Capabilities: enum
  Items: 'Flat' 'Minimum1' 'Minimum2' 'Maximum'
  Item0: 'Flat'

驱动中Kcontrol的初始化:

static const char *uda134x_dsp_setting[] = {"Flat", "Minimum1",
                        "Minimum2", "Maximum"};
static const char *uda134x_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
static const char *uda134x_mixmode[] = {"Differential", "Analog1",
                    "Analog2", "Both"};

static const struct soc_enum uda134x_mixer_enum[] = {
SOC_ENUM_SINGLE(UDA134X_DATA010, 0, 0x04, uda134x_dsp_setting),
SOC_ENUM_SINGLE(UDA134X_DATA010, 3, 0x04, uda134x_deemph),
SOC_ENUM_SINGLE(UDA134X_EA010, 0, 0x04, uda134x_mixmode),
};

static const struct snd_kcontrol_new uda1341_snd_controls[] = {
SOC_SINGLE("Master Playback Volume", UDA134X_DATA000, 0, 0x3F, 1),
SOC_SINGLE("Capture Volume", UDA134X_EA010, 2, 0x07, 0),
SOC_SINGLE("Analog1 Volume", UDA134X_EA000, 0, 0x1F, 1),
SOC_SINGLE("Analog2 Volume", UDA134X_EA001, 0, 0x1F, 1),

SOC_SINGLE("Mic Sensitivity", UDA134X_EA010, 2, 7, 0),
SOC_SINGLE("Mic Volume", UDA134X_EA101, 0, 0x1F, 0),

SOC_SINGLE("Tone Control - Bass", UDA134X_DATA001, 2, 0xF, 0),
SOC_SINGLE("Tone Control - Treble", UDA134X_DATA001, 0, 3, 0),

SOC_ENUM("Sound Processing Filter", uda134x_mixer_enum[0]),
SOC_ENUM("PCM Playback De-emphasis", uda134x_mixer_enum[1]),
SOC_ENUM("Input Mux", uda134x_mixer_enum[2]),

SOC_SINGLE("AGC Switch", UDA134X_EA100, 4, 1, 0),
SOC_SINGLE("AGC Target Volume", UDA134X_EA110, 0, 0x03, 1),
SOC_SINGLE("AGC Timing", UDA134X_EA110, 2, 0x07, 0),

SOC_SINGLE("DAC +6dB Switch", UDA134X_STATUS1, 6, 1, 0),
SOC_SINGLE("ADC +6dB Switch", UDA134X_STATUS1, 5, 1, 0),
SOC_SINGLE("ADC Polarity Switch", UDA134X_STATUS1, 4, 1, 0),
SOC_SINGLE("DAC Polarity Switch", UDA134X_STATUS1, 3, 1, 0),
SOC_SINGLE("Double Speed Playback Switch", UDA134X_STATUS1, 2, 1, 0),
SOC_SINGLE("DC Filter Enable Switch", UDA134X_STATUS0, 0, 1, 0),
};

现在我们将amixer命令显示的结果精简化,只保留名称。结果如下:

Simple mixer control 'Master'
Simple mixer control 'Bass'
Simple mixer control 'Treble'
Simple mixer control 'PCM Playback De-emphasis'
Simple mixer control 'Mic'
Simple mixer control 'Mic Sensitivity'
Simple mixer control 'Capture'
Simple mixer control 'ADC +6dB'
Simple mixer control 'ADC Polarity'
Simple mixer control 'AGC'
Simple mixer control 'AGC Target'
Simple mixer control 'AGC Timing'
Simple mixer control 'Analog1'
Simple mixer control 'Analog2'
Simple mixer control 'DAC +6dB'
Simple mixer control 'DAC Polarity'
Simple mixer control 'DC Filter Enable
Simple mixer control 'Double Speed'
Simple mixer control 'Input Mux'
Simple mixer control 'Sound Processing Filter'

这里我们再做下更加详细的对比:
这里写图片描述

有没有发现Kcontrol的初始化会在amixer中会有一一对应的关系?这就是我们Codec整个路由的各个节点。
我们控制了路由中各个节点,我们也就控制了整个Codec。

驱动的初始化


static int uda134x_soc_probe(struct platform_device *pdev)
{
    struct snd_soc_device *socdev = platform_get_drvdata(pdev);
    struct snd_soc_codec *codec;
    struct uda134x_priv *uda134x;
    void *codec_setup_data = socdev->codec_data;
    int ret = -ENOMEM;
    struct uda134x_platform_data *pd;

    printk(KERN_INFO "UDA134X SoC Audio Codec\n");

    if (!codec_setup_data) {
        printk(KERN_ERR "UDA134X SoC codec: "
               "missing L3 bitbang function\n");
        return -ENODEV;
    }

    pd = codec_setup_data;
    /* 对Codec进行甄别,这里的model在板子device中体现 */
    switch (pd->model) {
    case UDA134X_UDA1340:
    case UDA134X_UDA1341:
    case UDA134X_UDA1344:
        break;
    default:
        printk(KERN_ERR "UDA134X SoC codec: "
               "unsupported model %d\n",
            pd->model);
        return -EINVAL;
    }

    socdev->card->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
    if (socdev->card->codec == NULL)
        return ret;

    codec = socdev->card->codec;

    uda134x = kzalloc(sizeof(struct uda134x_priv), GFP_KERNEL);
    if (uda134x == NULL)
        goto priv_err;
    codec->private_data = uda134x;

    codec->reg_cache = kmemdup(uda134x_reg, sizeof(uda134x_reg),
                   GFP_KERNEL);
    if (codec->reg_cache == NULL)
        goto reg_err;

    mutex_init(&codec->mutex);
    /* 
        对codec进行赋值初始化,包含codec整个寄存器大小,寄存器读写。重点关注:
        codec->dai、codec->read/write
     */
    codec->reg_cache_size = sizeof(uda134x_reg);
    codec->reg_cache_step = 1;

    codec->name = "UDA134X";
    codec->owner = THIS_MODULE;
    codec->dai = &uda134x_dai;
    codec->num_dai = 1;
    codec->read = uda134x_read_reg_cache;
    codec->write = uda134x_write;
#ifdef POWER_OFF_ON_STANDBY
    codec->set_bias_level = uda134x_set_bias_level;
#endif
    INIT_LIST_HEAD(&codec->dapm_widgets);
    INIT_LIST_HEAD(&codec->dapm_paths);

    codec->control_data = codec_setup_data;

    if (pd->power)
        pd->power(1);
    /* 对codec进行复位 */
    uda134x_reset(codec);

    /* register pcms */
    ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
    if (ret < 0) {
        printk(KERN_ERR "UDA134X: failed to register pcms\n");
        goto pcm_err;
    }
    /* Kcontrol的注册 */
    switch (pd->model) {
    case UDA134X_UDA1340:
    case UDA134X_UDA1344:
        ret = snd_soc_add_controls(codec, uda1340_snd_controls,
                    ARRAY_SIZE(uda1340_snd_controls));
    break;
    case UDA134X_UDA1341:
        ret = snd_soc_add_controls(codec, uda1341_snd_controls,
                    ARRAY_SIZE(uda1341_snd_controls));
    break;
    default:
        printk(KERN_ERR "%s unkown codec type: %d",
            __func__, pd->model);
    return -EINVAL;
    }

    if (ret < 0) {
        printk(KERN_ERR "UDA134X: failed to register controls\n");
        goto pcm_err;
    }
    /* 对codec的注册 */
    ret = snd_soc_init_card(socdev);
    if (ret < 0) {
        printk(KERN_ERR "UDA134X: failed to register card\n");
        goto card_err;
    }

    return 0;

card_err:
    snd_soc_free_pcms(socdev);
    snd_soc_dapm_free(socdev);
pcm_err:
    kfree(codec->reg_cache);
reg_err:
    kfree(codec->private_data);
priv_err:
    kfree(codec);
    return ret;
}

接下来我们讲讲里面较为重要的:
uda134x_dai、uda134x_dai_ops :

struct snd_soc_dai uda134x_dai = {
    .name = "UDA134X",
    /* playback capabilities */
    .playback = {
        .stream_name = "Playback",/* 播放流还是录音流 */
        .channels_min = 1,        /* 通道 */
        .channels_max = 2,
        .rates = UDA134X_RATES,   /* 速率 */
        .formats = UDA134X_FORMATS,/* 位数,8bit还是16bit */
    },
    /* capture capabilities */
    .capture = {
        .stream_name = "Capture",
        .channels_min = 1,
        .channels_max = 2,
        .rates = UDA134X_RATES,
        .formats = UDA134X_FORMATS,
    },
    /* pcm operations */
    .ops = &uda134x_dai_ops,
};

static struct snd_soc_dai_ops uda134x_dai_ops = {
    .startup    = uda134x_startup,
    .shutdown   = uda134x_shutdown,
    .hw_params  = uda134x_hw_params,/* 硬件参数设置:DAI format、SYSCLK / fs ratio的一些设置 */
    .digital_mute   = uda134x_mute,/* 开启、关闭静音 */
    .set_sysclk = uda134x_set_dai_sysclk,/* 设置dai clk*/
    .set_fmt    = uda134x_set_dai_fmt,/* dai参数的设置:模式的设定等等 */
};

uda134x_set_bias_level:

主要涉及到对bias、电源上下电。

static int uda134x_set_bias_level(struct snd_soc_codec *codec,
                  enum snd_soc_bias_level level)
{
    u8 reg;
    struct uda134x_platform_data *pd = codec->control_data;
    int i;
    u8 *cache = codec->reg_cache;

    pr_debug("%s bias level %d\n", __func__, level);

    switch (level) {
    case SND_SOC_BIAS_ON:
        /* ADC, DAC on */
        reg = uda134x_read_reg_cache(codec, UDA134X_STATUS1);
        uda134x_write(codec, UDA134X_STATUS1, reg | 0x03);
        break;
    case SND_SOC_BIAS_PREPARE:
        /* power on */
        if (pd->power) {
            pd->power(1);
            /* Sync reg_cache with the hardware */
            for (i = 0; i < ARRAY_SIZE(uda134x_reg); i++)
                codec->write(codec, i, *cache++);
        }
        break;
    case SND_SOC_BIAS_STANDBY:
        /* ADC, DAC power off */
        reg = uda134x_read_reg_cache(codec, UDA134X_STATUS1);
        uda134x_write(codec, UDA134X_STATUS1, reg & ~(0x03));
        break;
    case SND_SOC_BIAS_OFF:
        /* power off */
        if (pd->power)
            pd->power(0);
        break;
    }
    codec->bias_level = level;
    return 0;
}

数据读写

/*
 * The codec has no support for reading its registers except for peak level...
 */
static inline unsigned int uda134x_read_reg_cache(struct snd_soc_codec *codec,
    unsigned int reg)
{
    u8 *cache = codec->reg_cache;

    if (reg >= UDA134X_REGS_NUM)
        return -1;
    return cache[reg];
}

/*
 * Write the register cache
 */
static inline void uda134x_write_reg_cache(struct snd_soc_codec *codec,
    u8 reg, unsigned int value)
{
    u8 *cache = codec->reg_cache;

    if (reg >= UDA134X_REGS_NUM)
        return;
    cache[reg] = value;
}
上面这两个函数主要是直接读取缓存。至于为什么要这么做,上面的注释很清楚了。
/*
 * Write to the uda134x registers
 *
 */
static int uda134x_write(struct snd_soc_codec *codec, unsigned int reg,
    unsigned int value)
{
    int ret;
    u8 addr;
    u8 data = value;
    struct uda134x_platform_data *pd = codec->control_data;

    pr_debug("%s reg: %02X, value:%02X\n", __func__, reg, value);

    if (reg >= UDA134X_REGS_NUM) {
        printk(KERN_ERR "%s unkown register: reg: %u",
               __func__, reg);
        return -EINVAL;
    }

    uda134x_write_reg_cache(codec, reg, value);

    switch (reg) {
    case UDA134X_STATUS0:
    case UDA134X_STATUS1:
        addr = UDA134X_STATUS_ADDR;
        break;
    case UDA134X_DATA000:
    case UDA134X_DATA001:
    case UDA134X_DATA010:
        addr = UDA134X_DATA0_ADDR;
        break;
    case UDA134X_DATA1:
        addr = UDA134X_DATA1_ADDR;
        break;
    default:
        /* It's an extended address register */
        addr =  (reg | UDA134X_EXTADDR_PREFIX);

        ret = l3_write(&pd->l3,
                   UDA134X_DATA0_ADDR, &addr, 1);
        if (ret != 1)
            return -EIO;

        addr = UDA134X_DATA0_ADDR;
        data = (value | UDA134X_EXTDATA_PREFIX);
        break;
    }

    ret = l3_write(&pd->l3,
               addr, &data, 1);
    if (ret != 1)
        return -EIO;

    return 0;
}
uda134x_write这个函数主要是基于L3总线对数据进行写入,先写地址,后写值,有点类似于I2C数据的读写。

基于L3总线的数据写入:

/*
 * Send one byte of data to the chip.  Data is latched into the chip on
 * the rising edge of the clock.
 */
static void sendbyte(struct l3_pins *adap, unsigned int byte)
{
    int i;

    for (i = 0; i < 8; i++) {
        adap->setclk(0);
        udelay(adap->data_hold);
        adap->setdat(byte & 1);
        udelay(adap->data_setup);
        adap->setclk(1);
        udelay(adap->clock_high);
        byte >>= 1;
    }
}

/*
 * Send a set of bytes to the chip.  We need to pulse the MODE line
 * between each byte, but never at the start nor at the end of the
 * transfer.
 */
static void sendbytes(struct l3_pins *adap, const u8 *buf,
              int len)
{
    int i;

    for (i = 0; i < len; i++) {
        if (i) {
            udelay(adap->mode_hold);
            adap->setmode(0);
            udelay(adap->mode);
        }
        adap->setmode(1);
        udelay(adap->mode_setup);
        sendbyte(adap, buf[i]);
    }
}

int l3_write(struct l3_pins *adap, u8 addr, u8 *data, int len)
{
    adap->setclk(1);
    adap->setdat(1);
    adap->setmode(1);
    udelay(adap->mode);

    adap->setmode(0);
    udelay(adap->mode_setup);
    sendbyte(adap, addr);
    udelay(adap->mode_hold);

    sendbytes(adap, data, len);

    adap->setclk(1);
    adap->setdat(1);
    adap->setmode(0);

    return len;
}

至于什么是ALSA框架,以及里面更加详细内容,这里不再赘述,请参考这里

猜你喜欢

转载自blog.csdn.net/lugandong/article/details/50008587