UDA1341 SSI音频驱动

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源码来对照。

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

时间: 2024-10-08 14:58:43

UDA1341 SSI音频驱动的相关文章

基于Linux ALSA音频驱动的wav文件解析及播放程序 2012

本设计思路:先打开一个普通wav音频文件,从定义的文件头前面的44个字节中,取出文件头的定义消息,置于一个文件头的结构体中.然后打开alsa音频驱动,从文件头结构体取出采样精度,声道数,采样频率三个重要参数,利用alsa音频驱动的API设置好参数,最后打开wav文件,定位到数据区,把音频数据依次写到音频驱动中去,开始播放,当写入完成后,退出写入的循环. 注意:本设计需要alsa的libasound-dev的库,编译链接时需要连接 —lasound. #include<stdio.h>#incl

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

SylixOS音频驱动移植

1. 适用范围 本文档为实现Nuc970平台音频驱动的方法总结,以此提供一些SylixOS音频驱动移植方法的参考. 2. 原理概述 2.1 Codec编解码芯片 声音信号分为模拟信号和数字信号,Codec编解码芯片主要功能就是实现模拟信号与数字信号的互相转换. 本文调试的Codec型号为NAU8822L,其结构如图 2-1所示. 图 2-1 NAU8822L编解码芯片 其中主要使用到的是以下三个部分: Mixer  混音器设备,它的作用是将多个信号组合或者叠加在一起. 由输入混音器(input

嵌入式驱动开发之---Linux ALSA音频驱动(一)

本文的部分内容参考来自DroidPhone的博客(http://blog.csdn.net/droidphone/article/details/6271122),关于ALSA写得很不错的文章,只是少了实例.本文就是结合实例来分析ALSA音频驱动. 开发环境:ubuntu10.04 目标板:linux-2.6.37 (通过命令uname -r 查看linux内核版信息) 编译器:arm-none-linux-gnueabi- (none 代表编译器的制作者,比如:fsl代表飞思卡尔,内核里面谈E

S3C2416裸机开发系列十八_音频驱动实现(1)

S3C2416裸机开发系列十八 音频驱动实现(1) 象棋小子    1048272975 在消费电子产品中,往往都会用到音频系统来播放音乐.进行通话等多媒体应用,此外,对于一些需语音提示的产品,音频部分都是不可或缺的功能.笔者此处就s3c2416的音频驱动实现作一个简单的介绍. 1. IIS音频总线 s3c2416支持IIS.PCM.AC97这三种音频接口,此处只分析IIS音频接口.IIS接口(Inter-IC Sound)在20世纪80年代首先被飞利浦公司用于消费音频,为数字音频设备之间的音频

Linux音频驱动简述

一.数字音频 音频信号是一种连续变化的模拟信号,但计算机只能处理和记录二进制的数字信号,由自然音源得到的音频信号必须经过一定的变换,成为数字音频信号之后,才能送到计算机中作进一步的处理. 数字音频系统通过将声波的波型转换成一系列二进制数据,来实现对原始声音的重现,实现这一步骤的设备常被称为模/数转换器(A/D).A/D转换器以每秒钟上万次的速率对声波进行采样,每个采样点都记录下了原始模拟声波在某一时刻的状态,通常称之为样本(sample),而每一秒钟所采样的数目则称为采样频率,通过将一串连续的样

alsa音频驱动科普第一课

做linux音频编程对alsa应该不陌生. 但是对于刚接触这块技术的同学来说是一件困难的事情.原因在于:网上关于alsa的资料太少了,特别国内的资料更是大部分重复.对于初学者来说特别苦恼. 由于笔者经历过一步步摸索的痛苦过程,现在回想起来有些问题当初可以避免的.因此把问题解决方法和经验教训写出来,引以为戒. 写一系列的技术贴与网友们分享. 录音 arecord -D hw:2,0 -f S16_LE -r 44100 -c 2 /root/record.wav 查看音频设备命令arecord:加

[TM4C123单片机实践] 配置SSI并驱动DAC7811显示正弦波

这几天做电赛学习了TM4C123 单片机, 总得来说, 结合官方例程与参考手册, 加上一个好的示波器, 效率会高很多. TI的SSI 实际上就是SPI. 我门先熟悉一下SPI SPI ,就是在主机与从机之间用来传输数据的 通过TX, RX传输数据, 通过CS 片选信号线激活主机与从机的通信, 通过CLK 时钟信号控制频率 #include <stdbool.h> #include <stdint.h> #include "inc/hw_memmap.h" #in

卸载mac多余的音频驱动:internal audio driver corel painter

$ kextstat | grep corel 130 0 0xffffff7f81042000 0x4000 0x4000 com.corel.painter.PainterAudioDriver (1) EE421600-684A-3D19-BA3A-FB205534621D <100 5 4 3> $ sudo kextunload -b com.corel.painter.PainterAudioDriver Password: $ sudo rm -r /System/Library