您的位置:首页 > 移动开发 > Android开发

Android 5.1-s5p6818平台音频无输出的问题

2016-11-08 14:32 399 查看
继续6818平台的外设驱动修改,其中音频输出的问题困扰了好几天,一直没有找到原因(按照原DEMO版相同电路相同代码但是新板子上面的音频就是没有输出~),在和硬件工程师的不断沟通当中,不断地思考到底问题在哪里。主要一个难以查找原因的原因是新的硬件把耳机插孔去掉了,但是这个原因似乎就是因为耳机检测的引脚电平和DEMO版的电平不一样导致的(后来证实的确是)。

音频的处理流程大致是系统输出数据流由NAU8822J进行解码,然后通过AW8733进行放大,最后将信号给喇叭。(当然了,这里讲的不是具体的音频系统组成和工作原理,请参考:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=27570663&id=4432842http://www.2cto.com/kf/201409/337057.html等)

根据AW8733 放大器数据手册,驱动是控制模式分为四种,不同点就是分贝不一样以及是否防破音。具体是这样讲的:

AW8733 采用一线脉冲方式控制四个状态,实现不同增益和NCN 模式的选择。状态1 的增益为12dB,没有防破音功能;状态2 的增益为16dB,开启防破音功能;状态3 的增益为24dB,
没有防破音功能;状态4 的增益为27.5dB,开启防破音功能。



首先找到驱动文件以及AW8733驱动的路径:~/kernel/sound/soc/nexell/nxp-wm8979.c。文件代码如下:

/*
* (C) Copyright 2009
* jung hyun kim, Nexell Co, <jhkim@nexell.co.kr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include <linux/module.h>
#include <linux/platform_device.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dai.h>
#include <sound/jack.h>
#include <linux/gpio.h>
#include <mach/platform.h>

#include <linux/delay.h>

#include "../codecs/wm8978.h"
#include "nxp-i2s.h"

/*
#define	pr_debug	printk
*/

#define	AUDIO_AMP_POWER		CFG_IO_AUDIO_AMP_POWER

static struct snd_soc_jack_gpio jack_gpio;
static struct snd_soc_codec *wm8976 = NULL;
static int codec_bias_level = 0;

static int wm8976_jack_status_check(void)
{
struct snd_soc_codec *codec = wm8976;
int jack = jack_gpio.gpio;
int invert = jack_gpio.invert;
int level = gpio_get_value_cansleep(jack);

if (!codec)
return -1;

printk("%s: LEVE= %d,invert=%d\n", __func__, level,invert);

if(invert)
level = !level;

printk("%s: hp jack %s\n", __func__, level?"IN":"OUT");

//	if (!level) {
/* HP off/AMP on */
snd_soc_update_bits(codec, WM8978_LOUT1_HP_CONTROL, 0x40, 0x40);
snd_soc_update_bits(codec, WM8978_ROUT1_HP_CONTROL, 0x40, 0x40);
gpio_direction_output(AUDIO_AMP_POWER, 1);
//	} else {
//		/* HP on/AMP off  */
//		snd_soc_update_bits(codec, WM8978_LOUT1_HP_CONTROL, 0x40, 0x0);
//		snd_soc_update_bits(codec, WM8978_ROUT1_HP_CONTROL, 0x40, 0x0);
//		gpio_direction_output(AUDIO_AMP_POWER, 0);
//	}

return level;
}

static int wm8976_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;
unsigned int freq = params_rate(params) * 256;	/* 48K * 256 = 12.288 Mhz */
unsigned int fmt  = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS;
int ret = 0;

pr_debug("%s\n", __func__);
ret = snd_soc_dai_set_sysclk(codec_dai, 0, freq, SND_SOC_CLOCK_IN);
if (0 > ret)
return ret;

ret = snd_soc_dai_set_fmt(codec_dai, fmt);
if (0 > ret)
return ret;
return ret;
}

static int wm8976_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_codec *codec = rtd->codec;
int stream = substream->stream;
int jack = jack_gpio.gpio;
int invert = jack_gpio.invert;
int level = gpio_get_value_cansleep(jack);

pr_debug("%s\n", __func__);

/* set jack detect gpio2 */
snd_soc_update_bits(codec, WM8978_JACK_DETECT_CONTROL_1, 0x30, 0x10);
snd_soc_update_bits(codec, WM8978_JACK_DETECT_CONTROL_1, 0x40, 0x00);	// disable jack detection

if (stream == SNDRV_PCM_STREAM_CAPTURE)
snd_soc_update_bits(codec, WM8978_POWER_MANAGEMENT_1, 0x10, 0x10);	// MICBIASEN

if(invert)
printk(KERN_INFO "%s %d %s\n", "invert=",invert,"-----2617");
printk(KERN_INFO "%s %d %s\n", "level=", level,"-----2617");
level = !level;

//	if (!level) {
pr_debug("AMP ON\n");
gpio_direction_output(AUDIO_AMP_POWER, 1);
//	}
return 0;
}

static void wm8976_shutdown(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_codec *codec = rtd->codec;
int stream = substream->stream;

pr_debug("%s\n", __func__);

if (stream == SNDRV_PCM_STREAM_CAPTURE)
snd_soc_update_bits(codec, WM8978_POWER_MANAGEMENT_1, 0x10, 0x00);	// MICBIASEN

if (stream == SNDRV_PCM_STREAM_PLAYBACK)
gpio_direction_output(AUDIO_AMP_POWER, 0);
}

static int wm8976_suspend_pre(struct snd_soc_card *card)
{
struct snd_soc_codec *codec = wm8976;

snd_soc_update_bits(codec, WM8978_LOUT1_HP_CONTROL, 0x40, 0x40);
snd_soc_update_bits(codec, WM8978_ROUT1_HP_CONTROL, 0x40, 0x40);
gpio_direction_output(AUDIO_AMP_POWER, 0);
return 0;
}

static int wm8976_resume_pre(struct snd_soc_card *card)
{
struct snd_soc_codec *codec = wm8976;
PM_DBGOUT("%s BAIAS=%d\n", __func__, codec->dapm.bias_level);
codec_bias_level = codec->dapm.bias_level;
return 0;
}

static int wm8976_resume_post(struct snd_soc_card *card)
{
struct snd_soc_codec *codec = wm8976;
PM_DBGOUT("%s BAIAS=%d\n", __func__, codec->dapm.bias_level);

if (SND_SOC_BIAS_OFF != codec_bias_level)
codec->driver->resume(codec);

wm8976_jack_status_check();
return 0;
}

static struct snd_soc_jack_gpio jack_gpio = {
.invert		= true,			// High detect : invert = false
.name		= "hp-gpio",
.report		= SND_JACK_HEADPHONE,
.debounce_time = 200,
.jack_status_check = wm8976_jack_status_check,
};

static struct snd_soc_jack hp_jack;

static int wm8976_dai_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_jack_gpio *jack = &jack_gpio;
int ret;

pr_debug("%s: %s\n", __func__, jack->name);

wm8976 = codec;

if (NULL == jack->name)
return 0;

/* Headset jack detection */
ret = snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE, &hp_jack);
if (ret)
return ret;

ret = snd_soc_jack_add_gpios(&hp_jack, 1, jack);
pr_debug("%s: %s register audio jack detect, %d\n",
ret?"Fail":"Done", __func__, jack->gpio);

return 0;
}

static struct snd_soc_ops wm8976_ops = {
.hw_params 	= wm8976_hw_params,
.startup	= wm8976_startup,
.shutdown	= wm8976_shutdown,
};

static char str_dai_name[16] = DEV_NAME_I2S;

static struct snd_soc_dai_link wm8976_dai_link = {
.name 			= "ASOC-WM8976",
.stream_name 	= "wm8976 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 = "wm8978_codec",		/* wm8976_dai's name */
.codec_name 	= "wm8978.0-001a",		/* wm8976_i2c_driver name + '.' + bus + '-' + address(7bit) */
.ops 			= &wm8976_ops,
.symmetric_rates = 1,
.init			= wm8976_dai_init,
.ops 			= &wm8976_ops,
};

static struct snd_soc_card wm8976_card = {
.name 			= "I2S-WM8976",		/* proc/asound/cards */
.owner 			= THIS_MODULE,
.dai_link 		= &wm8976_dai_link,
.num_links 		= 1,
.suspend_pre	= &wm8976_suspend_pre,
.resume_pre		= &wm8976_resume_pre,
.resume_post	= &wm8976_resume_post,
};

/*
* codec driver
*/
static int wm8976_probe(struct platform_device *pdev)
{
struct nxp_snd_dai_plat_data *plat = pdev->dev.platform_data;
struct snd_soc_card *card = &wm8976_card;
struct snd_soc_jack_gpio *jack = &jack_gpio;
struct nxp_snd_jack_pin *hpin = NULL;
unsigned int rates = 0, format = 0;
int ret;

if (plat) {
rates = plat->sample_rate;
format = plat->pcm_format;
hpin = &plat->hp_jack;
if (hpin->support) {
jack->gpio = hpin->detect_io;
jack->invert = hpin->detect_level ?  false : true;
jack->debounce_time = hpin->debounce_time ? : 200;
} else {
jack->name = NULL;
}
sprintf(str_dai_name, "%s.%d", DEV_NAME_I2S, plat->i2s_ch);	// set I2S name
}

gpio_request(AUDIO_AMP_POWER, "wm8978");

/*
* register card
*/
card->dev = &pdev->dev;
ret = snd_soc_register_card(card);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret);
return ret;
}

if (card->rtd->cpu_dai) {
struct snd_soc_dai *cpu_dai = card->rtd->cpu_dai;
struct snd_soc_dai_driver *i2s = cpu_dai->driver;
if (rates) {
rates = snd_pcm_rate_to_rate_bit(rates);
if (SNDRV_PCM_RATE_KNOT == rates)
printk("%s, invalid sample rates=%d\n", __func__, plat->sample_rate);
else {
i2s->playback.rates = rates;
i2s->capture.rates = rates;
}
}
if (format) {
i2s->playback.formats = format;
i2s->capture.formats = format;
}
}

pr_debug("wm8976-dai: register card %s -> %s\n",
card->dai_link->codec_dai_name, card->dai_link->cpu_dai_name);
return 0;
}

static int wm8976_remove(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
pr_debug("%s\n", __func__);
snd_soc_unregister_card(card);
gpio_free(AUDIO_AMP_POWER);
return 0;
}

static struct platform_driver wm8976_driver = {
.driver		= {
.name	= "wm8976-audio",
.owner	= THIS_MODULE,
.pm 	= &snd_soc_pm_ops,	/* for suspend */
},
.probe		= wm8976_probe,
.remove		= __devexit_p(wm8976_remove),
};
module_platform_driver(wm8976_driver);

MODULE_AUTHOR("jhkim <jhkim@nexell.co.kr>");
MODULE_DESCRIPTION("Sound codec-wm8976 driver for the SLSI");
MODULE_LICENSE("GPL");


在启动代码部分:

static int wm8976_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_codec *codec = rtd->codec;
int stream = substream->stream;
int jack = jack_gpio.gpio;
int invert = jack_gpio.invert;
int level = gpio_get_value_cansleep(jack);

pr_debug("%s\n", __func__);

/* set jack detect gpio2 */
snd_soc_update_bits(codec, WM8978_JACK_DETECT_CONTROL_1, 0x30, 0x10);
snd_soc_update_bits(codec, WM8978_JACK_DETECT_CONTROL_1, 0x40, 0x00);	// disable jack detection

if (stream == SNDRV_PCM_STREAM_CAPTURE)
snd_soc_update_bits(codec, WM8978_POWER_MANAGEMENT_1, 0x10, 0x10);	// MICBIASEN
<pre code_snippet_id="1972935" snippet_file_name="blog_20161108_2_6316795" name="code" class="plain">		printk(KERN_INFO "%s %d %s\n", "invert=",invert,"-----2617");
printk(KERN_INFO "%s %d %s\n", "level=", level,"-----2617");


if(invert){
level = !level;
}
//	if (!level) {
pr_debug("AMP ON\n");
gpio_direction_output(AUDIO_AMP_POWER, 1);
//	}
return 0;

根据kernel运行的log可以知道level值为“1”,invert的值为“0”,那么显然注释掉的if语句是不能够进入的,也就是说音频放大器AW8733模块没有工作。继续跟踪代码便知道就是invert导致level的修改,而invert的值就是耳机检测判断而来。所以问题肯定就是耳机检测的电平搞错了。

通过耳机检测引脚"MCU_HP_DET"找到对应引脚以及定义的结构体:

/* DAI */
struct nxp_snd_dai_plat_data i2s_dai_data = {
.i2s_ch = 0,
.sample_rate	= 48000,
.hp_jack		= {
.support		= 1,
.detect_io		= CFG_IO_HP_DETECT,
.detect_level	= 1,
},
};
从上述代码块中可以看出,detct_io是耳机检测脚,他是通过CFG_IO_HP_DETECT引脚电平状态来判断是否有耳机插入的,那么现在的这个引脚已经弃用,而代码里面还在等待这个引脚的输入状态,所以肯定出问题了,因此,将上述代码直接做这样的修改:

/* DAI */
struct nxp_snd_dai_plat_data i2s_dai_data = {
.i2s_ch = 0,
.sample_rate	= 48000,
.hp_jack		= {
.support		= 1,
//			.detect_io		= CFG_IO_HP_DETECT,
.detect_io		= 1,
.detect_level	= 1,
},
};


这样,检测引脚的电平直接设置好了,把耳机检测忽略。编译后果然验证了导致这个问题的分析。

关于耳机插拔硬件检测可以参考这个文章:http://blog.csdn.net/fengying765/article/details/38301483
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: