天天看點

imx6q ssi1 配置成CPU為主,codec PCM從模式 SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS

怪自己菜的真實,全靠百度大佬的文章解決問題。。。

目錄

1.alsa架構,數字音頻接口

2.平台移植過程

3.調試小工具

1.alsa架構,數字音頻接口

alsa架構:可以百度文章  Linux ALSA聲霸卡驅動之XX

數字音頻接口的解釋:數字音頻接口

2.平台移植過程

平台: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: [email protected]{
        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).

imx6q ssi1 配置成CPU為主,codec PCM從模式 SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS
imx6q ssi1 配置成CPU為主,codec PCM從模式 SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS

附上大佬連結:sgtl5000音頻調試

AUD的引腳定義在 imx6q-pinfunc.h 檔案中

imx6q ssi1 配置成CPU為主,codec PCM從模式 SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS

可以看見有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知道

imx6q ssi1 配置成CPU為主,codec PCM從模式 SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS

修改fsl_ssi.c檔案如下:

imx6q ssi1 配置成CPU為主,codec PCM從模式 SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS

修改完成後編譯燒錄核心,播放音頻檔案即可在示波器上看見對應的波形。

另外看見一篇好的文章:How to change audio clock from 24M to 24.576M   講的是如何修改 MCLK。

我的修改如下:在dtsi中配置

codec_codecName: [email protected] {
		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 中修改檔案

imx6q ssi1 配置成CPU為主,codec PCM從模式 SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS

以上修改輸出的時鐘為24.576Mhz,如果将時鐘改為180633600 ,那麼輸出的時鐘為  22.5792Mhz  ‬

唉,看不懂下圖。

imx6q ssi1 配置成CPU為主,codec PCM從模式 SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS

3.調試小工具

alsa接口檔案說明:ALSA project - the C library reference

linux播放聲音的工具:aplay(播放) arecord(錄音)amxier(設定codec屬性指令)

I2C調試工具:

i2cdetect -l   //檢視i2c控制器

imx6q ssi1 配置成CPU為主,codec PCM從模式 SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS

i2cdetect -y -r 2   //檢視i2c-2總線上的i2c裝置,其實在裝置樹中挂了i2c,在這裡就會顯示。

imx6q ssi1 配置成CPU為主,codec PCM從模式 SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS

i2cdump  -f -y 2 0x1a  //檢視i2c-2總線上的0x1a裝置上的寄存器的值

imx6q ssi1 配置成CPU為主,codec PCM從模式 SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS

遇到如下情況,需要檢查硬體了

imx6q ssi1 配置成CPU為主,codec PCM從模式 SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS

i2cset -f -y 1 0x20 0x77 0x3f (設定i2c-1上0x20器件的0x77寄存器值為0x3f)

i2cget -f -y 1 0x20 0x77      (讀取i2c-1上0x20器件的0x77寄存器值)

4.對香草冰淇淋過敏的汽車

原文連結:對香草冰淇淋過敏的汽車

這是一個發生在美國通用汽車的客戶與該公司客服部間的真實故事。

有一天美國通用汽車公司的龐帝雅克(Pontiac)部門收到一封客戶抱怨信,上面是這樣寫的:

這是我為了同一件事第二次寫信給你,我不會怪你們為什麼沒有回信給我,因為我也覺得這樣别人會認為我瘋了,但這的确是一個事實。

我們家有一個傳統的習慣,就是我們每天在吃完晚餐後,都會以冰淇淋來當我們的飯後甜點。由于冰淇淋的口味很多,是以我們家每天在飯後才投票決定要吃哪一種口味,等大家決定後我就會開車去買。但自從最近我買了一部新的龐帝雅克後,在我去買冰淇淋的這段路程問題就發生了。

你知道嗎?每當我買的冰淇淋是香草口味時,我從店裡出來後車子就發不動。但如果我買的是其它的口味,車子發動就順得很。我要讓你知道,我對這件事情是非常認真的,盡管這個問題聽起來很豬頭。但為什麼這部龐帝雅克當我買了香草冰淇淋它就秀逗,而我不管什麼時候買其它口味的冰淇淋,它就是一尾活龍?為什麼?為什麼?

這封不可思議的投訴信立即引來了龐帝雅克車技術服務人員的調侃:“汽車對香草冰淇淋過敏?那我的遊艇對可口可樂也要過敏了。”“找錯了對象,他應該去看看心理醫生。”“查查位址,這封投訴信可能是從瘋人院裡寄來的。”……雖然對這封信的真實性心存懷疑,但龐帝雅克的總經理沒有馬虎對待這近乎天方夜譚的投訴,他派了一位辦事嚴謹的工程師前去處理這件不可思議的投訴案。

當工程師去找這位仁兄時,很驚訝的發現這封信是出之于一位事業成功、樂觀且受了高等教育的人。工程師安排與這位仁兄的見面時間剛好是在用完晚餐的時間,兩人于是上了汽車,往冰淇淋店開去。那個晚上投票結果是香草口味,當買好香草冰淇淋回到車上後,車子又秀逗了。這位工程師之後又連續來了三個晚上。第一晚,巧克力冰淇淋,車子沒事。第二晚,草莓冰淇淋,車子也沒事。第三晚,香草冰淇淋,車子“秀逗”。看來,投訴者反映的“荒唐”問題一點兒也不荒唐,龐帝雅克汽車确實時香草冰淇淋“過敏”。

這位思考有邏輯的工程師,到這時還是死不相信這位仁兄的車子對香草“過敏”。是以,他仍然不放棄繼續安排相同的行程,希望能夠将這個神秘的問題解決。工程師開始記下從開始到現在所發生的種種詳細資料,如汽車經過的路線、使用汽油的種類、開出和開回以及停車所用的時間……根據記錄的資料,他得出了一個結論:這位仁兄買香草冰淇淋所花的時間比其它口味的要少。

為什麼呢?一絲不苟的工程師仔細研究了其中的一些細節問題後,終于發現了汽車對香草冰淇淋“過敏”的奧秘:這家冰淇淋店的内部設定是整個問題的關鍵。因為,香草冰淇淋是所有冰淇淋口味中最暢銷的口味,店家為了讓顧客每次都能很快的取拿,将香草口味特别分開陳列在單獨的冰櫃,并将冰櫃放置在店的前端;至于其它口味則放置在距離收銀台較遠的後端。

現在,工程師所要知道的疑問是,為什麼這部車會因為從熄火到重新激活的時間較短時就會“秀逗”?具有深厚專業知識的工程師很快找出了答案:問題出在汽車發動機那個小小的散熱裝置上。原來,當買其他口味的冰淇淋時,由于所花的時間較長,發動機有足夠的時間散熱,重新發動時就沒有太大的問題;但是當買香草冰淇淋時,由于所花的時間較短發動機太熱以至于還無法讓散熱裝置有足夠的時間散熱,在密閉管路中油跟油之間會出現一段氣體,阻塞了油路,人們通常把它叫做“氣阻”,引擎所吸收的燃料就會斷斷續續,進而引起發動機不能正常發動,汽車當然就時香草冰淇淋“過敏”了。原來這都是發動機散熱不暢惹的禍。

這位工程師向公司反映了汽車對香草冰淇淋“過敏”的原因,設計部門迅速進行了技術改進,彌補了散熱裝置的缺陷,解決了氣阻現象。不久,随時能重新發動引擎的散熱裝置應運而生。從此,龐帝雅克車對香草冰淇淋再也不“過敏”了,這個看似荒唐的投訴案得到了圓滿的解決。

希望養成能夠獨立看核心代碼的能力,擁有解決對香草冰淇淋過敏的汽車問題的能力。畢竟面試官說過C語言是最簡單的語言,而且代碼裡什麼都有。

遇到的錯誤都在自己立的flag上,難受。