Openwrt开发之声卡RT5670挂载

时间:2019-06-28 18:12:11   收藏:0   阅读:198

 

框架:

mt7688I2s做从机,codecRT5670做主机;mainclock由mt7688输出12MHz,测试音频为48K采样率。

设备树配置如下:

/dts-v1/;

#include "mt7628an.dtsi"

/ {
    compatible = "mediatek,mt7628an-eval-board", "mediatek,mt7628an-soc";
    model = "Mediatek MT7628AN evaluation board";
    chosen {
            bootargs = "console=ttyS0,115200";
    };
    memory@0 {
        device_type = "memory";
        reg = <0x0 0x2000000>;
    };
    
    sound {
        compatible = "simple-audio-card";
        simple-audio-card,name = "Audio-I2S";
        simple-audio-card,format = "i2s";
        simple-audio-card,bitclock-master = <&dailink0_master>;
        simple-audio-card,frame-master = <&dailink0_master>;
        simple-audio-card,widgets =
            "Headphone", "Headphones",
            "Speaker", "Speakers",
            "Microphone", "Microphones";
         simple-audio-card,routing =
            "Speakers", "LOUTL",
            "Speakers", "LOUTR",
            "Microphones", "MICBIAS2",
            "IN2P", "Microphones";
            
        simple-audio-card,mclk-fs = <512>; //codec做主,主控供给编解码芯片用的时钟 512FS

        simple-audio-card,cpu {
            sound-dai = <&i2s>;
        };

        dailink0_master: simple-audio-card,codec {
            sound-dai = <&codec>;
        };
    };    
};

&pinctrl {
    state_default: pinctrl0 {
        gpio {
            ralink,group = "gpio";
            ralink,function = "gpio";
        };
    };
};

&gpio1 {
    status = "okay";
};

&wmac {
    status = "okay";
    ralink,mtd-eeprom = <&factory 0x4>;
};

&ethernet {
    mtd-mac-address = <&factory 0x28>;
};

&gdma {
    status = "okay";
};
&i2c {
    status = "okay";
    codec: rt5670@1c {
        #sound-dai-cells = <0>;
        compatible = "ralink,rt5670";
        reg = <0x1c>;
        ralink,shared-lrclk;
    };    
    
};

&i2s {
    #sound-dai-cells = <0>;
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&i2s_pins>, <&refclk_pins>;
};


&spi0 {
    status = "okay";

    m25p80@0 {
        #address-cells = <1>;
        #size-cells = <1>;
        compatible = "jedec,spi-nor";
        reg = <0>;
        spi-max-frequency = <10000000>;
        m25p,chunked-io = <32>;

        partition@0 {
            label = "u-boot";
            reg = <0x0 0x30000>;
            read-only;
        };

        partition@30000 {
            label = "u-boot-env";
            reg = <0x30000 0x10000>;
            read-only;
        };

        factory: partition@40000 {
            label = "factory";
            reg = <0x40000 0x10000>;
            read-only;
        };

        partition@50000 {
            label = "firmware";
            reg = <0x50000 0x7b0000>;
        };
    };
};

audio平台使用 simple-audio-card,而非platform-machine-codec的架构自己重构个machine。

调试主要关注以下几个函数即可:

simple-card.c文件下的asoc_simple_card_hw_params

static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream,
                      struct snd_pcm_hw_params *params)
{
    struct snd_soc_pcm_runtime *rtd = substream->private_data;
    struct snd_soc_dai *codec_dai = rtd->codec_dai;
    struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
    struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
    struct simple_dai_props *dai_props =
        simple_priv_to_props(priv, rtd->num);
    unsigned int mclk, mclk_fs = 0;
    int ret = 0;

    if (priv->mclk_fs)
        mclk_fs = priv->mclk_fs;
    else if (dai_props->mclk_fs)
        mclk_fs = dai_props->mclk_fs;

    if (mclk_fs) {
        mclk = params_rate(params) * mclk_fs;
        ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
                         SND_SOC_CLOCK_IN);
        if (ret && ret != -ENOTSUPP)
            goto err;

        ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
                         SND_SOC_CLOCK_OUT);
        if (ret && ret != -ENOTSUPP)
            goto err;
    }
    return 0;
err:
    return ret;
}

该函数在播放或者录音的时候会被调用,会分别设置codec的I2S时钟,CPU Dai的时钟,主要是设置时钟源(外部mainclock、PLL output clock、internel clock),比如设置codec的时钟,最终会调用如下函数:

static int rt5670_set_sysclk(struct snd_soc_codec *codec,
                 int clk_id, int source, unsigned int freq, int dir)
{
    struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
    unsigned int reg_val = 0;

    if (freq == rt5670->sysclk && clk_id == rt5670->sysclk_src)
        return 0;

    switch (clk_id) {
    case RT5670_SCLK_S_MCLK:
        reg_val |= RT5670_SCLK_SRC_MCLK;
        break;
    case RT5670_SCLK_S_PLL1:
        reg_val |= RT5670_SCLK_SRC_PLL1;
        break;
    case RT5670_SCLK_S_RCCLK:
        reg_val |= RT5670_SCLK_SRC_RCCLK;
        break;
    default:
        dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id);
        return -EINVAL;
    }
    snd_soc_update_bits(codec, RT5670_GLB_CLK,
        RT5670_SCLK_SRC_MASK, reg_val);
    rt5670->sysclk = freq;
    rt5670->sysclk_src = clk_id;

    dev_dbg(codec->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id);

    return 0;

}

以上这个函数就是设置rt5670的i2ssysclk的时钟源,simple audio card默认的是设置外部mainclock为I2Ssysclk的时钟源,关于RT5670的i2s_sysclk时钟源的路径可以参考下图:

技术图片

 

 理论上只要CLK_sys可以分频出256FS的频率,codec就可以正常录音播放,但是调试的时候却发现几个问题:

1.如果采用如下路径配置CLK_sys时钟,codec不能正常录音播放

技术图片

推断可能是时钟不符合256FS,所以不能正常播放;

2.采用PLL分频出CLK_sys,设备树里面设置 simple-audio-card,mclk-fs = <256>; 按如下路径设置CLK_sys

 

技术图片

 

 12M 经过PLL后输出12.288M,MX73[14:12]不分频,输出刚好符合256FS,但是播放录音均不正常

3. 采用PLL分频出CLK_sys,设备树里面设置 simple-audio-card,mclk-fs = <512>; 按如下路径设置CLK_sys

技术图片

 

 12M 经过PLL后输出24.576M,MX73[14:12]2分频,输出为12.288M刚好符合256FS,播放录音正常;

以上3中配置情况测量lrclk Bclk mainclk 均为 48K  3M  12M左右;

经过以上3个系统时钟的配置测试,得出rt5670时钟的奇葩路径配置:

先由simple audio card端设置codec的sysclk时钟源为外部mainclok(即上面的 asoc_simple_card_hw_params 函数),然后调用codec的 rt5670_hw_params 将 pll的时钟源设置为mainclock,然后再倍频至24.576M,然后再通过MX73[14:12]分频至12.288M:

static int rt5670_hw_params(struct snd_pcm_substream *substream,
    struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
    struct snd_soc_codec *codec = dai->codec;
    struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
    unsigned int val_len = 0, val_clk, mask_clk;
    int pre_div, bclk_ms, frame_size;

    if (RT5670_AIF2 == dai->id) {
        snd_soc_update_bits(codec, RT5670_GPIO_CTRL1,
            RT5670_I2S2_PIN_MASK, RT5670_I2S2_PIN_I2S);
    }
    rt5670->lrck[dai->id] = params_rate(params);
    pre_div = rl6231_get_clk_info(rt5670->sysclk, rt5670->lrck[dai->id]);
    if (pre_div < 0) {
        dev_err(codec->dev, "Unsupported clock setting\n");
        return -EINVAL;
    }
    frame_size = snd_soc_params_to_frame_size(params);
    if (frame_size < 0) {
        dev_err(codec->dev, "Unsupported frame size: %d\n", frame_size);
        return -EINVAL;
    }
    bclk_ms = frame_size > 32;
    rt5670->bclk[dai->id] = rt5670->lrck[dai->id] * (32 << bclk_ms);

    dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n",
        rt5670->bclk[dai->id], rt5670->lrck[dai->id]);
    dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n",
                bclk_ms, pre_div, dai->id);

    switch (params_width(params)) {
    case 16:
        break;
    case 20:
        val_len |= RT5670_I2S_DL_20;
        break;
    case 24:
        val_len |= RT5670_I2S_DL_24;
        break;
    case 8:
        val_len |= RT5670_I2S_DL_8;
        break;
    default:
        return -EINVAL;
    }

    switch (dai->id) {
    case RT5670_AIF1:
         mask_clk = RT5670_I2S_BCLK_MS1_MASK | RT5670_I2S_PD1_MASK;
        val_clk = bclk_ms << RT5670_I2S_BCLK_MS1_SFT |
            pre_div << RT5670_I2S_PD1_SFT;
        snd_soc_update_bits(codec, RT5670_I2S1_SDP,
            RT5670_I2S_DL_MASK, val_len);
        snd_soc_update_bits(codec, RT5670_ADDA_CLK1, mask_clk, val_clk);  // 分频,确保i2s sysclk为 256FS
        break;
    case RT5670_AIF2:
        mask_clk = RT5670_I2S_BCLK_MS2_MASK | RT5670_I2S_PD2_MASK;
        val_clk = bclk_ms << RT5670_I2S_BCLK_MS2_SFT |
            pre_div << RT5670_I2S_PD2_SFT;
        snd_soc_update_bits(codec, RT5670_I2S2_SDP,
            RT5670_I2S_DL_MASK, val_len);
        snd_soc_update_bits(codec, RT5670_ADDA_CLK1, mask_clk, val_clk);
        break;
    }
    struct snd_soc_pcm_runtime *p = (struct snd_soc_pcm_runtime *)substream->private_data;
    struct snd_soc_dai *cpu_dai = p->cpu_dai;

    snd_soc_dai_set_pll(dai, dai->id,RT5670_PLL1_S_MCLK, 12000000, params_rate(params)*512);        // set pll source use outside mainclock and set pll output 24.576M
    snd_soc_dai_set_sysclk(dai, RT5670_SCLK_S_PLL1, params_rate(params)*512, SND_SOC_CLOCK_IN);     // set sysclk source use pll output, 
    if(cpu_dai)
    {
         printk("CPU DAI\r\n");
         snd_soc_dai_set_sysclk(cpu_dai, RT5670_SCLK_S_MCLK, 12000000, SND_SOC_CLOCK_OUT);
     }        

     return 0;
}

 

评论(0
© 2014 mamicode.com 版权所有 京ICP备13008772号-2  联系我们:gaon5@hotmail.com
迷上了代码!