AM335x(TQ335x)学习笔记——WM8960声卡驱动移植

经过一段时间的调试,终于调好了TQ335x的声卡驱动。TQ335x采用的Codec是WM8960,本文来总结下WM8960驱动在AM335x平台上的移植方法。Linux声卡驱动架构有OSS和ALSA两种架构,目前最常用的架构是ALSA,本文也使用ALSA架构对WM8960驱动进行移植。

ASoC是对ALSA驱动架构的进一步封装。ASoC将ALSA驱动中的各模块抽象为三部分:Platform、Codec和Machine。Platform主要是平台硬件驱动,包括SoC的IIS模块、DMA等,在本文中就是指AM335x的McASP模块及AM335x用于音频读写操作的EDMA。Codec是编解码芯片驱动,在本文中就是WM8960。Machine是用来描述单板音频系统连接关系的驱动,在本文中其作用是将WM8960与McASP绑定起来,注册声卡设备节点。由于3.17版本的内核已经带有TI维护的McASP驱动和Wolf公司维护的WM8960驱动,因此,原理上讲,我们只需要编写Machine部分,建立WM8960与McASP的连接关系即可。不幸的是Wolf对WM8960的维护不是太完善,还需要我们进一步修改。下面我们来看下WM8960在TQ335x上的移植方法。

1. 在DTS中添加声卡信息

Step1.  完善sound信息

在DTS有一个节点名为sound,该节点用来描述单板上声卡设备信息,修改后的内容如下:

sound {
	compatible = "ti,tq-evm-audio";
	ti,model = "AM335x-EVM";
	ti,audio-codec = <&wm8960>;
	ti,mcasp-controller = <&mcasp1>;
	ti,codec-clock-rate = <24576000>;
	ti,audio-routing =
		"Headphone Jack",       "HP_L",
		"Headphone Jack",       "HP_R",
		"LINPUT1",              "Line In";
};
含义解释:

(1) compatible = "ti,tq-evm-audio" -->  指定声卡兼容的设备,与Machine驱动中的compatible匹配。

(2) ti,model = "AM335x-EVM" --> 声卡的名称,原则上讲可以随意指定,但最好具有一定的可读性,这里没有修改。

(3) ti,audio-codec = <&wm8960> --> 指定单板使用的Codec,具体的Codec信息由其指向的节点wm8960描述。

(4) ti,mcasp-controller = <&mcasp1> --> 指定单板使用的Codec连接到AM335x的McASP1上,McASP1的具体信息由其指向的节点mcasp1描述。

(5) ti,codec-clock-rate = <24576000> --> 指定Codec的MCLK时钟频率,单位是HZ。TQ335x的Codec使用24.576MHZ的有源晶振提供MCLK,故设置为24576000。

(6) ti,audio-routing  --> DAPM信息描述,用来指定Codec与McASP的连接关系。此处若不设置,则需要在Machine驱动中进行设置。本文在这里做了修改。

Step2. 完善Codec信息

通过阅读TQ335x的原理图可知,WM8960的控制端口连接到了AM335x的I2C0端口上,因此,可以i2c0节点内添加如下信息(类似上篇文章中触摸设备驱动节点):

wm8960: wm8960@1a {
	compatible = "wlf,wm8960";
	reg = <0x1a>;
};
含义解释:

(1) compatible = "wlf,wm8960" --> 指定Codec兼容设备,与Codec驱动中的compatible匹配。

(2) reg = <0x1a> --> WM8960的I2C地址是1A,故设置为0x1a。

Step3. 完善Platform信息

AM335x的Platform信息主要指McASP和EMDA设置信息。由于默认的DTS已经配置好了McASP及EDMA的大部分信息,需要我们配置的是McASP的pinmux和i2s信息。

(1) 修改pinmux信息需要具体参考TQ335x的原理图,下面是根据原理图中的引脚连接方式修改的pinmux信息,如果有啥不懂的可以留言讨论:

am335x_evm_audio_pins: am335x_evm_audio_pins {
	pinctrl-single,pins = <
		0x1A0 (PIN_INPUT_PULLDOWN | MUX_MODE3) /* mcasp0_aclkr.mcasp1_aclkx */
		0x1A4 (PIN_INPUT_PULLDOWN | MUX_MODE3) /* mcasp0_fsr.mcasp1_fsx */
		0x1A8 (PIN_OUTPUT_PULLDOWN | MUX_MODE3) /* mcasp0_axr1.mcasp1_axr0 */
		0x1AC (PIN_INPUT_PULLDOWN | MUX_MODE3) /* mcasp0_ahclkx.mcasp1_axr1 */
	>;
};
(2) i2s的配置信息需要在mcasp1节点中修改,具体的修改如下:

&mcasp1 {
		pinctrl-names = "default";
		pinctrl-0 = <&am335x_evm_audio_pins>;

		status = "okay";

		op-mode = <0>;          /* MCASP_IIS_MODE */
		tdm-slots = <2>;
		/* 4 serializers */
		serial-dir = <  /* 0: INACTIVE, 1: TX, 2: RX */
			1 2 0 0
		>;
		tx-num-evt = <1>;
		rx-num-evt = <1>;
};
含义:

(1) pinctrl-0 = <&am335x_evm_audio_pins> --> 指定mcasp1的pinmux信息。

(2) op-mode = <0> --> 指定McASP为I2S工作模式。

(3) tdm-slots = <2> --> 指定通道数。AM335x的手册以更广泛意义的单词slot命名,具体到I2S接口,其含义就是Channel。

(4) serial-dir --> 指定serializer的方向。AM335x的手册中提到每个McASP有16个serializer,但AM335x这款芯片的McAPS只有4个serializer,分别用于AXR0、AXR1、AXR2和ARX3。由于TQ335x中将AXR0作为发送(输出)、ARX1作为接收(输入)且没有ARX2和ARX3,故设置4个serial-dir为1、2、0、0(0表示没有使用,1表示发送,2表示接收)。
(5) tx-num-evt = <1> --> 指定发送FIFO大小,本文设置为1。

(6) rx-num-evt = <1> --> 指定接收FIFO大小,本文设置为1。

至此,就完成了DTS的全部配置,后面我会将完整的DTS文件上传到我的资源。

2. Codec驱动完善

Step1. 修改Codec驱动,使其支持DTS

由于我们在DTS中指定了Codec的compatible为"wlf,wm8960",而Linux内核自带的WM8960驱动并没有支持新式的DTS模式关联。修改方法很简单,添加i2c_driver的.driver中指定of_match_table即可,修改后的代码片段如下:

static const struct of_device_id wm8960_of_match[] = {
	{ .compatible = "wlf,wm8960", },
	{ }
};
MODULE_DEVICE_TABLE(of, wm8960_of_match);

static struct i2c_driver wm8960_i2c_driver = {
	.driver = {
		.name = "wm8960",
		.owner = THIS_MODULE,
		.of_match_table = wm8960_of_match,
	},
	.probe =    wm8960_i2c_probe,
	.remove =   wm8960_i2c_remove,
	.id_table = wm8960_i2c_id,
};
Step2. 完善WM8960的初始化信息

默认的WM8960驱动初始化信息不够完整,还需要对WM8960进行额外的初始化,修改后的代码片段如下:

static int wm8960_probe(struct snd_soc_codec *codec)
{
	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
	struct wm8960_data *pdata = dev_get_platdata(codec->dev);
	int ret;

	wm8960->set_bias_level = wm8960_set_bias_level_out3;

	if (!pdata) {
		dev_warn(codec->dev, "No platform data supplied\n");
	} else {
		if (pdata->capless)
			wm8960->set_bias_level = wm8960_set_bias_level_capless;
	}

	ret = wm8960_reset(codec);
	if (ret < 0) {
		dev_err(codec->dev, "Failed to issue reset\n");
		return ret;
	}

	wm8960->set_bias_level(codec, SND_SOC_BIAS_STANDBY);

	/* Latch the update bits */
	snd_soc_update_bits(codec, WM8960_LINVOL, 0x100, 0x100);
	snd_soc_update_bits(codec, WM8960_RINVOL, 0x100, 0x100);
	snd_soc_update_bits(codec, WM8960_LADC, 0x100, 0x100);
	snd_soc_update_bits(codec, WM8960_RADC, 0x100, 0x100);
	snd_soc_update_bits(codec, WM8960_LDAC, 0x100, 0x100);
	snd_soc_update_bits(codec, WM8960_RDAC, 0x100, 0x100);
	snd_soc_update_bits(codec, WM8960_LOUT1, 0x100, 0x100);
	snd_soc_update_bits(codec, WM8960_ROUT1, 0x100, 0x100);
	snd_soc_update_bits(codec, WM8960_LOUT2, 0x100, 0x100);
	snd_soc_update_bits(codec, WM8960_ROUT2, 0x100, 0x100);

	/* other configuration */
    snd_soc_update_bits(codec, WM8960_POWER1, 0x1ea, 0x1ea);
    snd_soc_update_bits(codec, WM8960_POWER2, 0x1f8, 0x1f8);
    snd_soc_update_bits(codec, WM8960_POWER3, 0xcc, 0xcc);
	snd_soc_update_bits(codec, WM8960_LOUTMIX, 0x100, 0x100);
	snd_soc_update_bits(codec, WM8960_ROUTMIX, 0x100, 0x100);
	snd_soc_update_bits(codec, WM8960_POWER3, 0xc, 0xc);
	snd_soc_update_bits(codec, WM8960_LOUT1, 0x7f, 0x7f);
	snd_soc_update_bits(codec, WM8960_ROUT1, 0x7f, 0x7f);
	snd_soc_update_bits(codec, WM8960_IFACE2, 0x40, 0x40);
	snd_soc_update_bits(codec, WM8960_MONOMIX2, 0x120, 0x120);
	snd_soc_update_bits(codec, WM8960_LINPATH, 0x1f8, 0x138);
	snd_soc_update_bits(codec, WM8960_LINVOL, 0x19f, 0x11f);
	snd_soc_update_bits(codec, WM8960_RINVOL, 0x19f, 0x11f);
	snd_soc_update_bits(codec, WM8960_LOUT2, 0x1ff, 0x1ff);
	snd_soc_update_bits(codec, WM8960_ROUT2, 0x1ff, 0x1ff);
	snd_soc_update_bits(codec, WM8960_CLASSD3, 0x1a, 0x12);
	snd_soc_update_bits(codec, WM8960_CLASSD1, 0xc0, 0xc0);

	snd_soc_add_codec_controls(codec, wm8960_snd_controls,
				     ARRAY_SIZE(wm8960_snd_controls));
	wm8960_add_widgets(codec);

	return 0;
}
具体的含义可以参考WM8960的芯片手册,这里我就不一一介绍了。

Step3. 调整WM8960驱动结构

内核中自带的WM8960驱动结构很旧,编写Machine是需要过多的了解Codec芯片内部细节,本文对WM8960的驱动结构进行了调整,可以使Machine忽略Codec的内部细节。

修改的大体内容如下:

(1) 添加set_sysclk函数,接收Machine设置的sysclk时钟频率。具体本文就是DTS中设置的24576000。

(2) 在hw_params中添加BCLK、DACCLK、ADCCLK的配置操作。hw_params可以根据参数和sysclk对以上参数进行设置,放在这里很合适。

(3) 去除函数wm8960_set_dai_clkdiv,并将wm8960_set_dai_pll设置为驱动内部函数,不作为set_pll接口提供给内核驱动(实际上内核驱动也不调用这个函数)。

Step4. 修改WM8960的route信息

根据TQ335x的原理图可知,使用WM8960进行录音或放音时使用的LRCLK是同一个,都是DACCLK,故在snd_soc_dapm_route添加如下两行信息:

{ "Left DAC", NULL, "Left Input Mixer" },
{ "Right DAC", NULL, "Right Input Mixer" },
这样在录音时也会使能DAC产生LRCLK。

由于调试时间比较长,可能有些修改我没有描述到,完整的wm8960.c文件我会一并上传到我的资源,可以下载参考。

3. 编写Machine驱动
内核代码有个很好的例子就是davinci-evm.c,这是am335x-evm评估板的Machine驱动,该评估采用的Codec并不是WM8960,因此,我们在该文件中添加WM8960信息即可。具体的修改如下:

Step1. 添加compatible信息。修改后的内容如下:

static const struct of_device_id davinci_evm_dt_ids[] = {
	{
		.compatible = "ti,tq-evm-audio",
		.data = (void *) &evm_dai_wm8960,
	},
	{
		.compatible = "ti,da830-evm-audio",
		.data = (void *) &evm_dai_tlv320aic3x,
	},
	{ /* sentinel */ }
};
Step2. 实现em_dai_wm8960。需要添加如下代码:

static struct snd_soc_dai_link evm_dai_wm8960 = {
	.name		= "wm8960",
	.stream_name	= "wm8960-hifi",
	.codec_dai_name	= "wm8960-hifi",
	.ops            = &evm_wm8960_ops,
	.init           = evm_wm8960_init,
	.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM |
		SND_SOC_DAIFMT_NB_NF,
};
含义:

(1) codec_dai_name = "wm8960-hifi" --> 指定codec设备名称,与wm8960.c中指定的相同即可。

(2) ops --> 指定wm8960的各种操作函数,本文仅实现了hw_params函数。

(3) init --> 指定wm8960的初始化函数,主要是完成dapm相关的初始化。

(4) dai_fmt --> 指定音频的接口方式、主从关系和时钟翻转信息。SND_SOC_DAIFMT_I2S表示音频接口采用I2S协议;SND_SOC_DAIFMT_CBM_CFM表示Codec的BCLK为Master,LRCLK为Master,即wm8960为主,AM335x为从;SND_SOC_DAIFMT_NB_NF表示BCLK和LRCLK都不需要翻转。

Step3.实现evm_wm8960_init

这一部分主要是dapm相关的设置,本人理解也不是非常深刻,直接贴上代码,具体如下:

static const struct snd_soc_dapm_widget evm_wm8960_dapm_widgets[] = {
	SND_SOC_DAPM_SPK("Audio Out1", NULL),
	SND_SOC_DAPM_MIC("my Mic", NULL),
	SND_SOC_DAPM_MIC("my Line IN", NULL),
};

static const struct snd_kcontrol_new evm_wm8960_controls[] = {
	SOC_DAPM_PIN_SWITCH("Audio Out1"),
	SOC_DAPM_PIN_SWITCH("my Mic"),
	SOC_DAPM_PIN_SWITCH("my Line IN"),
};

static const struct snd_soc_dapm_route evm_wm8960_audio_map[] = {
	/* Connections to the ... */
	{"Audio Out1", NULL, "HP_L"},
	{"Audio Out1", NULL, "HP_R"},

	/* Mic */
	{"LINPUT1", NULL, "MICB"},
	{"MICB", NULL, "my Mic"},

	/* Line in */
	{"LINPUT3", NULL, "my Line IN"},
	{"RINPUT3", NULL, "my Line IN"},
};

static int evm_wm8960_init(struct snd_soc_pcm_runtime *rtd)
{
	int err;
	struct snd_soc_codec *codec = rtd->codec;
	struct snd_soc_dapm_context *dapm = &codec->dapm;

	snd_soc_dapm_new_controls(dapm, evm_wm8960_dapm_widgets,
			ARRAY_SIZE(evm_wm8960_dapm_widgets ) );

	err = snd_soc_add_codec_controls(codec, evm_wm8960_controls,
			ARRAY_SIZE(evm_wm8960_controls));

	if (err < 0)
		return err;

	snd_soc_dapm_add_routes(dapm, evm_wm8960_audio_map,
			ARRAY_SIZE(evm_wm8960_audio_map));

	snd_soc_dapm_enable_pin(dapm, "Audio Out1");
	snd_soc_dapm_enable_pin(dapm, "my Mic");

	snd_soc_dapm_sync( dapm );

	return 0;
}
Step4. 实现evm_wm8960_ops及相关函数,需要添加如下代码:

static int evm_wm8960_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 snd_soc_card *soc_card = rtd->card;
	int ret = 0;
	unsigned sysclk = ((struct snd_soc_card_drvdata_davinci *)
			   snd_soc_card_get_drvdata(soc_card))->sysclk;

	/* set the codec system clock */
	ret = snd_soc_dai_set_sysclk(codec_dai, 0, sysclk, SND_SOC_CLOCK_IN);
	if (ret < 0)
		return ret;

	/* set the CPU system clock */
	ret = snd_soc_dai_set_sysclk(cpu_dai, 0, sysclk, SND_SOC_CLOCK_IN);
	if (ret < 0)
		return ret;

	return 0;
}

static struct snd_soc_ops evm_wm8960_ops = {
	.startup = evm_startup,
	.shutdown = evm_shutdown,
	.hw_params = evm_wm8960_hw_params,
};
至此,就完成了代码移植的全部工作,修改涉及到的三个文件是:tq335x.dts、davinci-evm.c和wm8960.c,修改后的这三个文件我会上传到我的资源,如有需要,请去我的资源中下载。

4. 配置内核

完成了代码的移植工作之后还需要对内核进一步配置。默认的内核将ALSA作为module加载,本文将编译进内核。具体步骤如下:

Step1. 修改sound/soc/codecs/Kconfig,添加wm8960编译选项,修改后的内容如下:

config SND_SOC_WM8960
    tristate "Wolfson Microelectronics WM8960 CODEC"
    depends on I2C && INPUT
Step2.  通过menuconfig配置内核

执行指令:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- menuconfig
进行如下修改:

Device Drivers  --->
 	<*> Sound card support  ---> 
 	 	<*>   Advanced Linux Sound Architecture  --->
 	 	 	<*>   ALSA for SoC audio support  --->
 	 	 		<*>   SoC Audio for Texas Instruments chips using eDMA (AM33XX/43XX)
 	 	 		-*-   Multichannel Audio Serial Port (McASP) support
 	 	 		<*>   SoC Audio for the AM33XX chip based boards
 	 	 		CODEC drivers  --->
 	 	 			<*> Wolfson Microelectronics WM8960 CODEC
重新编译内核:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- -j8

5. 效果

将编译后的内核文件zImage和tq335x.dtb文件拷贝SD卡并启动开发板,按任意键进入uboot命令模式,输入如下指令:

load mmc 0:1 0x88000000 /boot/tq335x.dtb
load mmc 0:1 0x82000000 /boot/zImage
bootz 0x82000000 - 0x88000000
通过上面的三条指令可以启动内核,完整的Log信息如下:

Starting kernel ...

[    0.000000] Booting Linux on physical CPU 0x0
[    0.000000] Linux version 3.17.2 (lilianrong@smarter) (gcc version 4.7.3 (Ubuntu/Linaro 4.7.3-12ubuntu1) ) #68 SMP Sat Dec 20 00:03:09 CST 2014
[    0.000000] CPU: ARMv7 Processor [413fc082] revision 2 (ARMv7), cr=10c5387d
[    0.000000] CPU: PIPT / VIPT nonaliasing data cache, VIPT aliasing instruction cache
[    0.000000] Machine model: TI AM335x EVM
[    0.000000] cma: Reserved 16 MiB at 9e800000
[    0.000000] Memory policy: Data cache writeback
[    0.000000]   HighMem zone: 1048574 pages exceeds freesize 0
[    0.000000] CPU: All CPU(s) started in SVC mode.
[    0.000000] AM335X ES2.1 (sgx neon )
[    0.000000] PERCPU: Embedded 9 pages/cpu @dfa99000 s14336 r8192 d14336 u36864
[    0.000000] Built 1 zonelists in Zone order, mobility grouping on.  Total pages: 129792
[    0.000000] Kernel command line: console=ttyO0,115200n8 root=/dev/mmcblk0p2 rw rootfstype=ext3 rootwait
[    0.000000] PID hash table entries: 2048 (order: 1, 8192 bytes)
[    0.000000] Dentry cache hash table entries: 65536 (order: 6, 262144 bytes)
[    0.000000] Inode-cache hash table entries: 32768 (order: 5, 131072 bytes)
[    0.000000] Memory: 484124K/523264K available (6070K kernel code, 666K rwdata, 2444K rodata, 410K init, 8214K bss, 39140K reserved, 0K highmem)
[    0.000000] Virtual kernel memory layout:
[    0.000000]     vector  : 0xffff0000 - 0xffff1000   (   4 kB)
[    0.000000]     fixmap  : 0xffc00000 - 0xffe00000   (2048 kB)
[    0.000000]     vmalloc : 0xe0800000 - 0xff000000   ( 488 MB)
[    0.000000]     lowmem  : 0xc0000000 - 0xe0000000   ( 512 MB)
[    0.000000]     pkmap   : 0xbfe00000 - 0xc0000000   (   2 MB)
[    0.000000]     modules : 0xbf000000 - 0xbfe00000   (  14 MB)
[    0.000000]       .text : 0xc0008000 - 0xc0858bc0   (8515 kB)
[    0.000000]       .init : 0xc0859000 - 0xc08bf800   ( 410 kB)
[    0.000000]       .data : 0xc08c0000 - 0xc0966b50   ( 667 kB)
[    0.000000]        .bss : 0xc0966b50 - 0xc116c6e0   (8215 kB)
[    0.000000] Hierarchical RCU implementation.
[    0.000000]  RCU restricting CPUs from NR_CPUS=2 to nr_cpu_ids=1.
[    0.000000] RCU: Adjusting geometry for rcu_fanout_leaf=16, nr_cpu_ids=1
[    0.000000] NR_IRQS:16 nr_irqs:16 16
[    0.000000] IRQ: Found an INTC at 0xfa200000 (revision 5.0) with 128 interrupts
[    0.000000] Total of 128 interrupts on 1 active controller
[    0.000000] OMAP clockevent source: timer2 at 24000000 Hz
[    0.000015] sched_clock: 32 bits at 24MHz, resolution 41ns, wraps every 178956969942ns
[    0.000061] OMAP clocksource: timer1 at 24000000 Hz
[    0.000798] Console: colour dummy device 80x30
[    0.000849] Lock dependency validator: Copyright (c) 2006 Red Hat, Inc., Ingo Molnar
[    0.000858] ... MAX_LOCKDEP_SUBCLASSES:  8
[    0.000865] ... MAX_LOCK_DEPTH:          48
[    0.000873] ... MAX_LOCKDEP_KEYS:        8191
[    0.000880] ... CLASSHASH_SIZE:          4096
[    0.000887] ... MAX_LOCKDEP_ENTRIES:     32768
[    0.000894] ... MAX_LOCKDEP_CHAINS:      65536
[    0.000901] ... CHAINHASH_SIZE:          32768
[    0.000909]  memory used by lock dependency info: 5167 kB
[    0.000916]  per task-struct memory footprint: 1152 bytes
[    0.000956] Calibrating delay loop... 996.14 BogoMIPS (lpj=4980736)
[    0.079039] pid_max: default: 32768 minimum: 301
[    0.079431] Security Framework initialized
[    0.079555] Mount-cache hash table entries: 1024 (order: 0, 4096 bytes)
[    0.079568] Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes)
[    0.081736] CPU: Testing write buffer coherency: ok
[    0.082916] CPU0: thread -1, cpu 0, socket -1, mpidr 0
[    0.083033] Setting up static identity map for 0x805bf4f0 - 0x805bf560
[    0.086259] Brought up 1 CPUs
[    0.086278] SMP: Total of 1 processors activated.
[    0.086288] CPU: All CPU(s) started in SVC mode.
[    0.088875] devtmpfs: initialized
[    0.097689] VFP support v0.3: implementor 41 architecture 3 part 30 variant c rev 3
[    0.133508] omap_hwmod: tptc0 using broken dt data from edma
[    0.133865] omap_hwmod: tptc1 using broken dt data from edma
[    0.134203] omap_hwmod: tptc2 using broken dt data from edma
[    0.142102] omap_hwmod: debugss: _wait_target_disable failed
[    0.200093] pinctrl core: initialized pinctrl subsystem
[    0.202608] regulator-dummy: no parameters
[    0.232298] NET: Registered protocol family 16
[    0.240800] DMA: preallocated 256 KiB pool for atomic coherent allocations
[    0.243054] cpuidle: using governor ladder
[    0.243083] cpuidle: using governor menu
[    0.255025] OMAP GPIO hardware version 0.1
[    0.270226] omap-gpmc 50000000.gpmc: could not find pctldev for node /pinmux@44e10800/nandflash_pins_s0, deferring probe
[    0.270268] platform 50000000.gpmc: Driver omap-gpmc requests probe deferral
[    0.274762] hw-breakpoint: debug architecture 0x4 unsupported.
[    0.319722] edma-dma-engine edma-dma-engine.0: TI EDMA DMA engine driver
[    0.321054] vbat: 5000 mV 
[    0.321851] lis3_reg: no parameters
[    0.325260] SCSI subsystem initialized
[    0.326060] usbcore: registered new interface driver usbfs
[    0.326235] usbcore: registered new interface driver hub
[    0.330180] usbcore: registered new device driver usb
[    0.331021] omap_i2c 44e0b000.i2c: could not find pctldev for node /pinmux@44e10800/pinmux_i2c0_pins, deferring probe
[    0.331059] platform 44e0b000.i2c: Driver omap_i2c requests probe deferral
[    0.331115] omap_i2c 4802a000.i2c: could not find pctldev for node /pinmux@44e10800/pinmux_i2c1_pins, deferring probe
[    0.331139] platform 4802a000.i2c: Driver omap_i2c requests probe deferral
[    0.332318] Advanced Linux Sound Architecture Driver Initialized.
[    0.335684] Switched to clocksource timer1
[    0.486994] NET: Registered protocol family 2
[    0.488860] TCP established hash table entries: 4096 (order: 2, 16384 bytes)
[    0.489040] TCP bind hash table entries: 4096 (order: 5, 147456 bytes)
[    0.490403] TCP: Hash tables configured (established 4096 bind 4096)
[    0.490593] TCP: reno registered
[    0.490617] UDP hash table entries: 256 (order: 2, 20480 bytes)
[    0.490807] UDP-Lite hash table entries: 256 (order: 2, 20480 bytes)
[    0.491827] NET: Registered protocol family 1
[    0.493704] RPC: Registered named UNIX socket transport module.
[    0.493729] RPC: Registered udp transport module.
[    0.493738] RPC: Registered tcp transport module.
[    0.493747] RPC: Registered tcp NFSv4.1 backchannel transport module.
[    0.495022] hw perfevents: enabled with armv7_cortex_a8 PMU driver, 5 counters available
[    0.499438] futex hash table entries: 256 (order: 2, 16384 bytes)
[    0.504727] VFS: Disk quotas dquot_6.5.2
[    0.504882] Dquot-cache hash table entries: 1024 (order 0, 4096 bytes)
[    0.507359] NFS: Registering the id_resolver key type
[    0.507727] Key type id_resolver registered
[    0.507743] Key type id_legacy registered
[    0.507886] jffs2: version 2.2. (NAND) (SUMMARY)  漏 2001-2006 Red Hat, Inc.
[    0.508320] msgmni has been set to 977
[    0.513238] io scheduler noop registered
[    0.513269] io scheduler deadline registered
[    0.513341] io scheduler cfq registered (default)
[    0.515815] pinctrl-single 44e10800.pinmux: 142 pins at pa f9e10800 size 568
[    0.520975] backlight supply power not found, using dummy regulator
[    0.524309] Serial: 8250/16550 driver, 4 ports, IRQ sharing enabled
[    0.530939] omap_uart 44e09000.serial: no wakeirq for uart0
[    0.531575] 44e09000.serial: ttyO0 at MMIO 0x44e09000 (irq = 88, base_baud = 3000000) is a OMAP UART0
[    1.231857] console [ttyO0] enabled
[    1.241315] omap_rng 48310000.rng: OMAP Random Number Generator ver. 20
[    1.248987] [drm] Initialized drm 1.1.0 20060810
[    1.261218] [drm] Supports vblank timestamp caching Rev 2 (21.10.2013).
[    1.268316] [drm] No driver support for vblank timestamp query.
[    1.320991] Console: switching to colour frame buffer device 100x30
[    1.332042] tilcdc 4830e000.lcdc: fb0:  frame buffer device
[    1.337919] tilcdc 4830e000.lcdc: registered panic notifier
[    1.343814] [drm] Initialized tilcdc 1.0.0 20121205 on minor 0
[    1.380804] brd: module loaded
[    1.398539] loop: module loaded
[    1.404724] mtdoops: mtd device (mtddev=name/number) must be supplied
[    1.414737] usbcore: registered new interface driver asix
[    1.420619] usbcore: registered new interface driver ax88179_178a
[    1.427154] usbcore: registered new interface driver cdc_ether
[    1.433416] usbcore: registered new interface driver smsc95xx
[    1.439572] usbcore: registered new interface driver net1080
[    1.445625] usbcore: registered new interface driver cdc_subset
[    1.451932] usbcore: registered new interface driver zaurus
[    1.457997] usbcore: registered new interface driver cdc_ncm
[    1.465136] usbcore: registered new interface driver cdc_wdm
[    1.471412] usbcore: registered new interface driver usb-storage
[    1.477942] usbcore: registered new interface driver usbtest
[    1.486105] mousedev: PS/2 mouse device common for all mice
[    1.496690] omap_rtc 44e3e000.rtc: rtc core: registered 44e3e000.rtc as rtc0
[    1.504827] i2c /dev entries driver
[    1.508655] Driver for 1-wire Dallas network protocol.
[    1.517293] omap_wdt: OMAP Watchdog Timer Rev 0x01: initial timeout 60 sec
[    1.527282] omap_hsmmc 48060000.mmc: unable to get vmmc regulator -517
[    1.534612] platform 48060000.mmc: Driver omap_hsmmc requests probe deferral
[    1.544215] ledtrig-cpu: registered to indicate activity on CPUs
[    1.551195] usbcore: registered new interface driver usbhid
[    1.557065] usbhid: USB HID core driver
[    1.575960] davinci_evm sound: ASoC: CODEC (null) not registered
[    1.582524] davinci_evm sound: snd_soc_register_card failed (-517)
[    1.589121] platform sound: Driver davinci_evm requests probe deferral
[    1.596830] oprofile: using arm/armv7
[    1.601311] TCP: cubic registered
[    1.604789] Initializing XFRM netlink socket
[    1.609445] NET: Registered protocol family 17
[    1.614179] NET: Registered protocol family 15
[    1.619196] Key type dns_resolver registered
[    1.623867] omap_voltage_late_init: Voltage driver support not added
[    1.630565] sr_dev_init: No voltage domain specified for smartreflex0. Cannot initialize
[    1.639038] sr_dev_init: No voltage domain specified for smartreflex1. Cannot initialize
[    1.648614] ThumbEE CPU extension supported.
[    1.653130] Registering SWP/SWPB emulation handler
[    1.658211] SmartReflex Class3 initialized
[    1.667886] omap-gpmc 50000000.gpmc: GPMC revision 6.0
[    1.674877] nand: device found, Manufacturer ID: 0xec, Chip ID: 0xd3
[    1.681647] nand: Samsung NAND 1GiB 3,3V 8-bit
[    1.686312] nand: 1024MiB, SLC, page size: 2048, OOB size: 64
[    1.692312] nand: error: CONFIG_MTD_NAND_OMAP_BCH not enabled
[    1.698428] omap2-nand: probe of omap2-nand.0 failed with error -22
[    1.790962] tps65910 0-002d: No interrupt support, no core IRQ
[    1.807488] vrtc: 1800 mV 
[    1.810865] vrtc: supplied by vbat
[    1.818190] vio: at 1500 mV 
[    1.821460] vio: supplied by vbat
[    1.828397] vdd_mpu: 912 <--> 1312 mV at 1325 mV 
[    1.833532] vdd_mpu: supplied by vbat
[    1.840598] vdd_core: 912 <--> 1150 mV at 1137 mV 
[    1.845924] vdd_core: supplied by vbat
[    1.852537] vdd3: 5000 mV 
[    1.858001] vdig1: at 1800 mV 
[    1.861420] vdig1: supplied by vbat
[    1.867904] vdig2: at 1800 mV 
[    1.871302] vdig2: supplied by vbat
[    1.878529] vpll: at 1800 mV 
[    1.881866] vpll: supplied by vbat
[    1.888426] vdac: at 1800 mV 
[    1.891747] vdac: supplied by vbat
[    1.898169] vaux1: at 1800 mV 
[    1.901568] vaux1: supplied by vbat
[    1.908027] vaux2: at 3300 mV 
[    1.911421] vaux2: supplied by vbat
[    1.917833] vaux33: at 3300 mV 
[    1.921323] vaux33: supplied by vbat
[    1.927806] vmmc: 1800 <--> 3300 mV at 3300 mV 
[    1.932761] vmmc: supplied by vbat
[    1.938842] vbb: at 3000 mV 
[    1.942302] vbb: supplied by vbat
[    1.949841] omap_i2c 44e0b000.i2c: bus 0 rev0.11 at 400 kHz
[    1.963667] omap_i2c 4802a000.i2c: bus 1 rev0.11 at 100 kHz
[    2.007363] wm8960 0-001a: No platform data supplied
[    2.084399] mmc0: host does not support reading read-only switch. assuming write-enable.
[    2.095817] mmc0: new high speed SDHC card at address aaaa
[    2.104312] mmcblk0: mmc0:aaaa SL16G 14.8 GiB 
[    2.116665]  mmcblk0: p1 p2
[    2.131506] davinci_evm sound: wm8960-hifi <-> 4803c000.mcasp mapping ok
[    2.215823] davinci_mdio 4a101000.mdio: davinci mdio revision 1.6
[    2.222212] davinci_mdio 4a101000.mdio: detected phy mask ffffffde
[    2.232273] libphy: 4a101000.mdio: probed
[    2.236597] davinci_mdio 4a101000.mdio: phy[0]: device 4a101000.mdio:00, driver unknown
[    2.244957] davinci_mdio 4a101000.mdio: phy[5]: device 4a101000.mdio:05, driver unknown
[    2.254567] cpsw 4a100000.ethernet: Detected MACID = c4:ed:ba:88:b5:e4
[    2.266507] input: gpio_keyad@0 as /devices/gpio_keyad@0/input/input0
[    2.276227] omap_rtc 44e3e000.rtc: setting system clock to 2000-01-01 00:00:00 UTC (946684800)
[    2.285247] sr_init: No PMIC hook to init smartreflex
[    2.290884] sr_init: platform driver register failed for SR
[    2.313832] lis3_reg: disabling
[    2.317496] ALSA device list:
[    2.320598]   #0: AM335x-EVM
[    2.434398] kjournald starting.  Commit interval 5 seconds
[    2.444085] EXT3-fs (mmcblk0p2): using internal journal
[    2.452049] EXT3-fs (mmcblk0p2): recovery complete
[    2.457123] EXT3-fs (mmcblk0p2): mounted filesystem with ordered data mode
[    2.464445] VFS: Mounted root (ext3 filesystem) on device 179:2.
[    2.474111] devtmpfs: mounted
[    2.478057] Freeing unused kernel memory: 408K (c0859000 - c08bf000)
----------mount all..........
----------Starting mdev......

Please press Enter to activate this console. 
@tq335x #
从Log信息中很容易看到ALSA device list已经出现了AM335x-EVM项。到这里TQ335x已经能够识别到声卡了。

6. 测试

测试ALSA声卡驱动的常用方法是移植alsa-lib和alsa-utils,使用alsa-utils提供的arecord来测试声卡的录音,aplay来测试播放。alsa-lib和alsa-utils的移植教程有很多,这里我就不多讲了(如果有不明白这块的可以留言)。

录音测试:

arecord -f cd test.wav
播放测试:

aplay test.wav
播放时就可以听到之前录制的声音的。

7. 总结

调试声卡驱动大约进行了三周,也算是略有心得。

(1) 必要的工具:我调试的时候手头上工具不够齐全。我是在家里调试的,没有示波器,也就无法测量BCLK和LRCLK时钟,调试了很久都不知道WM8960到底有没有工作,因此,必要的工具可以有效的提高调试效率。

(2) 先调试放音,wm8960放音再调试录音。放音可以很容易检测效果,录音则没有方便的检测手段。同时,放音的配置比录音要简单些,可以有效检测驱动部分是否存在问题。


以上是完整的WM8960驱动移植过程,如果疑问欢迎留言讨论。




本文作者:girlkoo

本文连接:http://blog.csdn.net/girlkoo/article/details/42042555

已标记关键词 清除标记
这个启动内核时 [ 1.292527] ----wm8960_i2c_probe--- [ 1.295084] ---wm8960_register--- [ 1.810985] --s3c64xx_iis_dev_probe-- [ 1.811339] ---wm8960_probe--- [ 1.811388] GEC210 audio driver Playback mapping OK [ 1.811516] playback = 1 [ 1.811541] capture = 1 [ 1.812051] GEC210 audio driver Capture mapping OK [ 1.812182] playback = 1 [ 1.812488] capture = 1 [ 2.629128] ASoC: Failed to create HP-L/R debugfs file [ 2.629178] ASoC: Failed to create Speaker-L/R debugfs file [ 2.629406] ALSA device list: [ 2.629435] #0: gec (WM8960) 但是在播放时: ./mplayer gecmusic.mp3 MPlayer 1.0rc2-4.5.1 (C) 2000-2007 MPlayer Team CPU: ARM 正在播放 gecmusic.mp3。 检测到 Audio file 文件格式。 剪辑信息: Title: 09 ▒▒▒▒▒ Artist: Ⱥ▒▒ Album: ▒▒ɣ▒▒▒ķ▒▒▒▒▒▒▒ WIZOR+SRS Year: 2010 Comment: Track: 9 Genre: Other ========================================================================== 请求的音频编解码器族 [mp3] (afm=mp3lib) 不可用。 请在编译时启用它。 打开音频解码器: [ffmpeg] FFmpeg/libavcodec audio decoders invalid new backstep 1006 AUDIO: 44100 Hz, 2 ch, s16le, 320.0 kbit/22.68% (ratio: 40000->176400) 已选音频编解码器: [ffmp3] afm: ffmpeg (FFmpeg MPEG layer-3 audio decoder) ========================================================================== [ 29.942517] GEC210 PSR=32 RFS=256 BFS=32 [ 29.942763] DmaAddr=@3fe60000 Total=131072bytes PrdSz=16384 #Prds=8 dma_area=0xff004000 [ 29.943235] GEC210 PSR=32 RFS=256 BFS=32 [ 29.943394] DmaAddr=@3fe60000 Total=131072bytes PrdSz=16384 #Prds=8 dma_area=0xff004000 [ 29.950284] GEC210 PSR=32 RFS=256 BFS=32 [ 29.953687] DmaAddr=@3fe60000 Total=131072bytes PrdSz=4096 #Prds=32 dma_area=0xff004000 [ 29.961894] GEC210 PSR=5 RFS=256 BFS=32 [ 29.965546] DmaAddr=@3fe60000 Total=131072bytes PrdSz=8192 #Prds=16 dma_area=0xff004000 [ 29.983618] s3c-pl330 s3c-pl330.2: Reset Channel-0 CS-20000f FTC-40000 [ 29.983690] s3c-pl330 s3c-pl330.2: pl330_submit_req:1282 [ 29.985161] s3c-pl330 s3c-pl330.2: s3c_pl330_submit:475! [ 29.990476] s3c-pl330 s3c-pl330.2: pl330_submit_req:1282 [ 29.995733] s3c-pl330 s3c-pl330.2: s3c_pl330_submit:475! AO: [oss] 44100Hz 2ch s16le (2 bytes per sample) 视频: 没视频 开始播放... invalid new backstep 1007 invalid new backstep 1007 invalid new backstep 1007 invalid new backstep 1007 invalid new backstep 1007 invalid new backstep 1007 invalid new backstep 1007 invalid new backstep 1007 invalid new backstep 1007 invalid new backstep 1006 invalid new backstep 1007 invalid new backstep 1007 invalid new backstep 1007 invalid new backstep 1007 invalid new backstep 1007(05:18.0) ??,?% invalid new backstep 1007 A: 0.0 (00.0) of 318.0 (05:18.0) 4.0% 就不动了, cat *.wav > /dev/dsp 时会出现: cat 。。。。input/output error 在 dev 目录下有这几个节点:dsp mixer controlC0 pcmC0D0c pcmC0D0p pcmC0D1c pcmC0D1p seq timer.
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页