From: 陈金泉 Date: Wed, 30 Nov 2011 03:42:08 +0000 (+0800) Subject: add tlv320aic3111 codec driver X-Git-Tag: firefly_0821_release~9733^2~10 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=f0c3f25b04e29e8791112634ee556cd7141bce30;p=firefly-linux-kernel-4.4.55.git add tlv320aic3111 codec driver --- diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 9dfeb311759f..95da908c85f9 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -50,6 +50,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_TLV320AIC3X if I2C select SND_SOC_TPA6130A2 if I2C select SND_SOC_TLV320DAC33 if I2C + select SND_SOC_TLV320AIC3111 if I2C select SND_SOC_TWL4030 if TWL4030_CORE select SND_SOC_TWL6040 if TWL4030_CORE select SND_SOC_UDA134X @@ -242,6 +243,9 @@ config SND_SOC_TLV320AIC3X config SND_SOC_TLV320DAC33 tristate +config SND_SOC_TLV320AIC3111 + tristate + config SND_SOC_TWL4030 select TWL4030_CODEC tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index b5c69a02e817..e9d88b2d3ba5 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -33,6 +33,7 @@ snd-soc-tlv320aic23-objs := tlv320aic23.o snd-soc-tlv320aic26-objs := tlv320aic26.o snd-soc-tlv320aic3x-objs := tlv320aic3x.o snd-soc-tlv320aic32x4-objs := tlv320aic32x4.o +snd-soc-tlv320aic3111-objs := tlv320aic3111.o snd-soc-tlv320dac33-objs := tlv320dac33.o snd-soc-twl4030-objs := twl4030.o snd-soc-twl6040-objs := twl6040.o @@ -130,6 +131,7 @@ obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o obj-$(CONFIG_SND_SOC_TVL320AIC32X4) += snd-soc-tlv320aic32x4.o +obj-$(CONFIG_SND_SOC_TLV320AIC3111) += snd-soc-tlv320aic3111.o obj-$(CONFIG_SND_SOC_TLV320DAC33) += snd-soc-tlv320dac33.o obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o obj-$(CONFIG_SND_SOC_TWL6040) += snd-soc-twl6040.o diff --git a/sound/soc/codecs/tlv320aic3111.c b/sound/soc/codecs/tlv320aic3111.c new file mode 100644 index 000000000000..b065f0f446d4 --- /dev/null +++ b/sound/soc/codecs/tlv320aic3111.c @@ -0,0 +1,1925 @@ +/* + * linux/sound/soc/codecs/tlv320aic3111.c + * + * + * Copyright (C) 2010 Texas Instruments, Inc. + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * History: + * + * Rev 0.1 ASoC driver support Mistral 14-04-2010 + * + * Rev 0.2 Updated based Review Comments Mistral 29-06-2010 + * + * Rev 0.3 Updated for Codec Family Compatibility 12-07-2010 + */ + +/* + ***************************************************************************** + * Include Files + ***************************************************************************** + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include "tlv320aic3111.h" + +#if 0 +#define AIC3111_DEBUG +#define AIC_DBG(x...) printk(KERN_INFO x) +#else +#define AIC_DBG(x...) do { } while (0) +#endif + +#define HP_DET_PIN RK29_PIN6_PA0 +//#define AIC3111_DEBUG +/* codec status */ +#define AIC3110_IS_SHUTDOWN 0 +#define AIC3110_IS_CAPTURE_ON 1 +#define AIC3110_IS_PLAYBACK_ON 2 +#define AIC3110_IS_INITPOWER_ON 4 + +/* work type */ +#define AIC3110_POWERDOWN_NULL 0 +#define AIC3110_POWERDOWN_PLAYBACK 1 +#define AIC3110_POWERDOWN_CAPTURE 2 +#define AIC3110_POWERDOWN_PLAYBACK_CAPTURE 3 +#define JACK_DET_ADLOOP msecs_to_jiffies(200) + +//#define AIC3111_DEBUG +#define SPK 1 +#define HP 0 +static int aic3111_power_speaker (bool on); +struct speaker_data { + struct timer_list timer; + struct semaphore sem; +}; +enum +{ + POWER_STATE_OFF = 0, + POWER_STATE_ON, + + POWER_STATE_SW_HP = 0, + POWER_STATE_SW_SPK, +}; + +static void aic3111_work(struct work_struct *work); + +static struct workqueue_struct *aic3111_workq; +static DECLARE_DELAYED_WORK(delayed_work, aic3111_work); +static int aic3111_current_status = AIC3110_IS_SHUTDOWN, aic3111_work_type = AIC3110_POWERDOWN_NULL; +static bool isHSin = true, isSetHW = false; +int old_status = SPK; +/* + ***************************************************************************** + * Global Variables + ***************************************************************************** + */ +/* Used to maintain the Register Access control*/ +static u8 aic3111_reg_ctl; + +static struct snd_soc_codec *aic3111_codec; +struct aic3111_priv *aic3111_privdata; +struct i2c_client *aic3111_i2c; + + /* add a timer for checkout HP or SPK*/ +static struct timer_list aic3111_timer; + +/*Used to delay work hpdet switch irq handle*/ +struct delayed_work aic3111_hpdet_work; + +#ifdef CONFIG_MINI_DSP +extern int aic3111_minidsp_program (struct snd_soc_codec *codec); +extern void aic3111_add_minidsp_controls (struct snd_soc_codec *codec); +#endif + +/* + * AIC3111 register cache + * We are caching the registers here. + * NOTE: In AIC3111, there are 61 pages of 128 registers supported. + * The following table contains the page0, page1 and page2 registers values. + */ + +#ifdef AIC3111_CODEC_SUPPORT +static const u8 aic31xx_reg[AIC31xx_CACHEREGNUM] = { +/* Page 0 Registers */ +/* 0 */ 0x00, 0x00, 0x12, 0x00, 0x00, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x80, 0x80, +/* 10 */ 0x08, 0x00, 0x01, 0x01, 0x80, 0x80, 0x04, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, +/* 20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +/* 30 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x55, 0x55, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x14, +/* 40 */ 0x0c, 0x00, 0x00, 0x00, 0x6f, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xee, 0x10, 0xd8, 0x7e, 0xe3, +/* 50 */ 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +/* 60 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +/* 70 */ 0x00, 0x00, 0x10, 0x32, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x12, 0x02, +/* Page 1 Registers */ +/* 0 */ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +/* 10 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +/* 20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +/* 30 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x80, 0x00, 0x00, 0x00, +/* 40 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +/* 50 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +/* 60 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +/* 70 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +#elif defined(AIC3110_CODEC_SUPPORT) +/**************** AIC3110 REG CACHE ******************/ +static const u8 aic31xx_reg[AIC31xx_CACHEREGNUM] = { +/* Page 0 Registers */ +0x00, 0x00, 0x01, 0x56, 0x00, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x80, 0x80, +0x08, 0x00, 0x01, 0x01, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x02, 0x32, 0x12, 0x03, 0x02, 0x02, 0x11, 0x10, 0x00, 0x01, 0x04, 0x00, 0x14, +0x0c, 0x00, 0x00, 0x00, 0x0f, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0xee, 0x10, 0xd8, 0x7e, 0xe3, +0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* Page 1 Registers */ +0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, +0x06, 0x3e, 0x00, 0x00, 0x7f, 0x7f, 0x7f, 0x7f, 0x02, 0x02, 0x00, 0x00, 0x20, 0x86, 0x00, 0x80, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; /**************************** End of AIC3110 REG CAHE ******************/ + +#elif defined(AIC3100_CODEC_SUPPORT) +/******************************* AIC3100 REG CACHE ***********************/ +static const u8 aic31xx_reg[AIC31xx_CACHEREGNUM] = { +/* Page 0 Registers */ +/* 0 */ 0x00, 0x00, 0x12, 0x00, 0x00, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x80, 0x00, +/* 10 */ 0x00, 0x00, 0x01, 0x01, 0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, +/* 20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +/* 30 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x55, 0x55, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x14, +/* 40 */ 0x0c, 0x00, 0x00, 0x00, 0x6f, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xee, 0x10, 0xd8, 0x7e, 0xe3, +/* 50 */ 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +/* 60 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +/* 70 */ 0x00, 0x00, 0x10, 0x32, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x12, 0x02, +/* Page 1 Registers */ +/* 0 */ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +/* 10 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +/* 20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +/* 30 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x80, 0x00, 0x00, 0x00, +/* 40 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +/* 50 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +/* 60 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +/* 70 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +}; /**************************** End of AIC3100 REG CACHE ******************/ + +#else /*#ifdef AIC3120_CODEC_SUPPORT */ +static const u8 aic31xx_reg[AIC31xx_CACHEREGNUM] = { +/* Page 0 Registers */ +/* 0 */ 0x00, 0x00, 0x12, 0x00, 0x00, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x80, 0x80, +/* 10 */ 0x08, 0x00, 0x01, 0x01, 0x80, 0x80, 0x04, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, +/* 20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +/* 30 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x55, 0x55, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x14, +/* 40 */ 0x0c, 0x00, 0x00, 0x00, 0x6f, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xee, 0x10, 0xd8, 0x7e, 0xe3, +/* 50 */ 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +/* 60 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +/* 70 */ 0x00, 0x00, 0x10, 0x32, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x12, 0x02, +/* Page 1 Registers */ +/* 0 */ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +/* 10 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +/* 20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +/* 30 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x80, 0x00, 0x00, 0x00, +/* 40 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +/* 50 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +/* 60 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +/* 70 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +#endif + +/* + *---------------------------------------------------------------------------- + * Function : aic3111_change_page + * Purpose : This function is to switch between page 0 and page 1. + * + *---------------------------------------------------------------------------- + */ +static int aic3111_change_page (struct snd_soc_codec *codec, u8 new_page) +{ + struct aic3111_priv *aic3111 = aic3111_privdata; + u8 data[2]; + + if (new_page == 2 || new_page > 8) { + printk("ERROR::codec do not have page %d !!!!!!\n", new_page); + return -1; + } + + data[0] = 0; + data[1] = new_page; + aic3111->page_no = new_page; + + if (codec->hw_write (codec->control_data, data, 2) != 2) + { + printk ("Error in changing page to %d \n", new_page); + return -1; + } + + return 0; +} + +/* + *---------------------------------------------------------------------------- + * Function : aic3111_write_reg_cache + * Purpose : This function is to write aic3111 register cache + * + *---------------------------------------------------------------------------- + */ +static inline void aic3111_write_reg_cache (struct snd_soc_codec *codec, u16 reg, u8 value) +{ + u8 *cache = codec->reg_cache; + + if (reg >= AIC31xx_CACHEREGNUM) + { + return; + } + + cache[reg] = value; +} + +/* + *---------------------------------------------------------------------------- + * Function : aic3111_read + * Purpose : This function is to read the aic3111 register space. + * + *---------------------------------------------------------------------------- + */ +static unsigned int aic3111_read (struct snd_soc_codec *codec, unsigned int reg) +{ + struct aic3111_priv *aic3111 = aic3111_privdata; + u8 value; + u8 page = reg / 128; + + if (page == 2 || page > 8) { + printk("aic3111_read::Error page, there's not page %d in codec tlv320aic3111 !!!\n", page); + return -1; + } + + reg = reg % 128; + + if (aic3111->page_no != page) + { + aic3111_change_page (codec, page); + } + + i2c_master_send (codec->control_data, (char *) ®, 1); + i2c_master_recv (codec->control_data, &value, 1); + + return value; +} + +/* + *---------------------------------------------------------------------------- + * Function : aic3111_write + * Purpose : This function is to write to the aic3111 register space. + * + *---------------------------------------------------------------------------- + */ +static int aic3111_write (struct snd_soc_codec *codec, unsigned int reg, unsigned int value) +{ + struct aic3111_priv *aic3111 = aic3111_privdata; + u8 data[2]; + u8 page; + //printk("enter %s!!!!!!\n",__FUNCTION__); + //printk("RK29_PIN6_PB6 =%d!!!!!!\n",gpio_get_value(RK29_PIN6_PB6)); + page = reg / 128; + data[AIC3111_REG_OFFSET_INDEX] = reg % 128; + + if (page == 2 || page > 9) { + printk("aic3111_write::Error page, there's not page %d in codec tlv320aic3111 !!!\n", page); + return -1; + } + + if (aic3111->page_no != page) + { + aic3111_change_page (codec, page); + } + + /* data is + * D15..D8 aic3111 register offset + * D7...D0 register data + */ + data[AIC3111_REG_DATA_INDEX] = value & AIC3111_8BITS_MASK; +#if defined(EN_REG_CACHE) + if ((page == 0) || (page == 1)) + { + aic3111_write_reg_cache (codec, reg, value); + } +#endif + if (codec->hw_write (codec->control_data, data, 2) != 2) + { + printk ("Error in i2c write\n"); + return -EIO; + } + + return 0; +} + +static int aic3111_print_register_cache (struct platform_device *pdev) +{ + struct snd_soc_codec *codec = aic3111_codec; + u8 *cache = codec->reg_cache; + int reg; + + printk ("\n========3110 reg========\n"); + for (reg = 0; reg < codec->reg_size; reg++) + { + if (reg == 0) printk ("Page 0\n"); + if (reg == 128) printk ("\nPage 1\n"); + if (reg%16 == 0 && reg != 0 && reg != 128) printk ("\n"); + printk("0x%02x, ",aic3111_read(codec,reg)); + } + printk ("\n========3110 cache========\n"); + for (reg = 0; reg < codec->reg_size; reg++) + { + if (reg == 0) printk ("Page 0\n"); + if (reg == 128) printk ("\nPage 1\n"); + if (reg%16 == 0 && reg != 0 && reg != 128) printk ("\n"); + printk ("0x%02x, ",cache[reg]); + } + printk ("\n==========================\n"); + return 0; +} + +static void aic3111_soft_reset (void) +{ + struct snd_soc_codec *codec = aic3111_codec; + + AIC_DBG("CODEC::%s\n",__FUNCTION__); + + //aic3111_write (codec, 1, 0x01); + aic3111_write (codec, 63, 0x00); + gpio_set_value(RK29_PIN6_PB5, GPIO_LOW); + msleep(10); + aic3111_write (aic3111_codec, (68), 0x01); //disable DRC + aic3111_write (aic3111_codec, (128 + 31), 0xc4); + aic3111_write (aic3111_codec, (128 + 36), 0x28); //Left Analog Vol to HPL + aic3111_write (aic3111_codec, (128 + 37), 0x28); //Right Analog Vol to HPL + aic3111_write (aic3111_codec, (128 + 40), 0x4f); //HPL driver PGA + aic3111_write (aic3111_codec, (128 + 41), 0x4f); //HPR driver PGA + aic3111_power_speaker(POWER_STATE_OFF); + mdelay (20); + aic3111_write (codec, 1, 0x00); + + memcpy(codec->reg_cache, aic31xx_reg, + sizeof(aic31xx_reg)); + + isSetHW = false; + + return; +} + +/* + *---------------------------------------------------------------------------- + * Function : aic3111_set_bias_level + * Purpose : This function is to get triggered when dapm events occurs. + * + *---------------------------------------------------------------------------- + */ +static int aic3111_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct aic3111_priv *aic3111 = aic3111_privdata; + u8 value; + + if (isSetHW) + return 0; + + AIC_DBG ("CODEC::%s>>>>>>level:%d>>>>master:%d\n", __FUNCTION__, level, aic3111->master); + + switch (level) { + /* full On */ + case SND_SOC_BIAS_ON: + /* all power is driven by DAPM system */ + if (aic3111->master) + { + /* Switch on PLL */ + value = aic3111_read(codec, CLK_REG_2); + aic3111_write(codec, CLK_REG_2, (value | ENABLE_PLL)); + + /* Switch on NDAC Divider */ + value = aic3111_read(codec, NDAC_CLK_REG); + aic3111_write(codec, NDAC_CLK_REG, value | ENABLE_NDAC); + + /* Switch on MDAC Divider */ + value = aic3111_read(codec, MDAC_CLK_REG); + aic3111_write(codec, MDAC_CLK_REG, value | ENABLE_MDAC); + + /* Switch on NADC Divider */ + value = aic3111_read(codec, NADC_CLK_REG); + aic3111_write(codec, NADC_CLK_REG, value | ENABLE_MDAC); + + /* Switch on MADC Divider */ + value = aic3111_read(codec, MADC_CLK_REG); + aic3111_write(codec, MADC_CLK_REG, value | ENABLE_MDAC); + + /* Switch on BCLK_N Divider */ + value = aic3111_read(codec, BCLK_N_VAL); + aic3111_write(codec, BCLK_N_VAL, value | ENABLE_BCLK); + } else { + /* Switch on PLL */ + value = aic3111_read(codec, CLK_REG_2); + aic3111_write(codec, CLK_REG_2, (value | ENABLE_PLL)); + + /* Switch on NDAC Divider */ + value = aic3111_read(codec, NDAC_CLK_REG); + aic3111_write(codec, NDAC_CLK_REG, value | ENABLE_NDAC); + + /* Switch on MDAC Divider */ + value = aic3111_read(codec, MDAC_CLK_REG); + aic3111_write(codec, MDAC_CLK_REG, value | ENABLE_MDAC); + + /* Switch on NADC Divider */ + value = aic3111_read(codec, NADC_CLK_REG); + aic3111_write(codec, NADC_CLK_REG, value | ENABLE_MDAC); + + /* Switch on MADC Divider */ + value = aic3111_read(codec, MADC_CLK_REG); + aic3111_write(codec, MADC_CLK_REG, value | ENABLE_MDAC); + + /* Switch on BCLK_N Divider */ + value = aic3111_read(codec, BCLK_N_VAL); + aic3111_write(codec, BCLK_N_VAL, value | ENABLE_BCLK); + } + break; + + /* partial On */ + case SND_SOC_BIAS_PREPARE: + break; + + /* Off, with power */ + case SND_SOC_BIAS_STANDBY: + /* + * all power is driven by DAPM system, + * so output power is safe if bypass was set + */ + if (aic3111->master) + { + /* Switch off PLL */ + value = aic3111_read(codec, CLK_REG_2); + aic3111_write(codec, CLK_REG_2, (value & ~ENABLE_PLL)); + + /* Switch off NDAC Divider */ + value = aic3111_read(codec, NDAC_CLK_REG); + aic3111_write(codec, NDAC_CLK_REG, value & ~ENABLE_NDAC); + + /* Switch off MDAC Divider */ + value = aic3111_read(codec, MDAC_CLK_REG); + aic3111_write(codec, MDAC_CLK_REG, value & ~ENABLE_MDAC); + + /* Switch off NADC Divider */ + value = aic3111_read(codec, NADC_CLK_REG); + aic3111_write(codec, NADC_CLK_REG, value & ~ENABLE_NDAC); + + /* Switch off MADC Divider */ + value = aic3111_read(codec, MADC_CLK_REG); + aic3111_write(codec, MADC_CLK_REG, value & ~ENABLE_MDAC); + value = aic3111_read(codec, BCLK_N_VAL); + + /* Switch off BCLK_N Divider */ + aic3111_write(codec, BCLK_N_VAL, value & ~ENABLE_BCLK); + } + break; + + /* Off, without power */ + case SND_SOC_BIAS_OFF: + /* force all power off */ + break; + } + codec->dapm.bias_level = level; + return 0; +} + +/* the structure contains the different values for mclk */ +static const struct aic3111_rate_divs aic3111_divs[] = { +/* + * mclk, rate, p_val, pll_j, pll_d, dosr, ndac, mdac, aosr, nadc, madc, blck_N, + * codec_speficic_initializations + */ + /* 8k rate */ + {12000000, 8000, 1, 7, 6800, 768, 5, 3, 128, 5, 18, 24}, + //{12288000, 8000, 1, 7, 8643, 768, 5, 3, 128, 5, 18, 24}, + {24000000, 8000, 2, 7, 6800, 768, 15, 1, 64, 45, 4, 24}, + /* 11.025k rate */ + {12000000, 11025, 1, 7, 5264, 512, 8, 2, 128, 8, 8, 16}, + {24000000, 11025, 2, 7, 5264, 512, 16, 1, 64, 32, 4, 16}, + /* 16k rate */ + {12000000, 16000, 1, 7, 6800, 384, 5, 3, 128, 5, 9, 12}, + {24000000, 16000, 2, 7, 6800, 384, 15, 1, 64, 18, 5, 12}, + /* 22.05k rate */ + {12000000, 22050, 1, 7, 5264, 256, 4, 4, 128, 4, 8, 8}, + {24000000, 22050, 2, 7, 5264, 256, 16, 1, 64, 16, 4, 8}, + /* 32k rate */ + {12000000, 32000, 1, 7, 1680, 192, 2, 7, 64, 2, 21, 6}, + {24000000, 32000, 2, 7, 1680, 192, 7, 2, 64, 7, 6, 6}, + /* 44.1k rate */ + {12000000, 44100, 1, 7, 5264, 128, 2, 8, 128, 2, 8, 4}, + {11289600, 44100, 1, 8, 0, 128, 4, 4, 128, 4, 4, 4}, + {24000000, 44100, 2, 7, 5264, 128, 8, 2, 64, 8, 4, 4}, + /* 48k rate */ + {12000000, 48000, 1, 8, 1920, 128, 2, 8, 128, 2, 8, 4}, + {24000000, 48000, 2, 8, 1920, 128, 8, 2, 64, 8, 4, 4}, + /*96k rate */ + {12000000, 96000, 1, 8, 1920, 64, 2, 8, 64, 2, 8, 2}, + {24000000, 96000, 2, 8, 1920, 64, 4, 4, 64, 8, 2, 2}, + /*192k */ + {12000000, 192000, 1, 8, 1920, 32, 2, 8, 32, 2, 8, 1}, + {24000000, 192000, 2, 8, 1920, 32, 4, 4, 32, 4, 4, 1}, +}; + +/* + *---------------------------------------------------------------------------- + * Function : aic3111_get_divs + * Purpose : This function is to get required divisor from the "aic3111_divs" + * table. + * + *---------------------------------------------------------------------------- + */ +static inline int aic3111_get_divs (int mclk, int rate) +{ + int i; + + AIC_DBG("Enter::%s\n",__FUNCTION__); + + for (i = 0; i < ARRAY_SIZE (aic3111_divs); i++) + { + if ((aic3111_divs[i].rate == rate) && (aic3111_divs[i].mclk == mclk)) + { + return i; + } + } + + printk ("Master clock and sample rate is not supported\n"); + return -EINVAL; +} + +/* + *---------------------------------------------------------------------------- + * Function : aic3111_hw_params + * Purpose : This function is to set the hardware parameters for AIC3111. + * The functions set the sample rate and audio serial data word + * length. + * + *---------------------------------------------------------------------------- + */ +static int aic3111_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, + struct snd_soc_dai *codec_dai) +{ + struct snd_soc_codec *codec = aic3111_codec; + struct aic3111_priv *aic3111 = aic3111_privdata; + int i; + u8 data; + + if (isSetHW) + return 0; + + AIC_DBG("CODEC::%s\n", __FUNCTION__); + + aic3111_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + i = aic3111_get_divs(aic3111->sysclk, params_rate (params)); + + if (i < 0) { + printk ("sampling rate not supported\n"); + return i; + } + + /* We will fix R value to 1 and will make P & J=K.D as varialble */ + + /* Setting P & R values */ + aic3111_write(codec, CLK_REG_2, ((aic3111_divs[i].p_val << 4) | 0x01)); + + /* J value */ + aic3111_write(codec, CLK_REG_3, aic3111_divs[i].pll_j); + + /* MSB & LSB for D value */ + aic3111_write(codec, CLK_REG_4, (aic3111_divs[i].pll_d >> 8)); + aic3111_write(codec, CLK_REG_5, (aic3111_divs[i].pll_d & AIC3111_8BITS_MASK)); + + /* NDAC divider value */ + aic3111_write(codec, NDAC_CLK_REG, aic3111_divs[i].ndac); + + /* MDAC divider value */ + aic3111_write(codec, MDAC_CLK_REG, aic3111_divs[i].mdac); + + /* DOSR MSB & LSB values */ + aic3111_write(codec, DAC_OSR_MSB, aic3111_divs[i].dosr >> 8); + aic3111_write(codec, DAC_OSR_LSB, aic3111_divs[i].dosr & AIC3111_8BITS_MASK); + + /* NADC divider value */ + aic3111_write(codec, NADC_CLK_REG, aic3111_divs[i].nadc); + + /* MADC divider value */ + aic3111_write(codec, MADC_CLK_REG, aic3111_divs[i].madc); + + /* AOSR value */ + aic3111_write(codec, ADC_OSR_REG, aic3111_divs[i].aosr); + + /* BCLK N divider */ + aic3111_write(codec, BCLK_N_VAL, aic3111_divs[i].blck_N); + + aic3111_set_bias_level(codec, SND_SOC_BIAS_ON); + + data = aic3111_read(codec, INTERFACE_SET_REG_1); + + data = data & ~(3 << 4); + + switch (params_format (params)) { + case SNDRV_PCM_FORMAT_S16_LE: + break; + case SNDRV_PCM_FORMAT_S20_3LE: + data |= (AIC3111_WORD_LEN_20BITS << DATA_LEN_SHIFT); + break; + case SNDRV_PCM_FORMAT_S24_LE: + data |= (AIC3111_WORD_LEN_24BITS << DATA_LEN_SHIFT); + break; + case SNDRV_PCM_FORMAT_S32_LE: + data |= (AIC3111_WORD_LEN_32BITS << DATA_LEN_SHIFT); + break; + } + + aic3111_write(codec, INTERFACE_SET_REG_1, data); + + return 0; +} + +/* + *---------------------------------------------------------------------------- + * Function : aic3111_mute + * Purpose : This function is to mute or unmute the left and right DAC + * + *---------------------------------------------------------------------------- + */ +static int aic3111_mute (struct snd_soc_dai *codec_dai, int mute) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u8 dac_reg; + + AIC_DBG ("CODEC::%s>>>>mute:%d\n", __FUNCTION__, mute); + + dac_reg = aic3111_read (codec, DAC_MUTE_CTRL_REG) & ~MUTE_ON; + if (mute) + ;//aic3111_write (codec, DAC_MUTE_CTRL_REG, dac_reg | MUTE_ON); + else { + //aic3111_write (codec, DAC_MUTE_CTRL_REG, dac_reg); + isSetHW = true; + } + + return 0; +} + +/* + *---------------------------------------------------------------------------- + * Function : aic3111_set_dai_sysclk + * Purpose : This function is to set the DAI system clock + * + *---------------------------------------------------------------------------- + */ +static int aic3111_set_dai_sysclk (struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct aic3111_priv *aic3111 = aic3111_privdata; + + if (isSetHW) + return 0; + + AIC_DBG("Enter %s and line %d\n",__FUNCTION__,__LINE__); + + switch (freq) { + case AIC3111_FREQ_11289600: + case AIC3111_FREQ_12000000: + case AIC3111_FREQ_24000000: + aic3111->sysclk = freq; + return 0; + } + + printk ("Invalid frequency to set DAI system clock\n"); + return -EINVAL; +} + +/* + *---------------------------------------------------------------------------- + * Function : aic3111_set_dai_fmt + * Purpose : This function is to set the DAI format + * + *---------------------------------------------------------------------------- + */ +static int aic3111_set_dai_fmt (struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct aic3111_priv *aic3111 = aic3111_privdata; + u8 iface_reg; + + if (isSetHW) + return 0; + + AIC_DBG("Enter %s and line %d\n",__FUNCTION__,__LINE__); + + iface_reg = aic3111_read (codec, INTERFACE_SET_REG_1); + iface_reg = iface_reg & ~(3 << 6 | 3 << 2); //set I2S mode BCLK and WCLK is input + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + aic3111->master = 1; + iface_reg |= BIT_CLK_MASTER | WORD_CLK_MASTER; + break; + case SND_SOC_DAIFMT_CBS_CFS: + aic3111->master = 0; + iface_reg &= ~(BIT_CLK_MASTER | WORD_CLK_MASTER); + break; + case SND_SOC_DAIFMT_CBS_CFM: + aic3111->master = 0; + iface_reg |= BIT_CLK_MASTER; + iface_reg &= ~(WORD_CLK_MASTER); + break; + default: + printk ("Invalid DAI master/slave interface\n"); + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_DSP_A: + iface_reg |= (AIC3111_DSP_MODE << AUDIO_MODE_SHIFT); + break; + case SND_SOC_DAIFMT_RIGHT_J: + iface_reg |= (AIC3111_RIGHT_JUSTIFIED_MODE << AUDIO_MODE_SHIFT); + break; + case SND_SOC_DAIFMT_LEFT_J: + iface_reg |= (AIC3111_LEFT_JUSTIFIED_MODE << AUDIO_MODE_SHIFT); + break; + default: + printk ("Invalid DAI interface format\n"); + return -EINVAL; + } + + aic3111_write (codec, INTERFACE_SET_REG_1, iface_reg); + + return 0; +} + + +/* + *---------------------------------------------------------------------------- + * Function : aic3111_power_headphone + * Purpose : + * parameter: on = 1: power up; + * on = 0: power dn; + * xjq@rock-chips.com + *---------------------------------------------------------------------------- + */ +static int aic3111_power_headphone (bool on) +{ + struct snd_soc_codec *codec = aic3111_codec; + + AIC_DBG("Enter %s and line %d\n",__FUNCTION__,__LINE__); + + if (on == POWER_STATE_ON) { + aic3111_write (codec, (63), 0xd4); +// aic3111_write(codec, (128 + 35), 0x88); + aic3111_write (codec, (68), 0x01); //disable DRC + aic3111_write (codec, (128 + 31), 0xc4); + aic3111_write (codec, (128 + 44), 0x00); + aic3111_write (codec, (128 + 36), 0x28); //Left Analog Vol to HPL + aic3111_write (codec, (128 + 37), 0x28); //Right Analog Vol to HPL +// aic3111_write (codec, (128 + 40), 0x06); //HPL driver PGA +// aic3111_write (codec, (128 + 41), 0x06); //HPR driver PGA + aic3111_write (codec, (128 + 40), 0x4f); //HPL driver PGA + aic3111_write (codec, (128 + 41), 0x4f); //HPR driver PGA + + } else if (on == POWER_STATE_OFF) { + + aic3111_write (codec, (128 + 31), 0x00); + aic3111_write (codec, (128 + 44), 0x00); + aic3111_write (codec, (128 + 36), 0xff); + aic3111_write (codec, (128 + 37), 0xff); + aic3111_write (codec, (128 + 40), 0x02); + aic3111_write (codec, (128 + 41), 0x02); + } + + return 0; +} + +/* + *---------------------------------------------------------------------------- + * Function : aic3111_power_speaker + * Purpose : + * parameter: on = 1: power up; + * on = 0: power dn; + * xjq@rock-chips.com + *---------------------------------------------------------------------------- + */ +static int aic3111_power_speaker (bool on) +{ + struct snd_soc_codec *codec = aic3111_codec; + + AIC_DBG("Enter %s and line %d\n",__FUNCTION__,__LINE__); + + if (on == POWER_STATE_ON) { +#if 0 +// aic3111_write(codec, (128 + 32), 0x86); + aic3111_write(codec, (128 + 32), 0xc6); + aic3111_write(codec, (128 + 30), 0x00); +// aic3111_write(codec, (128 + 38), 0x08); //set left speaker analog gain to -4.88db + aic3111_write(codec, (128 + 38), 0x16); //set left speaker analog gain to -4.88db + aic3111_write(codec, (128 + 39), 0x16); //Right Analog Vol to SPR +// aic3111_write(codec, (128 + 38), 0x7f); //set left speaker analog gain to -4.88db +// aic3111_write(codec, (128 + 39), 0x7f); //Right Analog Vol to SPR + aic3111_write(codec, (128 + 42), 0x1d); //set left speaker driver gain to 12db + aic3111_write(codec, (128 + 43), 0x1d); //bit3-4 output stage gain +// aic3111_write(codec, (128 + 43), 0x00); //bit3-4 output stage gain + aic3111_write(codec, (37), 0x98); + +#if 1 /* DRC */ + aic3111_write(codec, (60), 0x02); //select PRB_P2 + aic3111_write(codec, (68), 0x61); //enable left and right DRC, set DRC threshold to -3db, set DRC hystersis to 1db + aic3111_write(codec, (69), 0x00); //set hold time disable + aic3111_write(codec, (70), 0x5D); //set attack time to 0.125db per sample period and decay time to 0.000488db per sample +#endif +#endif +#if 1 + aic3111_write(codec, (63), 0xfc); + aic3111_write(codec, (128 + 32), 0xc6); + aic3111_write(codec, (128 + 30), 0x00); + aic3111_write(codec, (128 + 39), 0x08); //set left speaker analog gain to -4.88db + aic3111_write(codec, (128 + 38), 0x08); //Right Analog Vol to SPR + aic3111_write(codec, (128 + 43), 0x0D); //set left speaker driver gain to 12db + aic3111_write(codec, (128 + 42), 0x0D); //bit3-4 output stage gain + aic3111_write(codec, (37), 0x99); + +#if 1 /* DRC */ + aic3111_write(codec, (60), 0x02); //select PRB_P2 + aic3111_write(codec, (68), 0x61); //enable left and right DRC, set DRC threshold to -3db, set DRC hystersis to 1db + aic3111_write(codec, (69), 0x00); //set hold time disable + aic3111_write(codec, (70), 0x5D); //set attack time to 0.125db per sample period and decay time to 0.000488db per sample +#endif +#endif + } else if (on == POWER_STATE_OFF) { + + aic3111_write(codec, (68), 0x01); //disable DRC + aic3111_write(codec, (128 + 32), 0x06); + aic3111_write(codec, (128 + 30), 0x00); + aic3111_write(codec, (128 + 38), 0xff); + aic3111_write(codec, (128 + 39), 0xff); + aic3111_write(codec, (128 + 42), 0x00); + aic3111_write(codec, (128 + 43), 0x00); + aic3111_write(codec, (37), 0x00); + } + + return 0; +} + +/* + *---------------------------------------------------------------------------- + * Function : aic3111_HS_switch + * Purpose : This function is to initialise the AIC3111 driver + * In PLAYBACK, switch between HP and SPK app. + * parameter: on = 1: SPK power up & HP power dn; + * on = 0: HP power up & SPK power dn; + * xjq@rock-chips.com + *---------------------------------------------------------------------------- + */ +static int aic3111_HS_switch (bool on) +{ + AIC_DBG("enter %s and line %d\n",__FUNCTION__,__LINE__); + + if (POWER_STATE_SW_SPK == on) { + + //aic3111_power_headphone (POWER_STATE_OFF); + aic3111_power_speaker (POWER_STATE_ON); + } else if (POWER_STATE_SW_HP == on) { + + aic3111_power_speaker (POWER_STATE_OFF); + //aic3111_power_headphone (POWER_STATE_ON); + + //aic3111_power_speaker (POWER_STATE_ON); + //aic3111_power_headphone (POWER_STATE_OFF); + } + + return 0; +} + +/* + *---------------------------------------------------------------------------- + * Function : aic3111_SPK_HS_powerdown + * Purpose : This function is to power down HP and SPK. + * xjq@rock-chips.com + *---------------------------------------------------------------------------- + */ +static int aic3111_SPK_HS_powerdown (void) +{ + AIC_DBG("enter %s and line %d\n",__FUNCTION__,__LINE__); + + //aic3111_power_headphone (POWER_STATE_OFF); + aic3111_power_speaker (POWER_STATE_OFF); +// aic3111_power_speaker (POWER_STATE_ON); + + return 0; +} + +/* + *---------------------------------------------------------------------------- + * Function : aic3111_power_init + * Purpose : pll clock setting + * parameter: on = 1: power up; + * on = 0: power dn; + * xjq@rock-chips.com + *---------------------------------------------------------------------------- + */ +static void aic3111_power_init (void) +{ + struct snd_soc_codec *codec = aic3111_codec; + + AIC_DBG("enter %s and line %d\n",__FUNCTION__,__LINE__); + + if (!(aic3111_current_status & AIC3110_IS_INITPOWER_ON)) { + + AIC_DBG ("CODEC::%s\n", __FUNCTION__); + + aic3111_write(codec, (128 + 46), 0x0b); + aic3111_write(codec, (128 + 35), 0x44); + aic3111_write(codec, (4), 0x03); + aic3111_write(codec, (29), 0x01); + aic3111_write(codec, (48), 0xC0); + aic3111_write(codec, (51), 0x14); + aic3111_write(codec, (67), 0x82); + + aic3111_current_status |= AIC3110_IS_INITPOWER_ON; + } + + return; +} + +/* + *---------------------------------------------------------------------------- + * Function : aic3111_power_playback + * Purpose : + * parameter: on = 1: power up; + * on = 0: power dn; + * xjq@rock-chips.com + *---------------------------------------------------------------------------- + */ +static int aic3111_power_playback (bool on) +{ + struct snd_soc_codec *codec = aic3111_codec; + + AIC_DBG ("CODEC::%s>>>>>>%d\n", __FUNCTION__, on); + gpio_set_value(RK29_PIN6_PB5, GPIO_LOW); + aic3111_power_init(); + + if ((on == POWER_STATE_ON) && + !(aic3111_current_status & AIC3110_IS_PLAYBACK_ON)) { +// if(1){ + //gpio_set_value(RK29_PIN6_PB5, GPIO_HIGH); + + /****open HPL and HPR*******/ + //aic3111_write(codec, (63), 0xfc); + msleep(10); + aic3111_write(codec, (65), 0x00); //LDAC VOL + aic3111_write(codec, (66), 0x00); //RDAC VOL + aic3111_write (aic3111_codec, (63), 0xd4); +// aic3111_write(codec, (128 + 35), 0x88); + + //aic3111_write (aic3111_codec, (68), 0x01); //disable DRC + //aic3111_write (aic3111_codec, (128 + 31), 0xc4); + aic3111_write (aic3111_codec, (128 + 44), 0x00); + //aic3111_write (aic3111_codec, (128 + 36), 0x28); //Left Analog Vol to HPL + //aic3111_write (aic3111_codec, (128 + 37), 0x28); //Right Analog Vol to HPL + aic3111_write (codec, (128 + 40), 0x06); //HPL driver PGA + aic3111_write (codec, (128 + 41), 0x06); //HPR driver PGA + //aic3111_write (aic3111_codec, (128 + 40), 0x4f); //HPL driver PGA + //aic3111_write (aic3111_codec, (128 + 41), 0x4f); //HPR driver PGA + //printk("HP INIT~~~~~~~~~~~~~~~~~~~~~~~~~`\n"); + /***************************/ + + aic3111_HS_switch(isHSin); + + aic3111_write(codec, (65), 0x10); //LDAC VOL to +8db + aic3111_write(codec, (66), 0x10); //RDAC VOL to +8db + msleep(10); + aic3111_write(codec, (64), 0x00); + + aic3111_current_status |= AIC3110_IS_PLAYBACK_ON; + + } else if ((on == POWER_STATE_OFF) && + (aic3111_current_status & AIC3110_IS_PLAYBACK_ON)) { + + aic3111_write(codec, (68), 0x01); //disable DRC + aic3111_write(codec, (64), 0x0c); + aic3111_write(codec, (63), 0x00); + aic3111_write(codec, (65), 0x00); //LDAC VOL + aic3111_write(codec, (66), 0x00); //RDAC VOL + + aic3111_SPK_HS_powerdown(); + + aic3111_current_status &= ~AIC3110_IS_PLAYBACK_ON; + } + //mdelay(800); + gpio_set_value(RK29_PIN6_PB5, GPIO_HIGH); + + return 0; +} + +/* + *---------------------------------------------------------------------------- + * Function : aic3111_power_capture + * Purpose : + * parameter: on = 1: power up; + * on = 0: power dn; + * xjq@rock-chips.com + *---------------------------------------------------------------------------- + */ +static int aic3111_power_capture (bool on) +{ + struct snd_soc_codec *codec = aic3111_codec; + + AIC_DBG ("CODEC::%s>>>>>>%d\n", __FUNCTION__, on); + + aic3111_power_init(); + + if ((on == POWER_STATE_ON) && + !(aic3111_current_status & AIC3110_IS_CAPTURE_ON)) { + aic3111_write(codec, (64), 0x0c); + msleep(10); + + aic3111_write(codec, (61), 0x0b); + aic3111_write(codec, (128 + 47), 0x00); //MIC PGA 0x80:0dB 0x14:10dB 0x28:20dB 0x3c:30dB 0x77:59dB + aic3111_write(codec, (128 + 48), 0x80); //MIC1LP\MIC1LM RIN = 10. + aic3111_write(codec, (128 + 49), 0x20); + aic3111_write(codec, (82), 0x00); //D7=0:0: ADC channel not muted + aic3111_write(codec, (83), 0x1A); //ADC Digital Volume 0 dB + aic3111_write(codec, (81), 0x80); //D7=1:ADC channel is powered up. + +#if 1 + /*configure register to creat a filter 20~3.5kHz*/ + + aic3111_write(codec, (128*4 + 14), 0x7f); + aic3111_write(codec, (128*4 + 15), 0x00); + aic3111_write(codec, (128*4 + 16), 0xc0); + aic3111_write(codec, (128*4 + 17), 0x18); + aic3111_write(codec, (128*4 + 18), 0x00); + + aic3111_write(codec, (128*4 + 19), 0x00); + aic3111_write(codec, (128*4 + 20), 0x3f); + aic3111_write(codec, (128*4 + 21), 0x00); + aic3111_write(codec, (128*4 + 22), 0x00); + aic3111_write(codec, (128*4 + 23), 0x00); + + aic3111_write(codec, (128*4 + 24), 0x05); + aic3111_write(codec, (128*4 + 25), 0xd2); + aic3111_write(codec, (128*4 + 26), 0x05); + aic3111_write(codec, (128*4 + 27), 0xd2); + aic3111_write(codec, (128*4 + 28), 0x05); + + aic3111_write(codec, (128*4 + 29), 0xd2); + aic3111_write(codec, (128*4 + 30), 0x53); + aic3111_write(codec, (128*4 + 31), 0xff); + aic3111_write(codec, (128*4 + 32), 0xc0); + aic3111_write(codec, (128*4 + 33), 0xb5); +#endif + msleep(10); + aic3111_write(codec, (64), 0x00); + aic3111_current_status |= AIC3110_IS_CAPTURE_ON; + + } else if ((on == POWER_STATE_OFF) && + (aic3111_current_status & AIC3110_IS_CAPTURE_ON)) { + + aic3111_write(codec, (61), 0x00); + aic3111_write(codec, (128 + 47), 0x00); //MIC PGA AOL + aic3111_write(codec, (128 + 48), 0x00); + aic3111_write(codec, (128 + 50), 0x00); + aic3111_write(codec, (81), 0x00); + aic3111_write(codec, (82), 0x80); + aic3111_write(codec, (83), 0x00); //ADC VOL + aic3111_write(codec, (86), 0x00); + + aic3111_current_status &= ~AIC3110_IS_CAPTURE_ON; + } + + return 0; +} + +/* + *---------------------------------------------------------------------------- + * Function : aic3111_powerdown + * Purpose : This function is to power down codec. + * + *---------------------------------------------------------------------------- + */ +static void aic3111_powerdown (void) +{ + AIC_DBG ("CODEC::%s\n", __FUNCTION__); + + if (aic3111_current_status != AIC3110_IS_SHUTDOWN) { + aic3111_soft_reset();//sai + aic3111_current_status = AIC3110_IS_SHUTDOWN; + } +} + +/* + *---------------------------------------------------------------------------- + * Function : aic3111_work + * Purpose : This function is to respond to HPDET handle. + * + *---------------------------------------------------------------------------- + */ +static void aic3111_work (struct work_struct *work) +{ + AIC_DBG("Enter %s and line %d\n",__FUNCTION__,__LINE__); + + switch (aic3111_work_type) { + case AIC3110_POWERDOWN_NULL: + break; + case AIC3110_POWERDOWN_PLAYBACK: + aic3111_power_playback(POWER_STATE_OFF); + break; + case AIC3110_POWERDOWN_CAPTURE: + aic3111_power_capture(POWER_STATE_OFF); + break; + case AIC3110_POWERDOWN_PLAYBACK_CAPTURE: + aic3111_powerdown();//sai + break; + default: + break; + } + + aic3111_work_type = AIC3110_POWERDOWN_NULL; +} + +/* + *---------------------------------------------------------------------------- + * Function : aic3111_startup + * Purpose : This function is to start up codec. + * + *---------------------------------------------------------------------------- + */ +static int aic3111_startup (struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ +/* + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec *codec = aic3111_codec; +*/ + + AIC_DBG ("CODEC::%s----substream->stream:%s \n", __FUNCTION__, + substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? "PLAYBACK":"CAPTURE"); + + cancel_delayed_work_sync(&delayed_work); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + + aic3111_power_playback(POWER_STATE_ON); + + } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + + aic3111_power_capture(POWER_STATE_ON); + } + + return 0; +} + +/* + *---------------------------------------------------------------------------- + * Function : aic3111_startup + * Purpose : This function is to shut down codec. + * + *---------------------------------------------------------------------------- + */ +static void aic3111_shutdown (struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_dai *codec_dai = dai; + + AIC_DBG ("CODEC::%s----substream->stream:%s \n", __FUNCTION__, + substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? "PLAYBACK":"CAPTURE"); + + if (!codec_dai->capture_active && !codec_dai->playback_active) { + + cancel_delayed_work_sync(&delayed_work); + + /* If codec is already shutdown, return */ + if (aic3111_current_status == AIC3110_IS_SHUTDOWN) + return; + + AIC_DBG ("CODEC::Is going to power down aic3111\n"); + + aic3111_work_type = AIC3110_POWERDOWN_PLAYBACK_CAPTURE; + + /* If codec is useless, queue work to close it */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + queue_delayed_work(aic3111_workq, &delayed_work, + msecs_to_jiffies(1000)); + } + else { + queue_delayed_work(aic3111_workq, &delayed_work, + msecs_to_jiffies(3000)); + } + } + else if (codec_dai->capture_active && !codec_dai->playback_active) { + + cancel_delayed_work_sync(&delayed_work); + + aic3111_work_type = AIC3110_POWERDOWN_PLAYBACK; + + /* Turn off playback and keep record on */ + queue_delayed_work(aic3111_workq, &delayed_work, + msecs_to_jiffies(1000)); + } + else if (!codec_dai->capture_active && codec_dai->playback_active) { + + cancel_delayed_work_sync(&delayed_work); + + aic3111_work_type = AIC3110_POWERDOWN_CAPTURE; + + /* Turn off record and keep playback on */ + queue_delayed_work(aic3111_workq, &delayed_work, + msecs_to_jiffies(3000)); + } +} + +/* + *---------------------------------------------------------------------------- + * Function : aic3111_trigger + * Purpose : This function is to respond to playback trigger. + * + *---------------------------------------------------------------------------- + */ +static int aic3111_trigger(struct snd_pcm_substream *substream, + int status, + struct snd_soc_dai *dai) +{ + struct snd_soc_dai *codec_dai = dai; + + if(status == 0) + { + gpio_set_value(RK29_PIN6_PB5, GPIO_LOW); + mdelay(10); + } + + AIC_DBG ("CODEC::%s----status = %d substream->stream:%s \n", __FUNCTION__, status, + substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? "PLAYBACK":"CAPTURE"); + + if (status == 1 || status == 0) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + codec_dai->playback_active = status; + } else { + codec_dai->capture_active = status; + } + } + + return 0; +} + +static struct snd_soc_dai_ops aic3111_dai_ops = { + .hw_params = aic3111_hw_params, + .digital_mute = aic3111_mute, + .set_sysclk = aic3111_set_dai_sysclk, + .set_fmt = aic3111_set_dai_fmt, + .startup = aic3111_startup, + .shutdown = aic3111_shutdown, + .trigger = aic3111_trigger, +}; + +static struct snd_soc_dai_driver aic3111_dai[] = { + { + .name = "AIC3111 HiFi", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = AIC3111_RATES, + .formats = AIC3111_FORMATS, + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = AIC3111_RATES, + .formats = AIC3111_FORMATS, + }, + .ops = &aic3111_dai_ops, + }, +}; + +#ifdef AIC3111_DEBUG +static struct class *aic3111_debug_class = NULL; +static int reg_128_39 = 12, reg_128_43 = 0; +static int i=52;j=1,k=0; +static int CheckCommand(const char __user *buffer) +{ + switch(*buffer) { + case '1': + if (*(buffer + 1) == '+') { + if (reg_128_39 < 12) { + if (reg_128_39 % 2 == 0) + printk("write reg 128 + 39 vol + : %ddB -> -%d.5dB\n", (reg_128_39 - 12) / 2, (11 - reg_128_39) / 2); + else + printk("write reg 128 + 39 vol + : -%d.5dB -> %ddB\n", (12 - reg_128_39) / 2, (reg_128_39 - 11) / 2); + reg_128_39++; + aic3111_write(aic3111_codec, (128 + 39), 0x04 + (12 - reg_128_39)); + } else { + printk("128 + 39 max vol 0dB\n"); + } + } else if (*(buffer + 1) == '-') { + if (reg_128_39 > 0) { + if (reg_128_39 % 2 == 0) + printk("write reg 128 + 39 vol - : %ddB -> -%d.5dB\n", (reg_128_39 - 12) / 2, (13 - reg_128_39) / 2); + else + printk("write reg 128 + 39 vol - : -%d.5dB -> %ddB\n", (12 - reg_128_39) / 2, (reg_128_39 - 13) / 2); + reg_128_39--; + aic3111_write(aic3111_codec, (128 + 39), 0x08 + (12 - reg_128_39)); + } else { + printk("128 + 39 min vol -6dB\n"); + } + } + break; + case '2': + if (*(buffer + 1) == '+') { + if (reg_128_43 < 2) { + printk("write reg 128 + 43 vol + : %ddB -> %ddB\n", (reg_128_43) * 6, (reg_128_43 + 1) * 6); + reg_128_43++; + aic3111_write(aic3111_codec, (128 + 43), 0x04 + ((reg_128_43 + 1) << 3)); + } else { + printk("128 + 43 max vol 12dB\n"); + } + } else if (*(buffer + 1) == '-') { + if (reg_128_43 > 0) { + printk("write reg 128 + 43 vol - : %ddB -> %ddB\n", (reg_128_43) * 6, (reg_128_43 - 1) * 6); + reg_128_43--; + aic3111_write(aic3111_codec, (128 + 43), 0x04 + ((reg_128_43 + 1) << 3)); + } else { + printk("128 + 43 min vol 0dB\n"); + } + } + break; + case 'o': + aic3111_write(aic3111_codec, (128 + 39), 0x08 + (12 - reg_128_39)); + aic3111_write(aic3111_codec, (128 + 43), 0x04 + ((reg_128_43 + 1) << 3)); + case 'l': + if (reg_128_39 % 2 == 0) + printk("reg 128 + 43 vol : %ddB reg 128 + 39 vol : %ddB\n", (reg_128_43) * 6, (reg_128_39 - 12) / 2); + else + printk("reg 128 + 43 vol : %ddB reg 128 + 39 vol : -%d.5dB\n", (reg_128_43) * 6, (12 - reg_128_39) / 2); + break; + case 's': + aic3111_power_speaker (POWER_STATE_ON); + break; + + case 'h': + aic3111_power_headphone (POWER_STATE_ON); + break; + + case 'q': + aic3111_power_speaker (POWER_STATE_OFF); + break; + + case 'w': + aic3111_power_headphone (POWER_STATE_OFF); + break; + + case 'a': + i--; + gpio_set_value(RK29_PIN6_PB5, GPIO_HIGH); + //printk("reg[128+39]=0x%x\n",aic3111_read(aic3111_codec,(128 + 39))); + printk("-db add\n"); + break; + case 'r': + i++; + gpio_set_value(RK29_PIN6_PB5, GPIO_LOW); + //printk("reg[128+39]=0x%x\n",aic3111_read(aic3111_codec,(128 + 39))); + printk("-db down\n"); + break; + case 't': + + printk("PB5 = %d\n",gpio_get_value(RK29_PIN6_PB5)); + break; + + case 'z': + j++; + aic3111_write(aic3111_codec, (66), j); + printk("DAC db add\n"); + printk("reg[66]=0x%x\n",aic3111_read(aic3111_codec,66)); + break; + case 'x': + j--; + aic3111_write(aic3111_codec, (66), j); + printk("DAC db down\n"); + printk("reg[66]=0x%x\n",aic3111_read(aic3111_codec,66)); + break; + + case 'c': + j--; + aic3111_write(aic3111_codec, (63), 0xfc); + printk("reg[63]=0x%x\n",aic3111_read(aic3111_codec,66)); + break; + + case 'n': + k++; + if(k==1) + { + aic3111_write(aic3111_codec, (128 + 40), 0x0e); + aic3111_write(aic3111_codec, (128 + 41), 0x0e); + printk("HPR and HPL 1 DB\n",k); + } + if(k==2) + { + aic3111_write(aic3111_codec, (128 + 40), 0x1e); + aic3111_write(aic3111_codec, (128 + 40), 0x1e); + printk("HPR and HPL 3 DB\n",k); + } + if(k==3) + { + aic3111_write(aic3111_codec, (128 + 40), 0x2e); + aic3111_write(aic3111_codec, (128 + 40), 0x2e); + printk("HPR and HPL 5 DB\n",k); + } + break; + + case 'm': + k--; + if(k==1) + { + aic3111_write(aic3111_codec, (128 + 40), 0x0e); + aic3111_write(aic3111_codec, (128 + 41), 0x0e); + printk("HPR and HPL 1 DB\n",k); + } + if(k==2) + { + aic3111_write(aic3111_codec, (128 + 40), 0x1e); + aic3111_write(aic3111_codec, (128 + 40), 0x1e); + printk("HPR and HPL 3 DB\n",k); + } + if(k==3) + { + aic3111_write(aic3111_codec, (128 + 40), 0x2e); + aic3111_write(aic3111_codec, (128 + 40), 0x2e); + printk("HPR and HPL 5 DB\n",k); + } + break; + + + default: + printk("Please press '1' '2' 'o' 'l' !\n"); + break; + } + return 0; +} + +static int aic3111_proc_write(struct file *file, const char __user *buffer, + unsigned long count, void *data) +{ + if (CheckCommand(buffer) != 0) { + printk("Write proc error !\n"); + return -1; + } + + return sizeof(buffer); +} +static const struct file_operations aic3111_proc_fops = { + .owner = THIS_MODULE, + .write = aic3111_proc_write, +}; + +static int aic3111_proc_init(void) { + + struct proc_dir_entry *aic3111_proc_entry; + //printk("!!!!!!!!!!!!!!!!!!!!\n"); + aic3111_proc_entry = create_proc_entry("driver/aic3111_ts", 0777, NULL); + + if (aic3111_proc_entry != NULL) { + + aic3111_proc_entry->write_proc = aic3111_proc_write; + + return -1; + }else { + printk("create proc error !\n"); + } + + return 0; +} +#endif + +struct delayed_work aic3111_speaker_delayed_work; +int speakeronoff; + +static void aic3111_speaker_delayed_work_func(struct work_struct *work) +{ + struct snd_soc_codec *codec = aic3111_codec; + + if (aic3111_current_status & AIC3110_IS_PLAYBACK_ON){ + if(speakeronoff) { + + //aic3111_write(codec, (128 + 32), 0xc6); + //printk("reg 128+32 = %x\n"aic3111_read(codec, (128 + 32))); + isHSin = 0; + //gpio_set_value(RK29_PIN6_PB5, GPIO_LOW); + aic3111_power_speaker(POWER_STATE_OFF); + gpio_set_value(RK29_PIN6_PB5, GPIO_HIGH); + //aic3111_power_headphone(POWER_STATE_ON); + //aic3111_write(codec, (128 + 35), 0x88); + printk("now hp sound\n"); + } else { + + //aic3111_power_speaker(POWER_STATE_ON); + + isHSin = 1; + //aic3111_power_headphone(POWER_STATE_OFF); + gpio_set_value(RK29_PIN6_PB5, GPIO_LOW); + aic3111_power_speaker(POWER_STATE_ON); + aic3111_write(codec, (128 + 35), 0x44); + aic3111_write(codec, (63), 0xfc); + printk("now spk sound\n"); + + } + } + //printk("----------------------------mma7660_work_func------------------------\n"); + +} + +/**for check hp or spk****/ +static int speaker_timer(unsigned long _data) +{ + struct speaker_data *spk = (struct speaker_data *)_data; + int new_status; + + if (gpio_get_value(RK29_PIN6_PB6) == 0) { + new_status = HP; + isHSin = 0; + //printk("hp now\n"); + if(old_status != new_status) + { + old_status = new_status; + // printk("new_status = %d,old_status = %d\n",new_status,old_status); + old_status = new_status; + + schedule_delayed_work(&aic3111_speaker_delayed_work,msecs_to_jiffies(30)); + speakeronoff=1; + //printk("HS RUN!!!!!!!!!!\n"); + } + } + + if (gpio_get_value(RK29_PIN6_PB6) == 1) { + new_status = SPK; + isHSin = 1; + //printk("speak now\n"); + if(old_status != new_status) + { + old_status = new_status; + printk("new_status = %d,old_status = %d\n",new_status,old_status); + old_status = new_status; + + schedule_delayed_work(&aic3111_speaker_delayed_work,msecs_to_jiffies(30)); + speakeronoff=0; + //printk("HS RUN!!!!!!!!!!\n"); + } + } + + mod_timer(&spk->timer, jiffies + msecs_to_jiffies(200)); + + return 0; +} +/* + *---------------------------------------------------------------------------- + * Function : aic3111_probe + * Purpose : This is first driver function called by the SoC core driver. + * + *---------------------------------------------------------------------------- + */ +static int aic3111_probe (struct snd_soc_codec *codec) +{ + int ret = 0;//, flags, hp_det_irq; + + codec->hw_write = (hw_write_t) i2c_master_send; + codec->control_data = aic3111_i2c; + aic3111_codec = codec; + +#if 1 + gpio_set_value(RK29_PIN6_PB6,1); + struct speaker_data *spk; + + spk = kzalloc(sizeof(struct speaker_data), GFP_KERNEL); + if (spk == NULL) { + printk("Allocate Memory Failed!\n"); + ret = -ENOMEM; + //goto exit_gpio_free; + } + + setup_timer(&spk->timer, speaker_timer, (unsigned long)spk); + mod_timer(&spk->timer, jiffies + JACK_DET_ADLOOP); + INIT_DELAYED_WORK(&aic3111_speaker_delayed_work, aic3111_speaker_delayed_work_func); + +/*********************/ + //pio_set_value(RK29_PIN6_PB5, GPIO_LOW); + //aic3111_power_speaker(POWER_STATE_OFF); + //aic3111_power_headphone(POWER_STATE_ON); +#endif + + aic3111_workq = create_freezable_workqueue("aic3111"); + if (aic3111_workq == NULL) { + return -ENOMEM; + } + +/* INIT_DELAYED_WORK (&aic3111_hpdet_work, aic3111_hpdet_work_handle); + if (gpio_request (HP_DET_PIN, "hp_det")) { + gpio_free (HP_DET_PIN); + printk ("CODEC::tlv3110 hp det pin request error\n"); + } + else { + gpio_direction_input (HP_DET_PIN); + gpio_pull_updown (HP_DET_PIN, PullDisable); + hp_det_irq = gpio_to_irq (HP_DET_PIN); + isHSin = gpio_get_value (HP_DET_PIN); + + flags = isHSin ? IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING; + ret = request_irq (hp_det_irq, aic3111_hpdet_isr, flags, "hpdet", codec); + if (ret < 0) { + printk ("CODEC::request hp_det_irq error\n"); + } + } +*/ + /* Just Reset codec */ + aic3111_soft_reset(); + gpio_set_value(RK29_PIN6_PB5, GPIO_LOW); + msleep(10); + aic3111_write (aic3111_codec, (68), 0x01); //disable DRC + aic3111_write (aic3111_codec, (128 + 31), 0xc4); + aic3111_write (aic3111_codec, (128 + 36), 0x28); //Left Analog Vol to HPL + aic3111_write (aic3111_codec, (128 + 37), 0x28); //Right Analog Vol to HPL + aic3111_write (aic3111_codec, (128 + 40), 0x4f); //HPL driver PGA + aic3111_write (aic3111_codec, (128 + 41), 0x4f); //HPR driver PGA + +#ifdef AIC3111_DEBUG + aic3111_proc_init(); +#endif + + aic3111_set_bias_level (codec, SND_SOC_BIAS_STANDBY); + + return 0; +} + +/* + *---------------------------------------------------------------------------- + * Function : aic3111_remove + * Purpose : to remove aic3111 soc device + * + *---------------------------------------------------------------------------- + */ +static int aic3111_remove (struct snd_soc_codec *codec) +{ + AIC_DBG ("CODEC::%s\n", __FUNCTION__); + + /* Disable HPDET irq */ + //disable_irq_nosync (HP_DET_PIN); + + /* power down chip */ + aic3111_set_bias_level (codec, SND_SOC_BIAS_OFF); + + return 0; +} + +/* + *---------------------------------------------------------------------------- + * Function : aic3111_suspend + * Purpose : This function is to suspend the AIC3111 driver. + * + *---------------------------------------------------------------------------- + */ +static int aic3111_suspend (struct snd_soc_codec *codec, pm_message_t state) +{ + + AIC_DBG ("CODEC::%s\n", __FUNCTION__); + + aic3111_set_bias_level (codec, SND_SOC_BIAS_STANDBY); + + aic3111_soft_reset();//sai + + return 0; +} + +/* + *---------------------------------------------------------------------------- + * Function : aic3111_resume + * Purpose : This function is to resume the AIC3111 driver + * + *---------------------------------------------------------------------------- + */ +static int aic3111_resume (struct snd_soc_codec *codec) +{ + //isHSin = gpio_get_value(HP_DET_PIN); + aic3111_set_bias_level (codec, SND_SOC_BIAS_STANDBY); + //aic3111_set_bias_level(codec, codec->suspend_bias_level); + + return 0; +} + +/* + *---------------------------------------------------------------------------- + * @struct snd_soc_codec_device | + * This structure is soc audio codec device sturecute which pointer + * to basic functions aic3111_probe(), aic3111_remove(), + * aic3111_suspend() and aic3111_resume() + *---------------------------------------------------------------------------- + */ +static struct snd_soc_codec_driver soc_codec_dev_aic3111 = { + .probe = aic3111_probe, + .remove = aic3111_remove, + .suspend = aic3111_suspend, + .resume = aic3111_resume, + .set_bias_level = aic3111_set_bias_level, + .reg_cache_size = ARRAY_SIZE(aic31xx_reg), + .reg_word_size = sizeof(u16), + .reg_cache_default = aic31xx_reg, + .reg_cache_step = 1, +}; + +static const struct i2c_device_id tlv320aic3111_i2c_id[] = { + { "aic3111", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tlv320aic3111_i2c_id); + +static int tlv320aic3111_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct aic3111_priv *aic3111; + int ret; + + aic3111 = kzalloc(sizeof(struct aic3111_priv), GFP_KERNEL); + if (NULL == aic3111) + return -ENOMEM; + + aic3111_i2c = i2c; + + i2c_set_clientdata(i2c, aic3111); + + aic3111_privdata = aic3111; + + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_aic3111, + aic3111_dai, ARRAY_SIZE(aic3111_dai)); + if (ret < 0) + kfree(aic3111); + + return ret; +} + +static __devexit int tlv320aic3111_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + kfree(i2c_get_clientdata(client)); + return 0; +} + +struct i2c_driver tlv320aic3111_i2c_driver = { + .driver = { + .name = "AIC3111", + .owner = THIS_MODULE, + }, + .probe = tlv320aic3111_i2c_probe, + .remove = __devexit_p(tlv320aic3111_i2c_remove), + .id_table = tlv320aic3111_i2c_id, +}; + +static int __init tlv320aic3111_init (void) +{ + return i2c_add_driver(&tlv320aic3111_i2c_driver); +} + +static void __exit tlv320aic3111_exit (void) +{ + i2c_del_driver(&tlv320aic3111_i2c_driver); +} + +module_init (tlv320aic3111_init); +module_exit (tlv320aic3111_exit); + +#ifdef CONFIG_PROC_FS +#include +#include +static int proc_3110_reg_show (struct seq_file *s, void *v) +{ + struct snd_soc_codec *codec = aic3111_codec; + int reg; + u8 *cache = codec->reg_cache; + + seq_printf (s, "========3110register========\n"); + for (reg = 0; reg < 256; reg++) { + if (reg == 0) seq_printf (s, "Page 0\n"); + if (reg == 128) seq_printf (s, "\nPage 1\n"); + if (reg%8 == 0 && reg != 0 && reg != 128) seq_printf (s, "\n"); + seq_printf (s, "[%d]0x%02x, ",reg,aic3111_read (codec, reg)); + } + seq_printf (s, "\n========3110cache========\n"); + for (reg = 0; reg < codec->reg_size; reg++) { + if (reg == 0) seq_printf (s, "Page 0\n"); + if (reg == 128) seq_printf (s, "\nPage 1\n"); + if (reg%16 == 0 && reg != 0 && reg != 128) seq_printf (s, "\n"); + seq_printf (s, "0x%02x, ",cache[reg]); + } + printk ("\n==========================\n"); + return 0; +} + +static int proc_3110_reg_open (struct inode *inode, struct file *file) +{ + return single_open (file, proc_3110_reg_show, NULL); +} + +static const struct file_operations proc_3110_reg_fops = { + .open = proc_3110_reg_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int __init codec_proc_init (void) +{ + proc_create ("aic3110_register", 0, NULL, &proc_3110_reg_fops); + + return 0; +} +late_initcall (codec_proc_init); +#endif /* CONFIG_PROC_FS */ + +MODULE_DESCRIPTION (" ASoC TLV320AIC3111 codec driver "); +MODULE_AUTHOR (" Jaz B John "); +MODULE_LICENSE ("GPL"); + diff --git a/sound/soc/codecs/tlv320aic3111.h b/sound/soc/codecs/tlv320aic3111.h new file mode 100644 index 000000000000..7b5b17092db7 --- /dev/null +++ b/sound/soc/codecs/tlv320aic3111.h @@ -0,0 +1,621 @@ +/* + * linux/sound/soc/codecs/tlv320aic3111.h + * + * Copyright (C) 2010 Texas Instruments, Inc. + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * History: + * + * Rev 0.1 ASoC driver support Mistral 14-04-2010 + * + * Rev 0.2 Updated based Review Comments Mistral 29-06-2010 + * + * Rev 0.3 Updated for Codec Family Compatibility 12-07-2010 + */ + +#ifndef _TLV320AIC3111_H +#define _TLV320AIC3111_H + +/* #define AUDIO_NAME "aic3111" +#define AIC3111_VERSION "0.1" +*/ + + +/* + ******************AIC31xx CODEC SUPPORT ***************** + * THE CURRENT CODE-BASE SUPPORTS BUILDING of the CODEC + * DRIVER FOR AIC3111, AIC3110, AIC3100 and AIC3120. + * PLEASE NOTE THAT FOR EACH OF THE ABOVE CODECS, the + * Linux Developer needs to enable a particular flag in + * this header file before performing a build of the + * Audio Codec Driver. + ********************************************************* +*/ + +//#define AIC3111_CODEC_SUPPORT +#define AIC3110_CODEC_SUPPORT +//#define AIC3100_CODEC_SUPPORT +//#define AIC3120_CODEC_SUPPORT + +/* NOTE THAT AIC3110 and AIC3100 do not support miniDSP */ +#ifdef AIC3110_CODEC_SUPPORT +#undef CONFIG_MINI_DSP + +#define AUDIO_NAME "aic3110" +#define AIC3111_VERSION "0.1" + +#endif + +#ifdef AIC3100_CODEC_SUPPORT +#undef CONFIG_MINI_DSP + +#define AUDIO_NAME "aic3100" +#define AIC3111_VERSION "0.1" + +#endif + +#ifdef AIC3120_CODEC_SUPPORT +#undef CONFIG_MINI_DSP + +#define AUDIO_NAME "aic3120" +#define AIC3111_VERSION "0.1" + +#endif + +/* The user has a choice to enable or disable miniDSP code + * when building for AIC3111 Codec. + */ +#ifdef AIC3111_CODEC_SUPPORT +/* Macro enables or disables support for miniDSP in the driver */ +//#define CONFIG_MINI_DSP +#undef CONFIG_MINI_DSP + +#define AUDIO_NAME "aic3111" +#define AIC3111_VERSION "0.1" + +#endif + + +/* Enable slave / master mode for codec */ +//#define AIC3111_MCBSP_SLAVE +#undef AIC3111_MCBSP_SLAVE + +/* Enable register caching on write */ +#define EN_REG_CACHE + +/* Enable headset detection */ +#define HEADSET_DETECTION + +/* AIC3111 supported sample rate are 8k to 192k */ +#define AIC3111_RATES SNDRV_PCM_RATE_8000_192000 + +/* AIC3111 supports the word formats 16bits, 20bits, 24bits and 32 bits */ +#define AIC3111_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \ + | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) + +#define AIC3111_FREQ_12000000 12000000 +#define AIC3111_FREQ_24000000 24000000 +#define AIC3111_FREQ_11289600 11289600 + +/* AIC3111 register space */ +#define AIC31xx_CACHEREGNUM 384 + +/* Audio data word length = 16-bits (default setting) */ +#define AIC3111_WORD_LEN_16BITS 0x00 +#define AIC3111_WORD_LEN_20BITS 0x01 +#define AIC3111_WORD_LEN_24BITS 0x02 +#define AIC3111_WORD_LEN_32BITS 0x03 +#define DATA_LEN_SHIFT 4 + +/* sink: name of target widget */ +#define AIC3111_WIDGET_NAME 0 +/* control: mixer control name */ +#define AIC3111_CONTROL_NAME 1 +/* source: name of source name */ +#define AIC3111_SOURCE_NAME 2 + +/* D15..D8 aic3111 register offset */ +#define AIC3111_REG_OFFSET_INDEX 0 +/* D7...D0 register data */ +#define AIC3111_REG_DATA_INDEX 1 + +/* Serial data bus uses I2S mode (Default mode) */ +#define AIC3111_I2S_MODE 0x00 +#define AIC3111_DSP_MODE 0x01 +#define AIC3111_RIGHT_JUSTIFIED_MODE 0x02 +#define AIC3111_LEFT_JUSTIFIED_MODE 0x03 +#define AUDIO_MODE_SHIFT 6 + +/* number of codec specific register for configuration */ +#define NO_FEATURE_REGS 2 + +/* 8 bit mask value */ +#define AIC3111_8BITS_MASK 0xFF + +#ifndef AIC3120_CODEC_SUPPORT /* for AIC3111, AIC3110 and AIC3100 */ +/* ****************** Page 0 Registers **************************************/ +#define PAGE_SELECT 0 +#define RESET 1 +#define CLK_REG_1 4 +#define CLK_REG_2 5 +#define CLK_REG_3 6 +#define CLK_REG_4 7 +#define CLK_REG_5 8 +#define NDAC_CLK_REG 11 +#define MDAC_CLK_REG 12 +#define DAC_OSR_MSB 13 +#define DAC_OSR_LSB 14 +#define NADC_CLK_REG 18 +#define MADC_CLK_REG 19 +#define ADC_OSR_REG 20 +#define CLK_MUX_REG 25 +#define CLK_MVAL_REG 26 +#define INTERFACE_SET_REG_1 27 +#define DATA_SLOT_OFFSET 28 +#define INTERFACE_SET_REG_2 29 +#define BCLK_N_VAL 30 +#define INTERFACE_SET_REG_3 31 +#define INTERFACE_SET_REG_4 32 +#define INTERFACE_SET_REG_5 33 +#define I2C_BUS_COND 34 + +#define INTL_CRTL_REG_1 48 +#define INTL_CRTL_REG_2 49 +#define GPIO_CRTL_REG_1 51 + +#define DAC_PRB_SEL_REG 60 +#define ADC_PRB_SEL_REG 61 +#define DAC_CHN_REG 63 +#define DAC_MUTE_CTRL_REG 64 +#define LDAC_VOL 65 +#define RDAC_VOL 66 + +#define HEADSET_DETECT 67 + +#define DRC_CTL_REG_1 68 +#define DRC_CTL_REG_2 69 +#define DRC_CTL_REG_3 70 + +#define LEFT_BEEP_GEN 71 +#define RIGHT_BEEP_GEN 72 +#define BEEP_LENGTH_MSB 73 +#define BEEP_LENGTH_MID 74 +#define BEEP_LENGTH_LSB 75 +#define BEEP_SINX_MSB 76 +#define BEEP_SINX_LSB 77 +#define BEEP_COSX_MSB 78 +#define BEEP_COSX_LSB 79 + + +#define ADC_DIG_MIC 81 +#define ADC_FGA 82 +#define ADC_CGA 83 + +#define AGC_CTRL_1 86 +#define AGC_CTRL_2 87 +#define AGC_CTRL_3 88 +#define AGC_CTRL_4 89 +#define AGC_CTRL_5 90 +#define AGC_CTRL_6 91 +#define AGC_CTRL_7 92 +#define AGC_CTRL_8 93 + +#define ADC_DC_1 102 +#define ADC_DC_2 103 +#define PIN_VOL_CTRL 116 +#define PIN_VOL_GAIN 117 + + +/******************** Page 1 Registers **************************************/ +#define PAGE_1 128 +#define HEADPHONE_DRIVER (PAGE_1 + 31) +#define CLASSD_SPEAKER_AMP (PAGE_1 + 32) +#define HP_POP_CTRL (PAGE_1 + 33) +#define PGA_RAMP_CTRL (PAGE_1 + 34) +#define DAC_MIX_CTRL (PAGE_1 + 35) +#define L_ANLOG_VOL_2_HPL (PAGE_1 + 36) +#define R_ANLOG_VOL_2_HPR (PAGE_1 + 37) +#define L_ANLOG_VOL_2_SPL (PAGE_1 + 38) +#define R_ANLOG_VOL_2_SPR (PAGE_1 + 39) +#define HPL_DRIVER (PAGE_1 + 40) +#define HPR_DRIVER (PAGE_1 + 41) +#define SPL_DRIVER (PAGE_1 + 42) +#define SPR_DRIVER (PAGE_1 + 43) +#define HP_DRIVER_CTRL (PAGE_1 + 44) +#define MICBIAS_CTRL (PAGE_1 + 46) +#define MIC_PGA (PAGE_1 + 47) +#define MIC_GAIN (PAGE_1 + 48) +#define ADC_IP_SEL (PAGE_1 + 49) +#define CM_SET (PAGE_1 + 50) + +#define L_MICPGA_P (PAGE_1 + 52) +#define L_MICPGA_N (PAGE_1 + 54) +#define R_MICPGA_P (PAGE_1 + 55) +#define R_MICPGA_N (PAGE_1 + 57) + +/****************************************************************************/ +/****************************************************************************/ +#define BIT7 (1 << 7) +#define BIT6 (1 << 6) +#define BIT5 (1 << 5) +#define BIT4 (1 << 4) +#define BIT3 (1 << 3) +#define BIT2 (1 << 2) +#define BIT1 (1 << 1) +#define BIT0 (1 << 0) + +#define HP_UNMUTE BIT2 +#define HPL_UNMUTE BIT3 +#define ENABLE_DAC_CHN (BIT6 | BIT7) +#define ENABLE_ADC_CHN (BIT6 | BIT7) +#define BCLK_DIR_CTRL (BIT2 | BIT3) +#define CODEC_CLKIN_MASK 0x03 +#define MCLK_2_CODEC_CLKIN 0x00 +#define CODEC_MUX_VALUE 0X03 +/*Bclk_in selection*/ +#define BDIV_CLKIN_MASK 0x03 +#define DAC_MOD_CLK_2_BDIV_CLKIN BIT0 +#define SOFT_RESET 0x01 +#define PAGE0 0x00 +#define PAGE1 0x01 +#define BIT_CLK_MASTER BIT3 +#define WORD_CLK_MASTER BIT2 +#define HIGH_PLL BIT6 +#define ENABLE_PLL BIT7 +#define ENABLE_NDAC BIT7 +#define ENABLE_MDAC BIT7 +#define ENABLE_NADC BIT7 +#define ENABLE_MADC BIT7 +#define ENABLE_BCLK BIT7 +#define LDAC_2_LCHN BIT4 +#define RDAC_2_RCHN BIT2 +#define RDAC_2_RAMP BIT2 +#define LDAC_2_LAMP BIT6 +#define LDAC_CHNL_2_HPL BIT3 +#define RDAC_CHNL_2_HPR BIT3 +#define SOFT_STEP_2WCLK BIT0 +#define MUTE_ON 0x00 +#define DEFAULT_VOL 0x0 +//#define DEFAULT_VOL 0xAf +#define DISABLE_ANALOG BIT3 +#define LDAC_2_HPL_ROUTEON BIT3 +#define RDAC_2_HPR_ROUTEON BIT3 +#define LINEIN_L_2_LMICPGA_10K BIT6 +#define LINEIN_L_2_LMICPGA_20K BIT7 +#define LINEIN_L_2_LMICPGA_40K (0x3 << 6) +#define LINEIN_R_2_RMICPGA_10K BIT6 +#define LINEIN_R_2_RMICPGA_20K BIT7 +#define LINEIN_R_2_RMICPGA_40K (0x3 << 6) + + +/**************************************************************************** + * DAPM widget related #defines + *************************************************************************** + */ +#define LMUTE_ENUM 0 +#define RMUTE_ENUM 1 +#define DACEXTRA_ENUM 2 +#define DACCONTROL_ENUM 3 +#define SOFTSTEP_ENUM 4 +#define BEEP_ENUM 5 +#define MICBIAS_ENUM 6 +#define DACLEFTIP_ENUM 7 +#define DACRIGHTIP_ENUM 8 +#define VOLTAGE_ENUM 9 +#define HSET_ENUM 10 +#define DRC_ENUM 11 +#define MIC1LP_ENUM 12 +#define MIC1RP_ENUM 13 +#define MIC1LM_ENUM 14 +#define MIC_ENUM 15 +#define CM_ENUM 16 +#define MIC1LMM_ENUM 17 +#define MIC1_ENUM 18 +#define MIC2_ENUM 19 +#define MIC3_ENUM 20 +#define ADCMUTE_ENUM 21 +#endif + +#ifdef AIC3120_CODEC_SUPPORT /*for AIC3120 */ + /* ****************** Page 0 Registers **************************************/ + #define PAGE_SELECT 0 + #define RESET 1 + #define CLK_REG_1 4 + #define CLK_REG_2 5 + #define CLK_REG_3 6 + #define CLK_REG_4 7 + #define CLK_REG_5 8 + #define NDAC_CLK_REG 11 + #define MDAC_CLK_REG 12 + #define DAC_OSR_MSB 13 + #define DAC_OSR_LSB 14 + #define NADC_CLK_REG 18 + #define MADC_CLK_REG 19 + #define ADC_OSR_REG 20 + #define CLK_MUX_REG 25 + #define CLK_MVAL_REG 26 + #define INTERFACE_SET_REG_1 27 + #define DATA_SLOT_OFFSET 28 + #define INTERFACE_SET_REG_2 29 + #define BCLK_N_VAL 30 + #define INTERFACE_SET_REG_3 31 + #define INTERFACE_SET_REG_4 32 + #define INTERFACE_SET_REG_5 33 + #define I2C_BUS_COND 34 + + #define INTL_CRTL_REG_1 48 + #define INTL_CRTL_REG_2 49 + #define GPIO_CRTL_REG_1 51 + + #define DAC_PRB_SEL_REG 60 + #define ADC_PRB_SEL_REG 61 + #define DAC_CHN_REG 63 + #define DAC_MUTE_CTRL_REG 64 + #define LDAC_VOL 65 /*Mistral: AIC3120 has mono dac...renamed from LDAC_VOL to DAC_VOL.*/ + #define DAC_VOL 65 /*Mistral:..DUAL definition for compatibility*/ + /*Mistral: 3120 is mono..RDAC_VOL is reserved..register removed*/ + + #define HEADSET_DETECT 67 + + #define DRC_CTL_REG_1 68 + #define DRC_CTL_REG_2 69 + #define DRC_CTL_REG_3 70 + + /* 3120 does not have beep generation circuits...So removed beep related registers*/ + + #define ADC_DIG_MIC 81 + #define ADC_FGA 82 + #define ADC_CGA 83 + + #define AGC_CTRL_1 86 + #define AGC_CTRL_2 87 + #define AGC_CTRL_3 88 + #define AGC_CTRL_4 89 + #define AGC_CTRL_5 90 + #define AGC_CTRL_6 91 + #define AGC_CTRL_7 92 + #define AGC_CTRL_8 93 + + #define ADC_DC_1 102 + #define ADC_DC_2 103 + #define PIN_VOL_CTRL 116 + #define PIN_VOL_GAIN 117 + + + /******************** Page 1 Registers **************************************/ + #define PAGE_1 128 + #define HEADPHONE_DRIVER (PAGE_1 + 31) + #define CLASSD_SPEAKER_AMP (PAGE_1 + 32) + #define HP_POP_CTRL (PAGE_1 + 33) + #define PGA_RAMP_CTRL (PAGE_1 + 34) + #define DAC_MIX_CTRL (PAGE_1 + 35) + #define L_ANLOG_VOL_2_HPL (PAGE_1 + 36) + #define ANALOG_HPOUT_VOL (PAGE_1 + 36) + /*Mistral: 3120 DAC is mono.. R_ANALOG_VOL_2_HPR register is removed*/ + #define L_ANLOG_VOL_2_SPL (PAGE_1 + 38) + #define ANALOG_CDOUT_VOL (PAGE_1 + 38) /*Mistral: Dual definition for compatibility*/ + /*Mistral: 3120 DAC is mono.. R_ANALOG_VOL_2_SPR register is removed*/ + #define HPL_DRIVER (PAGE_1 + 40) /*Mistral: kept for compatibility*/ + #define HP_DRIVER (PAGE_1 + 40) /*Mistral: Dual definition for compatibility*/ + #define HPOUT_DRIVER (PAGE_1 + 40) /*Mistral: Triple definition for compatibility*/ + /*Mistral: 3120 DAC is mono.. HPR_DRIVER register is removed*/ + #define SPL_DRIVER (PAGE_1 + 42) /*Mistral: kept for compatibility*/ + #define SP_DRIVER (PAGE_1 + 42) /*Mistral: Dual Definition for compatibility*/ + #define CD_OUT_DRIVER (PAGE_1 + 42) /*Mistral: Triple definition for compatibility*/ + /*Mistral: 3120 DAC is mono.. HPR_DRIVER register is removed*/ + #define HP_DRIVER_CTRL (PAGE_1 + 44) + #define MICBIAS_CTRL (PAGE_1 + 46) + #define MIC_PGA (PAGE_1 + 47) + #define MIC_GAIN (PAGE_1 + 48) + #define ADC_IP_SEL (PAGE_1 + 49) + #define CM_SET (PAGE_1 + 50) + + #define L_MICPGA_P (PAGE_1 + 52) + #define L_MICPGA_N (PAGE_1 + 54) + #define R_MICPGA_P (PAGE_1 + 55) + #define R_MICPGA_N (PAGE_1 + 57) + + /****************************************************************************/ + /****************************************************************************/ + #define BIT7 (1 << 7) + #define BIT6 (1 << 6) + #define BIT5 (1 << 5) + #define BIT4 (1 << 4) + #define BIT3 (1 << 3) + #define BIT2 (1 << 2) + #define BIT1 (1 << 1) + #define BIT0 (1 << 0) + + #define HP_UNMUTE BIT2 /*Mistral: Kept for compatibility*/ + #define HPL_UNMUTE BIT3 + #define ENABLE_DAC_CHN BIT7 /*Mistral reg [0][63] only right DAC is present*/ + #define ENABLE_ADC_CHN (BIT6 | BIT7) + #define BCLK_DIR_CTRL (BIT2 | BIT3) + #define CODEC_CLKIN_MASK 0x03 + #define MCLK_2_CODEC_CLKIN 0x00 + #define CODEC_MUX_VALUE 0X03 + /*Bclk_in selection*/ + #define BDIV_CLKIN_MASK 0x03 + #define DAC_MOD_CLK_2_BDIV_CLKIN BIT0 + #define SOFT_RESET 0x01 + #define PAGE0 0x00 + #define PAGE1 0x01 + #define BIT_CLK_MASTER BIT3 + #define WORD_CLK_MASTER BIT2 + #define HIGH_PLL BIT6 + #define ENABLE_PLL BIT7 + #define ENABLE_NDAC BIT7 + #define ENABLE_MDAC BIT7 + #define ENABLE_NADC BIT7 + #define ENABLE_MADC BIT7 + #define ENABLE_BCLK BIT7 + #define LDAC_2_LCHN BIT4 + /*Mistral: RDAC_2_LCHN...Removed as its reserved*/ + #define RDAC_2_RAMP BIT2 /*Mistral: kept for compatibility*/ + #define LDAC_2_LAMP BIT6 + #define LDAC_CHNL_2_HPL BIT3 + #define SOFT_STEP_2WCLK BIT0 + #define MUTE_ON 0x00 + #define DEFAULT_VOL 0x0 + #define DISABLE_ANALOG BIT3 + #define LDAC_2_HPL_ROUTEON BIT3 + /*#define RDAC_2_HPR_ROUTEON BIT3*/ /*Mistral: NOT present in AIC3120*/ + #define LINEIN_L_2_LMICPGA_10K BIT6 + #define LINEIN_L_2_LMICPGA_20K BIT7 + #define LINEIN_L_2_LMICPGA_40K (0x3 << 6) + #define LINEIN_R_2_RMICPGA_10K BIT6 + #define LINEIN_R_2_RMICPGA_20K BIT7 + #define LINEIN_R_2_RMICPGA_40K (0x3 << 6) + + + /**************************************************************************** + * DAPM widget related #defines + *************************************************************************** + */ + #define LMUTE_ENUM 0 + /*#define RMUTE_ENUM 1*/ /*Mistral: NOT Present in AIC3120*/ + #define DACEXTRA_ENUM 2 + #define DACCONTROL_ENUM 3 + #define SOFTSTEP_ENUM 4 + /*#define BEEP_ENUM 5*/ /*Mistral: not present in AIC3120*/ + #define MICBIAS_ENUM 6 + #define DACLEFTIP_ENUM 7 + #define DACRIGHTIP_ENUM 8 + #define VOLTAGE_ENUM 9 + #define HSET_ENUM 10 + #define DRC_ENUM 11 + #define MIC1LP_ENUM 12 + #define MIC1RP_ENUM 13 + #define MIC1LM_ENUM 14 + #define MIC_ENUM 15 + #define CM_ENUM 16 + #define MIC1LMM_ENUM 17 + #define MIC1_ENUM 18 + #define MIC2_ENUM 19 + #define MIC3_ENUM 20 + #define ADCMUTE_ENUM 21 +#endif + + +/***************************************************************************** + * Structures Definitions + ***************************************************************************** + */ +/* + *---------------------------------------------------------------------------- + * @struct aic3111_setup_data | + * i2c specific data setup for AIC3111. + * @field unsigned short |i2c_address | + * Unsigned short for i2c address. + *---------------------------------------------------------------------------- + */ +struct aic3111_setup_data +{ + unsigned short i2c_address; +}; + +/* + *---------------------------------------------------------------------------- + * @struct aic3111_priv | + * AIC3111 priviate data structure to set the system clock, mode and + * page number. + * @field u32 | sysclk | + * system clock + * @field s32 | master | + * master/slave mode setting for AIC3111 + * @field u8 | page_no | + * page number. Here, page 0 and page 1 are used. + *---------------------------------------------------------------------------- + */ +struct aic3111_priv +{ + u32 sysclk; + s32 master; + u8 page_no; +}; + +/* + *---------------------------------------------------------------------------- + * @struct aic3111_configs | + * AIC3111 initialization data which has register offset and register + * value. + * @field u16 | reg_offset | + * AIC3111 Register offsets required for initialization.. + * @field u8 | reg_val | + * value to set the AIC3111 register to initialize the AIC3111. + *---------------------------------------------------------------------------- + */ +struct aic3111_configs +{ + u16 reg_offset; + u8 reg_val; +}; + +/* + *---------------------------------------------------------------------------- + * @struct aic3111_rate_divs | + * Setting up the values to get different freqencies + * + * @field u32 | mclk | + * Master clock + * @field u32 | rate | + * sample rate + * @field u8 | p_val | + * value of p in PLL + * @field u32 | pll_j | + * value for pll_j + * @field u32 | pll_d | + * value for pll_d + * @field u32 | dosr | + * value to store dosr + * @field u32 | ndac | + * value for ndac + * @field u32 | mdac | + * value for mdac + * @field u32 | aosr | + * value for aosr + * @field u32 | nadc | + * value for nadc + * @field u32 | madc | + * value for madc + * @field u32 | blck_N | + * value for block N + * @field u32 | aic3111_configs | + * configurations for aic3111 register value + *---------------------------------------------------------------------------- + */ +struct aic3111_rate_divs +{ + u32 mclk; + u32 rate; + u8 p_val; + u8 pll_j; + u16 pll_d; + u16 dosr; + u8 ndac; + u8 mdac; + u8 aosr; + u8 nadc; + u8 madc; + u8 blck_N; + struct aic3111_configs codec_specific_regs[NO_FEATURE_REGS]; +}; + +/* + *---------------------------------------------------------------------------- + * @struct snd_soc_codec_dai | + * It is SoC Codec DAI structure which has DAI capabilities viz., + * playback and capture, DAI runtime information viz. state of DAI + * and pop wait state, and DAI private data. + *---------------------------------------------------------------------------- + */ +extern struct snd_soc_dai tlv320aic3111_dai; + +#endif /* _TLV320AIC3111_H */ diff --git a/sound/soc/rk29/Kconfig b/sound/soc/rk29/Kconfig index 9474a30ea572..d2772c079bb1 100755 --- a/sound/soc/rk29/Kconfig +++ b/sound/soc/rk29/Kconfig @@ -95,7 +95,16 @@ config SND_RK29_SOC_CS42L52 help Say Y if you want to add support for SoC audio on rockchip with the CS42L52. - + +config SND_RK29_SOC_AIC3111 + tristate "SoC I2S Audio support for rockchip - AIC3111" + depends on SND_RK29_SOC && I2C_RK29 + select SND_RK29_SOC_I2S + select SND_SOC_TLV320AIC3111 + help + Say Y if you want to add support for SoC audio on rockchip + with the AIC3111. + config SND_RK29_SOC_RK1000 tristate "SoC I2S Audio support for rockchip - RK1000" depends on SND_RK29_SOC && RK1000_CONTROL && I2C_RK29 @@ -105,7 +114,7 @@ config SND_RK29_SOC_RK1000 Say Y if you want to add support for SoC audio on rockchip with the RK1000. -if SND_RK29_SOC_WM8988 || SND_RK29_SOC_RK1000 || SND_RK29_SOC_WM8994 || SND_RK29_SOC_WM8900 || SND_RK29_SOC_RT5621 || SND_RK29_SOC_RT5631 || SND_RK29_SOC_RT5625 || SND_RK29_SOC_CS42L52 +if SND_RK29_SOC_WM8988 || SND_RK29_SOC_RK1000 || SND_RK29_SOC_WM8994 || SND_RK29_SOC_WM8900 || SND_RK29_SOC_RT5621 || SND_RK29_SOC_RT5631 || SND_RK29_SOC_RT5625 || SND_RK29_SOC_CS42L52 || SND_RK29_SOC_AIC3111 choice prompt "Set i2s type" config SND_RK29_CODEC_SOC_MASTER diff --git a/sound/soc/rk29/Makefile b/sound/soc/rk29/Makefile index cc0c09b0b40d..3092da30c1e5 100644 --- a/sound/soc/rk29/Makefile +++ b/sound/soc/rk29/Makefile @@ -11,6 +11,7 @@ snd-soc-rt5621-objs := rk29_rt5621.o snd-soc-rt5631-objs := rk29_rt5631.o snd-soc-rt5625-objs := rk29_rt5625.o snd-soc-cs42l52-objs := rk29_cs42l52.o +snd-soc-aic3111-objs := rk29_aic3111.o snd-soc-wm8988-objs := rk29_wm8988.o snd-soc-rk1000-objs := rk29_rk1000codec.o snd-soc-wm8994-objs := rk29_wm8994.o @@ -23,3 +24,4 @@ obj-$(CONFIG_SND_RK29_SOC_RT5631) += snd-soc-rt5631.o obj-$(CONFIG_SND_RK29_SOC_RT5625) += snd-soc-rt5625.o obj-$(CONFIG_SND_RK29_SOC_RK1000) += snd-soc-rk1000.o obj-$(CONFIG_SND_RK29_SOC_CS42L52) += snd-soc-cs42l52.o +obj-$(CONFIG_SND_RK29_SOC_AIC3111) += snd-soc-aic3111.o diff --git a/sound/soc/rk29/rk29_aic3111.c b/sound/soc/rk29/rk29_aic3111.c new file mode 100644 index 000000000000..68311b117077 --- /dev/null +++ b/sound/soc/rk29/rk29_aic3111.c @@ -0,0 +1,219 @@ +/* + * rk29_tlv320dac3100.c -- SoC audio for rockchip + * + * Driver for rockchip tlv320aic3100 audio + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../codecs/tlv320aic3111.h" +#include "rk29_pcm.h" +#include "rk29_i2s.h" + +#if 0 +#define AIC_DBG(x...) printk(KERN_INFO x) +#else +#define AIC_DBG(x...) do { } while (0) +#endif + +#ifdef CODECHPDET + #define HP_DET_PIN RK29_PIN6_PA0 +#endif + + + +static int rk29_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + unsigned int pll_out = 0; + int ret; + + AIC_DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + + /*by Vincent Hsiung for EQ Vol Change*/ + #define HW_PARAMS_FLAG_EQVOL_ON 0x21 + #define HW_PARAMS_FLAG_EQVOL_OFF 0x22 + if ((params->flags == HW_PARAMS_FLAG_EQVOL_ON)||(params->flags == HW_PARAMS_FLAG_EQVOL_OFF)) + { + ret = codec_dai->driver->ops->hw_params(substream, params, codec_dai); + AIC_DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + } + else + { + /* set codec DAI configuration */ + #if defined (CONFIG_SND_RK29_CODEC_SOC_SLAVE) + ret = snd_soc_dai_set_fmt(codec_dai, + SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS); + #endif + + #if defined (CONFIG_SND_RK29_CODEC_SOC_MASTER) + ret = snd_soc_dai_set_fmt(codec_dai, + SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM ); + #endif + + if (ret < 0) + return ret; + + /* set cpu DAI configuration */ + #if defined (CONFIG_SND_RK29_CODEC_SOC_SLAVE) + ret = snd_soc_dai_set_fmt(cpu_dai, + SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM); + #endif + + #if defined (CONFIG_SND_RK29_CODEC_SOC_MASTER) + ret = snd_soc_dai_set_fmt(cpu_dai, + SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS); + #endif + + if (ret < 0) + return ret; + } + + switch(params_rate(params)) { + case 8000: + case 16000: + case 24000: + case 32000: + case 48000: + pll_out = 12288000; + break; + case 11025: + case 22050: + case 44100: + pll_out = 11289600; + break; + default: + printk("Enter:%s, %d, Error rate=%d\n",__FUNCTION__,__LINE__,params_rate(params)); + return -EINVAL; + break; + } + AIC_DBG("Enter:%s, %d, rate=%d, pll_out = %d\n",__FUNCTION__,__LINE__,params_rate(params), pll_out); + //pll_out = 12000000; + snd_soc_dai_set_sysclk(cpu_dai, 0, pll_out, 0); + snd_soc_dai_set_sysclk(codec_dai, 0, pll_out, SND_SOC_CLOCK_IN); + + return 0; +} + +static const struct snd_soc_dapm_widget dac3100_dapm_widgets[] = { +/* SND_SOC_DAPM_LINE("Audio Out", NULL), + SND_SOC_DAPM_LINE("Line in", NULL), + SND_SOC_DAPM_MIC("Micn", NULL), + SND_SOC_DAPM_MIC("Micp", NULL),*/ +}; + +static const struct snd_soc_dapm_route audio_map[]= { +/* {"Audio Out", NULL, "HPL"}, + {"Audio Out", NULL, "HPR"}, + {"Line in", NULL, "RINPUT1"}, + {"Line in", NULL, "LINPUT1"}, + {"Micn", NULL, "RINPUT2"}, + {"Micp", NULL, "LINPUT2"},*/ +}; + +/* + * Logic for a tlv320dac3100 as connected on a rockchip board. + */ +static int rk29_aic3111_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; + + AIC_DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + + /* Add specific widgets */ + snd_soc_dapm_new_controls(dapm, dac3100_dapm_widgets, + ARRAY_SIZE(dac3100_dapm_widgets)); + + /* Set up specific audio path audio_mapnects */ + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); + AIC_DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + snd_soc_dapm_nc_pin(dapm, "HPL"); + AIC_DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + snd_soc_dapm_nc_pin(dapm, "HPR"); + AIC_DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + snd_soc_dapm_sync(dapm); + AIC_DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + return 0; +} + +static struct snd_soc_ops rk29_ops = { + .hw_params = rk29_hw_params, +}; + +static struct snd_soc_dai_link rk29_dai = { + .name = "AIC3111", + .stream_name = "AIC3111 PCM", + .codec_name = "AIC3111.0-0018", + .platform_name = "rockchip-audio", + .cpu_dai_name = "rk29_i2s.0", + .codec_dai_name = "AIC3111 HiFi", + .init = rk29_aic3111_init, + .ops = &rk29_ops, +}; + +static struct snd_soc_card snd_soc_card_rk29 = { + .name = "RK29_AIC3111", + .dai_link = &rk29_dai, + .num_links = 1, +}; + +static struct platform_device *rk29_snd_device; + +static int __init audio_card_init(void) +{ + int ret =0; + + AIC_DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + rk29_snd_device = platform_device_alloc("soc-audio", -1); + if (!rk29_snd_device) { + AIC_DBG("platform device allocation failed\n"); + ret = -ENOMEM; + return ret; + } + platform_set_drvdata(rk29_snd_device, &snd_soc_card_rk29); + ret = platform_device_add(rk29_snd_device); + if (ret) { + AIC_DBG("platform device add failed\n"); + platform_device_put(rk29_snd_device); + } + return ret; +} + +static void __exit audio_card_exit(void) +{ + platform_device_unregister(rk29_snd_device); +} + +module_init(audio_card_init); +module_exit(audio_card_exit); +/* Module information */ +MODULE_AUTHOR("rockchip"); +MODULE_DESCRIPTION("ROCKCHIP i2s ASoC Interface"); +MODULE_LICENSE("GPL");