在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;
}
type = script_get_item("audio0", "agc_used", &val);
if (SCIRPT_ITEM_VALUE_TYPE_INT != type) {
pr_err("[audiocodec] agc_used type err!\n");
} else {
agc_used = val.val;
}
type = script_get_item("audio0", "drc_used", &val);
if (SCIRPT_ITEM_VALUE_TYPE_INT != type) {
pr_err("[audiocodec] drc_used type err!\n");
} else {
drc_used = val.val;
}
pr_debug("headphone_vol=0x%x, earpiece_vol=0x%x, cap_vol=0x%x, \
phone_headset_mic_vol=0x%x, phone_main_mic_vol=0x%x, \
pa_double_used=0x%x, pa_vol=0x%x \n" \
,headphone_vol, earpiece_vol, cap_vol, \
phone_headset_mic_vol, phone_main_mic_vol, \
pa_double_used, pa_vol);
}
/*
* enable the codec function which should be enable during system init.
*/
static void codec_init(void)
{
get_audio_param();
if (headphone_direct_used) {
codec_wr_prcm_control(PAEN_HP_CTRL, 0x3, HPCOM_FC, 0x3);
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, COMPTEN, 0x1);
} else {
codec_wr_prcm_control(PAEN_HP_CTRL, 0x3, HPCOM_FC, 0x0);
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, COMPTEN, 0x0);
}
if (drc_used){
codec_wr_control(0x48c, 0x7ff, 0, 0x1);
// codec_wr_control(0x48c, 0x7ff, 0, 0x0);
codec_wr_control(0x490, 0xffff, 0, 0x2baf);
//codec_wr_control(0x490, 0xffff, 0, 0x1fb6);
codec_wr_control(0x494, 0x7ff, 0, 0x1);
codec_wr_control(0x498, 0xffff, 0, 0x2baf);
codec_wr_control(0x49c, 0x7ff, 0, 0x0);
codec_wr_control(0x4a0, 0xffff, 0, 0x44a);
codec_wr_control(0x4a4, 0x7ff, 0, 0x0);
codec_wr_control(0x4a8, 0xffff, 0, 0x1e06);
//codec_wr_control(0x4ac, 0x7ff, 0, 0x27d);
codec_wr_control(0x4ac, 0x7ff, 0, 0x352);
//codec_wr_control(0x4b0, 0xffff, 0, 0xcf68);
codec_wr_control(0x4b0, 0xffff, 0, 0x6910);
codec_wr_control(0x4b4, 0x7ff, 0, 0x77a);
codec_wr_control(0x4b8, 0xffff, 0, 0xaaaa);
//codec_wr_control(0x4bc, 0x7ff, 0, 0x1fe);
codec_wr_control(0x4bc, 0x7ff, 0, 0x2de);
codec_wr_control(0x4c0, 0xffff, 0, 0xc982);
codec_wr_control(0x258, 0xffff, 0, 0x9f9f);
}
if (agc_used){
codec_wr_control(0x4d0, 0x3, 6, 0x3);
codec_wr_control(0x410, 0x3f, 8, 0x31);
codec_wr_control(0x410, 0xff, 0, 0x28);
codec_wr_control(0x414, 0x3f, 8, 0x31);
codec_wr_control(0x414, 0xff, 0, 0x28);
codec_wr_control(0x428, 0x7fff, 0, 0x24);
codec_wr_control(0x42c, 0x7fff, 0, 0x2);
codec_wr_control(0x430, 0x7fff, 0, 0x24);
codec_wr_control(0x434, 0x7fff, 0, 0x2);
codec_wr_control(0x438, 0x1f, 8, 0xf);
codec_wr_control(0x438, 0x1f, 0, 0xf);
codec_wr_control(0x44c, 0x7ff, 0, 0xfc);
codec_wr_control(0x450, 0xffff, 0, 0xabb3);
}
/*mute headphone pa*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPPAMUTE, 0x0);
}
/*
* the system voice come out from speaker
* this function just used for the system voice(such as music and moive voice and so on).
*/
static int codec_pa_play_open(void)
{
int i = 0;
int reg_val = 0;
if(codec_speakerout_en != 1) {
codec_wr_prcm_control(HP_VOLC, 0x3f, HPVOL, 0);
}
if (drc_used){
codec_wr_control(0x4d4, 0xffff, 0, 0x80);
codec_wr_control(0x210, 0x1, 6, 0x1);
codec_wr_control(0x214, 0x1, 6, 0x1);
codec_wr_control(0x480, 0x7, 0, 0x7);
}
/*enable AIF1 DAC Timeslot0 channel enable*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x1, AIF1_DA0L_ENA, 0x1);
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x1, AIF1_DA0R_ENA, 0x1);
/*confige AIF1 DAC Timeslot0 left channel data source*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x3, AIF1_DA0L_SRC, 0);
/*confige AIF1 DAC Timeslot0 right channel data source*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x3, AIF1_DA0R_SRC, 0);
/*confige dac digital mixer source */
codec_wr_control(SUNXI_DAC_MXR_SRC, 0x1, DACL_MXR_SRC_AIF1DA0L, 0x1);
codec_wr_control(SUNXI_DAC_MXR_SRC, 0x1, DACR_MXR_SRC_AIF1DA0R, 0x1);
codec_wr_control(SUNXI_DAC_VOL_CTRL, 0xffff, 0, dac_vol_ctrl_spk);
/*enable dac digital*/
codec_wr_control(SUNXI_DAC_DIG_CTRL, 0x1, ENDA, 0x1);
codec_wr_prcm_control(HP_VOLC, 0x1, PA_CLK_GC, 0x0);
/*enable dac analog*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, DACALEN, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, DACAREN, 0x1);
if (!pa_double_used) {/*single speaker*/
//codec_wr_prcm_control(LOMIXSC, 0x1, LMIXMUTEDACL, 0x1);
//codec_wr_prcm_control(ROMIXSC, 0x1, LMIXMUTEDACR, 0x1);
codec_wr_prcm_control(ROMIXSC, 0x7f, RMIXMUTE, 0x0);
codec_wr_prcm_control(LOMIXSC, 0x7f, LMIXMUTE, 0x3);
/*enable output mixer */
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LMIXEN, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RMIXEN, 0x0);
/*pa input source select:l_mixer*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPIS, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPIS, 0x0);
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, LTRNMUTE, 0x1);
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, RTLNMUTE, 0x0);
/*unmute headphone pa*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPPAMUTE, 0x0);
} else {/*double speaker*/
if(codec_speakerout_en) {
/*right output mixer source : dacr*/
codec_wr_prcm_control(ROMIXSC, 0x1, RMIXMUTEDACR, 0x1);
/*left output mixer source : dacl*/
codec_wr_prcm_control(LOMIXSC, 0x1, LMIXMUTEDACL, 0x1);
/*enable output mixer */
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LMIXEN, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RMIXEN, 0x1);
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, LTRNMUTE, 0x0);
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, RTLNMUTE, 0x0);
/*pa input source select:r_mixer,l_mixer*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPIS, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPIS, 0x1);
}else {
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, LTRNMUTE, 0x0);
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, RTLNMUTE, 0x0);
/*pa input source select:r_mixer,l_mixer*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPIS, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPIS, 0x0);
}
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPPAMUTE, 0x1);
}
if(play_running == 1) {/*used for change the path*/
pr_debug("%s,line:%d\n",__func__,__LINE__);
reg_val = read_prcm_wvalue(HP_VOLC);
reg_val &= 0x3f;
if (!reg_val) {
for(i=0; i < pa_vol; i++) {
/*set HPVOL volume*/
codec_wr_prcm_control(HP_VOLC, 0x3f, HPVOL, i);
reg_val = read_prcm_wvalue(HP_VOLC);
reg_val &= 0x3f;
if ((i%2==0))
usleep_range(1000,2000);
}
}
usleep_range(2000, 3000);
gpio_set_value(item.gpio.gpio, 1);
//msleep(62);
}
return 0;
}
/*
* the system voice come out from headphone
* this function just used for the system voice(such as music and moive voice and so on).
*/
static int codec_headphone_play_open(void)
{
int i = 0;
int reg_val =0;
/*close spk pa*/
gpio_set_value(item.gpio.gpio, 0);
usleep_range(1000, 2000);
codec_wr_prcm_control(ADDA_APT2, 0x1, ZERO_CROSS_EN, 0x0);
/*mute headphone pa*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPPAMUTE, 0x0);
if(codec_headphoneout_en != 1) {
codec_wr_prcm_control(HP_VOLC, 0x3f, HPVOL, 0);
}
if (drc_used){
codec_wr_control(0x4d4, 0xffff, 0, 0x0);
codec_wr_control(0x210, 0x1, 6, 0x0);
codec_wr_control(0x214, 0x1, 6, 0x0);
codec_wr_control(0x480, 0x7, 0, 0x0);
}
/*enable AIF1 DAC Timeslot0 channel enable*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x1, AIF1_DA0L_ENA, 0x1);
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x1, AIF1_DA0R_ENA, 0x1);
/*confige AIF1 DAC Timeslot0 left channel data source*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x3, AIF1_DA0L_SRC, 0);
/*confige AIF1 DAC Timeslot0 right channel data source*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x3, AIF1_DA0R_SRC, 0);
/*confige dac digital mixer source */
codec_wr_control(SUNXI_DAC_MXR_SRC, 0x1, DACL_MXR_SRC_AIF1DA0L, 0x1);
codec_wr_control(SUNXI_DAC_MXR_SRC, 0x1, DACR_MXR_SRC_AIF1DA0R, 0x1);
codec_wr_control(SUNXI_DAC_VOL_CTRL, 0xffff, 0, dac_vol_ctrl_headphone);
/*enable dac digital*/
codec_wr_control(SUNXI_DAC_DIG_CTRL, 0x1, ENDA, 0x1);
/*enable dac_l and dac_r*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, DACALEN, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, DACAREN, 0x1);
if(codec_headphoneout_en == 1) {
//codec_wr_prcm_control(ROMIXSC, 0x1, RMIXMUTEDACR, 0x1);
//codec_wr_prcm_control(LOMIXSC, 0x1, LMIXMUTEDACL, 0x1);
codec_wr_prcm_control(ROMIXSC, 0x7f, RMIXMUTE, 0x2);
codec_wr_prcm_control(LOMIXSC, 0x7f, LMIXMUTE, 0x2);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LMIXEN, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RMIXEN, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPIS, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPIS, 0x1);
}else{
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LMIXEN, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RMIXEN, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPIS, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPIS, 0x0);
}
/*hpout negative mute*/
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, LTRNMUTE, 0x0);
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, RTLNMUTE, 0x0);
if(play_running == 1) {/*used for change the path*/
pr_debug("%s,line:%d\n",__func__,__LINE__);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPPAMUTE, 0x1);
reg_val = read_prcm_wvalue(HP_VOLC);
reg_val &= 0x3f;
if (!reg_val) {
for(i=0; i < headphone_vol; i++) {
/*set HPVOL volume*/
codec_wr_prcm_control(HP_VOLC, 0x3f, HPVOL, i);
reg_val = read_prcm_wvalue(HP_VOLC);
reg_val &= 0x3f;
if ((i%2==0))
usleep_range(1000,2000);
}
}
}
return 0;
}
static int codec_earpiece_play_open(void)
{
gpio_set_value(item.gpio.gpio, 0);
usleep_range(2000, 3000);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPPAMUTE, 0x0);
/*enable AIF1 DAC Timeslot0 channel enable*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x1, AIF1_DA0L_ENA, 0x1);
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x1, AIF1_DA0R_ENA, 0x1);
/*confige AIF1 DAC Timeslot0 left channel data source*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x3, AIF1_DA0L_SRC, 0);
/*confige AIF1 DAC Timeslot0 right channel data source*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x3, AIF1_DA0R_SRC, 0);
/*confige dac digital mixer source */
codec_wr_control(SUNXI_DAC_MXR_SRC, 0xf, DACL_MXR_SRC, 0x8);
codec_wr_control(SUNXI_DAC_MXR_SRC, 0xf, DACR_MXR_SRC, 0x8);
/*enable dac digital*/
codec_wr_control(SUNXI_DAC_DIG_CTRL, 0x1, ENDA, 0x1);
/*0*/
codec_wr_prcm_control(HP_VOLC, 0x1, PA_CLK_GC, 0x0);
/*enable dac ananlog*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, DACALEN, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, DACAREN, 0x1);
/*left output negative*/
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, LTRNMUTE, 0x1);
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, RTLNMUTE, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPIS, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPIS, 0x0);
/*slect output mixer source*/
codec_wr_prcm_control(ROMIXSC, 0x7f, RMIXMUTE, 0x0);
codec_wr_prcm_control(LOMIXSC, 0x7f, LMIXMUTE, 0x3);
codec_wr_prcm_control(PAEN_HP_CTRL, 0x3, HPCOM_FC, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LMIXEN, 0x1);
return 0;
}
/*
* the system voice come out from headphone and speaker
* while the phone call in, the phone use the headset, you can hear the voice from speaker and headset.
* this function just used for the system voice(such as music and moive voice and so on).
*/
static int codec_pa_and_headset_play_open(void)
{
/*mute hppa*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPPAMUTE, 0x0);
/*enable AIF1 DAC Timeslot0 channel enable*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x1, AIF1_DA0L_ENA, 0x1);
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x1, AIF1_DA0R_ENA, 0x1);
/*confige AIF1 DAC Timeslot0 left channel data source*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x3, AIF1_DA0L_SRC, 0);
/*confige AIF1 DAC Timeslot0 right channel data source*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x3, AIF1_DA0R_SRC, 0);
codec_wr_control(SUNXI_DAC_VOL_CTRL, 0xffff, 0, dac_vol_ctrl_spk);
/*confige dac digital mixer source */
codec_wr_control(SUNXI_DAC_MXR_SRC, 0xf, DACL_MXR_SRC, 0x8);
codec_wr_control(SUNXI_DAC_MXR_SRC, 0xf, DACR_MXR_SRC, 0x8);
/*enable dac digital*/
codec_wr_control(SUNXI_DAC_DIG_CTRL, 0x1, ENDA, 0x1);
/*enble dac analog*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, DACALEN, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, DACAREN, 0x1);
codec_wr_prcm_control(HP_VOLC, 0x1, PA_CLK_GC, 0x0);
if (!pa_double_used) {/*single speaker*/
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, LTRNMUTE, 0x1);
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, RTLNMUTE, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPIS, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPIS, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LMIXEN, 0x1);
codec_wr_prcm_control(ROMIXSC, 0x7f, RMIXMUTE, 0x0);
codec_wr_prcm_control(LOMIXSC, 0x7f, LMIXMUTE, 0x3);
/*unmute headphone pa*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPPAMUTE, 0x0);
} else {/*double speaker*/
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, LTRNMUTE, 0x0);
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, RTLNMUTE, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPIS, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPIS, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LMIXEN, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RMIXEN, 0x1);
/*right output mixer source : dacr*/
codec_wr_prcm_control(ROMIXSC, 0x1, RMIXMUTEDACR, 0x1);
/*left output mixer source : dacl*/
codec_wr_prcm_control(LOMIXSC, 0x1, LMIXMUTEDACL, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPPAMUTE, 0x1);
}
return 0;
}
static int codec_system_btout_open(void)
{
/*config clk fmt*/
/*config aif2 from pll2*/
codec_wr_control(SUNXI_SYSCLK_CTL, 0x1, AIF2CLK_ENA, 0x1);
/*aif2 clk source select*/
codec_wr_control(SUNXI_SYSCLK_CTL, 0x3, AIF2CLK_SRC, 0x3);
codec_wr_control(SUNXI_MOD_CLK_ENA, 0x1, AIF2_MOD_CLK_EN, 0x1);
codec_wr_control(SUNXI_MOD_RST_CTL, 0x1, AIF2_MOD_RST_CTL, 0x1);
/*config aif2 fmt :pcm mono 16 lrck=8k,blck/lrck = 64*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL, 0x1, AIF2_MSTR_MOD, 0x0);/*master*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL, 0x1, AIF2_BCLK_INV, 0x0);/*bclk:normal*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL, 0x1, AIF2_LRCK_INV, 0x0);/*lrck:normal*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL, 0xf, AIF2_BCLK_DIV, 0x9);/*aif2/bclk=48*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL, 0x7, AIF2_LRCK_DIV, 0x2);/*bclk/lrck=64*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL, 0x3, AIF2_WORD_SIZ, 0x1);/*sr=16*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL, 0x3, AIF2_DATA_FMT, 0x3);/*dsp mode*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL, 0x1, AIF2_MONO_PCM, 1);/*dsp mode*/
/*aif3 mode enable*/
codec_wr_control(SUNXI_MOD_CLK_ENA, 0x1, AIF3_MOD_CLK_EN, 0x1);
codec_wr_control(SUNXI_MOD_RST_CTL, 0x1, AIF3_MOD_RST_CTL, 0x1);
/*config aif3: clk ,fmt*/
codec_wr_control(SUNXI_AIF3_CLK_CTRL, 0x1, AIF3_BCLK_INV, 0x0);
codec_wr_control(SUNXI_AIF3_CLK_CTRL, 0x1, AIF3_LRCK_INV, 0x0);
codec_wr_control(SUNXI_AIF3_CLK_CTRL, 0x3, AIF3_WORD_SIZ, 0x1);/*sr = 16*/
codec_wr_control(SUNXI_AIF3_CLK_CTRL, 0x3, AIF3_CLOC_SRC, 0x1);/*clk form aif2*/
/*enable AIF1 DAC Timeslot0 channel enable*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x1, AIF1_DA0L_ENA, 0x1);
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x1, AIF1_DA0R_ENA, 0x1);
/*confige AIF1 DAC Timeslot0 right channel data source*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x3, AIF1_DA0R_SRC, 3);
codec_wr_control(SUNXI_AIF2_MXR_SRC, 0x1, AIF2_ADCR_MXR_SRC_AIF1DA0R, 1);
/*enable aif2 adcl channel*/
codec_wr_control(SUNXI_AIF2_ADCDAT_CTRL, 0x1, AIF2_ADCR_EN, 1);
/*select aif3 pcm output source*/
codec_wr_control(SUNXI_AIF3_SGP_CTRL, 0x3, AIF3_ADC_SRC, 2);
return 0;
}
static int codec_system_bt_buttonvoice_open(void)
{
/*enable AIF1 DAC Timeslot0 channel enable*/
//codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x1, AIF1_DA0L_ENA, 0x1);
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x1, AIF1_DA0R_ENA, 0x1);
/*confige AIF1 DAC Timeslot0 right channel data source*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x3, AIF1_DA0R_SRC, 0);
codec_wr_control(SUNXI_AIF2_MXR_SRC, 0x1, AIF2_ADCR_MXR_SRC_AIF1DA0R, 1);
return 0;
}
/*
* use for the base system record(for pad record).
*/
static int codec_capture_open(void)
{
if (agc_used) {
codec_wr_control(0x210, 0x1, 7, 0x1);
codec_wr_control(0x214, 0x1, 7, 0x1);
codec_wr_control(0x408, 0xf, 0, 0x6);
codec_wr_control(0x408, 0x7, 12, 0x7);
codec_wr_control(0x40c, 0xf, 0, 0x6);
codec_wr_control(0x40c, 0x7, 12, 0x7);
}
/*enable AIF1 adc Timeslot0 channel enable*/
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL, 0x1, AIF1_AD0L_ENA, 0x1);
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL, 0x1, AIF1_AD0R_ENA, 0x1);
/*confige AIF1 adc Timeslot0 left channel data source*/
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL, 0x3, AIF1_AD0L_SRC, 0);
/*confige AIF1 adc Timeslot0 right channel data source*/
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL, 0x3, AIF1_AD0R_SRC, 0);
/*confige AIF1 Digital Mixer Source*/
codec_wr_control(SUNXI_AIF1_MXR_SRC, 0x1, AIF1_AD0L_MXL_SRC_ADCL, 0x1);
codec_wr_control(SUNXI_AIF1_MXR_SRC, 0x1, AIF1_AD0R_MXR_SRC_ADCR, 0x1);
/*just for test*/
// codec_wr_control(SUNXI_AIF1_MXR_SRC, 0xf, AIF1_AD0L_MXL_SRC, 0x8);
// codec_wr_control(SUNXI_AIF1_MXR_SRC, 0xf, AIF1_AD0R_MXR_SRC, 0x8);
/*enable ADC Digital part*/
codec_wr_control(SUNXI_ADC_DIG_CTRL, 0x1, ENAD, 1);
/*enable Digital microphone*/
codec_wr_control(SUNXI_ADC_DIG_CTRL, 0x1, ENDM, 0);
/*ADC Delay Time*/
codec_wr_control(SUNXI_ADC_DIG_CTRL, 0x3, ADOUT_DTS, 3);
codec_wr_control(SUNXI_ADC_DIG_CTRL, 0x1, ADOUT_DLY, 1);
/*enable adc_r adc_l analog*/
codec_wr_prcm_control(ADC_AP_EN, 0x1, ADCREN, 0x1);
codec_wr_prcm_control(ADC_AP_EN, 0x1, ADCLEN, 0x1);
/*enable Left MIC1 Boost stage*/
codec_wr_prcm_control(LADCMIXSC, 0x1, LADCMIXMUTEMIC1BOOST, 0x1);
/*enable Right MIC1 Boost stage*/
codec_wr_prcm_control(RADCMIXSC, 0x1, RADCMIXMUTEMIC1BOOST, 0x1);
/*enable mic1 pa*/
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL, 0x1, MIC1AMPEN, 0x1);
/*mic1 gain 36dB,if capture volume is too small, enlarge the mic1boost*/
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL, 0x7, MIC1BOOST, cap_vol);
/*enable Master microphone bias*/
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL, 0x1, MMICBIASEN, 0x1);
cap_running = 1;
/*hardware fifo delay*/
msleep(200);
return 0;
}
/*
* codec_speaker_headset_earpiece_en == 3, earpiece is open,speaker and headphone is close.
* codec_speaker_headset_earpiece_en == 2, speaker is open, headphone is open.
* codec_speaker_headset_earpiece_en == 1, speaker is open, headphone is close.
* codec_speaker_headset_earpiece_en == 0, speaker is closed, headphone is open.
* this function just used for the system voice(such as music and moive voice and so on),
* no the phone call.
*/
static int codec_set_spk_headset_earpiece(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
int ret = 0;
codec_speaker_headset_earpiece_en = ucontrol->value.integer.value[0];
if (codec_speaker_headset_earpiece_en == 1) {
ret = codec_pa_play_open();
} else if (codec_speaker_headset_earpiece_en == 0) {
ret = codec_headphone_play_open();
} else if (codec_speaker_headset_earpiece_en == 2) {
ret = codec_pa_and_headset_play_open();
} else if (codec_speaker_headset_earpiece_en == 3) {
ret = codec_earpiece_play_open();
}else if(codec_speaker_headset_earpiece_en == 4) {
pr_debug("%s,line:%d\n",__func__,__LINE__);
ret = codec_system_btout_open();
}else if(codec_speaker_headset_earpiece_en == 5){
ret = codec_system_bt_buttonvoice_open();
}
return 0;
}
static int codec_get_spk_headset_earpiece(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_speaker_headset_earpiece_en;
return 0;
}
static int sndpcm_unmute(struct snd_soc_dai *dai, int mute)
{
if (current_running == 0) {/*play stream*/
int i = 0;
int reg_val = 0;
if(codec_analog_phonein_en){
msleep(10);
codec_wr_prcm_control(ROMIXSC, 0x1, RMIXMUTEDACL, 0x1);
codec_wr_prcm_control(LOMIXSC, 0x1, LMIXMUTEDACL, 0x1);
}
if(mute == 0) {
switch (codec_speaker_headset_earpiece_en) {
case 0:
pr_debug("%s,line:%d\n",__func__,__LINE__);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPPAMUTE, 0x1);
reg_val = read_prcm_wvalue(HP_VOLC);
reg_val &= 0x3f;
if (!reg_val) {
for(i=0; i < headphone_vol; i++) {
/*set HPVOL volume*/
codec_wr_prcm_control(HP_VOLC, 0x3f, HPVOL, i);
reg_val = read_prcm_wvalue(HP_VOLC);
reg_val &= 0x3f;
if ((i%2==0))
usleep_range(1000,2000);
}
}
break;
case 1:
pr_debug("%s,line:%d\n",__func__,__LINE__);
reg_val = read_prcm_wvalue(HP_VOLC);
reg_val &= 0x3f;
if (!reg_val) {
for(i=0; i < pa_vol; i++) {
/*set HPVOL volume*/
codec_wr_prcm_control(HP_VOLC, 0x3f, HPVOL, i);
reg_val = read_prcm_wvalue(HP_VOLC);
reg_val &= 0x3f;
if ((i%2==0))
usleep_range(1000,2000);
}
}
usleep_range(2000, 3000);
gpio_set_value(item.gpio.gpio, 1);
msleep(62);
break;
case 2:
pr_debug("%s,line:%d\n",__func__,__LINE__);
if (!pa_double_used) {/*single speaker*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPPAMUTE, 0x0);
} else {/*double speaker*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPPAMUTE, 0x1);
}
reg_val = read_prcm_wvalue(HP_VOLC);
reg_val &= 0x3f;
if (!reg_val) {
for(i=0; i < pa_vol; i++) {
/*set HPVOL volume*/
codec_wr_prcm_control(HP_VOLC, 0x3f, HPVOL, i);
reg_val = read_prcm_wvalue(HP_VOLC);
reg_val &= 0x3f;
if ((i%2==0))
usleep_range(1000,2000);
}
}
/*set HPVOL volume*/
codec_wr_prcm_control(HP_VOLC, 0x3f, HPVOL, pa_vol);
usleep_range(2000, 3000);
gpio_set_value(item.gpio.gpio, 1);
msleep(62);
break;
case 3:
pr_debug("%s,line:%d\n",__func__,__LINE__);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0x1);
reg_val = read_prcm_wvalue(HP_VOLC);
reg_val &= 0x3f;
if (!reg_val) {
for(i=0; i < earpiece_vol; i++) {
/*set HPVOL volume*/
codec_wr_prcm_control(HP_VOLC, 0x3f, HPVOL, i);
reg_val = read_prcm_wvalue(HP_VOLC);
reg_val &= 0x3f;
if ((i%2==0))
usleep_range(1000,2000);
}
}
/*set HPVOL volume*/
codec_wr_prcm_control(HP_VOLC, 0x3f, HPVOL, earpiece_vol);
break;
default:
break;
}
} else {
}
}
return 0;
}
static int sndpcm_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
/*enble aif1 clk */
codec_wr_control(SUNXI_SYSCLK_CTL , 0x1, AIF1CLK_ENA, 0x1);
/*enble sys clk */
codec_wr_control(SUNXI_SYSCLK_CTL , 0x1, SYSCLK_ENA, 0x1);
/**for test loopback******/
//codec_wr_control(SUNXI_DA_CTL , 0x1, 3, 0x1);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
play_running = 1;
current_running = 0;
/*enable module AIF1,DAC*/
codec_wr_control(SUNXI_MOD_CLK_ENA , 0x1, AIF1_MOD_CLK_EN, 0x1);
codec_wr_control(SUNXI_MOD_CLK_ENA , 0x1, DAC_DIGITAL_MOD_CLK_EN, 0x1);
/*reset module AIF1, DAC*/
codec_wr_control(SUNXI_MOD_RST_CTL , 0x1, AIF1_MOD_RST_CTL, 0x1);
codec_wr_control(SUNXI_MOD_RST_CTL , 0x1, DAC_DIGITAL_MOD_RST_CTL, 0x1);
} else {
current_running = 1;
/*enable module AIF1,ADC*/
codec_wr_control(SUNXI_MOD_CLK_ENA , 0x1, AIF1_MOD_CLK_EN, 0x1);
codec_wr_control(SUNXI_MOD_CLK_ENA , 0x1, ADC_DIGITAL_MOD_CLK_EN, 0x1);
/*reset module AIF1, ADC*/
codec_wr_control(SUNXI_MOD_RST_CTL , 0x1, AIF1_MOD_RST_CTL, 0x1);
codec_wr_control(SUNXI_MOD_RST_CTL , 0x1, ADC_DIGITAL_MOD_RST_CTL, 0x1);
}
/*global enable*/
codec_wr_control(SUNXI_DA_CTL , 0x1, GEN, 0x1);
return 0;
}
static void sndpcm_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
int i = 0;
int cur_vol = 0;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
codec_wr_prcm_control(ADDA_APT2, 0x1, ZERO_CROSS_EN, 0x0);
cur_vol = read_prcm_wvalue(HP_VOLC);
cur_vol &= 0x3f;
if (!(codec_analog_mainmic_en || codec_analog_headsetmic_en ||codec_digital_headsetmic_en ||
codec_digital_mainmic_en ||codec_analog_btmic_en || codec_digital_btmic_en)){
gpio_set_value(item.gpio.gpio, 0);
if (cur_vol > 48) {
for (i = cur_vol; i > 52 ; i--) {
/*set HPVOL volume*/
codec_wr_prcm_control(HP_VOLC, 0x3f, HPVOL, i);
usleep_range(9000, 10000);
}
for (i = 52; i > 48 ; i--) {
/*set HPVOL volume*/
codec_wr_prcm_control(HP_VOLC, 0x3f, HPVOL, i);
usleep_range(6000,7000);
}
}
for (i = 48; i > 32 ; i--) {
/*set HPVOL volume*/
codec_wr_prcm_control(HP_VOLC, 0x3f, HPVOL, i);
usleep_range(1000,2000);
}
for (i = 32; i > 0 ; i--) {
/*set HPVOL volume*/
codec_wr_prcm_control(HP_VOLC, 0x3f, HPVOL, i);
usleep_range(100,200);
}
codec_wr_prcm_control(HP_VOLC, 0x3f, HPVOL, 0);
/*disable analog part*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPPAMUTE, 0x0);
/*by xzd left select dacl&dacr*/
codec_wr_prcm_control(ROMIXSC, 0x7f, RMIXMUTE, 0x0);
/*by xzd right don't select src*/
codec_wr_prcm_control(LOMIXSC, 0x7f, LMIXMUTE, 0x0);
/*enable output mixer */
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LMIXEN, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RMIXEN, 0x0);
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, LTRNMUTE, 0x0);
/*by xzd only right negative left*/
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, RTLNMUTE, 0x0);
/*enable dac analog*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, DACALEN, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, DACAREN, 0x0);
/*disable dac digital*/
codec_wr_control(SUNXI_DAC_DIG_CTRL, 0x1, ENDA, 0x0);
codec_wr_prcm_control(HP_VOLC, 0x1, PA_CLK_GC, 0x0);
/*confige dac digital mixer source */
codec_wr_control(SUNXI_DAC_MXR_SRC, 0xf, DACL_MXR_SRC, 0x0);
codec_wr_control(SUNXI_DAC_MXR_SRC, 0xf, DACR_MXR_SRC, 0x0);
/*confige AIF1 DAC Timeslot0 left channel data source*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x3, AIF1_DA0L_SRC, 0);
/*confige AIF1 DAC Timeslot0 right channel data source*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x3, AIF1_DA0R_SRC, 0);
/*disable AIF1 DAC Timeslot0 channel*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x1, AIF1_DA0L_ENA, 0x0);
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x1, AIF1_DA0R_ENA, 0x0);
/*diable clk parts*/
if (1 == cap_running ){
pr_debug("[audio stream] capture is running!!!!\n");
}else{
/*disable aif1clk*/
codec_wr_control(SUNXI_SYSCLK_CTL , 0x1, AIF1CLK_ENA, 0);
/*disble sys clk */
codec_wr_control(SUNXI_SYSCLK_CTL , 0x1, SYSCLK_ENA, 0);
/*disable module AIF1*/
codec_wr_control(SUNXI_MOD_CLK_ENA , 0x1, AIF1_MOD_CLK_EN, 0x0);
/*reset module AIF1*/
codec_wr_control(SUNXI_MOD_RST_CTL , 0x1, AIF1_MOD_RST_CTL, 0x0);
/*global disable*/
codec_wr_control(SUNXI_DA_CTL , 0x1, GEN, 0x0);
}
/*disable module DAC*/
codec_wr_control(SUNXI_MOD_CLK_ENA , 0x1, DAC_DIGITAL_MOD_CLK_EN, 0x0);
/*reset module DAC*/
codec_wr_control(SUNXI_MOD_RST_CTL , 0x1, DAC_DIGITAL_MOD_RST_CTL, 0x0);
/* I2S0 TX DISABLE */
codec_wr_control(SUNXI_DA_CTL , 0x1, TXEN,0);
/*SDO ON*/
codec_wr_control(SUNXI_DA_CTL , 0x1, SDO_EN, 0);
}
if (drc_used){
codec_wr_control(0x4d4, 0xffff, 0, 0x0);
codec_wr_control(0x210, 0x1, 6, 0x0);
codec_wr_control(0x214, 0x1, 6, 0x0);
codec_wr_control(0x480, 0x7, 0, 0x0);
}
play_running = 0;
}else{
if (!(codec_analog_mainmic_en || codec_analog_headsetmic_en ||codec_digital_headsetmic_en ||
codec_digital_mainmic_en ||codec_analog_btmic_en || codec_digital_btmic_en)){
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL, 0x1, MIC1AMPEN, 0x0);
/*disable Master microphone bias*/
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL, 0x1, MMICBIASEN, 0x0);
codec_wr_prcm_control(MIC2G_LINEEN_CTRL, 0x1, MIC2AMPEN, 0x0);
/*disable Left MIC1 Boost stage*/
codec_wr_prcm_control(LADCMIXSC, 0x1, LADCMIXMUTEMIC1BOOST, 0x0);
/*disable Right MIC1 Boost stage*/
codec_wr_prcm_control(RADCMIXSC, 0x1, RADCMIXMUTEMIC1BOOST, 0x0);
/*disable Left MIC1 Boost stage*/
codec_wr_prcm_control(LADCMIXSC, 0x1, LADCMIXMUTEMIC2BOOST, 0x0);
/*disable Right MIC1 Boost stage*/
codec_wr_prcm_control(RADCMIXSC, 0x1, RADCMIXMUTEMIC2BOOST, 0x0);
/*disable PHONEP-PHONEN Boost stage*/
codec_wr_prcm_control(LADCMIXSC, 0x1, LADCMIXMUTEPHONEPN, 0x0);
/*disable PHONEP-PHONEN Boost stage*/
codec_wr_prcm_control(RADCMIXSC, 0x1, RADCMIXMUTEPHONEPN, 0x0);
/*disable LINEINL ADC*/
codec_wr_prcm_control(LADCMIXSC, 0x1, LADCMIXMUTELINEINL, 0x0);
/*disable LINEINR ADC*/
codec_wr_prcm_control(RADCMIXSC, 0x1, RADCMIXMUTELINEINR, 0x0);
/*disable adc_r adc_l analog*/
codec_wr_prcm_control(ADC_AP_EN, 0x1, ADCREN, 0x0);
codec_wr_prcm_control(ADC_AP_EN, 0x1, ADCLEN, 0x0);
/*disable AIF1 adc Timeslot0 channel*/
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL, 0x1, AIF1_AD0L_ENA, 0x0);
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL, 0x1, AIF1_AD0R_ENA, 0x0);
/*confige AIF1 Digital Mixer Source*/
codec_wr_control(SUNXI_AIF1_MXR_SRC, 0xf, AIF1_AD0L_MXL_SRC, 0x0);
codec_wr_control(SUNXI_AIF1_MXR_SRC, 0xf, AIF1_AD0R_MXR_SRC, 0x0);
/*disable ADC Digital part*/
codec_wr_control(SUNXI_ADC_DIG_CTRL, 0x1, ENAD, 0);
/*diable clk parts*/
if (play_running == 1 ) {
pr_debug("[audio stream]playback is running!!!!\n");
} else {
/*disable aif1clk*/
codec_wr_control(SUNXI_SYSCLK_CTL , 0x1, AIF1CLK_ENA, 0);
/*disble sys clk */
codec_wr_control(SUNXI_SYSCLK_CTL , 0x1, SYSCLK_ENA, 0);
/*disable module AIF1*/
codec_wr_control(SUNXI_MOD_CLK_ENA , 0x1, AIF1_MOD_CLK_EN, 0x0);
/*reset module AIF1*/
codec_wr_control(SUNXI_MOD_RST_CTL , 0x1, AIF1_MOD_RST_CTL, 0x0);
/*global disable*/
codec_wr_control(SUNXI_DA_CTL , 0x1, GEN, 0x0);
}
/*disable module ADC*/
codec_wr_control(SUNXI_MOD_CLK_ENA , 0x1, ADC_DIGITAL_MOD_CLK_EN, 0x0);
/*reset module ADC*/
codec_wr_control(SUNXI_MOD_RST_CTL , 0x1, ADC_DIGITAL_MOD_RST_CTL, 0x0);
}
/* I2S0 RX DISABLE */
codec_wr_control(SUNXI_DA_CTL , 0x1, RXEN, 0);
if (agc_used) {
codec_wr_control(0x210, 0x1, 7, 0x0);
codec_wr_control(0x214, 0x1, 7, 0x0);
codec_wr_control(0x408, 0xf, 0, 0x0);
codec_wr_control(0x408, 0x7, 12, 0x0);
codec_wr_control(0x40c, 0xf, 0, 0x0);
codec_wr_control(0x40c, 0x7, 12, 0x0);
}
cap_running = 0;
}
}
static int sndpcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
return 0;
}
/*
* phone record from main mic + phone in(digital bb) .
* or
* phone record from sub mic + phone in(digital bb) .
* mic1 uses as main mic. mic2 uses as sub mic
*/
static int codec_digital_voice_mic_bb_capture_open(void)
{
/*select phonein source */
codec_wr_control(SUNXI_AIF1_MXR_SRC , 0x1, AIF1_AD0L_MXL_SRC_AIF2DACL, 1);
/*select mic source*/
codec_wr_control(SUNXI_AIF1_MXR_SRC , 0x1, AIF1_AD0L_MXL_SRC_ADCL, 1);
/*aif1 adc left channel source select */
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL , 0x3, AIF1_AD0L_SRC, 0);
/*aif1 adc left channel enable */
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL , 0x1, AIF1_AD0L_ENA, 1);
msleep(200);
return 0;
}
static int codec_digital_voice_bb_bt_capture_open(void)
{
/*select AIF1 input mixer source */
codec_wr_control(SUNXI_AIF1_MXR_SRC , 0x1, AIF1_AD0L_MXL_SRC_AIF2DACR, 1);
codec_wr_control(SUNXI_AIF1_MXR_SRC , 0x1, AIF1_AD0L_MXL_SRC_AIF2DACL, 1);
/*AIF1 ADC Timeslot0 left channel data source select*/
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL , 0x3, AIF1_AD0L_SRC, 0);
/*open ADC channel slot0 switch*/
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL , 0x1, AIF1_AD0L_ENA, 1);
msleep(200);
return 0;
}
static int codec_analog_voice_bb_bt_capture_open(void)
{
/*select AIF1 input mixer source */
codec_wr_control(SUNXI_AIF1_MXR_SRC , 0x1, AIF1_AD0R_MXR_SRC_AIF2DACL, 1);
codec_wr_control(SUNXI_AIF1_MXR_SRC , 0x1, AIF1_AD0R_MXR_SRC_ADCR, 1);
/*AIF1 ADC Timeslot0 left channel data source select*/
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL , 0x3, AIF1_AD0L_SRC, 1);
/*open ADC channel slot0 switch*/
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL , 0x1, AIF1_AD0L_ENA, 1);
msleep(200);
return 0;
}
/*
* use for phone record from main/headset mic + phone in.
* .
*/
static int codec_analog_voice_capture_open(void)
{
if (codec_analog_mainmic_en) {
/*select mic source*/
codec_wr_prcm_control(LADCMIXSC, 0x1, LADCMIXMUTEMIC1BOOST, 0x1);
} else {
/*select mic2 source*/
codec_wr_prcm_control(LADCMIXSC, 0x1, LADCMIXMUTEMIC2BOOST, 0x1);
}
/*select phonein source*/
codec_wr_prcm_control(LADCMIXSC, 0x1, LADCMIXMUTEPHONEPN, 0x1);
/*enable adc analog*/
codec_wr_prcm_control(ADC_AP_EN, 0x1, ADCLEN, 0x1);
/*enable dac digital*/
codec_wr_control(SUNXI_ADC_DIG_CTRL , 0x1, ENAD, 1);
codec_wr_control(SUNXI_ADC_DIG_CTRL , 0x1, ENDM, 0);
/*select aif1 adc left channel mixer source */
codec_wr_control(SUNXI_AIF1_MXR_SRC , 0x1, AIF1_AD0L_MXL_SRC_ADCL,1);
/*select aif1 adc left channel source */
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL , 0x3, AIF1_AD0L_SRC,0);
/*enable aif1 adc left channel */
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL , 0x1, AIF1_AD0L_ENA,1);
msleep(200);
return 0;
}
/*
* use for the line_in record
*/
static int codec_voice_linein_capture_open(void)
{
/*enable AIF1 adc Timeslot0 channel enable*/
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL, 0x1, AIF1_AD0L_ENA, 0x1);
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL, 0x1, AIF1_AD0R_ENA, 0x1);
/*confige AIF1 adc Timeslot0 left channel data source*/
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL, 0x3, AIF1_AD0L_SRC, 0);
/*confige AIF1 adc Timeslot0 right channel data source*/
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL, 0x3, AIF1_AD0R_SRC, 0);
/*confige AIF1 Digital Mixer Source*/
codec_wr_control(SUNXI_AIF1_MXR_SRC, 0xf, AIF1_AD0L_MXL_SRC, 0x2);
codec_wr_control(SUNXI_AIF1_MXR_SRC, 0xf, AIF1_AD0R_MXR_SRC, 0x2);
/*enable dac digital*/
codec_wr_control(SUNXI_ADC_DIG_CTRL , 0x1, ENAD, 1);
codec_wr_control(SUNXI_ADC_DIG_CTRL , 0x1, ENDM, 0);
/*enable adc_r adc_l analog*/
codec_wr_prcm_control(ADC_AP_EN, 0x1, ADCREN, 0x1);
codec_wr_prcm_control(ADC_AP_EN, 0x1, ADCLEN, 0x1);
/*enable Right MIC2 Boost stage*/
codec_wr_prcm_control(RADCMIXSC, 0x1, RADCMIXMUTELINEINR, 0x1);
/*enable Left MIC2 Boost stage*/
codec_wr_prcm_control(LADCMIXSC, 0x1, LADCMIXMUTELINEINL, 0x1);
msleep(200);
return 0;
}
static int codec_system_bt_capture_open(void)
{
/*config clk fmt*/
/*config aif2 from pll2*/
codec_wr_control(SUNXI_SYSCLK_CTL, 0x1, AIF2CLK_ENA, 0x1);
/*aif2 clk source select*/
codec_wr_control(SUNXI_SYSCLK_CTL, 0x3, AIF2CLK_SRC, 0x3);
codec_wr_control(SUNXI_MOD_CLK_ENA, 0x1, AIF2_MOD_CLK_EN, 0x1);
codec_wr_control(SUNXI_MOD_RST_CTL, 0x1, AIF2_MOD_RST_CTL, 0x1);
/*config aif2 fmt :pcm mono 16 lrck=8k,blck/lrck = 64*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL, 0x1, AIF2_MSTR_MOD, 0x0);/*master*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL, 0x1, AIF2_BCLK_INV, 0x0);/*bclk:normal*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL, 0x1, AIF2_LRCK_INV, 0x0);/*lrck:normal*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL, 0xf, AIF2_BCLK_DIV, 0x9);/*aif2/bclk=48*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL, 0x7, AIF2_LRCK_DIV, 0x2);/*bclk/lrck=64*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL, 0x3, AIF2_WORD_SIZ, 0x1);/*sr=16*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL, 0x3, AIF2_DATA_FMT, 0x3);/*dsp mode*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL, 0x1, AIF2_MONO_PCM, 1);/*dsp mode*/
/*aif3 mode enable*/
codec_wr_control(SUNXI_MOD_CLK_ENA, 0x1, AIF3_MOD_CLK_EN, 0x1);
codec_wr_control(SUNXI_MOD_RST_CTL, 0x1, AIF3_MOD_RST_CTL, 0x1);
/*config aif3: clk ,fmt*/
codec_wr_control(SUNXI_AIF3_CLK_CTRL, 0x1, AIF3_BCLK_INV, 0x0);
codec_wr_control(SUNXI_AIF3_CLK_CTRL, 0x1, AIF3_LRCK_INV, 0x0);
codec_wr_control(SUNXI_AIF3_CLK_CTRL, 0x3, AIF3_WORD_SIZ, 0x1);/*sr = 16*/
codec_wr_control(SUNXI_AIF3_CLK_CTRL, 0x3, AIF3_CLOC_SRC, 0x1);/*clk form aif2*/
/*select aif2 dac input source*/
codec_wr_control(SUNXI_AIF3_SGP_CTRL, 0x3, AIF2_DAC_SRC, 1);
/*aif2 adc right channel enable*/
codec_wr_control(SUNXI_AIF2_ADCDAT_CTRL, 0x1, AIF2_ADCR_EN,1);
/*aif2 adc right channel enable*/
//codec_wr_control(SUNXI_AIF2_MXR_SRC, 0x1, AIF2_ADCL_MXR_SRC_AIF2DACR,1);
/*select bt source*/
codec_wr_control(SUNXI_AIF1_MXR_SRC , 0x1, AIF1_AD0L_MXL_SRC_AIF2DACL, 1);
/*aif1 adc left channel source select */
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL , 0x3, AIF1_AD0L_SRC, 0);
/*aif1 adc left channel enable */
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL , 0x1, AIF1_AD0L_ENA, 1);
pr_debug("%s,line:%d,SUNXI_AIF3_DACDAT_CTRL:%x\n",__func__,__LINE__,codec_rdreg(SUNXI_AIF3_DACDAT_CTRL));
return 0;
}
static int sndpcm_perpare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
int play_ret = 0, capture_ret = 0;
struct snd_pcm_runtime *runtime = substream->runtime;
/*confige the sample for adc,dac*/
switch (substream->runtime->rate) {
case 8000:
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF1_FS, 0x0);
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF2_FS, 0x0);
break;
case 11025:
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF1_FS, 0x1);
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF2_FS, 0x1);
break;
case 12000:
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF1_FS, 0x2);
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF2_FS, 0x2);
break;
case 16000:
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF1_FS, 0x3);
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF2_FS, 0x3);
break;
case 22050:
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF1_FS, 0x4);
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF2_FS, 0x4);
break;
case 24000:
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF1_FS, 0x5);
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF2_FS, 0x5);
break;
case 32000:
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF1_FS, 0x6);
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF2_FS, 0x6);
break;
case 44100:
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF1_FS, 0x7);
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF2_FS, 0x7);
break;
case 48000:
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF1_FS, 0x8);
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF2_FS, 0x8);
break;
case 96000:
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF1_FS, 0x9);
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF2_FS, 0x9);
break;
case 192000:
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF1_FS, 0xa);
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF2_FS, 0xa);
break;
default:
pr_err("AUDIO SAMPLE IS WRONG:There is no suitable sampling rate!!!\n");
break;
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
if (runtime->status->state == SNDRV_PCM_STATE_XRUN) {
if (!(codec_analog_mainmic_en || codec_analog_headsetmic_en ||codec_digital_headsetmic_en ||
codec_digital_mainmic_en ||codec_analog_btmic_en || codec_digital_btmic_en)){
pr_err("%s,-------playback: SNDRV_PCM_STATE_XRUN---------line:%d\n",__func__,__LINE__);
if (codec_speaker_headset_earpiece_en == 2) {
play_ret = codec_pa_and_headset_play_open();
} else if (codec_speaker_headset_earpiece_en == 1) {
play_ret = codec_pa_play_open();
} else if (codec_speaker_headset_earpiece_en == 3) {
play_ret = codec_earpiece_play_open();
} else if (codec_speaker_headset_earpiece_en == 0){
play_ret = codec_headphone_play_open();
}else if(codec_speaker_headset_earpiece_en == 4){
play_ret = codec_system_btout_open();
}
play_running = 1;
}
}
//play_running = 1;
return play_ret;
} else {
if (runtime->status->state == SNDRV_PCM_STATE_XRUN) {
pr_err("%s,-------capture: SNDRV_PCM_STATE_XRUN---------line:%d\n",__func__,__LINE__);
}
if (codec_voice_record_en && (codec_digital_mainmic_en ||codec_digital_headsetmic_en)) {
capture_ret = codec_digital_voice_mic_bb_capture_open();
} else if (codec_voice_record_en && codec_digital_btmic_en) {
pr_debug("%s,line:%d\n",__func__,__LINE__);
capture_ret = codec_digital_voice_bb_bt_capture_open();
} else if (codec_voice_record_en && codec_analog_btmic_en) {
capture_ret = codec_analog_voice_bb_bt_capture_open();
} else if (codec_voice_record_en && ( codec_analog_mainmic_en ||codec_analog_headsetmic_en )) {
capture_ret = codec_analog_voice_capture_open();
} else if (codec_lineinin_en && codec_lineincap_en) {
capture_ret = codec_voice_linein_capture_open();
} else if (codec_voice_record_en && codec_system_bt_capture_en) {
pr_debug("%s,line:%d\n",__func__,__LINE__);
capture_ret = codec_system_bt_capture_open();
} else if (!codec_voice_record_en) {
capture_ret = codec_capture_open();
}
return capture_ret;
}
}
int sunxi_i2s_set_rate(int freq) {
if (clk_set_rate(codec_pll2clk, freq)) {
pr_err("set codec_pll2clk rate fail\n");
}
if (clk_set_rate(codec_moduleclk, freq)) {
pr_err("set codec_moduleclk rate fail\n");
}
return 0;
}
EXPORT_SYMBOL(sunxi_i2s_set_rate);
static const char *spk_headset_earpiece_function[] = {"headset", "spk", "spk_headset", "earpiece","btout","bt_button_voice"};
static const struct soc_enum spk_headset_earpiece_enum[] = {
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_headset_earpiece_function), spk_headset_earpiece_function),
};
/*
* codec_analog_phoneout_en == 1, the phone out is open. receiver can hear the voice which you say.
* codec_analog_phoneout_en == 0, the phone out is close.
*/
static int codec_analog_set_phoneout(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_analog_phoneout_en = ucontrol->value.integer.value[0];
if (codec_analog_phoneout_en ) {
codec_wr_prcm_control(PHONEOUT_CTRL, 0x1, PHONEOUT_EN, 0x1);
} else {
codec_wr_prcm_control(PHONEOUT_CTRL, 0x1, PHONEOUT_EN, 0x0);
}
return 0;
}
static int codec_analog_get_phoneout(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_analog_phoneout_en;
return 0;
}
/*
* codec_analog_phonein_en == 1, the phone in is open.
* while you open one of the device(speaker,earpiece,headphone).
* you can hear the caller's voice.
* codec_analog_phonein_en == 0. the phone in is close.
*/
static int codec_analog_set_phonein(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_analog_phonein_en = ucontrol->value.integer.value[0];
if (codec_analog_phonein_en) {
pr_debug("%s,line:%d\n",__func__,__LINE__);
/*enable AIF1 DAC Timeslot0 channel enable*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x1, AIF1_DA0L_ENA, 0x1);
/*confige AIF1 DAC Timeslot0 left channel data source*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x3, AIF1_DA0L_SRC, 0);
/*confige dac digital mixer source */
codec_wr_control(SUNXI_DAC_MXR_SRC, 0x1, DACL_MXR_SRC_AIF1DA0L, 0x1);
/*enable dac digital*/
codec_wr_control(SUNXI_DAC_DIG_CTRL, 0x1, ENDA, 0x1);
/*enable dac_l and dac_r*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, DACALEN, 0x1);
//codec_wr_prcm_control(ROMIXSC, 0x1, RMIXMUTEDACL, 0x1);
//codec_wr_prcm_control(LOMIXSC, 0x1, LMIXMUTEDACL, 0x1);
/*select PHONEP-PHONEN*/
codec_wr_prcm_control(LOMIXSC, 0x1, LMIXMUTEPHONEPN, 0x1);
codec_wr_prcm_control(ROMIXSC, 0x1, RMIXMUTEPHONEPN, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LMIXEN, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RMIXEN, 0x1);
} else {
codec_wr_prcm_control(LOMIXSC, 0x1, LMIXMUTEPHONEPN, 0x0);
codec_wr_prcm_control(ROMIXSC, 0x1, RMIXMUTEPHONEPN, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LMIXEN, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RMIXEN, 0x0);
}
return 0;
}
static int codec_analog_get_phonein(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_analog_phonein_en;
return 0;
}
/*
* codec_earpieceout_en == 1, open the earpiece.
* codec_earpieceout_en == 0, close the earpiece.
*/
static int codec_set_earpieceout(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
int i =0;
int reg_val = 0;
codec_earpieceout_en = ucontrol->value.integer.value[0];
if (codec_earpieceout_en) {
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPIS, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPIS, 0x0);
gpio_set_value(item.gpio.gpio, 0);
/*mute l_pa and r_pa*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPPAMUTE, 0x0);
codec_wr_prcm_control(ADDA_APT2, 0x1, ZERO_CROSS_EN, 0x0);
/*open earpiece out routeway*/
/*unmute l_pa*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0x1);
/*select the analog mixer input source*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPIS, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPIS, 0x1);
/*select HPL inverting output*/
codec_wr_prcm_control(PAEN_HP_CTRL, 0x3, HPCOM_FC, 0x1);
codec_wr_prcm_control(ADDA_APT2, 0x1, ZERO_CROSS_EN, 0x1);
for(i=0; i < headphone_vol; i++) {
/*set HPVOL volume*/
codec_wr_prcm_control(HP_VOLC, 0x3f, HPVOL, i);
reg_val = read_prcm_wvalue(HP_VOLC);
reg_val &= 0x3f;
if ((i%2==0))
usleep_range(1000,2000);
}
codec_wr_prcm_control(HP_VOLC, 0x3f, HPVOL, earpiece_vol);
codec_speakerout_en = 0;
codec_headphoneout_en = 0;
} else {
/*mute l_pa and r_pa*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPPAMUTE, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPIS, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPIS, 0x0);
if (headphone_direct_used) {
codec_wr_prcm_control(PAEN_HP_CTRL, 0x3, HPCOM_FC, 0x3);
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, COMPTEN, 0x1);
} else {
codec_wr_prcm_control(PAEN_HP_CTRL, 0x3, HPCOM_FC, 0x0);
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, COMPTEN, 0x0);
}
//codec_wr_prcm_control(PAEN_HP_CTRL, 0x3, HPCOM_FC, 0x0);
codec_wr_prcm_control(ADDA_APT2, 0x1, ZERO_CROSS_EN, 0x0);
}
return 0;
}
static int codec_get_earpieceout(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_earpieceout_en;
return 0;
}
/*
* codec_speakerout_en == 1, open the speaker.
* codec_speakerout_en == 0, close the speaker.
*/
static int codec_set_speakerout(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_speakerout_en = ucontrol->value.integer.value[0];
if (codec_speakerout_en) {
//gpio_set_value(item.gpio.gpio, 0);
/*close headphone and earpiece out routeway*/
//codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0x0);
//codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPPAMUTE, 0x0);
//codec_wr_prcm_control(PAEN_HP_CTRL, 0x3, HPCOM_FC, 0x0);
codec_wr_prcm_control(ADDA_APT2, 0x1, ZERO_CROSS_EN, 0x0);
codec_wr_prcm_control(HP_VOLC, 0x3f, HPVOL, pa_vol);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPIS, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPIS, 0x0);
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, LTRNMUTE, 0x1);
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, RTLNMUTE, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPPAMUTE, 0x0);
if (headphone_direct_used) {
codec_wr_prcm_control(PAEN_HP_CTRL, 0x3, HPCOM_FC, 0x3);
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, COMPTEN, 0x1);
} else {
codec_wr_prcm_control(PAEN_HP_CTRL, 0x3, HPCOM_FC, 0x0);
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, COMPTEN, 0x0);
}
usleep_range(2000, 3000);
gpio_set_value(item.gpio.gpio, 1);
msleep(62);
codec_headphoneout_en = 0;
codec_earpieceout_en = 0;
} else {
gpio_set_value(item.gpio.gpio, 0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPIS, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPIS, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0);
}
return 0;
}
static int codec_get_speakerout(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_speakerout_en;
return 0;
}
/*
* codec_headphoneout_en == 1, open the headphone.
* codec_headphoneout_en == 0, close the headphone.
*/
static int codec_set_headphoneout(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_headphoneout_en = ucontrol->value.integer.value[0];
if (codec_headphoneout_en) {
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPIS, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPIS, 0x0);
gpio_set_value(item.gpio.gpio, 0);
//msleep(62);
if (headphone_direct_used) {
codec_wr_prcm_control(PAEN_HP_CTRL, 0x3, HPCOM_FC, 0x3);
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, COMPTEN, 0x1);
} else {
codec_wr_prcm_control(PAEN_HP_CTRL, 0x3, HPCOM_FC, 0x0);
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, COMPTEN, 0x0);
}
/*select HPL inverting output*/
//codec_wr_prcm_control(PAEN_HP_CTRL, 0x3, HPCOM_FC, 0x0);
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, LTRNMUTE, 0x0);
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, RTLNMUTE, 0x0);
/*select the analog mixer input source*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPIS, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPIS, 0x1);
codec_wr_prcm_control(ADDA_APT2, 0x1, ZERO_CROSS_EN, 0x1);
/*unmute l_pa and r_pa*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPPAMUTE, 0x1);
/*set HPVOL volume*/
codec_wr_prcm_control(HP_VOLC, 0x3f, HPVOL, headphone_vol);
codec_speakerout_en = 0;
codec_earpieceout_en = 0;
} else {
/*mute l_pa and r_pa*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPPAMUTE, 0x0);
/*select the default dac input source*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPIS, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPIS, 0x0);
codec_wr_prcm_control(ADDA_APT2, 0x1, ZERO_CROSS_EN, 0x0);
}
return 0;
}
static int codec_get_headphoneout(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_headphoneout_en;
return 0;
}
/*
* codec_analog_mainmic_en == 1, open mic1.
* codec_analog_mainmic_en == 0, close mic1.
*/
static int codec_analog_set_mainmic(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_analog_mainmic_en = ucontrol->value.integer.value[0];
if (codec_analog_mainmic_en) {
/*close headset mic(mic2) routeway*/
codec_wr_prcm_control(MIC2G_LINEEN_CTRL, 0x1, MIC2AMPEN, 0x0);
codec_wr_prcm_control(PHONEOUT_CTRL, 0xf, PHONEOUTS0, 0x0);
/*open main mic(mic1) routeway*/
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL, 0x1, MIC1AMPEN, 0x1);
/*mic1 gain 36dB,if capture volume is too small, enlarge the mic1boost*/
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL,0x7, MIC1BOOST, phone_main_mic_vol);
/*enable Master microphone bias*/
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL, 0x1, MMICBIASEN, 0x1);
codec_wr_prcm_control(PHONEOUT_CTRL, 0x1, PHONEOUTS3, 0x1);
codec_analog_headsetmic_en = 0;
} else {
/*disable mic pa*/
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL, 0x1, MIC1AMPEN, 0x0);
/*disable Master microphone bias*/
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL, 0x1, MMICBIASEN, 0x0);
codec_wr_prcm_control(PHONEOUT_CTRL, 0x1, PHONEOUTS3, 0x0);
}
return 0;
}
static int codec_analog_get_mainmic(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_analog_mainmic_en;
return 0;
}
/*
* codec_analog_headsetmic_en == 1, open mic2.
* codec_analog_headsetmic_en == 0, close mic2.
*/
static int codec_analog_set_headsetmic(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_analog_headsetmic_en = ucontrol->value.integer.value[0];
if (codec_analog_headsetmic_en) {
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL, 0x1, MIC1AMPEN, 0x0);
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL, 0x1, MMICBIASEN, 0x0);
codec_wr_prcm_control(PHONEOUT_CTRL, 0xf, PHONEOUTS0, 0x0);
/*open headset mic(mic2) routeway*/
/*enable mic2 pa*/
codec_wr_prcm_control(MIC2G_LINEEN_CTRL, 0x1, MIC2AMPEN, 0x1);
/*mic2 gain 36dB,if capture volume is too small, enlarge the mic2boost*/
codec_wr_prcm_control(MIC2G_LINEEN_CTRL,0x7,MIC2BOOST, phone_headset_mic_vol);
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL, 0x1, MIC2_SS, 0x1);
codec_wr_prcm_control(PHONEOUT_CTRL, 0x1, PHONEOUTS2, 0x1);
codec_analog_mainmic_en = 0;
} else {
codec_wr_prcm_control(MIC2G_LINEEN_CTRL, 0x1, MIC2AMPEN, 0x0);
codec_wr_prcm_control(PHONEOUT_CTRL, 0x1, PHONEOUTS2, 0x0);
}
return 0;
}
static int codec_analog_get_headsetmic(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_analog_headsetmic_en;
return 0;
}
/*
* codec_voice_record_en == 1, set status.
* codec_voice_record_en == 0, set status.
*/
static int codec_set_voicerecord(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_voice_record_en = ucontrol->value.integer.value[0];
return 0;
}
static int codec_get_voicerecord(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_voice_record_en;
return 0;
}
/*
* close all phone routeway
*/
static int codec_set_endcall(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
int cur_vol =0;
int i =0;
gpio_set_value(item.gpio.gpio, 0);
msleep(50);
cur_vol = read_prcm_wvalue(HP_VOLC);
cur_vol &= 0x3f;
if (cur_vol > 48) {
for (i = cur_vol; i > 52 ; i--) {
/*set HPVOL volume*/
codec_wr_prcm_control(HP_VOLC, 0x3f, HPVOL, i);
usleep_range(9000, 10000);
}
for (i = 52; i > 48 ; i--) {
/*set HPVOL volume*/
codec_wr_prcm_control(HP_VOLC, 0x3f, HPVOL, i);
usleep_range(6000,7000);
}
}
for (i = 48; i > 32 ; i--) {
/*set HPVOL volume*/
codec_wr_prcm_control(HP_VOLC, 0x3f, HPVOL, i);
usleep_range(1000,2000);
}
for (i = 32; i > 0 ; i--) {
/*set HPVOL volume*/
codec_wr_prcm_control(HP_VOLC, 0x3f, HPVOL, i);
usleep_range(100,200);
}
codec_wr_prcm_control(HP_VOLC, 0x3f, HPVOL, 0);
/*close analog parts*/
codec_wr_prcm_control(DAC_PA_SRC, 0xff, LHPIS, 0x0);
codec_wr_prcm_control(LOMIXSC, 0xff, LMIXMUTE, 0x0);
codec_wr_prcm_control(ROMIXSC, 0xff, RMIXMUTE, 0x0);
codec_wr_prcm_control(PAEN_HP_CTRL, 0x3, RTLNMUTE, 0x0);
codec_wr_prcm_control(PHONEOUT_CTRL, 0xff, PHONEOUTS0, 0x60);
codec_wr_prcm_control(MIC2G_LINEEN_CTRL, 0xff, LINEOUTR_SS, 0x40);
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL, 0xf, MIC1BOOST, 0x4);
codec_wr_prcm_control(RADCMIXSC, 0xff, RADCMIXMUTE, 0x0);
codec_wr_prcm_control(LADCMIXSC, 0xff, LADCMIXMUTE, 0x0);
codec_wr_prcm_control(ADC_AP_EN, 0x3, ADCLEN, 0x0);
/*close digital parts*/
codec_wr_control(SUNXI_DA_CTL, 0xffffffff, GEN, 0x0);
codec_wr_control(SUNXI_DA_FAT0, 0xffffffff, FMT, 0x0);
codec_wr_control(SUNXI_DA_INT, 0x1, TX_DRQ, 0x0);
codec_wr_control(SUNXI_DA_INT, 0x1, RX_DRQ, 0x0);
codec_wr_control(SUNXI_DA_CLKD, 0xff, MCLKDIV, 0x0);
codec_wr_control(SUNXI_SYSCLK_CTL, 0xfff, SYSCLK_SRC, 0x0);
codec_wr_control(SUNXI_MOD_CLK_ENA, 0xffff, MODULE_CLK_EN_CTL, 0x0);
codec_wr_control(SUNXI_MOD_RST_CTL, 0xffff, MODULE_RST_CTL_BIT, 0x0);
codec_wr_control(SUNXI_AIF1CLK_CTRL, 0xffff, AIF1_TDMM_ENA, 0x0);
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL, 0xffff, AIF1_SLOT_SIZ, 0x0);
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0xffff, AIF1_LOOP_ENA, 0x0);
codec_wr_control(SUNXI_AIF1_MXR_SRC, 0xffff, AIF1_AD1R_MXR_SRC_C, 0x0);
codec_wr_control(SUNXI_AIF2_CLK_CTRL, 0xffff, 0, 0x0);
codec_wr_control(SUNXI_AIF2_ADCDAT_CTRL, 0xffff, AIF2_LOOP_EN, 0x0);
codec_wr_control(SUNXI_AIF2_DACDAT_CTRL, 0xffff, 0, 0x0);
codec_wr_control(SUNXI_AIF2_MXR_SRC, 0xff, AIF2_ADCR_MXR_SRC, 0x0);
codec_wr_control(SUNXI_AIF3_CLK_CTRL, 0xffff, AIF3_CLOC_SRC, 0x0);
codec_wr_control(SUNXI_ADC_DIG_CTRL, 0x1, ENAD, 0x0);
codec_wr_control(SUNXI_DAC_DIG_CTRL, 0x1, ENDA, 0x0);
codec_wr_control(SUNXI_DAC_MXR_SRC, 0xff, DACR_MXR_SRC, 0x0);
/*set all routeway flag false*/
codec_analog_phonein_en = 0;
codec_analog_mainmic_en = 0;
codec_analog_phoneout_en = 0;
codec_lineinin_en = 0;
codec_lineincap_en = 0;
codec_analog_headsetmic_en = 0;
codec_speakerout_en = 0;
codec_earpieceout_en = 0;
codec_voice_record_en = 0;
codec_headphoneout_en = 0;
/*Digital_bb*/
codec_digital_headsetmic_en = 0;
codec_digital_mainmic_en = 0;
codec_digital_phoneout_en = 0;
codec_digital_phonein_en = 0;
codec_digital_bb_clk_format_init = 0;
/*bluetooth*/
codec_bt_clk_format = 0;
codec_bt_out_en = 0;
bt_bb_button_voice = 0;
codec_analog_btmic_en = 0;
codec_analog_btphonein_en = 0;
codec_digital_btmic_en = 0;
codec_digital_btphonein_en = 0;
codec_digital_bb_bt_clk_format = 0;
codec_system_bt_capture_en = 0;
return 0;
}
static int codec_get_endcall(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
return 0;
}
/*
* use for linein record
*/
static int codec_set_lineincap(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_lineincap_en = ucontrol->value.integer.value[0];
return 0;
}
static int codec_get_lineincap(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_lineincap_en;
return 0;
}
/*
* codec_lineinin_en == 1, open the linein in.
* codec_lineinin_en == 0, close the linein in.
*/
static int codec_set_lineinin(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_lineinin_en = ucontrol->value.integer.value[0];
if (codec_lineinin_en) {
/*select LINEINL*/
codec_wr_prcm_control(LOMIXSC, 0x1, LMIXMUTELINEINL, 0x1);
/*select LINEINR*/
codec_wr_prcm_control(ROMIXSC, 0x1, RMIXMUTELINEINR, 0x1);
/*enable output mixer*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LMIXEN, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RMIXEN, 0x1);
} else {
/*close LINEINL*/
codec_wr_prcm_control(LOMIXSC, 0x1, LMIXMUTELINEINL, 0x0);
/*close LINEINR*/
codec_wr_prcm_control(ROMIXSC, 0x1, RMIXMUTELINEINR, 0x0);
/*disable output mixer*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LMIXEN, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RMIXEN, 0x0);
}
return 0;
}
static int codec_get_lineinin(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_lineinin_en;
return 0;
}
/*
* codec_digital_mainmic_en == 1, open mic1 for digital bb.
* codec_digital_mainmic_en == 0, close mic1 for digital bb.
*/
static int codec_digital_set_mainmic(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_digital_mainmic_en = ucontrol->value.integer.value[0];
if (codec_digital_mainmic_en) {
/*open main mic(mic1) routeway*/
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL, 0x1, MIC1AMPEN, 0x1);
/*mic1 gain 36dB,if capture volume is too small, enlarge the mic1boost*/
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL,0x7, MIC1BOOST, phone_main_mic_vol);
/*enable Master microphone bias*/
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL, 0x1, MMICBIASEN, 0x1);
/*enable Left MIC1 Boost stage*/
codec_wr_prcm_control(LADCMIXSC, 0x1, LADCMIXMUTEMIC1BOOST, 0x1);
/*enable adc analog*/
codec_wr_prcm_control(ADC_AP_EN, 0x1, ADCLEN, 0x1);
/*enable ADC Digital part*/
codec_wr_control(SUNXI_ADC_DIG_CTRL, 0x1, ENAD, 1);
/*enable Digital microphone*/
codec_wr_control(SUNXI_ADC_DIG_CTRL, 0x1, ENDM, 0);
codec_wr_control(SUNXI_AIF2_MXR_SRC, 0x1, AIF2_ADCL_MXR_SRC, 1);
} else {
codec_wr_control(SUNXI_AIF2_MXR_SRC, 0x1, AIF2_ADCL_MXR_SRC, 0);
codec_wr_control(SUNXI_ADC_DIG_CTRL, 0x1, ENAD, 0);
codec_wr_prcm_control(ADC_AP_EN, 0x1, ADCLEN, 0);
codec_wr_prcm_control(LADCMIXSC, 0x1, LADCMIXMUTEMIC1BOOST, 0);
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL, 0x1, MMICBIASEN, 0);
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL, 0x1, MIC1AMPEN, 0);
}
return 0;
}
static int codec_digital_get_mainmic(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_digital_mainmic_en;
return 0;
}
/*
* codec_digital_headsetmic_en == 1, open mic2 for digital bb.
* codec_digital_headsetmic_en == 0, close mic2 for digital bb.
*/
static int codec_digital_set_headsetmic(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_digital_headsetmic_en = ucontrol->value.integer.value[0];
if (codec_digital_headsetmic_en) {
/*enable mic2 pa*/
codec_wr_prcm_control(MIC2G_LINEEN_CTRL, 0x1, MIC2AMPEN, 0x1);
/*mic2 gain 36dB,if capture volume is too small, enlarge the mic2boost*/
codec_wr_prcm_control(MIC2G_LINEEN_CTRL,0x7,MIC2BOOST, phone_headset_mic_vol);
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL, 0x1, MIC2_SS, 0x1);
/*enable Left MIC2 Boost stage*/
codec_wr_prcm_control(LADCMIXSC, 0x1, LADCMIXMUTEMIC2BOOST, 0x1);
/*enable adc analog*/
codec_wr_prcm_control(ADC_AP_EN, 0x1, ADCLEN, 0x1);
/*enable ADC Digital part*/
codec_wr_control(SUNXI_ADC_DIG_CTRL, 0x1, ENAD, 1);
/*enable Digital microphone*/
codec_wr_control(SUNXI_ADC_DIG_CTRL, 0x1, ENDM, 0);
codec_wr_control(SUNXI_AIF2_MXR_SRC, 0x1, AIF2_ADCL_MXR_SRC, 1);
} else {
codec_wr_control(SUNXI_AIF2_MXR_SRC, 0x1, AIF2_ADCL_MXR_SRC, 0);
codec_wr_control(SUNXI_ADC_DIG_CTRL, 0x1, ENAD, 0);
codec_wr_prcm_control(ADC_AP_EN, 0x1, ADCLEN, 0x0);
codec_wr_prcm_control(LADCMIXSC, 0x1, LADCMIXMUTEMIC2BOOST, 0x0);
codec_wr_prcm_control(MIC2G_LINEEN_CTRL, 0x1, MIC2AMPEN, 0);
}
return 0;
}
static int codec_digital_get_headsetmic(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_digital_headsetmic_en;
return 0;
}
/*
* codec_digital_phoneout_en == 1, enable phoneout for digital bb.
* codec_digital_phoneout_en == 0, disable phoneout for digital bb.
*/
static int codec_digital_set_phoneout(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_digital_phoneout_en = ucontrol->value.integer.value[0];
if (codec_digital_phoneout_en) {
/*enable aif2 adcl chanel*/
codec_wr_control(SUNXI_AIF2_ADCDAT_CTRL, 0x1, AIF2_ADCL_EN, 1);
/*select aif2 adc left channel source */
codec_wr_control(SUNXI_AIF2_ADCDAT_CTRL, 0x3, AIF2_ADCL_SRC, 0);
} else {
codec_wr_control(SUNXI_AIF2_ADCDAT_CTRL, 0x1, AIF2_ADCL_EN, 0);
}
return 0;
}
static int codec_digital_get_phoneout(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_digital_phoneout_en;
return 0;
}
/*
* codec_digital_phonein_en == 1, enble phonein for digital bb .
* codec_digital_phonein_en == 0. disable phonein for digital bb.
*/
static int codec_set_digital_phonein(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_digital_phonein_en = ucontrol->value.integer.value[0];
if (codec_digital_phonein_en) {
/*enable aif2 dacl chanel*/
codec_wr_control(SUNXI_AIF2_DACDAT_CTRL, 0x1, AIF2_DACL_ENA, 1);
/*select aif2 dac left channel source */
codec_wr_control(SUNXI_AIF2_DACDAT_CTRL, 0x3, AIF2_DACL_SRC, 0);
/*aif2 dac input source select*/
codec_wr_control(SUNXI_AIF3_SGP_CTRL, 0x3, AIF2_DAC_SRC, 0);
/*dac left channel source select*/
codec_wr_control(SUNXI_DAC_MXR_SRC, 0x1, DACL_MXR_SRC_AIF2DACL, 1);
/*dac digital part enable*/
codec_wr_control(SUNXI_DAC_DIG_CTRL, 0x1, ENDA, 1);
/*dac analog part enable*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, DACALEN, 0x1);
/*select output mixer source*/
codec_wr_prcm_control(LOMIXSC, 0x1, LMIXMUTEDACL, 0x1);
codec_wr_prcm_control(ROMIXSC, 0x1, RMIXMUTEDACL, 0x1);
/*enable output mixer*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LMIXEN, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RMIXEN, 0x1);
} else {
/*disable output mixer*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LMIXEN, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RMIXEN, 0x0);
/**/
codec_wr_prcm_control(LOMIXSC, 0x1, LMIXMUTEDACL, 0x0);
codec_wr_prcm_control(ROMIXSC, 0x1, RMIXMUTEDACL, 0x0);
/*disable dac digital part*/
codec_wr_control(SUNXI_DAC_DIG_CTRL, 0x1, ENDA, 0);
/*disable dac analog part*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, DACALEN, 0x0);
/*dac left channel source select*/
codec_wr_control(SUNXI_DAC_MXR_SRC, 0x1, DACL_MXR_SRC_AIF2DACL, 0);
/*disable aif2 dacl chanel*/
codec_wr_control(SUNXI_AIF2_DACDAT_CTRL, 0x1, AIF2_DACL_ENA, 0);
}
return 0;
}
static int codec_get_digital_phonein(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_digital_phonein_en;
return 0;
}
/*
*codec_set_digital_bb_clk_format:confige the clk fmt for call with digital bb
*
*/
static int codec_set_digital_bb_clk_format(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_digital_bb_clk_format_init = ucontrol->value.integer.value[0];
if (codec_digital_bb_clk_format_init) {
/*enable src clk*/
/*set pll2 :24576k*/
if (clk_set_rate(codec_pll2clk, 24576000)) {
pr_err("set codec_pll2clk rate fail\n");
}
/*enable pll2*4 for src*/
if ((!codec_srcclk)||(IS_ERR(codec_srcclk))) {
pr_err("try to get codec_srcclk failed!\n");
}
if (clk_prepare_enable(codec_srcclk)) {
pr_err("err:open codec_srcclk failed; \n");
}
/*enable aif2,system clk from aif2*/
codec_wr_control(SUNXI_SYSCLK_CTL, 0x1, AIF2CLK_ENA, 0x1);
/*aif2 clk source select*/
codec_wr_control(SUNXI_SYSCLK_CTL, 0x3, AIF2CLK_SRC, 0x3);
/*sysclk from aif2*/
codec_wr_control(SUNXI_SYSCLK_CTL, 0x1, SYSCLK_SRC, 0x1);
/*enable aif2/da/da module*/
codec_wr_control(SUNXI_MOD_CLK_ENA, 0x1, AIF2_MOD_CLK_EN, 0x1);
codec_wr_control(SUNXI_MOD_CLK_ENA, 0x1, DAC_DIGITAL_MOD_CLK_EN, 0x1);
codec_wr_control(SUNXI_MOD_CLK_ENA, 0x1, ADC_DIGITAL_MOD_CLK_EN, 0x1);
codec_wr_control(SUNXI_MOD_CLK_ENA, 0x1, SRC1_MOD_CLK_EN, 0x1);/*src1*/
codec_wr_control(SUNXI_MOD_CLK_ENA, 0x1, SRC2_MOD_CLK_EN, 0x1);/*src2*/
codec_wr_control(SUNXI_MOD_RST_CTL, 0x1, AIF2_MOD_RST_CTL, 0x1);
codec_wr_control(SUNXI_MOD_RST_CTL, 0x1, DAC_DIGITAL_MOD_RST_CTL, 0x1);
codec_wr_control(SUNXI_MOD_RST_CTL, 0x1, ADC_DIGITAL_MOD_RST_CTL, 0x1);
codec_wr_control(SUNXI_MOD_RST_CTL, 0x1, SRC2_MOD_RST_CTL, 0x1);/*src2*/
codec_wr_control(SUNXI_MOD_RST_CTL, 0x1, SRC1_MOD_RST_CTL, 0x1);/*src1*/
/*confige ad/da sr:8k*/
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF1_FS, 0x0);
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF2_FS, 0x0);
/*select src1 source from aif2*/
codec_wr_control(SUNXI_SYS_SR_CTRL , 0x1, SRC1_SRC, 0x1);
/*select src2 source from aif2*/
codec_wr_control(SUNXI_SYS_SR_CTRL , 0x1, SRC2_SRC, 0x1);
codec_wr_control(SUNXI_SYS_SR_CTRL , 0x1, SRC1_ENA, 0x1);/*enable src1*/
codec_wr_control(SUNXI_SYS_SR_CTRL , 0x1, SRC2_ENA, 0x1);/*enable src2*/
/*confige aif2 lrck:8k,pcm,mono,slave*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL , 0x1, AIF2_MSTR_MOD, 0x1);/*confige aif2 slave*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL , 0x1, AIF2_BCLK_INV, 0x0);/*bclk_inv:0*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL , 0x1, AIF2_LRCK_INV, 0x0);/*lrck_inv:0*/
// codec_wr_control(SUNXI_AIF2_CLK_CTRL , 0x3, AIF2_LRCK_DIV, 0x2);/*bclk/lrck:64*/
// codec_wr_control(SUNXI_AIF2_CLK_CTRL , 0xf, AIF2_BCLK_DIV, 0x2);/*aif2/bclk:48*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL , 0x3, AIF2_WORD_SIZ, 0x1);/*wss:16*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL , 0x3, AIF2_DATA_FMT, 0x3);/*fmt:pcm*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL , 0x1, AIF2_MONO_PCM, 0x1);/*fmt:pcm*/
} else {
/*disable aif2,system clk from aif2*/
codec_wr_control(SUNXI_SYSCLK_CTL, 0xfff, SYSCLK_SRC, 0);
/*disable aif2/da/da module*/
codec_wr_control(SUNXI_MOD_CLK_ENA, 0x1, AIF2_MOD_CLK_EN, 0x0);
codec_wr_control(SUNXI_MOD_CLK_ENA, 0x1, DAC_DIGITAL_MOD_CLK_EN, 0x0);
codec_wr_control(SUNXI_MOD_CLK_ENA, 0x1, ADC_DIGITAL_MOD_CLK_EN, 0x0);
codec_wr_control(SUNXI_MOD_CLK_ENA, 0x1, SRC1_MOD_CLK_EN, 0x0);/*src1*/
codec_wr_control(SUNXI_MOD_CLK_ENA, 0x1, SRC2_MOD_CLK_EN, 0x0);/*src2*/
codec_wr_control(SUNXI_MOD_RST_CTL, 0x1, AIF2_MOD_RST_CTL, 0x0);
codec_wr_control(SUNXI_MOD_RST_CTL, 0x1, DAC_DIGITAL_MOD_RST_CTL, 0x0);
codec_wr_control(SUNXI_MOD_RST_CTL, 0x1, ADC_DIGITAL_MOD_RST_CTL, 0x0);
codec_wr_control(SUNXI_MOD_RST_CTL, 0x1, SRC2_MOD_RST_CTL, 0x0);/*src2*/
codec_wr_control(SUNXI_MOD_RST_CTL, 0x1, SRC1_MOD_RST_CTL, 0x0);/*src1*/
codec_wr_control(SUNXI_SYS_SR_CTRL , 0x1, SRC1_ENA, 0x0);/*disable src1*/
codec_wr_control(SUNXI_SYS_SR_CTRL , 0x1, SRC2_ENA, 0x0);/*disable src2*/
/*confige aif2 lrck:8k,pcm,mono,slave*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL , 0xffff, 0, 0x0);
if ((NULL == codec_srcclk)||(IS_ERR(codec_srcclk))) {
pr_err("codec_srcclk handle is invaled, just return\n");
} else {
clk_disable_unprepare(codec_srcclk);
}
}
return 0;
}
static int codec_get_digital_bb_clk_format(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_digital_bb_clk_format_init;
return 0;
}
static int codec_set_bt_clk_format(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_bt_clk_format = ucontrol->value.integer.value[0];
if (codec_bt_clk_format) {
pr_debug("%s,line:%d\n",__func__,__LINE__);
/*enable src clk*/
if (clk_set_rate(codec_pll2clk, 24576000)) {
pr_err("err:set codec_pll2clk rate fail\n");
}
if ((!codec_srcclk)||(IS_ERR(codec_srcclk))) {
pr_err("err:try to get codec_srcclk failed!\n");
}
if (clk_prepare_enable(codec_srcclk)) {
pr_err("err:open codec_srcclk failed; \n");
}
/*enable aif2,system clk from aif2*/
codec_wr_control(SUNXI_SYSCLK_CTL, 0x1, AIF2CLK_ENA, 0x1);
/*aif2 clk source select*/
codec_wr_control(SUNXI_SYSCLK_CTL, 0x3, AIF2CLK_SRC, 0x3);
/*sysclk from aif2*/
codec_wr_control(SUNXI_SYSCLK_CTL, 0x1, SYSCLK_SRC, 0x1);
/*enable sysclk*/
codec_wr_control(SUNXI_SYSCLK_CTL, 0x1, SYSCLK_ENA, 0x1);
/*enable aif2/da/da module*/
codec_wr_control(SUNXI_MOD_CLK_ENA, 0x1, AIF2_MOD_CLK_EN, 0x1);
codec_wr_control(SUNXI_MOD_CLK_ENA, 0x1, DAC_DIGITAL_MOD_CLK_EN, 0x1);
codec_wr_control(SUNXI_MOD_CLK_ENA, 0x1, ADC_DIGITAL_MOD_CLK_EN, 0x1);
codec_wr_control(SUNXI_MOD_CLK_ENA, 0x1, SRC1_MOD_CLK_EN, 0x1);/*src1*/
codec_wr_control(SUNXI_MOD_CLK_ENA, 0x1, SRC2_MOD_CLK_EN, 0x1);/*src2*/
/*enable aif3*/
codec_wr_control(SUNXI_MOD_CLK_ENA, 0x1, AIF3_MOD_CLK_EN, 0x1);
/*reset aif2/da/da module*/
codec_wr_control(SUNXI_MOD_RST_CTL, 0x1, AIF2_MOD_RST_CTL, 0x1);
codec_wr_control(SUNXI_MOD_RST_CTL, 0x1, DAC_DIGITAL_MOD_RST_CTL, 0x1);
codec_wr_control(SUNXI_MOD_RST_CTL, 0x1, ADC_DIGITAL_MOD_RST_CTL, 0x1);
// codec_wr_control(SUNXI_MOD_RST_CTL, 0x1, SRC2_MOD_RST_CTL, 0x1);/*src2*/
// codec_wr_control(SUNXI_MOD_RST_CTL, 0x1, SRC1_MOD_RST_CTL, 0x1);/*src1*/
/*reset aif3*/
codec_wr_control(SUNXI_MOD_RST_CTL, 0x1, AIF3_MOD_RST_CTL, 0x1);
/*confige ad/da sr:8k*/
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF1_FS, 0x0);
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF2_FS, 0x0);
// /*select src1 source from aif2*/
// codec_wr_control(SUNXI_SYS_SR_CTRL , 0x1, SRC1_SRC, 0x1);
// /*select src2 source from aif2*/
// codec_wr_control(SUNXI_SYS_SR_CTRL , 0x1, SRC2_SRC, 0x1);
// codec_wr_control(SUNXI_SYS_SR_CTRL , 0x1, SRC1_ENA, 0x1);/*enable src1*/
// codec_wr_control(SUNXI_SYS_SR_CTRL , 0x1, SRC2_ENA, 0x1);/*enable src2*/
/*confige aif2 lrck:8k,pcm,mono,master*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL , 0x1, AIF2_MSTR_MOD, 0x0);/*confige aif2 master*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL , 0x1, AIF2_BCLK_INV, 0x0);/*bclk_inv:0*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL , 0x1, AIF2_LRCK_INV, 0x0);/*lrck_inv:0*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL , 0x3, AIF2_LRCK_DIV, 0x2);/*bclk/lrck:64*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL , 0xf, AIF2_BCLK_DIV, 0x9);/*aif2/bclk:48*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL , 0x3, AIF2_WORD_SIZ, 0x1);/*wss:16*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL , 0x3, AIF2_DATA_FMT, 0x3);/*fmt:pcm*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL , 0x1, AIF2_MONO_PCM, 0x1);/*fmt:pcm*/
/*confige aif3*/
codec_wr_control(SUNXI_AIF3_CLK_CTRL , 0x1, AIF3_BCLK_INV, 0x0);/*bclk_inv:0*/
codec_wr_control(SUNXI_AIF3_CLK_CTRL , 0x1, AIF3_LRCK_INV, 0x0);/*lrck_inv:0*/
codec_wr_control(SUNXI_AIF3_CLK_CTRL , 0x3, AIF3_WORD_SIZ, 0x1);/*wss:16*/
codec_wr_control(SUNXI_AIF3_CLK_CTRL , 0x3, AIF3_CLOC_SRC, 0x1);/*clk from aif2*/
} else {
codec_wr_control(SUNXI_SYSCLK_CTL, 0xfff, SYSCLK_SRC, 0);
/*enable aif2/da/da module*/
codec_wr_control(SUNXI_MOD_CLK_ENA, 0x1, AIF2_MOD_CLK_EN, 0x0);
codec_wr_control(SUNXI_MOD_CLK_ENA, 0x1, DAC_DIGITAL_MOD_CLK_EN, 0x0);
codec_wr_control(SUNXI_MOD_CLK_ENA, 0x1, ADC_DIGITAL_MOD_CLK_EN, 0x0);
/*enable aif3*/
codec_wr_control(SUNXI_MOD_CLK_ENA, 0x1, AIF3_MOD_CLK_EN, 0x0);
/*reset aif2/da/da module*/
codec_wr_control(SUNXI_MOD_RST_CTL, 0x1, AIF2_MOD_RST_CTL, 0x0);
codec_wr_control(SUNXI_MOD_RST_CTL, 0x1, DAC_DIGITAL_MOD_RST_CTL, 0x0);
codec_wr_control(SUNXI_MOD_RST_CTL, 0x1, ADC_DIGITAL_MOD_RST_CTL, 0x0);
/*reset aif3*/
codec_wr_control(SUNXI_MOD_RST_CTL, 0x1, AIF3_MOD_RST_CTL, 0x0);
codec_wr_control(SUNXI_AIF2_CLK_CTRL , 0xffff, 0, 0x0);
codec_wr_control(SUNXI_AIF3_CLK_CTRL , 0xffff, 0, 0x0);
codec_wr_control(SUNXI_MOD_CLK_ENA, 0x1, SRC1_MOD_CLK_EN, 0x0);/*src1*/
codec_wr_control(SUNXI_MOD_CLK_ENA, 0x1, SRC2_MOD_CLK_EN, 0x0);/*src2*/
codec_wr_control(SUNXI_MOD_RST_CTL, 0x1, SRC2_MOD_RST_CTL, 0x0);/*src2*/
codec_wr_control(SUNXI_MOD_RST_CTL, 0x1, SRC1_MOD_RST_CTL, 0x0);/*src1*/
codec_wr_control(SUNXI_SYS_SR_CTRL , 0x1, SRC1_ENA, 0x0);/*disable src1*/
codec_wr_control(SUNXI_SYS_SR_CTRL , 0x1, SRC2_ENA, 0x0);/*disable src2*/
if ((NULL == codec_srcclk)||(IS_ERR(codec_srcclk))) {
pr_err("codec_srcclk handle is invaled, just return\n");
} else {
clk_disable_unprepare(codec_srcclk);
}
}
return 0;
}
static int codec_get_bt_clk_format(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_bt_clk_format;
return 0;
}
static int codec_set_bt_out(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_bt_out_en = ucontrol->value.integer.value[0];
if (codec_bt_out_en) {
/*enable aif2 adcl channel(this bit for make clk)*/
codec_wr_control(SUNXI_AIF2_ADCDAT_CTRL, 0x1, AIF2_ADCL_EN, 1);
/*select aif3 pcm output source*/
codec_wr_control(SUNXI_AIF3_SGP_CTRL, 0x3, AIF3_ADC_SRC, 2);
} else {
/*disable aif2 adcl channel*/
codec_wr_control(SUNXI_AIF2_ADCDAT_CTRL, 0x1, AIF2_ADCR_EN, 0);
/*select aif3 pcm output source*/
codec_wr_control(SUNXI_AIF3_SGP_CTRL, 0x3, AIF3_ADC_SRC, 0);
}
return 0;
}
static int codec_get_bt_out(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_bt_out_en;
return 0;
}
static int codec_set_analog_btmic(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_analog_btmic_en = ucontrol->value.integer.value[0];
if (codec_analog_btmic_en) {
/*select aif2 dac input source*/
codec_wr_control(SUNXI_AIF3_SGP_CTRL, 0x3, AIF2_DAC_SRC, 1);
/*dac left channel mixer source select*/
codec_wr_control(SUNXI_DAC_MXR_SRC, 0x1, DACL_MXR_SRC_AIF2DACL, 1);
/*dac digital enble*/
codec_wr_control(SUNXI_DAC_DIG_CTRL, 0x1, ENDA, 1);
/*dac analog enble*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, DACALEN, 0x1);
/*left output mixer source select*/
codec_wr_prcm_control(LOMIXSC, 0x1, LMIXMUTEDACL, 0x1);
/*left output mixer enble*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LMIXEN, 0x1);
/*select phoneout source*/
codec_wr_prcm_control(PHONEOUT_CTRL, 0x1, PHONEOUTS0, 0x1);
} else {
codec_wr_prcm_control(PHONEOUT_CTRL, 0x1, PHONEOUTS0, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LMIXEN, 0x0);
codec_wr_prcm_control(LOMIXSC, 0x1, LMIXMUTEDACL, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, DACALEN, 0x0);
codec_wr_control(SUNXI_DAC_DIG_CTRL, 0x1, ENDA, 0);
codec_wr_control(SUNXI_DAC_MXR_SRC, 0x1, DACL_MXR_SRC_AIF2DACL, 0);
codec_wr_control(SUNXI_AIF3_SGP_CTRL, 0x3, AIF3_ADC_SRC, 0);
}
return 0;
}
static int codec_get_analog_btmic(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_analog_btmic_en;
return 0;
}
static int codec_set_analog_btphonein(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_analog_btphonein_en = ucontrol->value.integer.value[0];
if (codec_analog_btphonein_en) {
/*right mixer source :phone_pn*/
codec_wr_prcm_control(RADCMIXSC, 0x1, RADCMIXMUTEPHONEPN, 0x1);
/*enable adc analog */
codec_wr_prcm_control(ADC_AP_EN, 0x1, ADCREN, 0x1);
/*enable adc digital*/
codec_wr_control(SUNXI_ADC_DIG_CTRL, 0x1, ENAD, 1);
codec_wr_control(SUNXI_ADC_DIG_CTRL, 0x1, ENDM, 0);
/*select AIF2 dac right channel mixer source */
codec_wr_control(SUNXI_AIF2_MXR_SRC, 0x1, AIF2_ADCR_MXR_SRC_ADCR, 1);
} else {
codec_wr_control(SUNXI_AIF2_MXR_SRC, 0x1, AIF2_ADCR_MXR_SRC_ADCR, 0);
codec_wr_control(SUNXI_ADC_DIG_CTRL, 0x1, ENAD, 0);
codec_wr_prcm_control(ADC_AP_EN, 0x1, ADCREN, 0x0);
codec_wr_prcm_control(RADCMIXSC, 0x1, RADCMIXMUTEPHONEPN, 0);
}
return 0;
}
static int codec_get_analog_btphonein(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_analog_btphonein_en;
return 0;
}
static int codec_set_digital_btmic(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_digital_btmic_en = ucontrol->value.integer.value[0];
if (codec_digital_btmic_en) {
/*select aif2 dac input source*/
codec_wr_control(SUNXI_AIF3_SGP_CTRL, 0x3, AIF2_DAC_SRC, 2);
/*aif2 dac right channel enable*/
//codec_wr_control(SUNXI_AIF2_DACDAT_CTRL, 0x1, AIF2_DACR_ENA,1);
/*aif2 adc left channel micer source select*/
codec_wr_control(SUNXI_AIF2_MXR_SRC, 0x1, AIF2_ADCL_MXR_SRC_AIF2DACR,1);
} else {
codec_wr_control(SUNXI_AIF2_MXR_SRC, 0x1, AIF2_ADCL_MXR_SRC_AIF2DACR,0);
//codec_wr_control(SUNXI_AIF2_DACDAT_CTRL, 0x1, AIF2_DACR_ENA,0);
codec_wr_control(SUNXI_AIF3_SGP_CTRL, 0x3, AIF2_DAC_SRC, 0);
}
return 0;
}
static int codec_get_digital_btmic(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_digital_btmic_en;
return 0;
}
static int codec_set_digital_btphonein(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_digital_btphonein_en = ucontrol->value.integer.value[0];
if (codec_digital_btphonein_en) {
/*aif2 dac left channel enable*/
codec_wr_control(SUNXI_AIF2_DACDAT_CTRL, 0x1, AIF2_DACL_ENA, 1);
codec_wr_control(SUNXI_AIF2_DACDAT_CTRL, 0x3, AIF2_DACL_SRC, 0);
/*select aif2 dac input source*/
codec_wr_control(SUNXI_AIF3_SGP_CTRL, 0x3, AIF2_DAC_SRC,2);
/*select aif2 adc right channle mixer source */
codec_wr_control(SUNXI_AIF2_MXR_SRC, 0x1, AIF2_ADCR_MXR_SRC_AIF2DACL, 1);
} else {
codec_wr_control(SUNXI_AIF2_MXR_SRC, 0x1, AIF2_ADCR_MXR_SRC_AIF2DACL, 0);
codec_wr_control(SUNXI_AIF2_DACDAT_CTRL, 0x1, AIF2_DACL_ENA, 0);
codec_wr_control(SUNXI_AIF3_SGP_CTRL, 0x3, AIF2_DAC_SRC,0);
}
return 0;
}
static int codec_get_digital_btphonein(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_digital_btphonein_en;
return 0;
}
static int codec_set_bt_button_voice(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
bt_bb_button_voice = ucontrol->value.integer.value[0];
return 0;
}
static int codec_get_bt_button_voice(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = bt_bb_button_voice ;
return 0;
}
static int codec_set_digital_bb_bt_clk_format(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_digital_bb_bt_clk_format = ucontrol->value.integer.value[0];
if(codec_digital_bb_bt_clk_format){
codec_wr_control(SUNXI_MOD_CLK_ENA, 0x1, AIF3_MOD_CLK_EN, 0x1);
codec_wr_control(SUNXI_MOD_RST_CTL, 0x1, AIF3_MOD_RST_CTL, 0x1);
codec_wr_control(SUNXI_AIF3_CLK_CTRL , 0x1, AIF3_BCLK_INV, 0x0);/*bclk_inv:0*/
codec_wr_control(SUNXI_AIF3_CLK_CTRL , 0x1, AIF3_LRCK_INV, 0x0);/*lrck_inv:0*/
codec_wr_control(SUNXI_AIF3_CLK_CTRL , 0x3, AIF3_WORD_SIZ, 0x1);/*wss:16*/
codec_wr_control(SUNXI_AIF3_CLK_CTRL , 0x3, AIF3_CLOC_SRC, 0x1);/*clk from aif2*/
}else{
codec_wr_control(SUNXI_MOD_CLK_ENA, 0x1, AIF3_MOD_CLK_EN, 0x0);
codec_wr_control(SUNXI_MOD_RST_CTL, 0x1, AIF3_MOD_RST_CTL, 0x0);
codec_wr_control(SUNXI_AIF3_CLK_CTRL , 0x3, AIF3_WORD_SIZ, 0);
codec_wr_control(SUNXI_AIF3_CLK_CTRL , 0x3, AIF3_CLOC_SRC, 0);
}
return 0;
}
static int codec_get_digital_bb_bt_clk_format(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_digital_bb_bt_clk_format;
return 0;
}
static int codec_set_system_bt_capture_flag(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_system_bt_capture_en = ucontrol->value.integer.value[0];
return 0;
}
static int codec_get_system_bt_capture_flag(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0]=codec_system_bt_capture_en;
return 0;
}
static int codec_set_analog_bb_capture_mic(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_analog_bb_capture_mic = ucontrol->value.integer.value[0];
if (codec_analog_bb_capture_mic) {
codec_analog_voice_capture_open();
} else {
/*enable aif1 adc left channel */
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL , 0x1, AIF1_AD0L_ENA,0);
/*select mic source*/
codec_wr_prcm_control(LADCMIXSC, 0x1, LADCMIXMUTEMIC1BOOST, 0x0);
/*select mic2 source*/
codec_wr_prcm_control(LADCMIXSC, 0x1, LADCMIXMUTEMIC2BOOST, 0x0);
/*select phonein source*/
codec_wr_prcm_control(LADCMIXSC, 0x1, LADCMIXMUTEPHONEPN, 0x0);
/*enable adc analog*/
codec_wr_prcm_control(ADC_AP_EN, 0x1, ADCLEN, 0x0);
/*enable dac digital*/
codec_wr_control(SUNXI_ADC_DIG_CTRL , 0x1, ENAD, 0);
codec_wr_control(SUNXI_ADC_DIG_CTRL , 0x1, ENDM, 0);
/*select aif1 adc left channel mixer source */
codec_wr_control(SUNXI_AIF1_MXR_SRC , 0x1, AIF1_AD0L_MXL_SRC_ADCL,0);
/*select aif1 adc left channel source */
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL , 0x3, AIF1_AD0L_SRC,0);
}
return 0;
}
static int codec_get_analog_bb_capture_mic(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0]=codec_analog_bb_capture_mic;
return 0;
}
static const struct snd_kcontrol_new sunxi_codec_controls[] = {
/*volume ctl: headset ,speaker,earpiece*/
//CODEC_SINGLE("Master Playback Volume", HP_VOLC, 0, 0x3f, 0),
CODEC_SINGLE("headphone volume control", HP_VOLC, 0, 0x3f, 0),
CODEC_SINGLE("earpiece volume control", HP_VOLC, 0, 0x3f, 0),
CODEC_SINGLE("speaker volume control", HP_VOLC, 0, 0x3f, 0),
CODEC_SINGLE("MIC1_G boost stage output mixer control", MICIN_GCTRL, MIC1G, 0x7, 0),
CODEC_SINGLE("MIC2_G boost stage output mixer control", MICIN_GCTRL, MIC2G, 0x7, 0),
CODEC_SINGLE("LINEIN_G boost stage output mixer control", LINEIN_GCTRL, LINEING, 0x7, 0),
CODEC_SINGLE("PHONE_G boost stage output mixer control", LINEIN_GCTRL, PHONEG, 0x7, 0),
CODEC_SINGLE("PHONE_PG boost stage output mixer control", PHONEIN_GCTRL, PHONEPG, 0x7, 0),
CODEC_SINGLE("PHONE_NG boost stage output mixer control", PHONEIN_GCTRL, PHONENG, 0x7, 0),
CODEC_SINGLE("MIC1 boost AMP gain control", MIC1G_MICBIAS_CTRL, MIC1BOOST, 0x7, 0),
CODEC_SINGLE("MIC2 boost AMP gain control", MIC2G_LINEEN_CTRL, MIC2BOOST, 0x7, 0),
CODEC_SINGLE("Lineout volume control", HP_VOLC, 0, 0x3f, 0),
CODEC_SINGLE("PHONEP-PHONEN pre-amp gain control", LINEOUT_VOLC, PHONEPREG, 0x7, 0),
CODEC_SINGLE("Phoneout gain control", PHONEOUT_CTRL, PHONEOUTG, 0x7, 0),
CODEC_SINGLE("ADC input gain ctrl", ADC_AP_EN, ADCG, 0x7, 0),
SOC_SINGLE_BOOL_EXT("Audio phone out", 0, codec_analog_get_phoneout, codec_analog_set_phoneout), /*enable phoneout*/
SOC_SINGLE_BOOL_EXT("Audio phone in", 0, codec_analog_get_phonein, codec_analog_set_phonein), /*open the phone in call*/
SOC_SINGLE_BOOL_EXT("Audio earpiece out", 0, codec_get_earpieceout, codec_set_earpieceout), /*set the phone in call voice through earpiece out*/
SOC_SINGLE_BOOL_EXT("Audio headphone out", 0, codec_get_headphoneout, codec_set_headphoneout), /*set the phone in call voice through headphone out*/
SOC_SINGLE_BOOL_EXT("Audio speaker out", 0, codec_get_speakerout, codec_set_speakerout), /*set the phone in call voice through speaker out*/
SOC_SINGLE_BOOL_EXT("Audio analog main mic", 0, codec_analog_get_mainmic, codec_analog_set_mainmic), /*set main mic(mic1)*/
SOC_SINGLE_BOOL_EXT("Audio analog headsetmic", 0, codec_analog_get_headsetmic, codec_analog_set_headsetmic), /*set headset mic(mic2)*/
SOC_SINGLE_BOOL_EXT("Audio phone voicerecord", 0, codec_get_voicerecord, codec_set_voicerecord), /*set voicerecord status*/
SOC_SINGLE_BOOL_EXT("Audio phone endcall", 0, codec_get_endcall, codec_set_endcall), /*set endcall*/
SOC_SINGLE_BOOL_EXT("Audio linein record", 0, codec_get_lineincap, codec_set_lineincap),
SOC_SINGLE_BOOL_EXT("Audio linein in", 0, codec_get_lineinin, codec_set_lineinin),
SOC_ENUM_EXT("Speaker Function", spk_headset_earpiece_enum[0], codec_get_spk_headset_earpiece, codec_set_spk_headset_earpiece),
/*audio digital interface for phone case*/
SOC_SINGLE_BOOL_EXT("Audio digital main mic", 0, codec_digital_get_mainmic, codec_digital_set_mainmic), /*set mic1 for digital bb*/
SOC_SINGLE_BOOL_EXT("Audio digital headset mic", 0, codec_digital_get_headsetmic, codec_digital_set_headsetmic),/*set mic2 for digital bb*/
SOC_SINGLE_BOOL_EXT("Audio digital phone out", 0, codec_digital_get_phoneout, codec_digital_set_phoneout),/*set phoneout for digital bb*/
SOC_SINGLE_BOOL_EXT("Audio digital phonein", 0, codec_get_digital_phonein, codec_set_digital_phonein),/*set phonein for digtal bb*/
SOC_SINGLE_BOOL_EXT("Audio digital clk format status", 0, codec_get_digital_bb_clk_format, codec_set_digital_bb_clk_format),/*set clk,format for digtal bb*/
/*bluetooth*/
SOC_SINGLE_BOOL_EXT("Audio bt clk format status", 0, codec_get_bt_clk_format, codec_set_bt_clk_format),/*set clk,format for bt*/
SOC_SINGLE_BOOL_EXT("Audio bt out", 0, codec_get_bt_out, codec_set_bt_out),/*set bt out*/
SOC_SINGLE_BOOL_EXT("Audio analog bt mic", 0, codec_get_analog_btmic, codec_set_analog_btmic),/*set analog bt mic*/
SOC_SINGLE_BOOL_EXT("Audio analog bt phonein", 0, codec_get_analog_btphonein, codec_set_analog_btphonein),/*set analog bt phonein*/
SOC_SINGLE_BOOL_EXT("Audio digital bt mic", 0, codec_get_digital_btmic, codec_set_digital_btmic),/* set bt mic for dbb*/
SOC_SINGLE_BOOL_EXT("Audio digital bt phonein", 0, codec_get_digital_btphonein, codec_set_digital_btphonein),/*set bt phonein for dbb*/
SOC_SINGLE_BOOL_EXT("Audio bt button voice", 0, codec_get_bt_button_voice, codec_set_bt_button_voice),/*bt_bb_out*/
SOC_SINGLE_BOOL_EXT("Audio digital bb bt clk format", 0, codec_get_digital_bb_bt_clk_format, codec_set_digital_bb_bt_clk_format),/*set bt phonein for dbb*/
SOC_SINGLE_BOOL_EXT("Audio system bt capture flag", 0, codec_get_system_bt_capture_flag, codec_set_system_bt_capture_flag),/*set bt phonein for dbb*/
SOC_SINGLE_BOOL_EXT("Audio analog bb capture mic", 0, codec_get_analog_bb_capture_mic, codec_set_analog_bb_capture_mic),/*set bt phonein for dbb*/
#if 1
CODEC_SINGLE_DIGITAL("aif3 loopback", SUNXI_AIF3_DACDAT_CTRL, AIF3_LOOP_ENA, 0x1, 0),/*test :1,default:0*/
CODEC_SINGLE_DIGITAL("aif2 loopback", SUNXI_AIF2_DACDAT_CTRL, AIF2_LOOP_EN, 0x1, 0),/*test:1,default:0*/
CODEC_SINGLE_DIGITAL("digital_bb_bt", SUNXI_AIF2_MXR_SRC, AIF2_ADCR_MXR_SRC_AIF2DACL, 0x1, 0),/*test:1,default:0*/
CODEC_SINGLE_DIGITAL("system play_capture set 1", SUNXI_AIF1_MXR_SRC, AIF1_AD0R_MXR_SRC, 0xf, 0),/*teset:0x8*/
CODEC_SINGLE_DIGITAL("system play_capture set 2", SUNXI_AIF1_MXR_SRC, AIF1_AD0L_MXL_SRC_AIF2DACR, 0xf, 0),/*teset:0x8*/
/*0x24c*/
CODEC_SINGLE_DIGITAL("AIF1_AD0L_MXR_SRC AIF1DA0Ldata", SUNXI_AIF1_MXR_SRC, AIF1_AD0L_MXL_SRC_AIF1DA0L, 0x1, 0),
CODEC_SINGLE_DIGITAL("AIF1_AD0L_MXR_SRC AIF2DACLdata", SUNXI_AIF1_MXR_SRC, AIF1_AD0L_MXL_SRC_AIF2DACL, 0x1, 0),
CODEC_SINGLE_DIGITAL("AIF1_AD0L_MXR_SRC ADCLdata", SUNXI_AIF1_MXR_SRC, AIF1_AD0L_MXL_SRC_ADCL, 0x1, 0),
CODEC_SINGLE_DIGITAL("AIF1_AD0L_MXR_SRC AIF2DACRdata", SUNXI_AIF1_MXR_SRC, AIF1_AD0L_MXL_SRC_AIF2DACR, 0x1, 0),
CODEC_SINGLE_DIGITAL("AIF1_AD0R_MXR_SRC AIF1DA0Rdata", SUNXI_AIF1_MXR_SRC, AIF1_AD0R_MXR_SRC_AIF1DA0R, 0x1, 0),
CODEC_SINGLE_DIGITAL("AIF1_AD0R_MXR_SRC AIF2DACRdata", SUNXI_AIF1_MXR_SRC, AIF1_AD0R_MXR_SRC_AIF2DACR, 0x1, 0),
CODEC_SINGLE_DIGITAL("AIF1_AD0R_MXR_SRC ADCRdata", SUNXI_AIF1_MXR_SRC, AIF1_AD0R_MXR_SRC_ADCR, 0x1, 0),
CODEC_SINGLE_DIGITAL("AIF1_AD0R_MXR_SRC AIF2DACLdata", SUNXI_AIF1_MXR_SRC, AIF1_AD0R_MXR_SRC_AIF2DACL, 0x1, 0),
/*ADC source*/
CODEC_SINGLE("Analog cap test disable phonein", LADCMIXSC, LADCMIXMUTEPHONEPN, 0x1, 0),
CODEC_SINGLE("Analog cap test disable mic1", LADCMIXSC, LADCMIXMUTEMIC1BOOST, 0x1, 0),
CODEC_SINGLE("Analog cap test disable mic2", LADCMIXSC, LADCMIXMUTEMIC2BOOST, 0x1, 0),
#endif
};
static struct snd_soc_dai_ops sndpcm_dai_ops = {
.startup = sndpcm_startup,
.shutdown = sndpcm_shutdown,
.prepare = sndpcm_perpare,
//.trigger = sndpcm_trigger,
.hw_params = sndpcm_hw_params,
.digital_mute = sndpcm_unmute,
//.set_sysclk = sndpcm_set_dai_sysclk,
//.set_clkdiv = sndpcm_set_dai_clkdiv,
//.set_fmt = sndpcm_set_dai_fmt,
};
static struct snd_soc_dai_driver sndpcm_dai = {
.name = "sndcodec",
/* playback capabilities */
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = sndpcm_RATES,
.formats = sndpcm_FORMATS,
},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = sndpcm_RATES,
.formats = sndpcm_FORMATS,
},
/* pcm operations */
.ops = &sndpcm_dai_ops,
};
EXPORT_SYMBOL(sndpcm_dai);
static int sndpcm_soc_probe(struct snd_soc_codec *codec)
{
/* Add virtual switch */
snd_soc_add_codec_controls(codec, sunxi_codec_controls,
ARRAY_SIZE(sunxi_codec_controls));
return 0;
}
static int sndpcm_suspend(struct snd_soc_codec *codec)
{
pr_debug("[audio codec]:suspend start\n");
/* check if called in talking standby */
if (check_scene_locked(SCENE_TALKING_STANDBY) == 0) {
pr_err("In talking standby, audio codec do not suspend!!\n");
return 0;
}
gpio_set_value(item.gpio.gpio, 0);
/*mute l_pa and r_pa*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPPAMUTE, 0x0);
/*digital part*/
codec_wr_control(SUNXI_DA_CTL , 0xffffffff, GEN, 0x0);
/*SUNXI_AIF1_ADCDAT_CTRL:disable AIF1_AD0L_ENA, disable AIF1_AD0R_ENA*/
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL , 0x3, AIF1_AD0R_ENA, 0x0);
/*SUNXI_AIF1_DACDAT_CTRL:disable AIF1_DA0L_ENA, disable AIF1_DA0R_ENA*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL , 0x3, AIF1_DA0R_ENA, 0x0);
/*SUNXI_AIF1_MXR_SRC:mute*/
codec_wr_control(SUNXI_AIF1_MXR_SRC , 0xffff, AIF1_AD1R_MXR_SRC_C, 0x0);
/*dsable adc digital*/
codec_wr_control(SUNXI_ADC_DIG_CTRL , 0x1, ENAD, 0x0);
/*disable dac digital*/
codec_wr_control(SUNXI_DAC_DIG_CTRL , 0x1, ENDA, 0x0);
/*analog parts*/
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL, 0x1, MMICBIASEN, 0x0);
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL, 0x1, HMICBIAS_MODE, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LMIXEN, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RMIXEN, 0x0);
/*disable dac analog*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, DACALEN, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, DACAREN, 0x0);
/*disable adc analog*/
codec_wr_prcm_control(ADC_AP_EN, 0x1, ADCREN, 0x0);
codec_wr_prcm_control(ADC_AP_EN, 0x1, ADCLEN, 0x0);
codec_wr_prcm_control(ADDA_APT2, 0x1, ZERO_CROSS_EN, 0x0);
//codec_wr_prcm_control(LINEOUT_VOLC, 0x1f, LINEOUTVOL, 0x0);
if ((NULL == codec_moduleclk)||(IS_ERR(codec_moduleclk))) {
pr_err("codec_moduleclk handle is invaled, just return\n");
} else {
clk_disable_unprepare(codec_moduleclk);
}
pr_debug("[audio codec]:suspend end\n");
return 0;
}
static int sndpcm_resume(struct snd_soc_codec *codec)
{
pr_debug("[audio codec]:resume start\n");
if ((!codec_moduleclk)||(IS_ERR(codec_moduleclk))) {
pr_err("try to get codec_moduleclk failed!\n");
}
if (clk_prepare_enable(codec_moduleclk)) {
pr_err("open codec_moduleclk failed; \n");
}
if (headphone_direct_used) {
codec_wr_prcm_control(PAEN_HP_CTRL, 0x3, HPCOM_FC, 0x3);
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, COMPTEN, 0x1);
} else {
codec_wr_prcm_control(PAEN_HP_CTRL, 0x3, HPCOM_FC, 0x0);
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, COMPTEN, 0x0);
}
if (drc_used){
codec_wr_control(0x48c, 0x7ff, 0, 0x1);
// codec_wr_control(0x48c, 0x7ff, 0, 0x0);
codec_wr_control(0x490, 0xffff, 0, 0x2baf);
//codec_wr_control(0x490, 0xffff, 0, 0x1fb6);
codec_wr_control(0x494, 0x7ff, 0, 0x1);
codec_wr_control(0x498, 0xffff, 0, 0x2baf);
codec_wr_control(0x49c, 0x7ff, 0, 0x0);
codec_wr_control(0x4a0, 0xffff, 0, 0x44a);
codec_wr_control(0x4a4, 0x7ff, 0, 0x0);
codec_wr_control(0x4a8, 0xffff, 0, 0x1e06);
//codec_wr_control(0x4ac, 0x7ff, 0, 0x27d);
codec_wr_control(0x4ac, 0x7ff, 0, 0x352);
//codec_wr_control(0x4b0, 0xffff, 0, 0xcf68);
codec_wr_control(0x4b0, 0xffff, 0, 0x6910);
codec_wr_control(0x4b4, 0x7ff, 0, 0x77a);
codec_wr_control(0x4b8, 0xffff, 0, 0xaaaa);
//codec_wr_control(0x4bc, 0x7ff, 0, 0x1fe);
codec_wr_control(0x4bc, 0x7ff, 0, 0x2de);
codec_wr_control(0x4c0, 0xffff, 0, 0xc982);
codec_wr_control(0x258, 0xffff, 0, 0x9f9f);
}
if (agc_used){
codec_wr_control(0x4d0, 0x3, 6, 0x3);
codec_wr_control(0x410, 0x3f, 8, 0x31);
codec_wr_control(0x410, 0xff, 0, 0x28);
codec_wr_control(0x414, 0x3f, 8, 0x31);
codec_wr_control(0x414, 0xff, 0, 0x28);
codec_wr_control(0x428, 0x7fff, 0, 0x24);
codec_wr_control(0x42c, 0x7fff, 0, 0x2);
codec_wr_control(0x430, 0x7fff, 0, 0x24);
codec_wr_control(0x434, 0x7fff, 0, 0x2);
codec_wr_control(0x438, 0x1f, 8, 0xf);
codec_wr_control(0x438, 0x1f, 0, 0xf);
codec_wr_control(0x44c, 0x7ff, 0, 0xfc);
codec_wr_control(0x450, 0xffff, 0, 0xabb3);
}
/*process for normal standby*/
if (NORMAL_STANDBY == standby_type) {
/*process for super standby*/
} else if(SUPER_STANDBY == standby_type) {
/*when TX FIFO available room less than or equal N,
* DRQ Requeest will be de-asserted.
*/
}
pr_debug("[audio codec]:resume end\n");
return 0;
}
/* power down chip */
static int sndpcm_soc_remove(struct snd_soc_codec *codec)
{
return 0;
}
static struct snd_soc_codec_driver soc_codec_dev_sndpcm = {
.probe = sndpcm_soc_probe,
.remove = sndpcm_soc_remove,
.suspend = sndpcm_suspend,
.resume = sndpcm_resume,
};
static ssize_t show_audio_reg(struct device *dev, struct device_attribute *attr,
char *buf)
{
int count = 0;
int i = 0;
int reg_group =0;
printk("%s,line:%d\n",__func__,__LINE__);
count += sprintf(buf, "dump audio reg:\n");
while (reg_labels[i].name != NULL){
if (reg_labels[i].value == 0){
reg_group++;
}
if (reg_group == 1){
count +=sprintf(buf + count, "%s 0x%p: 0x%x\n", reg_labels[i].name,
(baseaddr + reg_labels[i].value),
readl(baseaddr + reg_labels[i].value) );
} else if (reg_group == 2){
count +=sprintf(buf + count, "%s 0x%x: 0x%x\n", reg_labels[i].name,
(reg_labels[i].value),
read_prcm_wvalue(reg_labels[i].value) );
}
i++;
}
return count;
}
/* ex:
read:
echo 0,1,0x00> audio_reg
echo 0,2,0x00> audio_reg
write:
echo 1,1,0x00,0xa > audio_reg
echo 1,2,0x00,0xff > audio_reg
*/
static ssize_t store_audio_reg(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
int ret;
//int reg_group =0;
//int find_labels_reg_offset =-1;
int input_reg_group =0;
int input_reg_offset =0;
int input_reg_val =0;
int reg_val_read;
//int i = 0;
int rw_flag;
printk("%s,line:%d\n",__func__,__LINE__);
ret = sscanf(buf, "%d,%d,0x%x,0x%x", &rw_flag,&input_reg_group, &input_reg_offset, &input_reg_val);
printk("ret:%d, reg_group:%d, reg_offset:%d, reg_val:0x%x\n", ret, input_reg_group, input_reg_offset, input_reg_val);
if (!(input_reg_group ==1 || input_reg_group ==2)){
printk("not exist reg group\n");
ret = count;
goto out;
}
if (!(rw_flag ==1 || rw_flag ==0)){
printk("not rw_flag\n");
ret = count;
goto out;
}
if (input_reg_group == 1){
if (rw_flag){
writel(input_reg_val, baseaddr + input_reg_offset);
}else{
reg_val_read = readl(baseaddr + input_reg_offset);
printk("\n\n Reg[0x%x] : 0x%x\n\n",input_reg_offset,reg_val_read);
}
} else if (input_reg_group == 2){
if(rw_flag) {
write_prcm_wvalue(input_reg_offset, input_reg_val & 0xff);
}else{
reg_val_read = read_prcm_wvalue(input_reg_offset);
printk("\n\n Reg[0x%x] : 0x%x\n\n",input_reg_offset,reg_val_read);
}
}
ret = count;
out:
return ret;
}
static DEVICE_ATTR(audio_reg, 0644, show_audio_reg, store_audio_reg);
static struct attribute *audio_debug_attrs[] = {
&dev_attr_audio_reg.attr,
NULL,
};
static struct attribute_group audio_debug_attr_group = {
.name = "audio_reg_debug",
.attrs = audio_debug_attrs,
};
static int __init sndpcm_codec_probe(struct platform_device *pdev)
{
int err = -1;
int reg_val = 0;
int req_status;
//long unsigned int config_set = 0;
script_item_u val;
script_item_value_type_e type;
/* codec_pll2clk */
codec_pll2clk = clk_get(NULL, "pll2");
if ((!codec_pll2clk)||(IS_ERR(codec_pll2clk))) {
pr_err("[ audio ] err:try to get codec_pll2clk failed!\n");
}
if (clk_prepare_enable(codec_pll2clk)) {
pr_err("[ audio ] err:enable codec_pll2clk failed; \n");
}
/* codec_moduleclk */
codec_moduleclk = clk_get(NULL, "adda");
if ((!codec_moduleclk)||(IS_ERR(codec_moduleclk))) {
pr_err("[ audio ] err:try to get codec_moduleclk failed!\n");
}
if (clk_set_parent(codec_moduleclk, codec_pll2clk)) {
pr_err("[ audio ] err:try to set parent of codec_moduleclk to codec_pll2clk failed!\n");
}
if (clk_set_rate(codec_moduleclk, 24576000)) {
pr_err("[ audio ] err:set codec_moduleclk clock freq 24576000 failed!\n");
}
if (clk_prepare_enable(codec_moduleclk)) {
pr_err("[ audio ] err:open codec_moduleclk failed; \n");
}
/*clk pll_audiox4 for audiocodec src*/
codec_srcclk = clk_get(NULL, "pll_audiox4");
if ((!codec_srcclk)||(IS_ERR(codec_srcclk))) {
pr_err("[ audio ] err:try to get codec_srcclk failed!\n");
}
codec_init();
/* check if hp_vcc_ldo exist, if exist enable it */
type = script_get_item("audio0", "audio_hp_ldo", &item);
if (SCIRPT_ITEM_VALUE_TYPE_STR != type) {
pr_err("script_get_item return type err, consider it no ldo\n");
} else {
if (!strcmp(item.str, "none"))
hp_ldo = NULL;
else {
hp_ldo_str = item.str;
hp_ldo = regulator_get(NULL, hp_ldo_str);
if (!hp_ldo) {
pr_err("get audio hp-vcc(%s) failed\n", hp_ldo_str);
return -EFAULT;
}
regulator_set_voltage(hp_ldo, 3000000, 3000000);
regulator_enable(hp_ldo);
}
}
/*get the default pa ctl(close)*/
type = script_get_item("audio0", "audio_pa_ctrl", &item);
if (SCIRPT_ITEM_VALUE_TYPE_PIO != type) {
pr_err("[ audio ] err:try to get audio_pa_ctrl failed!\n");
return -EFAULT;
}
/**
*If use the aif2,aif3 interface in the audiocodec,
* you need config the related interfaces about aif2 and aif3.
*And can not use daudio0 and daudio1
*/
type = script_get_item("audio0", "aif2_used", &val);
if (SCIRPT_ITEM_VALUE_TYPE_INT != type) {
pr_err("[audiocodec] aif2_used type err!\n");
} else {
aif2_used = val.val;
}
if(aif2_used){
pr_debug("[audiocodec]: aif2 initialize PB04 PB05 PB06 PB07!!\n");
#if 0
config_set = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC,3);
pin_config_set(SUNXI_PINCTRL,"PB07",config_set);
config_set = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC,3);
pin_config_set(SUNXI_PINCTRL,"PB06",config_set);
config_set = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC,3);
pin_config_set(SUNXI_PINCTRL,"PB05",config_set);
config_set = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC,3);
pin_config_set(SUNXI_PINCTRL,"PB04",config_set);
#endif
reg_val = readl((void __iomem *)0xf1c20824);
reg_val &= 0xffff;
reg_val |= (0x3333<<16);
writel(reg_val, (void __iomem *)0xf1c20824);
//pr_debug("%s,line:%d,reg_val:%x\n",__func__,__LINE__,readl((void __iomem *)0xf1c20824));
} else {
pr_err("[audiocodec] : aif2 not used!\n");
}
type = script_get_item("audio0", "aif3_used", &val);
if (SCIRPT_ITEM_VALUE_TYPE_INT != type) {
pr_err("[audiocodec] aif3_used type err!\n");
} else {
aif3_used = val.val;
}
if(aif3_used){
pr_err("[audiocodec}: aif3 initialize PG10 PG11 PG12 PG13!!\n");
#if 0
config_set = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC,3);
pin_config_set(SUNXI_PINCTRL,"PG13",config_set);
config_set = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC,3);
pin_config_set(SUNXI_PINCTRL,"PG12",config_set);
config_set = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC,3);
pin_config_set(SUNXI_PINCTRL,"PG11",config_set);
config_set = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC,3);
pin_config_set(SUNXI_PINCTRL,"PG10",config_set);
#endif
reg_val = readl((void __iomem *)0xf1c208dc);
reg_val &= 0xff;
reg_val |= (0x3333<<8);
writel(reg_val, (void __iomem *)0xf1c208dc);
//pr_err("%s,line:%d,reg_val:%x\n",__func__,__LINE__,readl((void __iomem *)0xf1c208dc));
} else {
pr_err("[audiocodec] : aif3 not used!\n");
}
/*request gpio*/
req_status = gpio_request(item.gpio.gpio, NULL);
if (0 != req_status) {
pr_err("request gpio failed!\n");
}
gpio_direction_output(item.gpio.gpio, 1);
gpio_set_value(item.gpio.gpio, 0);
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, HPPAEN, 0x1);
snd_soc_register_codec(&pdev->dev, &soc_codec_dev_sndpcm, &sndpcm_dai, 1);
err=sysfs_create_group(&pdev->dev.kobj, &audio_debug_attr_group);
if (err){
pr_err("failed to create attr group\n");
}
return 0;
}
static int __exit sndpcm_codec_remove(struct platform_device *pdev)
{
if ((NULL == codec_moduleclk)||(IS_ERR(codec_moduleclk))) {
pr_err("codec_moduleclk handle is invaled, just return\n");
return -EINVAL;
} else {
clk_disable_unprepare(codec_moduleclk);
}
if ((NULL == codec_pll2clk)||(IS_ERR(codec_pll2clk))) {
pr_err("codec_pll2clk handle is invaled, just return\n");
return -EINVAL;
} else {
clk_put(codec_pll2clk);
}
/* disable audio hp-vcc ldo if it exist */
if (hp_ldo) {
regulator_disable(hp_ldo);
hp_ldo = NULL;
}
sysfs_remove_group(&pdev->dev.kobj, &audio_debug_attr_group);
snd_soc_unregister_codec(&pdev->dev);
return 0;
}
static void sunxi_codec_shutdown(struct platform_device *devptr)
{
item.gpio.data = 0;
pr_debug("%s,line:%d\n",__func__,__LINE__);
codec_wr_prcm_control(ADDA_APT2, 0x1, ZERO_CROSS_EN, 0x0);
/*mute l_pa and r_pa*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPPAMUTE, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, DACALEN, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, DACAREN, 0x0);
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL, 0x1, MMICBIASEN, 0x0);
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL, 0x1, HMICBIAS_MODE, 0x0);
gpio_set_value(item.gpio.gpio, 0);
if ((NULL == codec_moduleclk)||(IS_ERR(codec_moduleclk))) {
pr_err("codec_moduleclk handle is invaled, just return\n");
} else {
clk_disable_unprepare(codec_moduleclk);
}
}
/*data relating*/
static struct platform_device sndpcm_codec_device = {
.name = "sunxi-pcm-codec",
.id = -1,
};
/*method relating*/
static struct platform_driver sndpcm_codec_driver = {
.driver = {
.name = "sunxi-pcm-codec",
.owner = THIS_MODULE,
},
.probe = sndpcm_codec_probe,
.remove = __exit_p(sndpcm_codec_remove),
.shutdown = sunxi_codec_shutdown,
};
static int __init sndpcm_codec_init(void)
{
int err = 0;
if((err = platform_device_register(&sndpcm_codec_device)) < 0)
return err;
if ((err = platform_driver_register(&sndpcm_codec_driver)) < 0)
return err;
return 0;
}
module_init(sndpcm_codec_init);
static void __exit sndpcm_codec_exit(void)
{
platform_driver_unregister(&sndpcm_codec_driver);
}
module_exit(sndpcm_codec_exit);
MODULE_DESCRIPTION("SNDPCM ALSA soc codec driver");
MODULE_AUTHOR("huanxin<[email protected]>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:sunxi-pcm-codec");
就先说这么多吧.