码迷,mamicode.com
首页 > 其他好文 > 详细

UDA1341 SSI音频驱动

时间:2015-07-23 17:40:00      阅读:142      评论:0      收藏:0      [点我收藏+]

标签:音频-uda134x

SSI音频驱动

音频驱动的文章网上有很多,分析的也很具体,这里只记录本人在调试音频驱动的过程和理解。调试一个驱动,最主要的还是从本质上去理解它的工作原理,包括时钟,数据,中断,寄存器配置等。或许并不需要全部知道,但是追根到底会对以后的驱动有很大帮助。

音频驱动

linux音频驱动的结构我个人感觉做得很漂亮,有面向对象编程的人不难看出这个结构与类图很想,将变化的东西封装了起来,层次清晰,方便理解和调试:

cpu 驱动:即总线驱动,如I2S,SSI,AC97,ESAI,SPDIF等。封装了对总线的配置,如主从模式的设置。
codec驱动:编码器驱动,对应的是解码芯片,封装了对解码芯片的操作,包括设置寄存器等。
machine驱动:将cpu驱动和codec驱动糅合起来,有人把machine驱动比作插座,我觉得很形象,machine驱动就是将对应的cpu和codec插到板子上。

1、board文件

在板级文件中添加音频支持。
static struct platform_device mxc_uda134x_device = {
    .name = "imx-3stack-uda134x",
};
static struct mxc_audio_platform_data uda134x_data = {
    .ssi_num = 1,
    .src_port = 2,
    .ext_port = 5,
    .init = mxc_uda134x_init,
};
static int mxc_uda134x_init(void)
{
    struct clk *ssi_ext1;
    struct clk *ssi_sys_clk;

    u32 rate;

    ssi_ext1 = clk_get(NULL, "ssi_ext1_clk");
    if (IS_ERR(ssi_ext1))
            return -1;
    rate = clk_round_rate(ssi_ext1, 3000000);
    if (rate < 2000000 || rate > 24000000) {
            printk(KERN_ERR "Error: UDA134x mclk freq %d out of range!\n",
                   rate);
            clk_put(ssi_ext1);
            return -1;
    }
    clk_set_rate(ssi_ext1, rate);
    clk_enable(ssi_ext1);

    uda134x_data.sysclk = rate;

    return 0;
}
这里的ssi_num对应的是SSI的通道号,SSI总线功能功能配置
    MX53_PAD_KEY_COL0__AUDMUX_AUD5_TXC,
    MX53_PAD_KEY_ROW0__AUDMUX_AUD5_TXD,
    MX53_PAD_KEY_COL1__AUDMUX_AUD5_TXFS,
    MX53_PAD_KEY_ROW1__AUDMUX_AUD5_RXD,

AUDMUX5是指AUDMUX的外部端口(即与codec相连的端口)号,也就是ext_num,而src_num是指内部端口(即与cpu相连的端口)号。端口的连接规则可以从芯片手册上查到。很奇怪,系统时钟设置为3M就可以正常播放,4M就没有声音了,然后20M又有声音,但是噪音很大,有知道原因的还请告知。
连接示意图如下:
技术分享

2、时钟:

SSI的时钟示意图:
技术分享

可以看到SSI_EXT1_CLK可以由pll3,pll2,ssi_lp_apm_clk产生,而与它相关的寄存器是CCM_CCGR3。时钟的配置并不需要我们处理,我们只需要了解就好。SSI的配置如下

    _REGISTER_CLOCK("mxc_ssi.0", NULL, ssi1_clk[0]),
    _REGISTER_CLOCK("mxc_ssi.1", NULL, ssi2_clk[0]),
    _REGISTER_CLOCK("mxc_ssi.2", NULL, ssi3_clk[0]),
    _REGISTER_CLOCK(NULL, "ssi_ext1_clk", ssi_ext1_clk),
    _REGISTER_CLOCK(NULL, "ssi_ext2_clk", ssi_ext2_clk),

这里的_REGISTER_CLOCK宏我没太看懂它前两个参数的意思。如果有知道的麻烦告诉我。

3、machine驱动

machine驱动大部分都可以参考其他的音频驱动,因为主体基本都一样,而uda1341有一点点的区别,它有一个L3总线,L3总线驱动linux已经有了,我们需要做的只是指定对应的引脚(可以参考s3c24xx_uda1341.c)。
需要特别指出的是2个函数:

1、imx_3stack_init_dam

static void imx_3stack_init_dam(int ssi_port, int dai_port)
{
    unsigned int ssi_ptcr = 0;
    unsigned int dai_ptcr = 0;
    unsigned int ssi_pdcr = 0;
    unsigned int dai_pdcr = 0;
    /* UDA134X uses SSI1 via AUDMUX port dai_port for audio */

    /* reset port ssi_port & dai_port */
    __raw_writel(0, DAM_PTCR(ssi_port));
    __raw_writel(0, DAM_PTCR(dai_port));
    __raw_writel(0, DAM_PDCR(ssi_port));
    __raw_writel(0, DAM_PDCR(dai_port));

    /* set to synchronous */
    ssi_ptcr |= AUDMUX_PTCR_SYN;
    dai_ptcr |= AUDMUX_PTCR_SYN;

    /* set Rx sources ssi_port <--> dai_port */
    ssi_pdcr |= AUDMUX_PDCR_RXDSEL(dai_port);
    dai_pdcr |= AUDMUX_PDCR_RXDSEL(ssi_port);

    /* set Tx frame direction and source  dai_port --> ssi_port output */
    dai_ptcr |= AUDMUX_PTCR_TFSDIR;
    dai_ptcr |= AUDMUX_PTCR_TFSSEL(AUDMUX_FROM_TXFS, ssi_port);

    /* set Tx Clock direction and source dai_port--> ssi_port output */
    dai_ptcr |= AUDMUX_PTCR_TCLKDIR;
    dai_ptcr |= AUDMUX_PTCR_TCSEL(AUDMUX_FROM_TXFS, ssi_port);

    __raw_writel(ssi_ptcr, DAM_PTCR(ssi_port));
    __raw_writel(dai_ptcr, DAM_PTCR(dai_port));
    __raw_writel(ssi_pdcr, DAM_PDCR(ssi_port));
    __raw_writel(dai_pdcr, DAM_PDCR(dai_port));
}

SSI总线是兼容I2S的,所以也需要设定主从模式。而uda1341只能工作在从模式,所以我们需要将SSI设置成主模式。上面的函数其实是在配置AUDMUX的数据流方向。
The default port-to-port connections are as follows:
? Port 1 to Port 6
? Port 6 provides the clock and frame sync.
? Port 2 to Port 5
? Port 5 provides the clock and frame sync.
? Port 3 to Port 4
? Port 4 provides the clock and frame sync.
? Port 7 to Port 7 (in data loopback mode)
? Clock and frame syncs are inputs.
从文档中我们看到BCLK和frame sync等信号都是由port5提供,也就是说我们需要将port5配置成输出模式,而port2配置成输入模式。就是配置PTCR寄存器。

2、imx_3stack_audio_hw_params


static int imx_3stack_audio_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_link *machine = rtd->dai;
    struct snd_soc_dai *cpu_dai = machine->cpu_dai;
    struct snd_soc_dai *codec_dai = machine->codec_dai;
    unsigned int rate = params_rate(params);
    struct imx_ssi *ssi_mode = (struct imx_ssi *)cpu_dai->private_data;
    int ret = 0;

    u32 dai_format;

    int bits;
    int channels = params_channels(params);

    dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
        SND_SOC_DAIFMT_CBS_CFS;

    ssi_mode->sync_mode = 1;
    ssi_mode->network_mode = 1;

    /* set cpu DAI configuration */
    ret = snd_soc_dai_set_fmt(cpu_dai, dai_format);
    if (ret < 0)
        return ret;

    /* set i.MX active slot mask */
    snd_soc_dai_set_tdm_slot(cpu_dai,
                 channels == 1 ? 0xfffffffe : 0xfffffffc,
                 channels == 1 ? 0xfffffffe : 0xfffffffc,
                 2, 32);


    /* set the SSI clock div  */
//  snd_soc_dai_set_clkdiv(cpu_dai, IMX_SSI_TX_DIV_2, 0);
//  snd_soc_dai_set_clkdiv(cpu_dai, IMX_SSI_TX_DIV_PSR, 0);
//  snd_soc_dai_set_clkdiv(cpu_dai, IMX_SSI_TX_DIV_PM, 23);
//  snd_soc_dai_set_clkdiv(cpu_dai, IMX_SSI_RX_DIV_2, 0);
//  snd_soc_dai_set_clkdiv(cpu_dai, IMX_SSI_RX_DIV_PSR, 0);
//  snd_soc_dai_set_clkdiv(cpu_dai, IMX_SSI_RX_DIV_PM, 23);

    /* set the SSI system clock as output  */
    snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0, SND_SOC_CLOCK_OUT);

    /* set codec DAI configuration */
    snd_soc_dai_set_fmt(codec_dai, dai_format);

    /* Set codec clock*/
    snd_soc_dai_set_sysclk(codec_dai, UDA134X_SYSCLK,rate*384, SND_SOC_CLOCK_OUT);

    return 0;
}
这个函数的功能主要是在播放之前配置SSI和CODEC,要理解它们的含义需要结合imx_ssi.c和uda1341.c。首先SSI部分配置模式,SND_SOC_DAIFMT_CBS_CFS在imx_ssi.c中其实是配置成主模式。
然后是codec的系统时钟,需要查看手册,uda1341的系统时钟是256fs、384fs、512fs。fs表示采样率。

这个音频驱动断断续续搞了许久,要完全弄懂需要结合手册一点点的看。不过最后发现其实只是移植的话还是很简单的,只要注意几个地方就够了。
1、板级文件中音频结构体,包括端口的设置,ssi的编号。codec系统时钟的设置。
2、AUDMUX端口功能的配置。注意数据流的方向,以及主从模式的区别。
3、hw_param函数中dai的配置。需要结合cpu_dai和codec_dai源码来对照。

版权声明:本文为博主原创文章,未经博主允许不得转载。

UDA1341 SSI音频驱动

标签:音频-uda134x

原文地址:http://blog.csdn.net/sddsighhz/article/details/46931161

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!