blob: 2ab0349a7f5ba2aa9a5f5609dbcfe88901f926f4 [file] [log] [blame]
/*
* Base driver for ASR NAU8810
*
* Copyright (C) 2019 ASR.
*
* This file is subject to the terms and conditions of the GNU General
* Public License. See the file "COPYING" in the main directory of this
* archive for more details.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/irq.h>
#include <linux/mfd/core.h>
#include <linux/mfd/88pm80x.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/of_device.h>
#include <linux/uaccess.h>
#include <linux/proc_fs.h>
#include <linux/fs.h>
#include <linux/debugfs.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <linux/regulator/consumer.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <linux/cputype.h>
#include <soc/asr/addr-map.h>
#include "nau8810.h"
#include <linux/clk.h>
#ifndef APB_VIRT_BASE
#define APB_VIRT_BASE IOMEM(0xfe000000)
#endif
#define SSP1_I2S_CLOCK_REG_ADDR (0xD4050044)
#ifdef Kestrel_Z2
//Kestrel Z2
static void __iomem *VCXO_OUT_MFPR_reg = APB_VIRT_BASE + 0x1E000 + 0x208;/* 0xD401E000+0x208 */
static void __iomem *PM_MN_CLK_reg = APB_VIRT_BASE + 0x51000 + 0xA4;/* 0xD4051000+0xA4 */
static void __iomem *GPCR_reg = APB_VIRT_BASE + 0x50000 + 0x30;/* 0xD4050030 */
#else
//Kestrel A0
static void __iomem *MCLK_CLK_CTRL_reg = NULL;/* 0xD6800000+0x4C */
static void __iomem *MCLK_MN_DIV_reg = NULL;/* 0xD6800000+0x50 */
#endif
//NezhaC/Falcon
static void __iomem *SSP1_I2S_CLOCK_reg = APB_VIRT_BASE + 0x50000 + 0x44;/* 0xD4050000+0x44 */
#if defined(CONFIG_CODEC_PCM_NB)
/* Narrowband */
char Audio_Codec_Fsync_Rate = 0;
#elif defined(CONFIG_CODEC_PCM_WB)
/* Wideband */
char Audio_Codec_Fsync_Rate = 1;
#elif defined(CONFIG_CODEC_PCM_32KHz)
/* 32KHz */
char Audio_Codec_Fsync_Rate = 2;
#elif defined(CONFIG_CODEC_PCM_48KHz)
/* 48KHz */
char Audio_Codec_Fsync_Rate = 3;
#else
/* Narrowband */
char Audio_Codec_Fsync_Rate = 0;
#endif
#ifdef CONFIG_CODEC_PCM_MASTER
/* CODEC is master, GSSP is slave. */
char G_AudioModemMaster = 0;
#else
/* CODEC PCM config is slave in config/defconfig_asr1802sp201
CODEC is slave, GSSP is master. */
char G_AudioModemMaster = 1;
#endif
static const struct i2c_device_id nau8810_id_table[] = {
{"nau8810", 0},
{} /* NULL terminated */
};
MODULE_DEVICE_TABLE(i2c, nau8810_id_table);
static const struct of_device_id nau8810_dt_ids[] = {
{ .compatible = "marvell,nau8810", },
{},
};
MODULE_DEVICE_TABLE(of, nau8810_dt_ids);
struct i2c_client *g_client = NULL;
#define PM80X_AUDIO_REG_NUM 0x50
//#define NAU8810_DEBUG
//#define NAU8810_DEBUG_CLOSE
static int g_8810_mclk_type = 1; /* 0: use bitclk, 1: use i2s_mclk, 2, use bitclk and turn on i2s_mclk */
static int g_8810_mainmic_type = 0; /* 0: differential mode, 1: single mode */
//////////////////////////////////////////////////////////////
//base functions
//read function
////////////////////////////////////////////////////////////////////////////////////
/******************************************************************************
* Function : nau8810_reg_read
*******************************************************************************
*
* Description :
*
* Parameters : UINT8 RegAddr
* Parameters : UINT16 *value
*
* Output Param : None.
*
* Return value :
*
* Notes :
|-----|----------------------|-----|-----------------|---|-----|-----|---------------------|...
| |<- slave address ->| |<- reg address ->|NA | | | <-slave address-> |...
|-----|----------------------|-----|-----------------|---|-----|-----|---------------------|...
|start|0|0|1|1|0|1|0|WRITE(0)| ACK |A6|A5|A4|A3|A2|A1| 0 | ACK |start|0|0|1|1|0|1|0|READ(1)|...
|----------------------------|-----|---------------------|-----|---------------------------|...
| master send |slave| master send |slave| master send |...
|----------------------------|-----|---------------------|-----|---------------------------|...
... |----------------|------|-----------------------|-----------|
... |<- high data ->| |<- low data ->| |
... |----------------|------|-----------------------|-----------|
... |0|0|0|0|0|0|0|D8| ACK |D7|D6|D5|D4|D3|D2|D1|D0| NACK |stop|
... |----------------|------|-----------------------|-----------|
... | slave send |master| slave send | master |
... |----------------|------|-----------------------|-----------|
*******************************************************************************/
static int nau8810_reg_read(struct i2c_client *client, char reg)
{
char data[2] = {0};
int value = 0x0;
data[0] = (reg << 1) & 0xFE;
if(i2c_master_send(client, data, 1) == 1) {
i2c_master_recv(client, data, 2);
value = (((unsigned short)data[0] & 0x01) << 8) | data[1];
#ifdef NAU8810_DEBUG
printk(KERN_INFO"%s: nau8810 read reg:[0x%02x]=0x%03x\n", __FUNCTION__, reg, value);
#endif
return value;
} else {
printk(KERN_INFO"%s: nau8810 read faile\n", __FUNCTION__);
return -EIO;
}
}
static int nau8810_read(char reg, unsigned short *oValue)
{
int value;
value = nau8810_reg_read(g_client, reg);
if (value < 0)
{
printk(KERN_INFO"%s: nau8810_reg read fail\n", __FUNCTION__);
*oValue = 0x00;
return -EIO;
}
*oValue = value & 0x1FF;
return value;
}
//////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
/******************************************************************************
* Function : nau8810_reg_write
*******************************************************************************
*
* Description :
*
* Parameters : UINT8 RegAddr
* Parameters : UINT16 RegData
*
* Output Param : None.
*
* Return value :
*
* Notes :
|-----|----------------------|-----|---------------------|-----|-----------------------|-----|------|
| |<- slave address ->| |<- reg address ->| D8| |<- low data ->| | |
|-----|----------------------|-----|---------------------|-----|-----------------------|-----|------|
|start|0|0|1|1|0|1|0|WRITE(0)| ACK |A6|A5|A4|A3|A2|A1| D8| ACK |D7|D6|D5|D4|D3|D2|D1|D0| ACK | stop |
|----------------------------|-----|---------------------|-----|-----------------------|-----|------|
| master send |slave| master send |slave| master send |slave|master|
|----------------------------|-----|---------------------|-----|-----------------------|-----|------|
*******************************************************************************/
static int nau8810_reg_write(struct i2c_client *client, char reg, unsigned short value)
{
char data[2];
/* data is
* D15..D9 NAU8810 register offset
* D8...D0 register data
*/
data[0] = ((reg << 1) & 0xFE) | ((value >> 8) & 0x01);
data[1] = (char)(value & 0xFF);
if (i2c_master_send(client, data, 2) == 2) {
#ifdef NAU8810_DEBUG
printk(KERN_INFO"%s=> reg:0x%02x,value:0x%03x success\n", __FUNCTION__, reg, value);
#endif
return 0;
} else {
printk(KERN_INFO"%s=> reg:0x%02x,value:0x%03x error\n", __FUNCTION__, reg, value);
return -1;
}
}
static int nau8810_write(char reg, unsigned short value)
{
int ret;
#ifdef NAU8810_DEBUG
unsigned short read_value;
#endif
ret = nau8810_reg_write(g_client, reg, value);
if (ret < 0)
{
printk(KERN_INFO"%s:L%d: nau8810_reg_write() error.\n", __FUNCTION__, __LINE__);
}
#ifdef NAU8810_DEBUG
//for check whether write is OK or not.
ret = nau8810_read(reg, &read_value);
if (ret < 0)
{
printk(KERN_INFO"%s:L%d: nau8810_read() error.\n", __FUNCTION__, __LINE__);
}
printk(KERN_INFO"%s:L%d: reg=0x%02x, value=0x%04x, read_value=0x%04x. Please check it:%s.\n",
__FUNCTION__, __LINE__, reg, value, read_value, (value == read_value)?"OK":"FAIL");
#endif
return ret;
}
static int nau8810_update_bit(char reg, unsigned short mask, unsigned short value)
{
int status = 0;
unsigned short orig = 0;
unsigned short tmp = 0;
status = nau8810_read(reg, &orig);
if (status < 0)
{
printk(KERN_INFO"%s:L%d: nau8810_read() error.\n", __FUNCTION__, __LINE__);
return status;
}
else
{
tmp = orig & (~mask);
tmp |= (value & mask);
if (tmp != orig)
{
status = nau8810_write(reg, tmp);
if (status < 0)
{
printk(KERN_INFO"%s:L%d: nau8810_write() error.\n", __FUNCTION__, __LINE__);
return status;
}
}
}
#ifdef NAU8810_DEBUG
printk(KERN_INFO"%s:L%d: reg=0x%02x, mask=0x%02x, value=0x%04x, orig=0x%04x, last_value=0x%04x, please check it:%s.\n",
__FUNCTION__, __LINE__, reg, mask, value, orig, tmp, (orig == tmp)?"EQUAL":"UNEQUAL");
#endif
return status;
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
#define NAU8810_I2C_SLAVE_ADDR 0x34 //7-MSB bits: 0011_010
static char l_connect_nau8810 = 0;
static char l_outGain = 4;
static char l_inGain = 4;
extern int IsDisableDefaultCodec(void);
int codec_nau8810_is_connect(void)
{
return l_connect_nau8810;
}
int codec_nau8810_init(void)
{
static char nau8810_init_ok = 0;
short value = 0;
if(0 == nau8810_init_ok){
//codec_nau8810_enable_mfpr();
/* check if has codec nau8810*/
nau8810_read(0x3F, &value);
if ((NAU8810_I2C_SLAVE_ADDR >> 1) == value ) {
nau8810_write(0, 0x00);
nau8810_init_ok = 1;
l_connect_nau8810 = 1;
}
}
printk(KERN_INFO"%s:L%d: nau8810_init_ok=%d, l_connect_nau8810 =%d.\n",
__FUNCTION__, __LINE__, nau8810_init_ok, l_connect_nau8810);
return !nau8810_init_ok;
}
void codec_nau8810_set_pll(void)
{
printk(KERN_INFO"%s:L%d: enter.\n", __FUNCTION__, __LINE__);
//printk(KERN_INFO"%s:L%d: enter.\n", __FUNCTION__, __LINE__);
#ifdef CONFIG_CODEC_PCM_SLAVE
//codec is slave
printk(KERN_INFO"%s:L%d: codec slave.\n", __FUNCTION__, __LINE__);
nau8810_update_bit(0x06, 1, 0); //nau8810 slave mode
//NOTES:not must config 0x06 to generate BCLK, it is provided by GSSP. when code is slave.
nau8810_update_bit(0x06, 7 << 2, 0); //bclk = mclk
nau8810_update_bit(0x06, 7 << 5, 0); //IMCLK = MCLK
nau8810_update_bit(0x06, 1 << 8, 0); //pll bypassed
#ifdef CONFIG_CODEC_PCM_NB
//NB
printk(KERN_INFO"%s:L%d: codec slave.NB\n", __FUNCTION__, __LINE__);
nau8810_update_bit(0x07, 7 << 1, 5 << 1); // digital filter sample rate, 101b for 8k
#else
//WB
printk(KERN_INFO"%s:L%d: codec slave.WB\n", __FUNCTION__, __LINE__);
nau8810_update_bit(0x07, 7 << 1, 3 << 1); // digital filter sample rate, 011b for 16k
#endif
/////////////////////////////////////////////////////////////////////////////////////////////
#else
//codec is master
//NOTES: MCLK needs input 2M when NB, 4M when WB.
printk(KERN_INFO"%s:L%d: codec master.\n", __FUNCTION__, __LINE__);
nau8810_update_bit(0x06, 1, 1); //nau8810 master mode
//NOTES:must config 0x06 to generate BCLK, when code is master.
nau8810_update_bit(0x06, 7 << 2, 0); //bclk = mclk; 256FS, N=1
nau8810_update_bit(0x06, 7 << 5, 0); //IMCLK = MCLK
nau8810_update_bit(0x06, 1 << 8, 0); //pll bypassed
#ifdef CONFIG_CODEC_PCM_NB
printk(KERN_INFO"%s:L%d: codec master.NB\n", __FUNCTION__, __LINE__);
nau8810_update_bit(0x07, 7 << 1, 5 << 1); // digital filter sample rate, 101b for 8k
#else
printk(KERN_INFO"%s:L%d: codec master.WB\n", __FUNCTION__, __LINE__);
nau8810_update_bit(0x07, 7 << 1, 3 << 1); // digital filter sample rate, 011b for 16k
#endif
#endif
/////////////////////////////////////////////////////////////////////////////////////////////
nau8810_write(0x24, 0x00);
nau8810_write(0x25, 0x00);
nau8810_write(0x26, 0x00);
nau8810_write(0x27, 0x00);
return;
}
void codec_nau8810_set_fmt(void)
{
#ifdef NAU8810_DEBUG
printk(KERN_INFO"%s:L%d: enter.\n", __FUNCTION__, __LINE__);
#endif
nau8810_update_bit(0x04, 3 << 5, 0); // wlen = 16
nau8810_update_bit(0x04, 3 << 3, 3 << 3); // PCM A
nau8810_update_bit(0x04, 1 << 8, 1 << 8); // bclk polarity interted
return;
}
void codec_nau8810_clear_digital_filter(void)
{
#ifdef NAU8810_DEBUG
printk(KERN_INFO"%s:L%d: enter.\n", __FUNCTION__, __LINE__);
#endif
nau8810_update_bit(0x0e, 1 << 8, 0); // disabe high pass filter
//#if 0
/*EQ*/
nau8810_write(0x12, 0x00);
nau8810_write(0x13, 0x00);
nau8810_write(0x14, 0x00);
nau8810_write(0x15, 0x00);
nau8810_write(0x16, 0x00);
/*notch filter*/
nau8810_write(0x1b, 0x00);
nau8810_write(0x1c, 0x00);
nau8810_write(0x1d, 0x00);
nau8810_write(0x1e, 0x00);
//#endif
return;
}
//Speaker
void codec_nau8810_dac_power_on(void)
{
#ifdef NAU8810_DEBUG
printk(KERN_INFO"%s:L%d: enter.\n", __FUNCTION__, __LINE__);
#endif
nau8810_update_bit(0x02, 1 << 2, 1 << 2); // unused input/output tie off buffer enable
nau8810_update_bit(0x01, 1 << 3, 1 << 3); // analogue amplifier bias control enable
nau8810_update_bit(0x01, 3, 1); // REFIMP
nau8810_update_bit(0x03, 1, 1); // DAC enable
nau8810_update_bit(0x32, 1, 1); // DAC to speaker mixer connect
nau8810_update_bit(0x03, 1 << 2, 1 << 2); // speaker mixer enable
nau8810_update_bit(0x03, 1 << 6, 1 << 6); // speaker out nagative enable
nau8810_update_bit(0x03, 1 << 5, 1 << 5); // speaker out positive enable
//nau8810_update_bit(0x03, 1 << 4, 1 << 4); // bias enable
nau8810_update_bit(0x19, 0xf, 0xc); // DAC limiter attack time
//nau8810_update_bit(0x36, 0x3f, 0x20); // SPEAKER GAIN
nau8810_update_bit(0x36, 0x3f, (l_outGain << 3)); // SPEAKER GAIN
return;
}
void codec_nau8810_dac_power_off(void)
{
#ifdef NAU8810_DEBUG
printk(KERN_INFO"%s:L%d: enter.\n", __FUNCTION__, __LINE__);
#endif
nau8810_update_bit(0x03, 1 << 5, 0); // speaker out positive enable
nau8810_update_bit(0x03, 1 << 6, 0); // speaker out nagative enable
nau8810_update_bit(0x03, 1, 0); // DAC enable
nau8810_update_bit(0x03, 1 << 2, 0); // speaker mixer enable
nau8810_update_bit(0x03, 1 << 4, 0); // bias enable
nau8810_update_bit(0x01, 3, 0); // REFIMP
nau8810_update_bit(0x01, 1 << 3, 0); // analogue amplifier bias control enable
nau8810_update_bit(0x02, 1 << 2, 0); // unused input/output tie off buffer enable
nau8810_update_bit(0x32, 1, 0); // DAC to speaker mixer connect
return;
}
//MIC1
void codec_nau8810_adc_power_on(void)
{
#ifdef NAU8810_DEBUG
printk(KERN_INFO"%s:L%d: enter.\n", __FUNCTION__, __LINE__);
#endif
nau8810_update_bit(0x02, 1 << 2, 1 << 2); // unused input/output tie off buffer enable
nau8810_update_bit(0x01, 1 << 3, 1 << 3); // analogue amplifier bias control enable
nau8810_update_bit(0x01, 3, 1); // REFIMP
nau8810_update_bit(0x02, 1, 1); // ADC enable
nau8810_update_bit(0x02, 1 << 4, 1 << 4); // input Boost enable
nau8810_update_bit(0x02, 1 << 2, 1 << 2); // Mic PGA enable
nau8810_update_bit(0x2C, 1 << 1, 1 << 1); // Mic nagative to input PGA
nau8810_update_bit(0x2C, 1 << 0, 1 << 0); // Mic positive to input PGA
nau8810_update_bit(0x01, 1 << 4, 1 << 4); // Mic bias enable
//nau8810_update_bit(0x2d, 0x3f, 0x24); // PGAGAIN
nau8810_update_bit(0x2d, 0x3f, l_inGain << 3); // PGAGAIN
nau8810_update_bit(0x2f, 1 << 8, 0); // PGABST
return;
}
void codec_nau8810_adc_power_off(void)
{
#ifdef NAU8810_DEBUG
printk(KERN_INFO"%s:L%d: enter.\n", __FUNCTION__, __LINE__);
#endif
nau8810_update_bit(0x01, 1 << 4, 0); // Mic bias enable
//nau8810_update_bit(0x2C, 1 << 0, 0); // Mic positive to input PGA
//nau8810_update_bit(0x2C, 1 << 1, 0); // Mic nagative to input PGA
nau8810_update_bit(0x02, 1 << 2, 0); // Mic PGA enable
nau8810_update_bit(0x02, 1 << 4, 0); // input Boost enable
nau8810_update_bit(0x02, 1, 0); // ADC enable
nau8810_update_bit(0x01, 3, 0); // REFIMP
nau8810_update_bit(0x01, 1 << 3, 0); // analogue amplifier bias control enable
nau8810_update_bit(0x02, 1 << 2, 0); // unused input/output tie off buffer enable
return;
}
void codec_nau8810_enable_path(void)
{
#ifdef NAU8810_DEBUG
printk(KERN_INFO"%s:L%d: enter.\n", __FUNCTION__, __LINE__);
#endif
if(!codec_nau8810_is_connect()){
return;
}
codec_nau8810_set_fmt();
codec_nau8810_set_pll();
codec_nau8810_clear_digital_filter();
codec_nau8810_dac_power_on();//speaker
codec_nau8810_adc_power_on();//MIC1
// remove opening codec path pop noise
//nau8810_reg_write(0x4f, 0x100);
//nau8810_reg_write(0x4b, 0x08F);
return;
}
void codec_nau8810_disable_path(void)
{
#ifdef NAU8810_DEBUG
printk(KERN_INFO"%s:L%d: enter.\n", __FUNCTION__, __LINE__);
#endif
if(!codec_nau8810_is_connect()){
return;
}
codec_nau8810_dac_power_off();
codec_nau8810_adc_power_off();
return;
}
void codec_nau8810_set_speaker_gain_app(char gain)
{
if(!codec_nau8810_is_connect()){
return;
}
nau8810_write(0x36, gain);
return;
}
void codec_nau8810_set_speaker_gain(char index)
{
char gain = ((index > 7) ? 7 : index);
if(!codec_nau8810_is_connect()){
return;
}
printk(KERN_INFO"%s/L%d: index - 0x%02x", __FUNCTION__, __LINE__, index);
gain = gain << 3;
if(0 == nau8810_update_bit(0x36, 0x3F, gain)){
l_outGain = gain >> 3;
}
printk(KERN_INFO"%s/L%d: index = 0x%02x, gain = 0x%02x, l_outGain = 0x%02x.", __FUNCTION__, __LINE__, index, gain, l_outGain);
return;
}
unsigned char codec_nau8810_get_speaker_gain(void)
{
if(!codec_nau8810_is_connect()){
return 0xFF;
}
printk(KERN_INFO"%s/L%d: l_outGain - %x", __FUNCTION__, __LINE__, l_outGain);
return l_outGain;
}
void codec_nau8810_set_mic_gain_app(char gain)
{
if(!codec_nau8810_is_connect()){
return;
}
nau8810_write(0x2d, gain);
return;
}
void codec_nau8810_set_mic_gain(char index)
{
char gain = ((index > 7) ? 7 : index);
if(!codec_nau8810_is_connect()){
return;
}
gain = gain << 3;
if(0 == nau8810_update_bit(0x2d, 0x3F, gain)){
l_inGain = gain >> 3;
}
printk(KERN_INFO"%s/L%d: index = 0x%02x, gain = 0x%02x, l_inGain = 0x%02x.", __FUNCTION__, __LINE__, index, gain, l_inGain);
return;
}
unsigned char codec_nau8810_get_mic_gain(void)
{
if(!codec_nau8810_is_connect()){
return 0xFF;
}
printk(KERN_INFO"%s/L%d: l_inGain - 0x%02x", __FUNCTION__, __LINE__, l_inGain);
return l_inGain;
}
/*
0x36 0x0000, 0x2d 0x0000 //Volume 0
0x36 0x0008, 0x2d 0x0008 //Volume 1
0x36 0x0010, 0x2d 0x0010 //Volume 2
0x36 0x0018, 0x2d 0x0018 //Volume 3
0x36 0x0020, 0x2d 0x0020 //Volume 4
0x36 0x0028, 0x2d 0x0028 //Volume 5
0x36 0x0030, 0x2d 0x0030 //Volume 6
0x36 0x0038, 0x2d 0x0038 //Volume 7
0x36 0x0039, 0x2d 0x0039 //Volume 8
0x36 0x003b, 0x2d 0x003b //Volume 9
0x36 0x003f, 0x2d 0x003f //Volume 10
*/
void codec_nau8810_set_MIC_SPK_gain_app(char gain)
{
if(!codec_nau8810_is_connect()){
return;
}
nau8810_write(0x2d, gain);//MIC
nau8810_write(0x36, gain);//SPEAKER
return;
}
/////////////////////////////////////////////////////////////////////////////////////
//write registers to control path for Application.
void codec_nau8810_set_fmt_app(void)
{
nau8810_write(0x04, 0x118);
return;
}
void codec_nau8810_set_pll_app(void)
{
nau8810_write(0x06, 0x00);
nau8810_write(0x07, 0x0a);
nau8810_write(0x24, 0x00);
nau8810_write(0x25, 0x00);
nau8810_write(0x26, 0x00);
nau8810_write(0x27, 0x00);
return;
}
void codec_nau8810_clear_digital_filter_app(void)
{
nau8810_write(0x0e, 0x00);
/*EQ*/
nau8810_write(0x12, 0x00);
nau8810_write(0x13, 0x00);
nau8810_write(0x14, 0x00);
nau8810_write(0x15, 0x00);
nau8810_write(0x16, 0x00);
/*notch filter*/
nau8810_write(0x1b, 0x00);
nau8810_write(0x1c, 0x00);
nau8810_write(0x1d, 0x00);
nau8810_write(0x1e, 0x00);
return;
}
void codec_nau8810_dac_power_on_app(void)
{
nau8810_write(0x01, 0x19);
nau8810_write(0x02, 0x15);
nau8810_write(0x03, 0x65);
nau8810_write(0x19, 0x0c);
nau8810_write(0x32, 0x01);
nau8810_write(0x36, 0x20);
return;
}
void codec_nau8810_adc_power_on_app(void)
{
nau8810_write(0x01, 0x19);
nau8810_write(0x02, 0x15);
nau8810_write(0x2C, 0x03);
nau8810_write(0x2d, 0x20);
nau8810_write(0x2f, 0x00);
return;
}
//MIC disable
void codec_nau8810_adc_power_off_app(void)
{
#ifdef NAU8810_DEBUG
printk(KERN_INFO"%s:L%d: enter.\n", __FUNCTION__, __LINE__);
#endif
nau8810_write(0x01, 0x0);
nau8810_write(0x02, 0x0);
return;
}
//speaker disable
void codec_nau8810_dac_power_off_app(void)
{
#ifdef NAU8810_DEBUG
printk(KERN_INFO"%s:L%d: enter.\n", __FUNCTION__, __LINE__);
#endif
nau8810_write(0x01, 0x0);
nau8810_write(0x02, 0x0);
nau8810_write(0x03, 0x0);
nau8810_write(0x32, 0x0);
return;
}
void codec_nau8810_disable_path_app(void)
{
#ifdef NAU8810_DEBUG
printk(KERN_INFO"%s:L%d: enter.\n", __FUNCTION__, __LINE__);
#endif
if(!codec_nau8810_is_connect()){
return;
}
codec_nau8810_dac_power_off_app();
codec_nau8810_adc_power_off_app();
return;
}
void mute_input_device(void)
{
#ifdef NAU8810_DEBUG
printk(KERN_INFO"%s:L%d: enter.\n", __FUNCTION__, __LINE__);
#endif
nau8810_update_bit(0x02, 1 << 2, 0); // unused input/output tie off buffer enable
nau8810_update_bit(0x2C, 1 << 1, 0); // Mic nagative to input PGA
nau8810_update_bit(0x2C, 1 << 0, 0); // Mic positive to input PGA
return;
}
void unmute_input_device(void)
{
#ifdef NAU8810_DEBUG
printk(KERN_INFO"%s:L%d: enter.\n", __FUNCTION__, __LINE__);
#endif
nau8810_update_bit(0x02, 1 << 2, 1 << 2); // Mic PGA enable
nau8810_update_bit(0x2C, 1 << 1, 1 << 1); // Mic nagative to input PGA
nau8810_update_bit(0x2C, 1 << 0, 1 << 0); // Mic positive to input PGA
return;
}
//disable MIC
void disable_voice_input_device(void)
{
//printk(KERN_INFO"%s:L%d: enter.\n", __FUNCTION__, __LINE__);
nau8810_write(0x01, 0x0);
nau8810_write(0x02, 0x0);
return;
}
//disable speaker
void disable_voice_output_device(void)
{
//printk(KERN_INFO"%s:L%d: enter.\n", __FUNCTION__, __LINE__);
nau8810_write(0x01, 0x0);
nau8810_write(0x02, 0x0);
nau8810_write(0x03, 0x0);
nau8810_write(0x32, 0x0);
return;
}
//enable speaker, earphone, headphone
void enable_voice_output_device(void)
{
if(!codec_nau8810_is_connect()){
return;
}
//codec_nau8810_set_fmt_app();
nau8810_write(0x04, 0x118);
//codec_nau8810_set_pll_app();
nau8810_write(0x06, 0x00);
nau8810_write(0x07, 0x0a);
nau8810_write(0x24, 0x00);
nau8810_write(0x25, 0x00);
nau8810_write(0x26, 0x00);
nau8810_write(0x27, 0x00);
//codec_nau8810_clear_digital_filter_app();
nau8810_write(0x0e, 0x00);
/*EQ*/
nau8810_write(0x12, 0x00);
nau8810_write(0x13, 0x00);
nau8810_write(0x14, 0x00);
nau8810_write(0x15, 0x00);
nau8810_write(0x16, 0x00);
/*notch filter*/
nau8810_write(0x1b, 0x00);
nau8810_write(0x1c, 0x00);
nau8810_write(0x1d, 0x00);
nau8810_write(0x1e, 0x00);
//codec_nau8810_dac_power_on_app();//speaker
nau8810_write(0x01, 0x19);
nau8810_write(0x02, 0x15);
nau8810_write(0x03, 0x65);
nau8810_write(0x19, 0x0c);
nau8810_write(0x32, 0x01);
nau8810_write(0x36, 0x20);
return;
}
//enable MIC, HS
void enable_voice_input_device(void)
{
if(!codec_nau8810_is_connect()){
return;
}
//codec_nau8810_set_fmt_app();
nau8810_write(0x04, 0x118);
//codec_nau8810_set_pll_app();
nau8810_write(0x06, 0x00);
nau8810_write(0x07, 0x0a);
nau8810_write(0x24, 0x00);
nau8810_write(0x25, 0x00);
nau8810_write(0x26, 0x00);
nau8810_write(0x27, 0x00);
//codec_nau8810_clear_digital_filter_app();
nau8810_write(0x0e, 0x00);
/*EQ*/
nau8810_write(0x12, 0x00);
nau8810_write(0x13, 0x00);
nau8810_write(0x14, 0x00);
nau8810_write(0x15, 0x00);
nau8810_write(0x16, 0x00);
/*notch filter*/
nau8810_write(0x1b, 0x00);
nau8810_write(0x1c, 0x00);
nau8810_write(0x1d, 0x00);
nau8810_write(0x1e, 0x00);
//codec_nau8810_adc_power_on_app();//MIC1
nau8810_write(0x01, 0x19);
nau8810_write(0x02, 0x15);
nau8810_write(0x2C, 0x03);
nau8810_write(0x2d, 0x20);
nau8810_write(0x2f, 0x00);
return;
}
void codec_nau8810_enable_path_app(void)
{
#ifdef NAU8810_DEBUG
printk(KERN_INFO"%s:L%d: enter.\n", __FUNCTION__, __LINE__);
#endif
if(!codec_nau8810_is_connect()){
return;
}
codec_nau8810_set_fmt_app();
codec_nau8810_set_pll_app();
codec_nau8810_clear_digital_filter_app();
codec_nau8810_dac_power_on_app();//speaker
codec_nau8810_adc_power_on_app();//MIC1
return;
}
void codec_nau8810_test(void)
{
printk(KERN_INFO"%s/L%d: enter.", __FUNCTION__, __LINE__);
//power managerment
nau8810_write(0x00, 0x0);
nau8810_write(0x01, 0x19);
nau8810_write(0x02, 0x15);
nau8810_write(0x03, 0x65);
//audio control
nau8810_write(0x04, 0x118);
nau8810_write(0x05, 0x0);
nau8810_write(0x06, 0x0);
nau8810_write(0x07, 0xa);
nau8810_write(0x08, 0x0);
nau8810_write(0x09, 0x0);
nau8810_write(0x0a, 0x0);
nau8810_write(0x0b, 0xff);
nau8810_write(0x0c, 0x0);
nau8810_write(0x0d, 0x0);
nau8810_write(0x0e, 0x0);
nau8810_write(0x0f, 0xff);
//equalizer
nau8810_write(0x10, 0x0);
nau8810_write(0x11, 0x0);
nau8810_write(0x12, 0x0);
nau8810_write(0x13, 0x0);
nau8810_write(0x14, 0x0);
nau8810_write(0x15, 0x0);
nau8810_write(0x16, 0x0);
nau8810_write(0x17, 0x0);
//dac limiter
nau8810_write(0x18, 0x32);
nau8810_write(0x19, 0xc);
//notch filter
nau8810_write(0x1a, 0x0);
nau8810_write(0x1b, 0x0);
nau8810_write(0x1c, 0x0);
nau8810_write(0x1d, 0x0);
nau8810_write(0x1e, 0x0);
nau8810_write(0x1f, 0x0);
//alc control
nau8810_write(0x20, 0x38);
nau8810_write(0x21, 0xb);
nau8810_write(0x22, 0x32);
nau8810_write(0x23, 0x0);
//pll contrl
nau8810_write(0x24, 0x0);
nau8810_write(0x25, 0x0);
nau8810_write(0x26, 0x0);
nau8810_write(0x27, 0x0);
//byp control
nau8810_write(0x28, 0x0);
nau8810_write(0x29, 0x0);
nau8810_write(0x2a, 0x0);
nau8810_write(0x2b, 0x0);
// input output mixer
nau8810_write(0x2c, 0x3);
nau8810_write(0x2d, 0x20);
nau8810_write(0x2e, 0x0);
nau8810_write(0x2f, 0x0);
nau8810_write(0x30, 0x0);
nau8810_write(0x31, 0x2);
nau8810_write(0x32, 0x1);
nau8810_write(0x33, 0x0);
nau8810_write(0x34, 0x0);
nau8810_write(0x35, 0x0);
nau8810_write(0x36, 0x20);
nau8810_write(0x37, 0x0);
nau8810_write(0x38, 0x1);
nau8810_write(0x39, 0x0);
nau8810_write(0x3a, 0x0);
nau8810_write(0x3b, 0x0);
nau8810_write(0x3c, 0x20);
nau8810_write(0x3d, 0x0);
nau8810_write(0x3e, 0xee);
nau8810_write(0x3f, 0x1a);
/*
nau8810_write(0x40, 0x0ca);
nau8810_write(0x41, 0x124);
nau8810_write(0x42, 0x000);
nau8810_write(0x43, 0x000);
nau8810_write(0x44, 0x000);
nau8810_write(0x45, 0x000);
nau8810_write(0x46, 0x020);
nau8810_write(0x47, 0x000);
nau8810_write(0x48, 0x006);
nau8810_write(0x49, 0x000);
nau8810_write(0x4a, 0x000);
nau8810_write(0x4b, 0x000);
nau8810_write(0x4c, 0x004);
nau8810_write(0x4d, 0x000);
nau8810_write(0x4e, 0x000);
nau8810_write(0x4f, 0x000);
*/
//-------------------------
/*nau8810_reg_write(0x3b, 0x00);
nau8810_reg_write(0x3c, 0x20);
nau8810_reg_write(0x3a, 0x00);
nau8810_reg_write(0x3a, 0x00);
nau8810_reg_write(0x3a, 0x00);
nau8810_reg_write(0x3a, 0x00);*/
return;
}
/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
//debug fs for nau8810 register interface of read and write.
static int reg_nau8810 = 0xffff;
struct dentry *nau8810_dump_reg = NULL;
static ssize_t nau8810_dump_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
unsigned short reg_val = 0;
unsigned short out_val = 0;
int i;
int len = 0;
char str[100] = {0};
if (reg_nau8810 == 0xffff) {
len = snprintf(str, sizeof(str) - 1, "%s\n",
"nau8810: register dump:");
for (i = 0; i < PM80X_AUDIO_REG_NUM; i++) {
reg_val = nau8810_read(i, &out_val);
pr_info("%s: [0x%02x]=0x%03x\n", __FUNCTION__, i, out_val);
}
} else {
//0xFF:get MIC Gain and Speaker Gain
//echo 0xFF 4 > /sys/kernel/debug/nau8810_reg
//cat /sys/kernel/debug/nau8810_reg
if (0xFF == reg_nau8810)
{
codec_nau8810_get_speaker_gain();
codec_nau8810_get_mic_gain();
}
else
{
reg_val = nau8810_read(reg_nau8810, &out_val);
len = snprintf(str, sizeof(str), "reg_nau8810=0x%02x, val=0x%03x\n",
reg_nau8810, out_val);
printk(KERN_INFO"%s:%s", __FUNCTION__, str);
}
}
return 0;
}
//read example:
// echo 0x90 > /sys/kernel/debug/nau8810_reg
// cat /sys/kernel/debug/nau8810_reg
//read all register:
// echo + > /sys/kernel/debug/nau8810_reg
// cat /sys/kernel/debug/nau8810_reg
//write example: echo 0x90 0x10 > /sys/kernel/debug/nau8810_reg
/*
//read register example:
echo 0x3F > /sys/kernel/debug/nau8810_reg
cat /sys/kernel/debug/nau8810_reg
//read all registers example:
echo + > /sys/kernel/debug/nau8810_reg
cat /sys/kernel/debug/nau8810_reg
//config Gain example:
echo 0xFF 0x00 > /sys/kernel/debug/nau8810_reg
echo 0xFF 0x01 > /sys/kernel/debug/nau8810_reg
echo 0xFF 0x02 > /sys/kernel/debug/nau8810_reg
echo 0xFF 0x03 > /sys/kernel/debug/nau8810_reg
echo 0xFF 0x04 > /sys/kernel/debug/nau8810_reg
echo 0xFF 0x05 > /sys/kernel/debug/nau8810_reg
echo 0xFF 0x06 > /sys/kernel/debug/nau8810_reg
echo 0xFF 0x07 > /sys/kernel/debug/nau8810_reg
cat /sys/kernel/debug/nau8810_reg
//write the register example:
echo 0x2C 0x01 > /sys/kernel/debug/nau8810_reg
cat /sys/kernel/debug/nau8810_reg
*/
static ssize_t nau8810_dump_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
int reg_val;
//struct pm80x_chip *chip = file->private_data;
int i = 0;
int ret;
char messages[20];
memset(messages, '\0', 20);
if (copy_from_user(messages, user_buf, count))
return -EFAULT;
if ('+' == messages[0]) {
/* enable to get all the reg value */
reg_nau8810 = 0xffff;
pr_info("%s: read all reg enabled!\n", __FUNCTION__);
} else {
if (messages[1] != 'x') {
pr_err("Right format: 0x[addr]\n");
return -EINVAL;
}
if (strlen(messages) > 5) {
while (messages[i] != ' ')
i++;
messages[i] = '\0';
if (kstrtouint(messages, 16, &reg_nau8810) < 0)
return -EINVAL;
i++;
if (kstrtouint(messages + i, 16, &reg_val) < 0)
return -EINVAL;
//0xFF:set MIC Gain and Speaker Gain
//value(0x00~0x07)
//echo 0xFF value > /sys/kernel/debug/nau8810_reg
//cat /sys/kernel/debug/nau8810_reg
if (0xFF == reg_nau8810)
{
codec_nau8810_set_speaker_gain(reg_val & 0xff);
codec_nau8810_set_mic_gain(reg_val & 0xff);
}
else
{
//config the registers
ret = nau8810_write(reg_nau8810, reg_val & 0xffff);
if (ret < 0) {
pr_err("write reg error!\n");
return -EINVAL;
}
printk(KERN_INFO"%s/L%d: addr=0x%02x, val=0x%03x.\n", __FUNCTION__, __LINE__, reg_nau8810, reg_val);
}
} else {
//point out the register address for read.
if (kstrtouint(messages, 16, &reg_nau8810) < 0)
return -EINVAL;
}
}
return count;
}
static const struct file_operations nau8810_dump_ops = {
.owner = THIS_MODULE,
.open = simple_open,
.read = nau8810_dump_read,
.write = nau8810_dump_write,
};
static inline int nau8810_dump_debugfs_init(struct pm80x_chip *chip)
{
nau8810_dump_reg = debugfs_create_file("nau8810_reg", S_IRUGO | S_IFREG,
NULL, NULL, &nau8810_dump_ops);
if (nau8810_dump_reg == NULL) {
pr_err("create nau8810 debugfs error!\n");
return -ENOENT;
} else if (nau8810_dump_reg == ERR_PTR(-ENODEV)) {
pr_err("CONFIG_DEBUG_FS is not enabled!\n");
return -ENOENT;
}
return 0;
}
static void nau8810_dump_debugfs_remove(struct pm80x_chip *chip)
{
if (NULL != nau8810_dump_reg){
debugfs_remove_recursive(nau8810_dump_reg);
}
return;
}
//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////
//audio control functions
void enable_voice_earphone(void)
{
#ifdef NAU8810_DEBUG
printk(KERN_INFO"%s/L%d.\n", __FUNCTION__, __LINE__);
#endif
enable_voice_output_device();
return;
}
void enable_voice_speaker(void)
{
#ifdef NAU8810_DEBUG
printk(KERN_INFO"%s/L%d.\n", __FUNCTION__, __LINE__);
#endif
enable_voice_output_device();
return;
}
void enable_voice_headphone(void)
{
#ifdef NAU8810_DEBUG
printk(KERN_INFO"%s/L%d.\n", __FUNCTION__, __LINE__);
#endif
enable_voice_output_device();
return;
}
void enable_voice_MIC1(void)
{
#ifdef NAU8810_DEBUG
printk(KERN_INFO"%s/L%d.\n", __FUNCTION__, __LINE__);
#endif
enable_voice_input_device();
return;
}
void enable_voice_HSMIC(void)
{
#ifdef NAU8810_DEBUG
printk(KERN_INFO"%s/L%d.\n", __FUNCTION__, __LINE__);
#endif
enable_voice_input_device();
return;
}
void disable_voice_earphone(void)
{
#ifdef NAU8810_DEBUG
printk(KERN_INFO"%s/L%d.\n", __FUNCTION__, __LINE__);
#endif
disable_voice_output_device();
return;
}
void disable_voice_speaker(void)
{
#ifdef NAU8810_DEBUG
printk(KERN_INFO"%s/L%d.\n", __FUNCTION__, __LINE__);
#endif
disable_voice_output_device();
return;
}
void disable_voice_headphone(void)
{
#ifdef NAU8810_DEBUG
printk(KERN_INFO"%s/L%d.\n", __FUNCTION__, __LINE__);
#endif
disable_voice_output_device();
return;
}
void disable_voice_MIC1(void)
{
#ifdef NAU8810_DEBUG
printk(KERN_INFO"%s/L%d.\n", __FUNCTION__, __LINE__);
#endif
disable_voice_input_device();
return;
}
void disable_voice_HSMIC(void)
{
#ifdef NAU8810_DEBUG
printk(KERN_INFO"%s/L%d.\n", __FUNCTION__, __LINE__);
#endif
disable_voice_input_device();
return;
}
/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
//debug fs for nau8810 audio control of Earphone, speaker or HS...
struct dentry *nau8810_audio_control = NULL;
static ssize_t nau8810_audio_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
#ifdef NAU8810_DEBUG
printk(KERN_INFO"%s/L%d.\n", __FUNCTION__, __LINE__);
#endif
return 0;
}
////////////////////////////////////////////////////////////////////////////
//1:enable earphone, enable MIC1
//2:disable earphone, enable MIC1
//3:enable speaker, enable MIC1
//4:disable speaker, enable MIC1
//5:enable Headphone, enable HSMIC
//6:disable Headphone, enable HSMIC
////////////////////////////////////////////////
//control command:
//enable earphone and MIC1: echo 1 > /sys/kernel/debug/nau8810_audio
//disable earphone and MIC1: echo 2 > /sys/kernel/debug/nau8810_audio
//enable speaker and MIC1: echo 3 > /sys/kernel/debug/nau8810_audio
//disable speaker and MIC1: echo 4 > /sys/kernel/debug/nau8810_audio
//enable Headphone and HSMIC echo 5 > /sys/kernel/debug/nau8810_audio
//disable Headphone and HSMIC echo 6 > /sys/kernel/debug/nau8810_audio
///////////////////////////////////////////////////////////////////////////
static char msg[10];
static ssize_t nau8810_audio_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
int ret = 0;
size_t tmp_count = 0;
printk(KERN_INFO"%s/L%d.\n", __FUNCTION__, __LINE__);
memset(msg, 0x00, sizeof(msg));
tmp_count = count;
if (tmp_count >= sizeof(msg)){
tmp_count = sizeof(msg) - 1;
}
//copy the content from user space to kernel space
ret = copy_from_user(msg, user_buf, tmp_count);
if (ret){
printk(KERN_ALERT"copy from user fail \n");
return -EFAULT;
}
switch (msg[0]){
case '1'://input command# echo 1 > /sys/kernel/debug/nau8810_audio
//Debug interface for enable earphone and MIC1
printk(KERN_INFO "input %c. \n", msg[0]);
//enable_voice_earphone();
//enable_voice_MIC1();
//enable the path.
//codec_nau8810_enable_path();
//codec_nau8810_enable_path_app();
enable_voice_input_device();
enable_voice_output_device();
break;
case '2'://input command# echo 2 > /sys/kernel/debug/nau8810_audio
//Debug interface for disable earphone and MIC1
printk(KERN_INFO "input %c. \n", msg[0]);
//disable_voice_earphone();
//disable_voice_MIC1();
//disable the path.
//codec_nau8810_disable_path();
//codec_nau8810_disable_path_app();
disable_voice_input_device();
disable_voice_output_device();
break;
case '3'://input command# echo 3 > /sys/kernel/debug/nau8810_audio
//Debug interface for enable speaker and MIC1
printk(KERN_INFO "input %c. \n", msg[0]);
enable_voice_speaker();
enable_voice_MIC1();
break;
case '4'://input command# echo 4 > /sys/kernel/debug/nau8810_audio
//Debug interface for the disable speaker and MIC1
printk(KERN_INFO "input %c. \n", msg[0]);
//disable the path.
disable_voice_speaker();
disable_voice_MIC1();
break;
case '5'://input command# echo 5 > /sys/kernel/debug/nau8810_audio
//Debug interface for the enable Headphone and HSMIC
printk(KERN_INFO "input %c. \n", msg[0]);
//enable_voice_headphone();
//enable_voice_HSMIC();
mute_input_device();
break;
case '6'://input command# echo 6 > /sys/kernel/debug/nau8810_audio
//Debug interface for disable Headphone and HSMIC
printk(KERN_INFO "input %c. \n", msg[0]);
//disable_voice_headphone();
//disable_voice_HSMIC();
unmute_input_device();
break;
default://input command#
printk(KERN_INFO "input invalid. \n");
break;
}
return tmp_count;
}
static const struct file_operations nau8810_audio_ops = {
.owner = THIS_MODULE,
.open = simple_open,
.read = nau8810_audio_read,
.write = nau8810_audio_write,
};
static inline int nau8810_audio_debugfs_init(struct pm80x_chip *chip)
{
nau8810_audio_control = debugfs_create_file("nau8810_audio", S_IRUGO | S_IFREG,
NULL, NULL, &nau8810_audio_ops);
if (nau8810_audio_control == NULL) {
pr_err("create nau8810 debugfs error!\n");
return -ENOENT;
} else if (nau8810_audio_control == ERR_PTR(-ENODEV)) {
pr_err("CONFIG_DEBUG_FS is not enabled!\n");
return -ENOENT;
}
return 0;
}
static void nau8810_audio_debugfs_remove(struct pm80x_chip *chip)
{
if (NULL != nau8810_audio_control){
debugfs_remove_recursive(nau8810_audio_control);
}
return;
}
///////////////////////////////////////////////////////////////
//register codec to ALSA.
////////////////////////////////////////////////////////////////////
//soc_codec_dev_nau8810
static const struct snd_kcontrol_new nau8810_snd_controls[] = {
SOC_SINGLE("Software Reset", NAU8810_SW_RESET, 0, 0x1FF, 0), //0x0
SOC_SINGLE("Power Management 1", NAU8810_POWER_MGR1, 0, 0x1FF, 0), //0x1
SOC_SINGLE("Power Management 2", NAU8810_POWER_MGR2, 0, 0x1FF, 0), //0x2
SOC_SINGLE("Power Management 3", NAU8810_POWER_MGR3, 0, 0x1FF, 0), //0x3
SOC_SINGLE("Audio Interface", NAU8810_AUDIO_IFACE, 0, 0x1FF, 0), //0x4
SOC_SINGLE("Companding", NAU8810_COMPANDING, 0, 0x1FF, 0), //0x5
SOC_SINGLE("Clock Control 1", NAU8810_CLOCK_CTRL1, 0, 0x1FF, 0), //0x6
SOC_SINGLE("Clock Control 2", NAU8810_CLOCK_CTRL2, 0, 0x1FF, 0), //0x7
////0x8
////0x9
SOC_SINGLE("DAC CTRL", NAU8810_DAC_CTRL, 0, 0x1FF, 0), //0xa
SOC_SINGLE("DAC Volume", NAU8810_DAC_VOL, 0, 0x1FF, 0), //0xb
////0xc
////0xd
SOC_SINGLE("ADC CTRL", NAU8810_ADC_CTRL, 0, 0x1FF, 0), //0xe
SOC_SINGLE("ADC Volume", NAU8810_ADC_VOL, 0, 0x1FF, 0), //0xf
////0x10
////0x11
SOC_SINGLE("EQ1-Low Cutoff", NAU8810_EQ1, 0, 0x1FF, 0), //0x12
SOC_SINGLE("EQ2-Peak 1", NAU8810_EQ2, 0, 0x1FF, 0), //0x13
SOC_SINGLE("EQ3-Peak 2", NAU8810_EQ3, 0, 0x1FF, 0), //0x14
SOC_SINGLE("EQ4-Peak 3", NAU8810_EQ4, 0, 0x1FF, 0), //0x15
SOC_SINGLE("EQ5-High Cutoff", NAU8810_EQ5, 0, 0x1FF, 0), //0x16
////0x17
SOC_SINGLE("DAC Limiter 1", NAU8810_DAC_LIMITER1, 0, 0x1FF, 0), //0x18
SOC_SINGLE("DAC Limiter 2", NAU8810_DAC_LIMITER2, 0, 0x1FF, 0), //0x19
////0x1a
SOC_SINGLE("Notch Filter High 1", NAU8810_NOTCH_HIGH1, 0, 0x1FF, 0), //0x1b
SOC_SINGLE("Notch Filter Low 1", NAU8810_NOTCH_LOW1, 0, 0x1FF, 0), //0x1c
SOC_SINGLE("Notch Filter High 2", NAU8810_NOTCH_HIGH2, 0, 0x1FF, 0), //0x1d
SOC_SINGLE("Notch Filter Low 2", NAU8810_NOTCH_LOW2, 0, 0x1FF, 0), //0x1e
////0x1f
SOC_SINGLE("ALC CTRL 1", NAU8810_ALC_CTRL1, 0, 0x1FF, 0), //0x20
SOC_SINGLE("ALC CTRL 2", NAU8810_ALC_CTRL2, 0, 0x1FF, 0), //0x21
SOC_SINGLE("ALC CTRL 3", NAU8810_ALC_CTRL3, 0, 0x1FF, 0), //0x22
SOC_SINGLE("Noise Gate", NAU8810_NOISE_GATE, 0, 0x1FF, 0), //0x23
SOC_SINGLE("PLL N CTRL", NAU8810_PLLN_CTRL, 0, 0x1FF, 0), //0x24
SOC_SINGLE("PLL K 1", NAU8810_PLLK1, 0, 0x1FF, 0), //0x25
SOC_SINGLE("PLL K 2", NAU8810_PLLK2, 0, 0x1FF, 0), //0x26
SOC_SINGLE("PLL K 3", NAU8810_PLLK3, 0, 0x1FF, 0), //0x27
SOC_SINGLE("Attenuation CTRL", NAU8810_ATTEN_CTRL, 0, 0x1FF, 0), //0x28
////0x29
////0x2a
////0x2b
SOC_SINGLE("Input CTRL", NAU8810_INPUT_CTRL, 0, 0x1FF, 0), //0x2c
SOC_SINGLE("PGA Gain", NAU8810_PGA_GAIN, 0, 0x1FF, 0), //0x2d
////0x2e
SOC_SINGLE("ADC Boost", NAU8810_ADC_BOOST, 0, 0x1FF, 0), //0x2f
////0x30
SOC_SINGLE("Output CTRL", NAU8810_OUTPUT_CTRL, 0, 0x1FF, 0), //0x31
SOC_SINGLE("Mixer CTRL", NAU8810_MIXER_CTRL, 0, 0x1FF, 0), //0x32
////0x33
////0x34
////0x35
SOC_SINGLE("SPKOUT Volume", NAU8810_SPKOUT_VOL, 0, 0x1FF, 0), //0x36
////0x37
SOC_SINGLE("MONO Mixer Control", NAU8810_MONO_MIXER_CTRL, 0, 0x1FF, 0), //0x38
////0x39
SOC_SINGLE("Power Management 4", NAU8810_PWR_MGR4, 0, 0x1FF, 0), //0x3a
SOC_SINGLE("Time Slot", NAU8810_TIMESLOT, 0, 0x1FF, 0), //0x3b
SOC_SINGLE("ADCOUT Drive", NAU8810_ADCOUT_DRV, 0, 0x1FF, 0), //0x3c
////0x3d
SOC_SINGLE("Silicon Revision", NAU8810_SILICON_REVISION, 0, 0x1FF, 0), //0x3e
SOC_SINGLE("NAU8810 2-Wire ID", NAU8810_2WIRE_ID, 0, 0x1FF, 0), //0x3f
SOC_SINGLE("Additional ID", NAU8810_ADDITIONAL_ID, 0, 0x1FF, 0), //0x40
SOC_SINGLE("Reserved", NAU8810_RESERVED, 0, 0x1FF, 0), //0x41
////0x42
////0x43
////0x44
SOC_SINGLE("High Voltage CTRL", NAU8810_HIGH_VOL_CTRL, 0, 0x1FF, 0), //0x45
SOC_SINGLE("ALC Enhancements 1", NAU8810_ALC_ENHANCEMENT1, 0, 0x1FF, 0), //0x46
SOC_SINGLE("ALC Enhancements 2", NAU8810_ALC_ENHANCEMENT2, 0, 0x1FF, 0), //0x47
////0x48
SOC_SINGLE("Additional IF CTRL", NAU8810_ADDITIONAL_IF_CTRL, 0, 0x1FF, 0), //0x49
////0x4a
SOC_SINGLE("Power/Tie-off CTRL", NAU8810_PWR_CTRL, 0, 0x1FF, 0), //0x4b
SOC_SINGLE("AGC P2P Detector", NAU8810_AGC_P2P_DETECTOR, 0, 0x1FF, 0), //0x4c
SOC_SINGLE("AGC Peak Detector", NAU8810_AGC_PEAK_DETECTOR, 0, 0x1FF, 0), //0x4d
SOC_SINGLE("Control and Status", NAU8810_CTRL_STATUS, 0, 0x1FF, 0), //0x4e
SOC_SINGLE("Output tie-off CTRL", NAU8810_OUTPUT_TIEOFF_CTRL, 0, 0x1FF, 0),//0x4f
};
static int nau8810_codec_probe(struct snd_soc_component *component)
{
#ifdef NAU8810_DEBUG
printk(KERN_INFO"%s/L%d.\n", __FUNCTION__, __LINE__);
#endif
snd_soc_add_component_controls(component, nau8810_snd_controls, ARRAY_SIZE(nau8810_snd_controls));
return 0;
}
extern char * get_MCLK_start_addr(void);
#ifdef CONFIG_CPU_ASR1901
extern int enable_pmu_audio_clk(void);
#endif
void enable_8810_MClock(void)
{
#ifdef NAU8810_DEBUG_CLOSE
int reg_value = 0;
printk(KERN_INFO"enable_8810_MClock, g_8810_mclk_type=%d, Audio_Codec_Fsync_Rate=%d\n", g_8810_mclk_type, Audio_Codec_Fsync_Rate);
if (cpu_is_asr1803()) {
printk(KERN_INFO"%s, platform is Falcon.\n", __FUNCTION__);
} else if (cpu_is_asr1806()) {
printk(KERN_INFO"%s, platform is Falcon-T.\n", __FUNCTION__);
} else if (cpu_is_asr1901() || cpu_is_asr1906()) {
printk(KERN_INFO"%s, platform is Kestrel.\n", __FUNCTION__);
} else if (cpu_is_asr1903()) {
printk(KERN_INFO"%s, platform is Lapwing.\n", __FUNCTION__);
} else {
printk(KERN_INFO"%s, platform is Nezhac or Nezha3.\n", __FUNCTION__);
}
#endif
if (cpu_is_asr1901() || cpu_is_asr1906()) {
printk(KERN_INFO"%s, platform is kestrel.\n", __FUNCTION__);
#ifdef Kestrel_Z2
__raw_writel(0x1043, VCXO_OUT_MFPR_reg);
__raw_writel(0x3, PM_MN_CLK_reg);
if(0 == Audio_Codec_Fsync_Rate){
/* config 2.048MHz MCLK for 8KHz Fsync of PCM */
__raw_writel(0x00980001, GPCR_reg);
} else {
/* config 4.096MHz MCLK for 16KHz Fsync of PCM */
__raw_writel(0x004c0001, GPCR_reg);
}
printk(KERN_INFO"%s, VCXO_OUT_MFPR_reg is 0x%0x, PM_MN_CLK_reg is 0x%0x, GPCR_reg is 0x%0x.\n",
__FUNCTION__, *(unsigned int *)VCXO_OUT_MFPR_reg, *(unsigned int *)PM_MN_CLK_reg, *(unsigned int *)GPCR_reg);
#else
MCLK_CLK_CTRL_reg = get_MCLK_start_addr();
if (NULL == MCLK_CLK_CTRL_reg) {
printk(KERN_INFO"%s, MCLK_CLK_CTRL_reg is NULL.\n", __FUNCTION__, MCLK_CLK_CTRL_reg);
return;
}
MCLK_MN_DIV_reg = (char *)MCLK_CLK_CTRL_reg + 4;
__raw_writel(0x3, MCLK_CLK_CTRL_reg);
if(0 == Audio_Codec_Fsync_Rate){
/* config 2.048MHz MCLK for 8KHz Fsync of PCM */
__raw_writel(0x004b0004, MCLK_MN_DIV_reg);
} else if (1 == Audio_Codec_Fsync_Rate){
/* config 4.096MHz MCLK for 16KHz Fsync of PCM */
__raw_writel(0x004b0008, MCLK_MN_DIV_reg);
} else if (3 == Audio_Codec_Fsync_Rate) {
/* config 12.288MHz MCLK for 48KHz Fsync of PCM */
//__raw_writel(0x004b0018, MCLK_MN_DIV_reg);//1.MCLK 12.288MHz direct connection
__raw_writel(0x000a0001, MCLK_MN_DIV_reg);//MCLK 3.84MHz for PLL and for HP detection
nau8810_write(0x73, 0x1002);
nau8810_write(0x80, 0x4000);//2.MCLK 3.84MHz for PLL
//nau8810_write(0x80, 0x5000);//3.BCLK 3.84MHz for PLL
nau8810_write(0x81, 0x3F02);
nau8810_write(0x82, 0x3000);
} else {
printk(KERN_INFO"%s, please check Audio_Codec_Fsync_Rate = %d.\n",__FUNCTION__, Audio_Codec_Fsync_Rate);
}
printk(KERN_INFO"%s, Audio_Codec_Fsync_Rate = %d.\n",__FUNCTION__, Audio_Codec_Fsync_Rate);
printk(KERN_INFO"%s, MCLK_CLK_CTRL_reg is 0x%0x, MCLK_MN_DIV_reg is 0x%0x.\n",
__FUNCTION__, *(unsigned int *)MCLK_CLK_CTRL_reg, *(unsigned int *)MCLK_MN_DIV_reg);
#endif
} else {
printk(KERN_INFO"%s, platform is NezhaC, Falcon, .etc.\n", __FUNCTION__);
struct clk * mclk = devm_clk_get(&g_client->dev, "i2s_sys_clk");
if(IS_ERR(mclk)){
printk(KERN_INFO"mclk get failed\n");
return;
}
int ret = clk_prepare_enable(mclk);
if(ret){
printk(KERN_INFO"mclk prepare enable failed");
return;
}
if(g_8810_mclk_type){
if(0 == Audio_Codec_Fsync_Rate){
/*i2s_sysclk = 2M for 8k*/
#ifdef NAU8810_DEBUG_CLOSE
reg_value = ioread32(SSP1_I2S_CLOCK_reg);
//reg_value = *(volatile int*)(i2s_clk_reg);
printk(KERN_INFO"read: reg_value=0x%x\n", reg_value);
//printk(KERN_INFO"read: *register_address = 0x%x, reg_value=0x%x\n",
//*(unsigned int *)i2s_clk_reg, reg_value);
#endif
clk_set_rate(mclk, 12000000);
int rate = clk_get_rate(mclk);
printk(KERN_INFO"mclk rate is %d", rate);
/* write value to register */
//*(volatile unsigned int *)i2s_clk_reg = 0xF820130B;
#ifdef NAU8810_DEBUG_CLOSE
/* read value from register */
printk(KERN_INFO"NB, write:0xF820130B(Nezhac, Nezha3),0xE0C472D8(Falcon),*register_address = 0x%x\n", *(unsigned int *)SSP1_I2S_CLOCK_reg);
#endif
}
else{
/*i2s_sysclk = 4M for 16k*/
#ifdef NAU8810_DEBUG_CLOSE
reg_value = ioread32(SSP1_I2S_CLOCK_reg);
printk(KERN_INFO"read: reg_value=0x%x\n", reg_value);
//printk(KERN_INFO"read: *register_address = %x\n", *(unsigned int *)i2s_clk_reg);
//reg_value = *(volatile int*)(i2s_clk_reg);
//printk(KERN_INFO"read: *register_address = 0x%x, reg_value=0x%x\n",
//*(unsigned int *)i2s_clk_reg, reg_value);
#endif
clk_set_rate(mclk, 12000000);
int rate = clk_get_rate(mclk);
printk(KERN_INFO"mclk rate is %d", rate);
#ifdef NAU8810_DEBUG_CLOSE
/* write value to register */
//*(volatile unsigned int *)i2s_clk_reg = 0xF840130B;
/* read value from register */
printk(KERN_INFO"WB, write: 0xF840130B(Nezhac, Nezha3),0xE18872D8(Falcon), *register_address = %x\n", *(unsigned int *)SSP1_I2S_CLOCK_reg);
#endif
}
}
}
return;
}
EXPORT_SYMBOL_GPL(enable_8810_MClock);
void nau8810_codec_remove(struct snd_soc_component *codec)
{
#ifdef NAU8810_DEBUG
printk(KERN_INFO"%s/L%d.\n", __FUNCTION__, __LINE__);
#endif
}
static unsigned int nau8810_codec_read(struct snd_soc_component *codec, unsigned int reg)
{
unsigned short out_val = 0;
nau8810_read(reg, &out_val);
#ifdef NAU8810_DEBUG
printk(KERN_INFO"%s/L%d.[0x%02x]=[0x%03x]\n", __FUNCTION__, __LINE__, reg, out_val);
#endif
return out_val;
}
static int nau8810_codec_write(struct snd_soc_component *codec,
unsigned int reg, unsigned int value)
{
int ret = 0;
#ifdef NAU8810_DEBUG
printk(KERN_INFO"%s/L%d.[0x%02x]=[0x%03x]\n", __FUNCTION__, __LINE__, reg, value);
#endif
ret = nau8810_write(reg, value & 0x1FF);
return ret;
}
///////////////////////////////////////////////////////////////////////
//nau8810_dai_ops
static int nau8810_digital_mute(struct snd_soc_dai *codec_dai, int mute)
{
#ifdef NAU8810_DEBUG
printk(KERN_INFO"%s/L%d.\n", __FUNCTION__, __LINE__);
#endif
return 0;
}
static int nau8810_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
#ifdef NAU8810_DEBUG
printk(KERN_INFO"%s/L%d.\n", __FUNCTION__, __LINE__);
#endif
return 0;
}
static int nau8810_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
#ifdef NAU8810_DEBUG
printk(KERN_INFO"%s/L%d.\n", __FUNCTION__, __LINE__);
#endif
return 0;
}
static int nau8810_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
#ifdef NAU8810_DEBUG
printk(KERN_INFO"%s/L%d.\n", __FUNCTION__, __LINE__);
#endif
return 0;
}
///////////////////////////////////////////////////////////////////////
static struct snd_soc_component_driver soc_codec_dev_nau8810 = {
.probe = nau8810_codec_probe,
.remove = nau8810_codec_remove,
.read = nau8810_codec_read,
.write = nau8810_codec_write,
};
static struct snd_soc_dai_ops nau8810_dai_ops = {
.digital_mute = nau8810_digital_mute,
.hw_params = nau8810_hw_params,
.set_fmt = nau8810_set_dai_fmt,
.set_sysclk = nau8810_set_dai_sysclk,
};
static struct snd_soc_dai_driver nau8810_dai[] ={
{
/* DAI I2S(SAI1) */
.name = "nau8810-i2s",
.id = 1,
.playback = {
.stream_name = "I2S Playback",
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FORMAT_S16_LE | \
SNDRV_PCM_FORMAT_S18_3LE,
},
.capture = {
.stream_name = "I2S Capture",
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FORMAT_S16_LE | \
SNDRV_PCM_FORMAT_S18_3LE,
},
.ops = &nau8810_dai_ops,
}, {
/* DAI PCM(SAI2) */
.name = "nau8810-pcm",
.id = 2,
.playback = {
.stream_name = "PCM Playback",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FORMAT_S8| \
SNDRV_PCM_FORMAT_S16_LE | \
SNDRV_PCM_FORMAT_S20_3LE | \
SNDRV_PCM_FORMAT_S24,
},
.capture = {
.stream_name = "PCM Capture",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FORMAT_S8| \
SNDRV_PCM_FORMAT_S16_LE | \
SNDRV_PCM_FORMAT_S20_3LE | \
SNDRV_PCM_FORMAT_S24,
},
.ops = &nau8810_dai_ops,
},
};
///////////////////////////////////////////////////////////////
//i2c driver
static int nau8810_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret = 0;
#ifdef NAU8810_DEBUG
printk(KERN_INFO"%s/L%d.\n", __FUNCTION__, __LINE__);
#endif
g_client = client;
nau8810_dump_debugfs_init(NULL);
nau8810_audio_debugfs_init(NULL);
//initiate the NAU8810 codec.
codec_nau8810_init();
if (!codec_nau8810_is_connect()) {
printk(KERN_INFO"Please check codec nau8810 OK or not.\n");
return 0;
}
//create the platform device, and platform driver will register codec to ALSA.
//which will be for device node, such as /dev/snd/timer, control0, ...
//snd_soc_register_codec(&client->dev, &soc_codec_dev_nau8810,
// nau8810_dai, ARRAY_SIZE(nau8810_dai));
//int snd_soc_register_card(struct snd_soc_card *card)
ret = devm_snd_soc_register_component(&client->dev, &soc_codec_dev_nau8810,
nau8810_dai, ARRAY_SIZE(nau8810_dai));
if (ret < 0) {
printk(KERN_INFO"Failed to register codec nau8810.\n");
}
//enable the path.
//codec_nau8810_enable_path();
//codec_nau8810_enable_path_app();
enable_8810_MClock();
return 0;
}
static int nau8810_remove(struct i2c_client *client)
{
#ifdef NAU8810_DEBUG
printk(KERN_INFO"%s/L%d.\n", __FUNCTION__, __LINE__);
#endif
nau8810_dump_debugfs_remove(NULL);
g_client = NULL;
nau8810_audio_debugfs_remove(NULL);
//disable the path.
//codec_nau8810_disable_path();
//codec_nau8810_disable_path_app();
return 0;
}
void nau8810_shutdown(struct i2c_client *client)
{
#ifdef NAU8810_DEBUG
printk(KERN_INFO"%s/L%d.\n", __FUNCTION__, __LINE__);
#endif
return;
}
static struct i2c_driver nau8810_driver = {
.driver = {
.name = "nau8810",
.owner = THIS_MODULE,
//.pm = &pm80x_pm_ops,
.of_match_table = of_match_ptr(nau8810_dt_ids),
},
.probe = nau8810_probe,
.remove = nau8810_remove,
.shutdown = nau8810_shutdown,
.id_table = nau8810_id_table,
};
static int nau8810_i2c_init(void)
{
return i2c_add_driver(&nau8810_driver);
}
module_init(nau8810_i2c_init);
static void nau8810_i2c_exit(void)
{
i2c_del_driver(&nau8810_driver);
}
module_exit(nau8810_i2c_exit);
MODULE_DESCRIPTION("Driver for nau8810");
MODULE_AUTHOR("wenchen@asrmicro.com");
MODULE_LICENSE("GPL");