I.mx6s上移植wm8960驱动(基于linux3.0.101版本)

2019-04-14 12:48发布

I.mx6s上移植wm8960驱动
  此篇博文只记录移植的步骤,其他不做分析。首先上一张wm8960的硬件连接图:







































1  上电操作
   配置wm8960的上电脚,文件位置:arch/arm/mach-mx6/board-mx6q_sabresd.c

#define SABRESD_CODEC_PWR_EN IMX_GPIO_NR(7, 12) 440 /* Enable wm8960 power supply */ 441 gpio_request(SABRESD_CODEC_PWR_EN, "audio-power"); 442 gpio_direction_output(SABRESD_CODEC_PWR_EN, 1); 443 msleep(1); 444 gpio_set_value(SABRESD_CODEC_PWR_EN, 1); 445 printk("Power up wm8960 successful %s ", __FUNCTION__);
另外,根据原理图可知上电脚为GPIO17,所以相关配置头文件里需将其配置为gpio口,文件位置:arch/arm/mach-mx6/board-mx6dl_sabresd.h

236 /* CODEC_PWR_EN */ 237 MX6DL_PAD_GPIO_17__GPIO_7_12,
2:配置I2C,用于client的生成,文件位置:arch/arm/mach-mx6/board-mx6q_sabresd.c 需确认你的i2c是接的哪个控制器。

805 static struct i2c_board_info mxc_i2c0_board_info[] __initdata = { 806 { 807 I2C_BOARD_INFO("wm8960", 0x1a), 808 }, };
3: 修改wm8960  codec相关的数据结构,此处根据wm8962修改而来,文件位置:arch/arm/mach-mx6/board-mx6q_sabresd.c
以下是修改的地方

57 #include 58 #include ...省略部分内容... 410 static struct platform_device mx6_sabresd_audio_wm8960_device = { 411 .name = "imx-wm8960", 412 }; 413 414 static struct mxc_audio_platform_data wm8960_data; 415 416 static int wm8960_clk_enable(int enable) 417 { 418 if (enable) { 419 clk_enable(clko); 420 printk("%s:wm clk enable ", __FUNCTION__); 421 } 422 else { 423 clk_disable(clko); 424 printk("%s:wm clk disable ", __FUNCTION__); 425 } 426 return 0; 427 } 428 429 static int mxc_wm8960_init(void) 430 { 431 int rate; 432 433 clko = clk_get(NULL, "clko_clk"); 434 if (IS_ERR(clko)) { 435 pr_err("can't get CLKO clock. "); 436 return PTR_ERR(clko); 437 } 438 /* both audio codec and comera use CLKO clk*/ 439 rate = clk_round_rate(clko, 24000000); 440 clk_set_rate(clko, rate); 441 442 wm8960_data.sysclk = rate; 443 444 /* Enable wm8960 power supply */ 445 gpio_request(SABRESD_CODEC_PWR_EN, "audio-power"); 446 gpio_direction_output(SABRESD_CODEC_PWR_EN, 1); 447 msleep(1); 448 gpio_set_value(SABRESD_CODEC_PWR_EN, 1); 449 printk("%s:Power up wm8960 successful ", __FUNCTION__); 450 451 return 0; 452 } 453 454 /* Note: we use struct wm8962_pdata for wm8960_config_data */ 455 /* struct wm8962_pdata defind at linux/wm8962.h */ 456 static struct wm8962_pdata wm8960_config_data = { 457 .gpio_init = { 458 [2] = WM8960_GPIO_FN_DMICCLK, 459 [4] = 0x8000 | WM8960_GPIO_FN_DMICDAT, 460 }, 461 .clock_enable = wm8960_clk_enable, 462 }; 463 464 static struct mxc_audio_platform_data wm8960_data = { 465 .ssi_num = 1, 466 .src_port = 2, 467 .ext_port = 3, 468 .hp_gpio = -1, //SABRESD_HEADPHONE_DET, 469 // .hp_active_low = 1, 470 .mic_gpio = -1, //SABRESD_MICROPHONE_DET, 471 // .mic_active_low = 1, 472 .init = mxc_wm8960_init, 473 .clock_enable = wm8960_clk_enable, 474 }; 475 476 static struct regulator_consumer_supply sabresd_vwm8960_consumers[] = { 477 REGULATOR_SUPPLY("SPKVDD1", "0-001a"), 478 REGULATOR_SUPPLY("SPKVDD2", "0-001a"), 479 }; 480 481 static struct regulator_init_data sabresd_vwm8960_init = { 482 .constraints = { 483 .name = "SPKVDD", 484 .valid_ops_mask = REGULATOR_CHANGE_STATUS, 485 .boot_on = 1, 486 }, 487 .num_consumer_supplies = ARRAY_SIZE(sabresd_vwm8960_consumers), 488 .consumer_supplies = sabresd_vwm8960_consumers, 489 }; 490 491 static struct fixed_voltage_config sabresd_vwm8960_reg_config = { 492 .supply_name = "SPKVDD", 493 .microvolts = 4200000, 494 .gpio = SABRESD_CODEC_PWR_EN, 495 .enable_high = 1, 496 .enabled_at_boot = 1, 497 .init_data = &sabresd_vwm8960_init, 498 }; 499 500 static struct platform_device sabresd_vwm8960_reg_devices = { 501 .name = "reg-fixed-voltage", 502 .id = 4, 503 .dev = { 504 .platform_data = &sabresd_vwm8960_reg_config, 505 }, 506 }; ...省略部分内容... 811 static struct i2c_board_info mxc_i2c0_board_info[] __initdata = { 812 { 813 I2C_BOARD_INFO("wm8960", 0x1a), 814 }, 815 { 816 I2C_BOARD_INFO("ov564x", 0x3c), 817 .platform_data = (void *)&camera_data, 818 }, 819 { 820 I2C_BOARD_INFO("mma8451", 0x1c), 821 .platform_data = (void *)&mma8451_position, 822 }, 823 }; 824 ...省略部分内容... 1535 static int __init imx6q_init_audio(void) 1536 { 1537 if (board_is_mx6_reva()) { 1538 mxc_register_device(&mx6_sabresd_audio_wm8958_device, 1539 &wm8958_data); 1540 imx6q_add_imx_ssi(1, &mx6_sabresd_ssi_pdata); 1541 1542 mxc_wm8958_init(); 1543 } else { 1544 platform_device_register(&sabresd_vwm8960_reg_devices); 1545 mxc_register_device(&mx6_sabresd_audio_wm8960_device, 1546 &wm8960_data); 1547 imx6q_add_imx_ssi(1, &mx6_sabresd_ssi_pdata); 1548 1549 mxc_wm8960_init(); 1550 printk("%s ", __FUNCTION__); 1551 } 1552 1553 return 0; 1554 } ...省略部分内容... 1859 if (board_is_mx6_reva()) { 1860 strcpy(mxc_i2c0_board_info[0].type, "wm8958"); 1861 mxc_i2c0_board_info[0].platform_data = &wm8958_config_data; 1862 } else { 1863 strcpy(mxc_i2c0_board_info[0].type, "wm8960"); 1864 mxc_i2c0_board_info[0].platform_data = &wm8960_config_data; 1865 }
4 修改wm8960.h:include/sound/wm8960.h

9 #ifndef _WM8960_PDATA_H 10 #define _WM8960_PDATA_H 11 12 #define WM8960_DRES_400R 0 13 #define WM8960_DRES_200R 1 14 #define WM8960_DRES_600R 2 15 #define WM8960_DRES_150R 3 16 #define WM8960_MAX_GPIO 6 17 18 #define WM8960_GPIO_FN_DMICCLK 19 19 #define WM8960_GPIO_FN_DMICDAT 20 20 21 22 struct wm8960_data { 23 bool capless; /* Headphone outputs configured in capless mode */ 24 25 int dres; /* Discharge resistance for headphone outputs */ 26 27 int gpio_base; 28 u32 gpio_init[WM8960_MAX_GPIO]; 29 30 u32 mic_cfg; 31 bool irq_active_low; 32 bool spk_mono; 33 }; 34 35 #endif
5 拷贝imx-wm8960.c到sound/soc/imx/目录下(获取地址:https://github.com/PDi-Communication-Systems-Inc/kernel-imx/blob/6bfe025386e4419a50b1b1d5a847a1329d1745cd/sound/soc/imx/imx-wm8960.c)

修改同目录下Kconfig,添加如下:
80 config SND_SOC_IMX_WM8960 81 tristate "SoC Audio support for IMX boards with WM8960" 82 select SND_MXC_SOC_MX2 83 select SND_SOC_WM8960 84 help 85 Say Y if you want to add support for SoC audio on an i.MX board with 86 a WM8960 codec.
修改同目录下Makefile:

18 snd-soc-imx-wm8960-objs := imx-wm8960.o 30 obj-$(CONFIG_SND_SOC_IMX_WM8960) += snd-soc-imx-wm8960.o
用make menuconfig配置上wm8960,编译下载至开发板即可进行验证。

 Ps :一些调试命令及输出log:
root@imx6solo ~$ cat /proc/asound/cards 0 [wm8960audio ]: wm8960-audio - wm8960-audio wm8960-audio
还可以用  ls /dev/snd 看是否有相关节点,如有,则声卡驱动大致ok。下面接着介绍测试的先关方法

移植alsa-lib

 从官网(http://www.alsa-project.org/main/index.php/Download)下载 alsa-lib-1.0.29.tar.bz2包,  6.1解压执行: $ cd alsa-lib-1.0.29 $./configure --host=arm-none-linux-gnueabi --prefix=/home/***/alsa/alsa-lib6.2 编译:
$ make
6.3 安装(需要root权限)
sudo make install
7 移植alsa-utils
从官网(http://www.alsa-project.org/main/index.php/Download)下载 alsa-utils-1.0.29.tar.bz2包, 7.1解压执行: ./configure --host=arm-none-linux-gnueabi --prefix=/home/**/alsa/alsa-utils --with-alsa-inc-prefix=/home/**/alsa/alsa-lib/include --with-alsa-prefix=/home/**/alsa/alsa-lib/lib --disable-alsamixer


注意上面最后的参数:--disable-alsamixer 若不加此参数编译会报错:configure error required courses helper header not found 具体原因不清楚!!

7.2 编译
make
7.3安装(需要root权限)
sudo make install
8 开发板配置: 8.1 库的拷贝: sudo cp alsa-lib/lib/libasound.* /home/**/rootfs/lib/
8.2  alsa-utils拷贝 将alsa-utils/bin目录小的内容拷贝到目标板根文件系统中的bin下 sudo cp alsa-utils/bin/* /home/**/rootfs/bin
8.3 alsa的配置文件拷贝  除了库之外alsa的配置文件也需要拷贝到目标板根文件系统中,这里需要注意的是share目录在目标板的存放位置必须和在主机的存放路径一致!! sudo cp -R **/alsa/alsa-lib/share/ /home/**/rootfs/**/alsa/alsa-lib/重新挂载文件系统到目标板,通过 aplay  -l可以查看我们的音频设备。
root@imx6solo ~$ aplay -L null Discard all samples (playback) or generate zero samples (capture) root@imx6solo ~$ aplay -l **** List of PLAYBACK Hardware Devices **** card 0: wm8960audio [wm8960-audio], device 0: HiFi wm8960-hifi-0 [] Subdevices: 1/1 Subdevice #0: subdevice #0
播放声音文件:
aplay **.wav
最开始一直没有声音,后来发现是wm8960没有初始化的原因,所以要对其进行初始化:

初始化步骤:

第1步 设置power1 2 3;
第2步 设置时钟;
第3步 设置ADC-DAC,注意设置非静音;
第4步 设置audio interface;
第5步 设置volume;
第6步 设置mixer;
参考如下代码(init at imx-wm8960.c:imx_hifi_hw_params function):
void wm8960_init(struct snd_soc_dai *codec_dai) { struct snd_soc_codec *codec = codec_dai->codec; snd_soc_write(codec, 0x19, 0xc0); /* power1, ok*/ snd_soc_write(codec, 0x1a, 0x199); /* power2, ok*/ snd_soc_write(codec, 0x31, 0xf7); /* classd1 : enable L&R, ok */ snd_soc_write(codec, 0x33, 0x11b); /* classd3 : volume max, ok */ snd_soc_write(codec, 0x28, 0x179); /* ok */ snd_soc_write(codec, 0x29, 0x179); /* ok */ snd_soc_write(codec, 0x22, 0x100); /* dac to mixer, ok */ snd_soc_write(codec, 0x25, 0x100); /* dac to mixer, ok */ snd_soc_write(codec, 0x2f, 0x0c); /* left & right output mixer enable, ok */ snd_soc_write(codec, 0x05, 0x00); /* ok */ }