在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;
}