怪自己菜的真实,全靠百度大佬的文章解决问题。。。
目录
1.alsa框架,数字音频接口
2.平台移植过程
3.调试小工具
alsa框架:可以百度文章 Linux ALSA声卡驱动之XX
数字音频接口的解释:数字音频接口
平台:imx6q ,设备树:imx6qdl-sabresd.dtsi ,内核版本:4.1.15
设备数的配置如下:
sound_codecName {
compatible = "fsl,imx6q-sabresd-codecName",
"fsl,imx-audio-codecName";
model = "codecName-audio";
cpu-dai = <&ssi3>;
audio-codec = <&codec_codecName>;
mux-int-port = <7>;
mux-ext-port = <4>;
};
codec_codecName: codecName@0c{
compatible = "vendor,codecName";
reg = <0x0c>;
};
pinctrl_audmux: audmuxgrp {
fsl,pins = <
MX6QDL_PAD_SD2_DAT0__AUD4_RXD 0x130b0
MX6QDL_PAD_SD2_DAT3__AUD4_TXC 0x130b0
MX6QDL_PAD_SD2_DAT2__AUD4_TXD 0x110b0
MX6QDL_PAD_SD2_DAT1__AUD4_TXFS 0x130b0
>;
};
&audmux {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_audmux>;
status = "okay";
};
&ssi3 {
status = "okay";
};
mux-int-port = <7>;
//mux-int-port 表示内部ssi接口ssi1,ssi2,ssi3, 分别对应 mux-int-port = <1>,mux-int-port = <2>,mux-int-port = <7>,
mux-ext-port = <4>;
//mux-ext-port 表示外部AUD接口,有 AUD3,AUD4,AUD5,AUD6,分别对应mux-ext-port = <3>,mux-ext-port = <4>,mux-ext-port = <5>,mux-ext-port = <6>,
The Synchronous Serial Interface (SSI) can be connected directly to the external pins or
through the Digital Audio Multiplexer (AUDMUX).
附上大佬链接:sgtl5000音频调试
AUD的引脚定义在 imx6q-pinfunc.h 文件中
可以看见有RXFS、RXC、TXC、TXD、TXFS、RXD,六个管脚,但是网上一般的都只用四个,TXC、TXD、TXFS、RXD。
大佬解释如下:TXC和TXFS,这2个脚,即可以输入也可以是输出。 codec做master时,这2个脚是输入。接受来自codec的BCLK和LRCLK。
ps:大佬建议:“ 在soc_core.c提供的函数接口,然后进到各个函数里看一下。platform driver和codec driver,都要给soc_core.c提供接口操作方式。 而machine drvier,除了做好dai_link之外,就是用soc_core.c里的linux 给出来的函数,操作codec和cpu端硬件。所以说,porting的任务,就是自己写一个machine driver. 用soc_core.c里的标准API做。因为codec driver和platform driver已经通过接口操作函数指针,把它们自己挂在了 soc_core.c的标准函数接口上了。codec大家不一样,platform大家不一样,但是soc_core.c的函数都一样, 不同厂家的codec和cpu i2s,都往soc_core.c里挂,就好了。porting的工程师,只要调用linux soc_core.c的API写自己的machine driver(声卡驱动,其实就是它)所以,把思路梳理好,反复梳理几次,对CPU端再读一下代码,说不定每个概念都很清楚了。”
读取int、ext端口的代码如下:这是配置成codec为主,cpu为从模式的。如果配置成cpu为主,codec为从模式,端口int与ext需要对调。链接:wm8960调试记录
static int imx_codecName_probe(struct platform_device *pdev)
{
......
struct device_node *np = pdev->dev.of_node;
struct device_node *cpu_np, *codec_np = NULL;
int int_port, ext_port;
int ret;
cpu_np = of_parse_phandle(pdev->dev.of_node, "cpu-dai", 0);
if (!cpu_np) {
dev_err(&pdev->dev, "cpu dai phandle missing or invalid\n");
ret = -EINVAL;
goto fail;
}
if (!strstr(cpu_np->name, "ssi"))
goto audmux_bypass;
ret = of_property_read_u32(np, "mux-int-port", &int_port);
if (ret) {
dev_err(&pdev->dev, "mux-int-port missing or invalid\n");
goto fail;
}
ret = of_property_read_u32(np, "mux-ext-port", &ext_port);
if (ret) {
dev_err(&pdev->dev, "mux-ext-port missing or invalid\n");
goto fail;
}
/*
* The port numbering in the hardware manual starts at 1, while
* the audmux API expects it starts at 0.
*/
int_port--;
ext_port--;
ret = imx_audmux_v2_configure_port(int_port,
IMX_AUDMUX_V2_PTCR_SYN |
IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) |
IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) |
IMX_AUDMUX_V2_PTCR_TFSDIR |
IMX_AUDMUX_V2_PTCR_TCLKDIR,
IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port));
if (ret) {
dev_err(&pdev->dev, "audmux internal port setup failed\n");
goto fail;
}
ret = imx_audmux_v2_configure_port(ext_port,
IMX_AUDMUX_V2_PTCR_SYN,
IMX_AUDMUX_V2_PDCR_RXDSEL(int_port));
if (ret) {
dev_err(&pdev->dev, "audmux external port setup failed\n");
goto fail;
}
audmux_bypass:
codec_np = of_parse_phandle(pdev->dev.of_node, "audio-codec", 0);
if (!codec_np) {
dev_err(&pdev->dev, "phandle missing or invalid\n");
ret = -EINVAL;
goto fail;
}
......
}
接下来的参考文章:imx6q ssi1 在slave模式和dsp_a格式下,aplay时DOUT脚没有输出
imx6q audio pcm format
做出以下修改:
static int imx_hifi_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
......
snd_pcm_format_t sample_format = params_format(params);
unsigned int channels = params_channels(params);
unsigned int sample_size = params_width(params);
unsigned int rate = params_rate(params);
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_card *card = rtd->card;
struct device *dev = card->dev;
unsigned int bclk = 0;
int ret = 0;
......
bclk = rate * 32;
/*设置cpu 端的 sysclk时钟,根据自身code决定,一般为channels * sample_size * rate*/
snd_soc_dai_set_sysclk(cpu_dai, 0, bclk, SND_SOC_CLOCK_OUT);
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBS_CFS);
/*TDM模式的设置*/
ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, 0, 2, params_width(params));
if (ret) {
dev_err(dev, "failed to set cpu dai tdm slot: %d\n", ret);
return ret;
}
dev_err(dev, "sample_format:%d sample_size:%d channels:%d rate:%d\n",
sample_format, sample_size, channels, rate);
......
return 0;
}
/*需要在struct snd_soc_dai_link dai; 填充这个结构体*/
static struct snd_soc_ops imx_hifi_ops = {
.hw_params = imx_hifi_hw_params,
.hw_free = imx_hifi_hw_free,
};
cpu端的ssi驱动文件为:fsl_ssi.c 可以通过设备树的imx6qdl.dtsi知道
修改fsl_ssi.c文件如下:
修改完成后编译烧录内核,播放音频文件即可在示波器上看见对应的波形。
另外看见一篇好的文章:How to change audio clock from 24M to 24.576M 讲的是如何修改 MCLK。
我的修改如下:在dtsi中配置
codec_codecName: codecName@1a {
compatible = "nuvoton,codecName";
reg = <0x1a>;
//clocks = <&clks IMX6QDL_CLK_CKO>;
clocks = <&clks 201>;
};
&iomuxc {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_hog>;
......
pinctrl_hog: hoggrp {
fsl,pins = <
......
/*CCM_CLKO1 为我的 MCLK 时钟提供脚*/
MX6QDL_PAD_GPIO_0__CCM_CLKO1 0x130b0
......
>;
};
......
};
在 clk-imx6q.c 中修改文件/* 文件在 kernel/arch/arm/mach-imx 中*/
以上修改输出的时钟为24.576Mhz,如果将时钟改为180633600 ,那么输出的时钟为 22.5792Mhz
唉,看不懂下图。
alsa接口文件说明:ALSA project - the C library reference
linux播放声音的工具:aplay(播放) arecord(录音)amxier(设置codec属性命令)
I2C调试工具:
i2cdetect -l //查看i2c控制器
i2cdetect -y -r 2 //查看i2c-2总线上的i2c设备,其实在设备树中挂了i2c,在这里就会显示。
i2cdump -f -y 2 0x1a //查看i2c-2总线上的0x1a设备上的寄存器的值
遇到如下情况,需要检查硬件了
i2cset -f -y 1 0x20 0x77 0x3f (设置i2c-1上0x20器件的0x77寄存器值为0x3f)
i2cget -f -y 1 0x20 0x77 (读取i2c-1上0x20器件的0x77寄存器值)
原文链接:对香草冰淇淋过敏的汽车
这是一个发生在美国通用汽车的客户与该公司客服部间的真实故事。
有一天美国通用汽车公司的庞帝雅克(Pontiac)部门收到一封客户抱怨信,上面是这样写的:
这是我为了同一件事第二次写信给你,我不会怪你们为什么没有回信给我,因为我也觉得这样别人会认为我疯了,但这的确是一个事实。
我们家有一个传统的习惯,就是我们每天在吃完晚餐后,都会以冰淇淋来当我们的饭后甜点。由于冰淇淋的口味很多,所以我们家每天在饭后才投票决定要吃哪一种口味,等大家决定后我就会开车去买。但自从最近我买了一部新的庞帝雅克后,在我去买冰淇淋的这段路程问题就发生了。
你知道吗?每当我买的冰淇淋是香草口味时,我从店里出来后车子就发不动。但如果我买的是其它的口味,车子发动就顺得很。我要让你知道,我对这件事情是非常认真的,尽管这个问题听起来很猪头。但为什么这部庞帝雅克当我买了香草冰淇淋它就秀逗,而我不管什么时候买其它口味的冰淇淋,它就是一尾活龙?为什么?为什么?
这封不可思议的投诉信立即引来了庞帝雅克车技术服务人员的调侃:“汽车对香草冰淇淋过敏?那我的游艇对可口可乐也要过敏了。”“找错了对象,他应该去看看心理医生。”“查查地址,这封投诉信可能是从疯人院里寄来的。”……虽然对这封信的真实性心存怀疑,但庞帝雅克的总经理没有马虎对待这近乎天方夜谭的投诉,他派了一位办事严谨的工程师前去处理这件不可思议的投诉案。
当工程师去找这位仁兄时,很惊讶的发现这封信是出之于一位事业成功、乐观且受了高等教育的人。工程师安排与这位仁兄的见面时间刚好是在用完晚餐的时间,两人于是上了汽车,往冰淇淋店开去。那个晚上投票结果是香草口味,当买好香草冰淇淋回到车上后,车子又秀逗了。这位工程师之后又连续来了三个晚上。第一晚,巧克力冰淇淋,车子没事。第二晚,草莓冰淇淋,车子也没事。第三晚,香草冰淇淋,车子“秀逗”。看来,投诉者反映的“荒唐”问题一点儿也不荒唐,庞帝雅克汽车确实时香草冰淇淋“过敏”。
这位思考有逻辑的工程师,到这时还是死不相信这位仁兄的车子对香草“过敏”。因此,他仍然不放弃继续安排相同的行程,希望能够将这个神秘的问题解决。工程师开始记下从开始到现在所发生的种种详细资料,如汽车经过的路线、使用汽油的种类、开出和开回以及停车所用的时间……根据记录的资料,他得出了一个结论:这位仁兄买香草冰淇淋所花的时间比其它口味的要少。
为什么呢?一丝不苟的工程师仔细研究了其中的一些细节问题后,终于发现了汽车对香草冰淇淋“过敏”的奥秘:这家冰淇淋店的内部设置是整个问题的关键。因为,香草冰淇淋是所有冰淇淋口味中最畅销的口味,店家为了让顾客每次都能很快的取拿,将香草口味特别分开陈列在单独的冰柜,并将冰柜放置在店的前端;至于其它口味则放置在距离收银台较远的后端。
现在,工程师所要知道的疑问是,为什么这部车会因为从熄火到重新激活的时间较短时就会“秀逗”?具有深厚专业知识的工程师很快找出了答案:问题出在汽车发动机那个小小的散热装置上。原来,当买其他口味的冰淇淋时,由于所花的时间较长,发动机有足够的时间散热,重新发动时就没有太大的问题;但是当买香草冰淇淋时,由于所花的时间较短发动机太热以至于还无法让散热装置有足够的时间散热,在密闭管路中油跟油之间会出现一段气体,阻塞了油路,人们通常把它叫做“气阻”,引擎所吸收的燃料就会断断续续,从而引起发动机不能正常发动,汽车当然就时香草冰淇淋“过敏”了。原来这都是发动机散热不畅惹的祸。
这位工程师向公司反映了汽车对香草冰淇淋“过敏”的原因,设计部门迅速进行了技术改进,弥补了散热装置的缺陷,解决了气阻现象。不久,随时能重新发动引擎的散热装置应运而生。从此,庞帝雅克车对香草冰淇淋再也不“过敏”了,这个看似荒唐的投诉案得到了圆满的解决。
希望养成能够独立看内核代码的能力,拥有解决对香草冰淇淋过敏的汽车问题的能力。毕竟面试官说过C语言是最简单的语言,而且代码里什么都有。
遇到的错误都在自己立的flag上,难受。