限于作者能力水平,本文可能存在谬误,因此而给读者带来的损失,作者不做任何承诺。
因项目需求,要将 esp8323 音频驱动移植到 S5P4418 开发板,内核版本为 linux-3.4.y。
驱动源原件列表如下
(1) 将codec的驱动代码es8323.h
和es8323.c
放到linux内核源码目录:
sound/soc/codecs/es8323.h
sound/soc/codecs/es8323.c
(2) 修改文件 sound/soc/codecs/Makefile,在其中增加如下内容:
obj-$(CONFIG_SND_SOC_ES8323) += snd-soc-es8323.o
(3) 修改文件 sound/soc/codecs/Kconfig,在其中增加如下内容:
config SND_SOC_ALL_CODECS
tristate “Build all AsoC CODEC drivers”
...
select SND_SOC_ES8323 if I2C
config SND_SOC_ES8323
tristate
(4) 运行如下命令:
cp sound/soc/nexell/nxp-es8316.c sound/soc/nexell/nxp-es8323.c
(5) 修改sound/soc/nexell/Makefile
,增加如下内容:
obj-$(CONFIG_SND_CODEC_ES8323) += nxp-snd-es8323.o
nxp-snd-es8323-objs := nxp-es8323.o
目的是将文件nxp-es8323.c
加入编译。
(6) 修改文件sound/soc/nexell/Kconfig,增加配置项SND_CODEC_ES8323
:
config SND_CODEC_ES8323
tristate “es8323 I2S audio codec.”
depends on SND_NXP_I2S
select SND_SOC_ES8323
到此,ES8323音频解码器的驱动代码目录,代码编译,配置选项已经搭建完成。
(1) 修改文件 sound/soc/codecs/es8323.c
/*#include
#include
#include */
/* 增加头文件包含行 */
#include
/* 增加如下内容 */
#define SPK_CON (PAD_GPIO_B + 24)
#define HP_DET (PAD_GPIO_B + 27)
#define GPIO_LOW 0
#define GPIO_HIGH 1
#ifdef DEBUG
#define DBG(x...) printk(x)
#else
#define DBG(x...) do {} while (0)
#endif
int es8323_jack_insert = 0;
extern int es8323_spk_on(int enable);
void es8323_mono_en(int enable)
{
struct snd_soc_codec *codec;
codec = es8323_codec;
if (enable) {
snd_soc_write(codec, ES8323_DACPOWER, 0x05);
snd_soc_write(codec, ES8323_DACCONTROL7, 0x20);
} else {
snd_soc_write(codec, ES8323_DACCONTROL7, 0x07);
snd_soc_write(codec, ES8323_DACPOWER, 0x00);
}
}
EXPORT_SYMBOL(es8323_mono_en);
/* 将函数es8323_off_amp的内容注释掉,只保留函数体 */
static void es8323_off_amp(bool on)
{
#if 0
int oldlevel = gpio_get_value(SPK_CON);
int newlevel = (on) ? GPIO_LOW : GPIO_HIGH;
printk("%s: entering...", __func__);
if (oldlevel != newlevel) {
gpio_set_value(SPK_CON, newlevel);
printk("%s: Amplifier turn %s\n", __func__, on ? "on" : "off");
dev_dbg(es8323_codec->dev, "Amplifier turn %s\n", on ? "off" : "on");
}
#endif
}
/*
* 将函数
* es8323_digital_mute()
* es8323_i2c_suspend()
* es8323_i2c_resume()
* 注释掉。
* 并将相应结构体es8323_ops ,es8323_i2c_driver 中相应结构中的赋
* 值去掉。
*/
/* 修改以下函数内容: */
static int es8323_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
switch (level) {
…
case SND_SOC_BIAS_PREPARE:
…
if (es8323_jack_insert) {
snd_soc_write(codec, ES8323_DACCONTROL7, 0x07);
} else {
snd_soc_write(codec, ES8323_DACCONTROL7, 0x20);
}
…
if (es8323_jack_insert) {
snd_soc_write(codec, ES8323_DACPOWER, 0x00);
} else {
snd_soc_write(codec, ES8323_DACPOWER, 0x05);
es8323_spk_on(1);
}
DBG("%s: jack_insert %d\n", __func__, es8323_jack_insert);
break;
case SND_SOC_BIAS_STANDBY:
…
if (es8323_jack_insert) {
snd_soc_write(codec, ES8323_DACCONTROL7, 0x07);
} else {
snd_soc_write(codec, ES8323_DACCONTROL7, 0x20);
}
…
if (es8323_jack_insert) {
snd_soc_write(codec, ES8323_DACPOWER, 0x00);
} else {
snd_soc_write(codec, ES8323_DACPOWER, 0x05);
es8323_spk_on(0);
}
break;
…
}
/* 修改函数 */
static void det_initalize(void)
{
if (gpio_request(HP_DET, "hp_det")) {
pr_err("%s %d request error", __func__, __LINE__);
return;
}
det_initalized = false;
schedule_delayed_work(&det_work, msecs_to_jiffies(100));
return;
}
/* 修改函数 */
static int es8323_probe(struct snd_soc_codec *codec)
{
…
ret = gpio_request(SPK_CON, "spk_con");
if (ret != 0) {
pr_err("%s %d request error", __func__, __LINE__);
goto err;
}
gpio_set_value(SPK_CON, GPIO_LOW);
//es8323_off_amp(true);
msleep(30);
…
es8323_init_regs(codec);
es8323_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
codec->dapm.idle_bias_off = 0;
ret = snd_soc_add_codec_controls(codec, es8323_snd_controls,
ARRAY_SIZE(es8323_snd_controls));
/*ret = snd_soc_add_controls(codec, es8323_snd_controls,
ARRAY_SIZE(es8323_snd_controls));*/
…
}
至此,文件的es8323.c
的修改完成主要是适应耳机、喇叭的切换,以及去掉不适应的平台的多余代码。
(2) 修改文件sound/soc/nexell/nxp-es8323.c
,将文件中变量名、函数名,字串中包含的es8316
字串全部替换为es8323
;字串ES8316
全部替换为ES8323
。关于ES8323 I2C从设的设备地址修改,很关键,如果不正确,系统将无法使ES8323正常工作。
static struct snd_soc_dai_link es8323_dai_link = {
.name = "ASOC-ES8323",
.stream_name = "es8323 HiFi",
.cpu_dai_name = str_dai_name, /* nxp_snd_i2s_driver name */
.platform_name = DEV_NAME_PCM, /* nxp_snd_pcm_driver name */
.codec_dai_name = "ES8323 HiFi", /* es8323_dai's name */
.codec_name = "ES8323.0-0010",/* es8323_i2c_driver name + '.' + bus + '-' + address(7bit) */
.ops = &es8323_ops,
.symmetric_rates = 1,
.init = es8323_dai_init,
};
(3) 修改文件arch/arm/plat-s5p4418/nanopi2/include/cfg_gpio.h
。 ES8323用到了I2C0和I2S0,以及耳机检测,喇叭声音控制等PIN脚,所以要缺确保这些GPIO的正确配置。具体配置如下:
/* I2C 0配置 */
#define PAD_GPIOD2 (PAD_MODE_ALT | PAD_FUNC_ALT1 |
PAD_LEVEL_LOW | PAD_PULL_OFF |
PAD_STRENGTH_1)
#define PAD_GPIOD3 (PAD_MODE_ALT | PAD_FUNC_ALT1 |
PAD_LEVEL_LOW | PAD_PULL_OFF |
PAD_STRENGTH_1)
/* I2S 0配置 */
#define PAD_GPIOD9 (PAD_MODE_ALT | PAD_FUNC_ALT1 | PAD_LEVEL_LOW | PAD_PULL_OFF |
PAD_STRENGTH_0)
#define PAD_GPIOD10 (PAD_MODE_ALT | PAD_FUNC_ALT1 |
PAD_LEVEL_LOW | PAD_PULL_OFF |
PAD_STRENGTH_0)
/* 耳机插入检测GPIOB27,耳机声音输出使能GPIOC0,喇叭声音输出使能
GPIOB24的配置 */
#define PAD_GPIOB27 (PAD_MODE_IN | PAD_FUNC_ALT1 |
PAD_LEVEL_LOW | PAD_PULL_OFF | PAD_LEVEL_LOW | PAD_PULL_OFF |
PAD_STRENGTH_0)
#define PAD_GPIOD10 (PAD_MODE_ALT | PAD_FUNC_ALT1 |
PAD_LEVEL_LOW | PAD_PULL_OFF |
PAD_STRENGTH_0)
/* 耳机插入检测GPIOB27,耳机声音输出使能GPIOC0,喇叭声音输出使能
GPIOB24的配置 */
#define PAD_GPIOB27 (PAD_MODE_IN | PAD_FUNC_ALT1 |
PAD_LEVEL_LOW | PAD_PULL_OFF |
(4) 修改文件arch/arm/plat-s5p4418/nanopi2/device.c
/* 首先增加如下内容 */
#if defined(CONFIG_SND_CODEC_ES8323) || defined(CONFIG_SND_CODEC_ES8323_MODULE)
#define ES8323_I2C_BUS (0)
/* CODEC */
static struct i2c_board_info __initdata es8323_i2c_bdi = {
.type = "es8323",
.addr = (0x20>>1), // 0x11 (7BIT), 0x22(8BIT)
};
/* DAI */
struct nxp_snd_dai_plat_data i2s_dai_data = {
.i2s_ch = 0,
.sample_rate = 48000,
.pcm_format = SNDRV_PCM_FMTBIT_S16_LE,
#if 1
.hp_jack = {
.support = 0,
.detect_io = PAD_GPIO_B + 27,
.detect_level = 1,
},
#endif
};
static struct platform_device es8323_dai = {
.name = "es8323-audio",
.id = 0,
.dev = {
.platform_data = &i2s_dai_data,
}
};
#endif
...
/* 同时在修改函数nxp_board_devices_register(),增加红色部分内容: */
void __init nxp_board_devices_register(void)
{
…
#if defined(CONFIG_SND_CODEC_ES8316) || defined(CONFIG_SND_CODEC_ES8316_MODULE)
if (board_with_es8316()) {
printk("plat: add device asoc-es8316\n");
if (board_is_nanopc() || \
board_is_smart4418() || board_is_smart4418sdk())
i2s_dai_data.hp_jack.support = 1;
i2c_register_board_info(ES8316_I2C_BUS, &es8316_i2c_bdi, 1);
platform_device_register(&es8316_dai);
}
#endif
#if defined(CONFIG_SND_CODEC_ES8323) || defined(CONFIG_SND_CODEC_ES8323_MODULE)
//if (board_with_es8323()) {
printk("plat: add device asoc-es8323\n");
if (board_is_nanopc() || \
board_is_smart4418() || board_is_smart4418sdk())
i2s_dai_data.hp_jack.support = 1;
i2c_register_board_info(ES8323_I2C_BUS, &es8323_i2c_bdi, 1);
platform_device_register(&es8323_dai);
//}
#endif
…
}
以此将I2C的board info以及将平台的DAI设备注册到系统。
(5) 修改文件arch/arm/configs/nanopi2_linux_defconfig
。修改该文件的目的是将ES8323音频解码选为nanopi2的默认配置,这一步并不是必须的。在文件中增加两行:
CONFIG_SND_CODEC_ES8323=y
CONFIG_SND_SOC_ES8323=y
至此,所有源代码,配置文件都已经修改完成,接下来就是编译测试。
make menuconfig # 打开 ESP8323 相关配置
make uImage
. mkbootimage
kernel.img
到开发板fastboot flash boot kernel.img
系统启动后,可使用系统自带的aplay测试.wav文件,如:
aplay test.wav
用 mplayer 测试更多格式,如.wav和.mp3。
mplayer test.wav
mplayer test.mp3
测试时,正常情况下可以使用耳机和喇叭听到声音,当有耳机插入时,喇叭将不会发出声音,只有耳机有声音。
sound/soc/codecs/es8323.h
sound/soc/codecs/es8323.c
sound/soc/codecs/Makefile
sound/soc/codecs/Kconfig
sound/soc/nexell/nxp-es8323.c
sound/soc/nexell/Makefile
sound/soc/nexell/Kconfig
arch/arm/plat-s5p4418/nanopi2/include/cfg_gpio.h
arch/arm/plat-s5p4418/nanopi2/device.c
arch/arm/configs/nanopi2_linux_defconfig
本文基于多年前的开发笔记,或有错漏之处,请读者仔细分辨。