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

你可能感兴趣的:(嵌入式开发)