㉕AW-A33 Linux驱动开发之audio子系统驱动程序

本文介绍了Linux sound子系统的历史以及音频接口的三种类型:IIS、PCM和AC'97。重点讲解了IIS协议的原理和操作模式,并探讨了PCM接口的灵活性。文章还提到了AC'97标准及其特点,然后转向全志A33芯片在音频驱动中的应用,特别是ALSA架构如何简化音频驱动开发。最后,作者分享了代码结构分析和ALSA设备文件的组织方式。
摘要由CSDN通过智能技术生成

在Linux源码里,Aduio这一部分现在是一个独立文件夹叫sound,在2.x的版本时,sound这个目录是在drivers里的,后来从这个里面剥离出来了,很多人不知道其中的原因,我也不知道,我们先回顾一下这段历史,当时Linux主线的audio还在使用OSS架构,一开始是在drivers里,后来就把OSS架构这个audio剥离出来了,可能是因为OSS走向了商业化的原因,后来因为商业化导致版权问题,ALSA就出现了,同样代码都在sound里,就这样一直延续了下来.

下边我们先来介绍一下Audio的基础知识,先从接口开始,常见接口有3种(IIS/ PCM/ AC'97):

IIS

Integrated Interchip Sound,这种接口跟IIC差不多,因为都是同一个公司:Philips做的,用于数字音频数据在系统内器件之间传输,例如编解码器CODEC、DSP、数字输入/输出接口、ADC、DAC和数字滤波器等。其与IIC无关联,这一点是需要注意的,IIS是个相对来说简单的接口协议,没有地址和片选机制,在总线上,只能同时存在一个主设备和发射设备;提供时钟的设备为主设备,可以是发射设备也可以是接收设备,或者是协调两者的其他控制设备。

IIS协议定义三根信号线:时钟信号SCK、数据信号SD和左右声道选择信号WS,如下如:

SCK:模块内的同步信号,从模式时由外部提供,主模式时由内部产生;

SD:串行数据,以二进制补码形式在数据线上传输;在WS变化后的第一个SCK脉冲,MSB先行;

WS:帧时钟,其电平变化频率等于声音采样率,高电平和低电平状态就是左右声道区分的状态;

IIS的操作模式有3种: 标准模式, 左对齐模式和右对齐模式.

标准模式:是左对齐模式延迟一个时钟位变化来的,左右通道的数据MSB均是在WS变化后第二个SCK/BCLK上升沿有效.

左对齐模式:标准左对齐格式的数据的MSB没有相对于BCLK延迟一个时钟。左对齐格式的左右声道数据的MSB在WS边沿变化后SCK/BCLK的第一个上升沿有效,支持16~32bit字长格式.

右对齐模式:接收设备必须事先知道待传数据的字长,目前是索尼公司采用这种格式.

这3种模式下,WS线的意义是不同的,在标准模式下的WS时钟高电平为右声道,低电平为左声道,左右对齐模式刚好相反.

SCK = 采样率(48K、44.1K、16K等) x  字长(16bit、24bit、32bit) x 2(左右两通道);

PCM

Pulse Code Modulation,是通过等时间隔(即采样率时钟周期)采样将模拟信号数字化的方法.接口传输的音频数据是通过PCM方式采样得到的,区别于PDM形式;IIS传输的也是PCM类型数据,属于其一个特例。相比于IIS,PCM接口更加灵活,通过时分复用TDM方式,PCM接口支持多大N个声道的数据.TDM不像IIS有统一标准,不同厂家TDM时有差异。

PCM Interface IIS Interface
PCM_OUT SD_OUT
PCM_IN SD_IN
PCM_SYNC WS
PCM_CLK SCK

接口名称有如下规律:
PCM:传输单声道数据,比如麦克风;
IIS:传输双声道数据,比如喇叭;
TDM:传输两个以上声道数据,同时区别于IIS特定格式。

根据SD相对于FSYNC的位置,TDM分两种基本模式:
Mode A: 数据在FSYNC有效后,BCLK的第二个上升沿有效;
Mode B: 数据在FSYNC有效后,BCLK的第一个上升沿有效;

不同厂商对于两种模式的定义可能有点差别。
FSYNC的高电平等于一个BCLK的周期,其频率就等于采样率,与通道数无关。
BCLK的频率会随通道数的增加成倍数增加:8 × 32 × 48kHz = 12.288 MHz。
其中又分为长帧同步和短帧同步:

>短帧同步:一个脉冲宽度等于一个BCLK的周期长度

>长帧同步:一个脉冲宽度等于一个slot的长度

AC'97

AC97是以Intel为首的5个PC厂商,在1997年共同提出的规格标准。与PCM/I2S不同:AC97不只是一种数据格式,它还具有控制功能。AC'97采用AC-Link与外部的编解码器相连,AC-Link接口包括:
(1)位时钟(BITCLK)
(2)同步信号校正(SYNC)
(3)从编码到处理器及从处理器中解码(SDATDIN与SDATAOUT)的数据队列

AC'97数据帧以SYNC脉冲开始,包括12个20位时间段(时间段为标准中定义的不同的目的服务)及16位“tag”段,共计256个数据序列。下图是AC97的接口图:

在全志A33上,支持PCM,IIS,所以,这里也只讨论这两种接口,下面,我们先通过外围电路了解A33上audio相关内容,先看一下电路图:

CPU部分:

耳机部分:

扬声器部分:

MIC部分:

剩下还有一些电路的供电,稳压等这里就不贴出来了,我们这里以CPU部分来说明,因为这部分比较抽象,容易说,每个管脚的意义如下面这个表格所示:

Signal Description
Signal Name Type Description
HBIAS OUT 耳机偏置电压
MBIAS OUT 主麦克偏置电压
PHONEOUTP OUT 听筒正极输出
PHONEOUTN OUT 听筒负极输出
MICIN1P IN 主麦克正极输入
MICIN1N IN 主麦克负极输入
MICIN2P IN 副麦克正极输入
MICIN2N IN 副麦克负极输入
PHONEP IN 听筒正极输入
PHONEN IN 听筒负极输入
LINEINL IN 左输入行
LINEINR IN 右输入行
HPCOMFB IN 耳机基准电压反馈
HPCOM OUT 耳机基准电压
HPOUTL OUT 耳机左声道
HPOUTR OUT 耳机右声道
Power Description
VRA1 OUT 参考电压
VRA2 OUT 参考电压
VRP OUT 参考电压
AVCC IN 模拟电压
HPVCCIN IN 耳机放大器电源
HPVCCBP OUT 耳机放大器功率旁路
AGND GND 模拟电压地线

其中标注蓝色字体的是实际硬件用到的,这里扬声器和耳机共用耳机声道,硬件上通过PA-SHDN引脚(PH9)进行切换,以上内容就是SoC给我们暴露的实际引脚,情况是这个样子的,就是音频编解码器是在SoC内部的,我们用的是片上外设,比买一个音频芯片要省出来一些钱,然后SoC内部,音频编解码器对SoC暴露PCM/IIS接口(因为IIS是PCM的特例,硬件结构类似,通过软件配置,即可实现切换接口),对外部直接暴露驱动外设的接口,本质上外部接口就是ADC,DAC,我们看一下datasheet上音频编解码器的大致框架图,因为寄存器比较多,有100多个,这里就不列举出来了:

有了基本的一个audio硬件的概念之后,我们就来学习ALSA框架,之后,后边结合代码看一下,下面说一下ALSA的一些基本知识,推荐一下这个人写的音频系列教程,特详细,此处就是借鉴他的:https://blog.csdn.net/DroidPhone

概述

上图展现了在Linux上Audio的ALSA架构,用户空间的alsa-lib对应用程序提供统一的API接口,这样可以隐藏了驱动层的实现细节,简化了应用程序的实现难度。内核空间中,alsa-soc其实是对alsa-driver的进一步封装,它针对嵌入式设备提供了一些列增强的功能.

ALSA设备文件结构

cd /dev/snd
ls -l
crw-rw----+ 1 root audio 116, 8 2011-02-23 21:38 controlC0
crw-rw----+ 1 root audio 116, 4 2011-02-23 21:38 midiC0D0
crw-rw----+ 1 root audio 116, 7 2011-02-23 21:39 pcmC0D0c
crw-rw----+ 1 root audio 116, 6 2011-02-23 21:56 pcmC0D0p
crw-rw----+ 1 root audio 116, 5 2011-02-23 21:38 pcmC0D1p
crw-rw----+ 1 root audio 116, 3 2011-02-23 21:38 seq
crw-rw----+ 1 root audio 116, 2 2011-02-23 21:38 timer

它们代表的意义是:

controlC0	-->	用于声卡的控制,例如通道选择,混音,麦克风的控制等
midiC0D0  	-->	用于播放midi音频
pcmC0D0c 	-->	用于录音的pcm设备
pcmC0D0p 	-->	用于播放的pcm设备
seq  		-->	音序器
timer 		-->	定时器

其中,C0D0代表的是声卡0中的设备0,pcmC0D0c最后一个c代表capture,pcmC0D0p最后一个p代表playback,这些都是alsa-driver中的命名规则。在我这用的这个平板上,设备没有这么多,只有下面这几个:

crw-rw---- system   audio    116,   0 1970-01-11 10:04 controlC0
crwxrwxrwx media    media    116,  24 1970-01-11 10:04 pcmC0D0c
crwxrwxrwx media    media    116,  16 1970-01-11 10:04 pcmC0D0p
crw-rw---- system   audio    116,  33 1970-01-11 10:04 timer

目录树大概是这么一种情况:

下面对这些文件做一个大概的介绍:

core 该目录包含了ALSA驱动的中间层,它是整个ALSA驱动的核心部分
core/oss 包含模拟旧的OSS架构的PCM和Mixer模块
core/seq 有关音序器相关的代码
include ALSA驱动的公共头文件目录,该目录的头文件需要导出给用户空间的应用程序使用,通常,驱动模块私有的头文件不应放置在这里
drivers 放置一些与CPU、BUS架构无关的公用代码
i2c ALSA自己的I2C控制代码
pci pci声卡的顶层目录,子目录包含各种pci声卡的代码
isa isa声卡的顶层目录,子目录包含各种isa声卡的代码
soc 针对system-on-chip体系的中间层代码
soc/codecs 针对soc体系的各种codec的代码,与平台无关

我们要操作的代码就在soc目录中,这部分是和厂商相关的,全志的是soc/sunxi.

下面我们分析一下代码结构以及实际的代码,通过分析menuconfig, Makefile, Kconfig以及.o文件,绘制了下面这张图:

其中,core部分是通用的,我们主要移植内容是SoC部分,此部分一般有厂商提供,此处全志是提供了的,全志提供的这部分内容,大多也是通用于全志平台的芯片,不同芯片区别就是类似于sun8iw5_sndcodec.c这种文件,sun8iw5代表A33,sun8iw7代表H3,这个根据实际情况配置的,其它部分大多数情况下通用,因为时间原因,这里不能深入探讨具体实现框架,后边,真正有空了,会对整个子系统进行深入研究,也会分享出来,我们这里就贴一下差异部分代码,sun8iw5_sndcodec.c

其它代码可参考这里:https://github.com/orangepi-xunlong/orangepi_h3_linux

#include <linux/module.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/gpio.h>
#include <linux/io.h>
#include <linux/regulator/consumer.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/initval.h>
#include <mach/sys_config.h>
#include <mach/gpio.h>
#include <linux/pinctrl/pinconf-sunxi.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pm.h>
#include <linux/power/scenelock.h>

#include "sunxi_codecdma.h"
#include "sun8iw5_sndcodec.h"

#define sndpcm_RATES  (SNDRV_PCM_RATE_8000_192000|SNDRV_PCM_RATE_KNOT)
#define sndpcm_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
		                     SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
static struct regulator* hp_ldo = NULL;
static char *hp_ldo_str = NULL;

static int play_running = 0;
static int cap_running = 0;
static volatile int current_running = -1;

/*for pa gpio ctrl*/
static script_item_u item;
//static script_item_value_type_e  type;

/*for phone call flag*/
static bool codec_analog_phonein_en		   = false;
static bool codec_analog_mainmic_en    = false;
static bool codec_analog_phoneout_en          = false;
static bool codec_lineinin_en          = false;
static bool codec_lineincap_en         = false;
static bool codec_analog_headsetmic_en = false;
static bool codec_speakerout_en        = false;

static bool codec_earpieceout_en       = false;

static bool codec_voice_record_en      = false;
static bool codec_headphoneout_en      = false;
/*Digital_bb*/
static bool codec_digital_headsetmic_en = false;
static bool codec_digital_mainmic_en	= false;
static bool codec_digital_phoneout_en	= false;

static bool codec_digital_phonein_en 	= false;
static bool codec_digital_bb_clk_format_init = false;

/*bluetooth*/
static bool codec_bt_clk_format 		= false;
static bool codec_bt_out_en 			= false;
static bool bt_bb_button_voice 			= false;

static bool codec_analog_btmic_en 		= false;
static bool codec_analog_btphonein_en 	= false;

static bool codec_digital_btmic_en 		= false;
static bool codec_digital_btphonein_en 	= false;
static bool codec_digital_bb_bt_clk_format = false;

static bool codec_system_bt_capture_en = false;
static bool codec_analog_bb_capture_mic = false;
static int codec_speaker_headset_earpiece_en=0;

static int pa_vol 						= 0;
static int cap_vol 						= 0;
static int earpiece_vol 				= 0;
static int headphone_vol 				= 0;
static int pa_double_used 				= 0;
static int phone_main_mic_vol 			= 0;
static int headphone_direct_used 		= 0;
static int phone_headset_mic_vol 		= 0;
static int aif2_used 				= 0;
static int aif3_used 				= 0;
static int dac_vol_ctrl_spk			=0x9e9e;
static int dac_vol_ctrl_headphone			=0xa0a0;
static int agc_used 		= 0;
static int drc_used 		= 0;
static struct label reg_labels[]={
        LABEL(DAC_Digital_Part_Control),
        LABEL(DAC_FIFO_Control),
        LABEL(DAC_FIFO_Status),
        LABEL(DAC_TX_DATA),
        LABEL(ADC_FIFO_Control),
        LABEL(ADC_FIFO_Status),
        LABEL(ADC_RX_DATA),
        LABEL(DAC_TX_Counter),
        LABEL(ADC_RX_Counter),
        LABEL(DAC_Debug),
        LABEL(ADC_Debug),
        LABEL(Headphone_Volume_Control),
        LABEL(Left_Output_Mixer_Source_Control),
        LABEL(Right_Output_Mixer_Source_Control),
        LABEL(DAC_Analog_Enable_and_PA_Source_Control),
        LABEL(Phonein_Stereo_Gain_Control),
	LABEL(Linein_and_Phone_P_N_Gain_Control),
        LABEL(MIC1_and_MIC2_GAIN_Control),
        LABEL(PA_Enable_and_HP_Control),
        LABEL(Phoneout_Control),
        LABEL(Lineout_Volume_Control),
        LABEL(Mic2_Boost_and_Lineout_Enable_Control),
        LABEL(Mic1_Boost_and_MICBIAS_Control),
        LABEL(Left_ADC_Mixer_Source_Control),
        LABEL(Right_ADC_Mixer_Source_Control),
	LABEL(PA_UPTIME_CTRL),
	LABEL(ADC_ANALIG_PART_ENABLE_REG),
       LABEL_END,
};

static struct clk *codec_pll2clk,*codec_moduleclk,*codec_srcclk;

static unsigned int read_prcm_wvalue(unsigned int addr)
{
	unsigned int reg;
	reg = readl(ADDA_PR_CFG_REG);
	reg |= (0x1<<28);
	writel(reg, ADDA_PR_CFG_REG);

	reg = readl(ADDA_PR_CFG_REG);
	reg &= ~(0x1<<24);
	writel(reg, ADDA_PR_CFG_REG);

	reg = readl(ADDA_PR_CFG_REG);
	reg &= ~(0x1f<<16);
	reg |= (addr<<16);
	writel(reg, ADDA_PR_CFG_REG);

	reg = readl(ADDA_PR_CFG_REG);
	reg &= (0xff<<0);

	return reg;
}

static void write_prcm_wvalue(unsigned int addr, unsigned int val)
{
	unsigned int reg;
	reg = readl(ADDA_PR_CFG_REG);
	reg |= (0x1<<28);
	writel(reg, ADDA_PR_CFG_REG);

	reg = readl(ADDA_PR_CFG_REG);
	reg &= ~(0x1f<<16);
	reg |= (addr<<16);
	writel(reg, ADDA_PR_CFG_REG);

	reg = readl(ADDA_PR_CFG_REG);
	reg &= ~(0xff<<8);
	reg |= (val<<8);
	writel(reg, ADDA_PR_CFG_REG);

	reg = readl(ADDA_PR_CFG_REG);
	reg |= (0x1<<24);
	writel(reg, ADDA_PR_CFG_REG);

	reg = readl(ADDA_PR_CFG_REG);
	reg &= ~(0x1<<24);
	writel(reg, ADDA_PR_CFG_REG);
}

/**
* codec_wrreg_bits - update codec register bits
* @reg: codec register
* @mask: register mask
* @value: new value
* Writes new register value.
* Return 1 for change else 0.
*/
static int codec_wrreg_prcm_bits(unsigned short reg, unsigned int mask, unsigned int value)
{
	unsigned int old, new;

	old	=	read_prcm_wvalue(reg);
	new	=	(old & ~mask) | value;
	write_prcm_wvalue(reg,new);

	return 0;
}

static int codec_wr_prcm_control(u32 reg, u32 mask, u32 shift, u32 val)
{
	u32 reg_val;
	reg_val = val << shift;
	mask = mask << shift;
	codec_wrreg_prcm_bits(reg, mask, reg_val);
	return 0;
}

/**
* codec_wrreg_bits - update codec register bits
* @reg: codec register
* @mask: register mask
* @value: new value
* Writes new register value.
* Return 1 for change else 0.
*/
int codec_wrreg_bits(unsigned short reg, unsigned int	mask,	unsigned int value)
{
	unsigned int old, new;

	old	=	codec_rdreg(reg);
	new	=	(old & ~mask) | value;
	codec_wrreg(reg,new);

	return 0;
}

/**
*	snd_codec_info_volsw	-	single	mixer	info	callback
*	@kcontrol:	mixer control
*	@uinfo:	control	element	information
*	Callback to provide information about a single mixer control
*	Returns 0 for success
*/
int snd_codec_info_volsw(struct snd_kcontrol *kcontrol,
		struct	snd_ctl_elem_info	*uinfo)
{
	struct	codec_mixer_control *mc	= (struct codec_mixer_control*)kcontrol->private_value;
	int	max	=	mc->max;
	unsigned int shift  = mc->shift;
	unsigned int rshift = mc->rshift;

	if (max	== 1)
		uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;//the info of type
	else
		uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;

	uinfo->count = shift ==	rshift	?	1:	2;	//the info of elem count
	uinfo->value.integer.min = 0;				//the info of min value
	uinfo->value.integer.max = max;				//the info of max value
	return	0;
}

/**
*	snd_codec_get_volsw	-	single	mixer	get	callback
*	@kcontrol:	mixer	control
*	@ucontrol:	control	element	information
*	Callback to get the value of a single mixer control
*	return 0 for success.
*/
int snd_codec_get_volsw(struct snd_kcontrol	*kcontrol,
		struct	snd_ctl_elem_value	*ucontrol)
{
	struct codec_mixer_control *mc= (struct codec_mixer_control*)kcontrol->private_value;
	unsigned int shift = mc->shift;
	unsigned int rshift = mc->rshift;
	int	max = mc->max;
	/*fls(7) = 3,fls(1)=1,fls(0)=0,fls(15)=4,fls(3)=2,fls(23)=5*/
	unsigned int mask = (1 << fls(max)) -1;
	unsigned int invert = mc->invert;
	unsigned int reg = mc->reg;

	ucontrol->value.integer.value[0] =
		(read_prcm_wvalue(reg)>>	shift) & mask;
	if (shift != rshift)
		ucontrol->value.integer.value[1] =
			(read_prcm_wvalue(reg) >> rshift) & mask;

	if (invert) {
		ucontrol->value.integer.value[0] =
			max - ucontrol->value.integer.value[0];
		if(shift != rshift)
			ucontrol->value.integer.value[1] =
				max - ucontrol->value.integer.value[1];
		}

		return 0;
}

/**
*	snd_codec_put_volsw	-	single	mixer put callback
*	@kcontrol:	mixer	control
*	@ucontrol:	control	element	information
*	Callback to put the value of a single mixer control
* return 0 for success.
*/
int snd_codec_put_volsw(struct	snd_kcontrol	*kcontrol,
	struct	snd_ctl_elem_value	*ucontrol)
{
	struct codec_mixer_control *mc= (struct codec_mixer_control*)kcontrol->private_value;
	unsigned int reg = mc->reg;
	unsigned int shift = mc->shift;
	unsigned int rshift = mc->rshift;
	int max = mc->max;
	unsigned int mask = (1<<fls(max))-1;
	unsigned int invert = mc->invert;
	unsigned int	val, val2, val_mask;

	val = (ucontrol->value.integer.value[0] & mask);
	if(invert)
		val = max - val;
	val <<= shift;
	val_mask = mask << shift;
	if(shift != rshift){
		val2	= (ucontrol->value.integer.value[1] & mask);
		if(invert)
			val2	=	max	- val2;
		val_mask |= mask <<rshift;
		val |= val2 <<rshift;
	}

	return codec_wrreg_prcm_bits(reg, val_mask, val);
}

/**
*	snd_codec_get_volsw_digital	-	single	mixer	get	callback
*	@kcontrol:	mixer	control
*	@ucontrol:	control	element	information
*	Callback to get the value of a single mixer control
*	return 0 for success.
*/
int snd_codec_get_volsw_digital(struct snd_kcontrol	*kcontrol,
		struct	snd_ctl_elem_value	*ucontrol)
{
	struct codec_mixer_control *mc= (struct codec_mixer_control*)kcontrol->private_value;
	unsigned int shift = mc->shift;
	unsigned int rshift = mc->rshift;
	int	max = mc->max;
	/*fls(7) = 3,fls(1)=1,fls(0)=0,fls(15)=4,fls(3)=2,fls(23)=5*/
	unsigned int mask = (1 << fls(max)) -1;
	unsigned int invert = mc->invert;
	unsigned int reg = mc->reg;

	ucontrol->value.integer.value[0] =
		(codec_rdreg(reg)>>	shift) & mask;
	if (shift != rshift)
		ucontrol->value.integer.value[1] =
			(codec_rdreg(reg) >> rshift) & mask;

	if (invert) {
		ucontrol->value.integer.value[0] =
			max - ucontrol->value.integer.value[0];
		if(shift != rshift)
			ucontrol->value.integer.value[1] =
				max - ucontrol->value.integer.value[1];
		}

		return 0;
}

/**
*	snd_codec_put_volsw_digital	-	single	mixer put callback
*	@kcontrol:	mixer	control
*	@ucontrol:	control	element	information
*
*	Callback to put the value of a single mixer control
*
* return 0 for success.
*/
int snd_codec_put_volsw_digital(struct	snd_kcontrol	*kcontrol,
	struct	snd_ctl_elem_value	*ucontrol)
{
	struct codec_mixer_control *mc= (struct codec_mixer_control*)kcontrol->private_value;
	unsigned int reg = mc->reg;
	unsigned int shift = mc->shift;
	unsigned int rshift = mc->rshift;
	int max = mc->max;
	unsigned int mask = (1<<fls(max))-1;
	unsigned int invert = mc->invert;
	unsigned int	val, val2, val_mask;

	val = (ucontrol->value.integer.value[0] & mask);
	if(invert)
		val = max - val;
	val <<= shift;
	val_mask = mask << shift;
	if(shift != rshift){
		val2	= (ucontrol->value.integer.value[1] & mask);
		if(invert)
			val2	=	max	- val2;
		val_mask |= mask <<rshift;
		val |= val2 <<rshift;
	}

	return codec_wrreg_bits(reg,val_mask,val);
}



int codec_wr_control(u32 reg, u32 mask, u32 shift, u32 val)
{
	u32 reg_val;
	reg_val = val << shift;
	mask = mask << shift;
	codec_wrreg_bits(reg, mask, reg_val);
	return 0;
}
#if 0
static int codec_rd_control(u32 reg, u32 bit, u32 *val)
{
	return 0;
}
static void codec_resume_events(struct work_struct *work)
{
	codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, HPPAEN, 0x1);
	//by xzd
	msleep(400);
	codec_wr_prcm_control(MIC1G_MICBIAS_CTRL, 0x1, HMICBIASEN, 0x1);
	pr_debug("====codec_resume_events===\n");
}
#endif
static void get_audio_param(void)
{
	script_item_value_type_e  type;
	script_item_u val;

	type = script_get_item("audio0", "headphone_vol", &val);
	if (SCIRPT_ITEM_VALUE_TYPE_INT != type) {
		pr_err("[audiocodec] headphone_vol type err!\n");
	}  else {
		headphone_vol = val.val;
	}

	type = script_get_item("audio0", "earpiece_vol", &val);
	if (SCIRPT_ITEM_VALUE_TYPE_INT != type) {
		pr_err("[audiocodec] earpiece_vol type err!\n");
	}  else {
		earpiece_vol = val.val;
	}

	type = script_get_item("audio0", "cap_vol", &val);
	if (SCIRPT_ITEM_VALUE_TYPE_INT != type) {
		pr_err("[audiocodec] cap_vol type err!\n");
	}  else {
		cap_vol = val.val;
	}

	type = script_get_item("audio0", "headset_mic_vol", &val);
	if (SCIRPT_ITEM_VALUE_TYPE_INT != type) {
		pr_err("[audiocodec] headset_mic_vol type err!\n");
	}  else {
		phone_headset_mic_vol = val.val;
	}

	type = script_get_item("audio0", "main_mic_vol", &val);
	if (SCIRPT_ITEM_VALUE_TYPE_INT != type) {
		pr_err("[audiocodec] main_mic_vol type err!\n");
	}  else {
		phone_main_mic_vol = val.val;
	}

	type = script_get_item("audio0", "pa_double_used", &val);
	if (SCIRPT_ITEM_VALUE_TYPE_INT != type) {
		pr_err("[audiocodec] pa_double_used type err!\n");
	}  else {
		pa_double_used = val.val;
	}

	if (!pa_double_used) {
		type = script_get_item("audio0", "pa_single_vol", &val);
		if (SCIRPT_ITEM_VALUE_TYPE_INT != type) {
			pr_err("[audiocodec] pa_single_vol type err!\n");
		}  else {
			pa_vol = val.val;
		}
	} else {
		type = script_get_item("audio0", "pa_double_vol", &val);
		if (SCIRPT_ITEM_VALUE_TYPE_INT != type) {
			pr_err("[audiocodec] pa_double_vol type err!\n");
		}  else {
			pa_vol = val.val;
		}
	}

	type = script_get_item("audio0", "DAC_VOL_CTRL_SPK", &val);
	if (SCIRPT_ITEM_VALUE_TYPE_INT != type) {
		pr_err("[audiocodec] DAC_VOL_CTRL_SPK type err!\n");
	} else {
		dac_vol_ctrl_spk = val.val;
	}
	type = script_get_item("audio0", "DAC_VOL_CTRL_HEADPHONE", &val);
	if (SCIRPT_ITEM_VALUE_TYPE_INT != type) {
		pr_err("[audiocodec] DAC_VOL_CTRL_HEADPHONE type err!\n");
	} else {
		dac_vol_ctrl_headphone = val.val;
	}

	type = script_get_item("audio0", "headphone_direct_used", &val);
	if (SCIRPT_ITEM_VALUE_TYPE_INT != type) {
		pr_err("[audiocodec] headphone_direct_used type err!\n");
	} else {
		headphone_direct_used = val.val;
	}
	
  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值