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

I.mx6s上移植wm8960驱动
  此篇博文只记录移植的步骤,其他不做分析。首先上一张wm8960的硬件连接图:
I.mx6s上移植wm8960驱动(基于linux3.0.101版本)_第1张图片






































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\n", __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\n", __FUNCTION__);
 421     }
 422     else {
 423         clk_disable(clko);
 424         printk("%s:wm clk disable\n", __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.\n");
 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\n", __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\n", __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-lib
6.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 */
}


你可能感兴趣的:(Linux/android驱动)