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
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
return ret;
/* set the CPU system clock */
ret = snd_soc_dai_set_sysclk(cpu_dai, 0, sysclk, SND_SOC_CLOCK_IN);
if (ret
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
[1] [2] [3]
404-电子工程世界
北京市海淀区知春路23号集成电路设计园量子银座1305
电话:(010)82350740
邮编:100191
电子工程世界版权所有
京ICP证060456号
Copyright © 2005-2021 EEWORLD.com.cn, Inc. All rights reserved