From f22be8c687af8e8975c895f662c08781b031166d Mon Sep 17 00:00:00 2001 From: =?utf8?q?=E9=99=88=E9=87=91=E6=B3=89?= Date: Tue, 22 Nov 2011 18:10:23 +0800 Subject: [PATCH] add rt5621 driver for kernel 3.0 and change wm8994 driver --- arch/arm/configs/rk29_ddr3sdk_defconfig | 4 +- arch/arm/configs/rk29_k97_defconfig | 2 +- arch/arm/mach-rk29/board-rk29-ddr3sdk.c | 4 +- arch/arm/mach-rk29/board-rk29-k97.c | 4 +- drivers/mfd/wm8994-core.c | 2 +- drivers/video/hdmi/hdmi-codec.c | 2 +- include/linux/mfd/wm8994/core.h | 54 +- include/linux/mfd/wm8994/pdata.h | 111 ++- sound/soc/codecs/Kconfig | 4 +- sound/soc/codecs/Makefile | 4 +- sound/soc/codecs/rt5621.c | 1221 +++++++++++++++++++++++ sound/soc/codecs/rt5621.h | 515 ++++++++++ sound/soc/codecs/wm8994.c | 4 +- sound/soc/rk29/Kconfig | 10 +- sound/soc/rk29/Makefile | 4 +- sound/soc/rk29/rk29_rt5621.c | 229 +++++ sound/soc/rk29/rk29_wm8994.c | 363 +++++-- 17 files changed, 2394 insertions(+), 143 deletions(-) create mode 100644 sound/soc/codecs/rt5621.c create mode 100644 sound/soc/codecs/rt5621.h create mode 100644 sound/soc/rk29/rk29_rt5621.c diff --git a/arch/arm/configs/rk29_ddr3sdk_defconfig b/arch/arm/configs/rk29_ddr3sdk_defconfig index 6a67e306859a..f6ae57985f5f 100755 --- a/arch/arm/configs/rk29_ddr3sdk_defconfig +++ b/arch/arm/configs/rk29_ddr3sdk_defconfig @@ -1527,8 +1527,8 @@ CONFIG_SND_RK29_SOC_I2S=y CONFIG_SND_RK29_SOC_I2S_8CH=y # CONFIG_SND_RK29_SOC_WM8988 is not set CONFIG_SND_RK29_SOC_WM8900=y -# CONFIG_SND_RK29_SOC_alc5621 is not set -# CONFIG_SND_RK29_SOC_alc5631 is not set +# CONFIG_SND_RK29_SOC_RT5621 is not set +# CONFIG_SND_RK29_SOC_RT5631 is not set # CONFIG_SND_RK29_SOC_RT5625 is not set # CONFIG_SND_RK29_SOC_WM8994 is not set # CONFIG_SND_RK29_SOC_CS42L52 is not set diff --git a/arch/arm/configs/rk29_k97_defconfig b/arch/arm/configs/rk29_k97_defconfig index c9dc742bedad..3253901ed3ae 100644 --- a/arch/arm/configs/rk29_k97_defconfig +++ b/arch/arm/configs/rk29_k97_defconfig @@ -1841,7 +1841,7 @@ CONFIG_SND_RK29_SOC_I2S_8CH=y CONFIG_SND_I2S_DMA_EVENT_STATIC=y # CONFIG_SND_RK29_SOC_WM8988 is not set # CONFIG_SND_RK29_SOC_WM8900 is not set -# CONFIG_SND_RK29_SOC_alc5621 is not set +# CONFIG_SND_RK29_SOC_RT5621 is not set CONFIG_SND_RK29_SOC_RT5631=y # CONFIG_SND_RK29_SOC_RT5625 is not set # CONFIG_SND_RK29_SOC_WM8994 is not set diff --git a/arch/arm/mach-rk29/board-rk29-ddr3sdk.c b/arch/arm/mach-rk29/board-rk29-ddr3sdk.c index 295cb617c47f..cfb6a066dfe2 100755 --- a/arch/arm/mach-rk29/board-rk29-ddr3sdk.c +++ b/arch/arm/mach-rk29/board-rk29-ddr3sdk.c @@ -1411,9 +1411,9 @@ static struct i2c_board_info __initdata board_i2c0_devices[] = { .flags = 0, }, #endif -#if defined (CONFIG_SND_SOC_alc5621) +#if defined (CONFIG_SND_SOC_RT5621) { - .type = "ALC5621", + .type = "rt5621", .addr = 0x1a, .flags = 0, }, diff --git a/arch/arm/mach-rk29/board-rk29-k97.c b/arch/arm/mach-rk29/board-rk29-k97.c index c99bfebec354..58509d254253 100755 --- a/arch/arm/mach-rk29/board-rk29-k97.c +++ b/arch/arm/mach-rk29/board-rk29-k97.c @@ -1449,9 +1449,9 @@ static struct i2c_board_info __initdata board_i2c0_devices[] = { .flags = 0, }, #endif -#if defined (CONFIG_SND_SOC_alc5621) +#if defined (CONFIG_SND_SOC_RT5621) { - .type = "ALC5621", + .type = "rt5621", .addr = 0x1a, .flags = 0, }, diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index e198d40292e7..7ecd7de78c33 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -651,7 +651,7 @@ static UNIVERSAL_DEV_PM_OPS(wm8994_pm_ops, wm8994_suspend, wm8994_resume, static struct i2c_driver wm8994_i2c_driver = { .driver = { - .name = "wm8994", + .name = "WM8994", .owner = THIS_MODULE, .pm = &wm8994_pm_ops, }, diff --git a/drivers/video/hdmi/hdmi-codec.c b/drivers/video/hdmi/hdmi-codec.c index 49c824899e5e..e0360ceb7fd6 100755 --- a/drivers/video/hdmi/hdmi-codec.c +++ b/drivers/video/hdmi/hdmi-codec.c @@ -1,5 +1,5 @@ #include -#if defined CONFIG_SND_SOC_WM8900 || defined CONFIG_SND_SOC_RT5631 +#if defined CONFIG_SND_SOC_WM8900 || defined CONFIG_SND_SOC_RT5631 || defined CONFIG_SND_SOC_RT5621 /* sound/soc/codecs/wm8900.c */ extern void codec_set_spk(bool on); #else diff --git a/include/linux/mfd/wm8994/core.h b/include/linux/mfd/wm8994/core.h index 7929fc9d6709..f0b69cdae41c 100644 --- a/include/linux/mfd/wm8994/core.h +++ b/include/linux/mfd/wm8994/core.h @@ -15,7 +15,6 @@ #ifndef __MFD_WM8994_CORE_H__ #define __MFD_WM8994_CORE_H__ -<<<<<<< HEAD #include enum wm8994_type { @@ -23,16 +22,36 @@ enum wm8994_type { WM8958 = 1, }; -======= ->>>>>>> parent of 15f7fab... temp revert rk change struct regulator_dev; struct regulator_bulk_data; #define WM8994_NUM_GPIO_REGS 11 -#define WM8994_NUM_LDO_REGS 2 +#define WM8994_NUM_LDO_REGS 2 +#define WM8994_NUM_IRQ_REGS 2 + +#define WM8994_IRQ_TEMP_SHUT 0 +#define WM8994_IRQ_MIC1_DET 1 +#define WM8994_IRQ_MIC1_SHRT 2 +#define WM8994_IRQ_MIC2_DET 3 +#define WM8994_IRQ_MIC2_SHRT 4 +#define WM8994_IRQ_FLL1_LOCK 5 +#define WM8994_IRQ_FLL2_LOCK 6 +#define WM8994_IRQ_SRC1_LOCK 7 +#define WM8994_IRQ_SRC2_LOCK 8 +#define WM8994_IRQ_AIF1DRC1_SIG_DET 9 +#define WM8994_IRQ_AIF1DRC2_SIG_DET 10 +#define WM8994_IRQ_AIF2DRC_SIG_DET 11 +#define WM8994_IRQ_FIFOS_ERR 12 +#define WM8994_IRQ_WSEQ_DONE 13 +#define WM8994_IRQ_DCS_DONE 14 +#define WM8994_IRQ_TEMP_WARN 15 + +/* GPIOs in the chip are numbered from 1-11 */ +#define WM8994_IRQ_GPIO(x) (x + WM8994_IRQ_TEMP_WARN) struct wm8994 { struct mutex io_lock; + struct mutex irq_lock; enum wm8994_type type; @@ -45,6 +64,11 @@ struct wm8994 { void *control_data; int gpio_base; + int irq_base; + + int irq; + u16 irq_masks_cur[WM8994_NUM_IRQ_REGS]; + u16 irq_masks_cache[WM8994_NUM_IRQ_REGS]; /* Used over suspend/resume */ bool suspended; @@ -67,4 +91,26 @@ int wm8994_bulk_read(struct wm8994 *wm8994, unsigned short reg, int wm8994_bulk_write(struct wm8994 *wm8994, unsigned short reg, int count, const u16 *buf); + +/* Helper to save on boilerplate */ +static inline int wm8994_request_irq(struct wm8994 *wm8994, int irq, + irq_handler_t handler, const char *name, + void *data) +{ + if (!wm8994->irq_base) + return -EINVAL; + return request_threaded_irq(wm8994->irq_base + irq, NULL, handler, + IRQF_TRIGGER_RISING, name, + data); +} +static inline void wm8994_free_irq(struct wm8994 *wm8994, int irq, void *data) +{ + if (!wm8994->irq_base) + return; + free_irq(wm8994->irq_base + irq, data); +} + +int wm8994_irq_init(struct wm8994 *wm8994); +void wm8994_irq_exit(struct wm8994 *wm8994); + #endif diff --git a/include/linux/mfd/wm8994/pdata.h b/include/linux/mfd/wm8994/pdata.h index f63b990b366e..97cf4f27d647 100755 --- a/include/linux/mfd/wm8994/pdata.h +++ b/include/linux/mfd/wm8994/pdata.h @@ -26,10 +26,16 @@ struct wm8994_ldo_pdata { struct regulator_init_data *init_data; }; -#define WM8994_CONFIGURE_GPIO 0x8000 +#define WM8994_CONFIGURE_GPIO 0x10000 #define WM8994_DRC_REGS 5 -#define WM8994_EQ_REGS 19 +#define WM8994_EQ_REGS 20 +#define WM8958_MBC_CUTOFF_REGS 20 +#define WM8958_MBC_COEFF_REGS 48 +#define WM8958_MBC_COMBINED_REGS 56 +#define WM8958_VSS_HPF_REGS 2 +#define WM8958_VSS_REGS 148 +#define WM8958_ENH_EQ_REGS 32 /** * DRC configurations are specified with a label and a set of register @@ -59,8 +65,53 @@ struct wm8994_retune_mobile_cfg { u16 regs[WM8994_EQ_REGS]; }; -#define PCM_BB 1 -#define NO_PCM_BB 0 +/** + * Multiband compressor configurations are specified with a label and + * two sets of values to write. Configurations are expected to be + * generated using the multiband compressor configuration panel in + * WISCE - see http://www.wolfsonmicro.com/wisce/ + */ +struct wm8958_mbc_cfg { + const char *name; + u16 cutoff_regs[WM8958_MBC_CUTOFF_REGS]; + u16 coeff_regs[WM8958_MBC_COEFF_REGS]; + + /* Coefficient layout when using MBC+VSS firmware */ + u16 combined_regs[WM8958_MBC_COMBINED_REGS]; +}; + +/** + * VSS HPF configurations are specified with a label and two values to + * write. Configurations are expected to be generated using the + * multiband compressor configuration panel in WISCE - see + * http://www.wolfsonmicro.com/wisce/ + */ +struct wm8958_vss_hpf_cfg { + const char *name; + u16 regs[WM8958_VSS_HPF_REGS]; +}; + +/** + * VSS configurations are specified with a label and array of values + * to write. Configurations are expected to be generated using the + * multiband compressor configuration panel in WISCE - see + * http://www.wolfsonmicro.com/wisce/ + */ +struct wm8958_vss_cfg { + const char *name; + u16 regs[WM8958_VSS_REGS]; +}; + +/** + * Enhanced EQ configurations are specified with a label and array of + * values to write. Configurations are expected to be generated using + * the multiband compressor configuration panel in WISCE - see + * http://www.wolfsonmicro.com/wisce/ + */ +struct wm8958_enh_eq_cfg { + const char *name; + u16 regs[WM8958_ENH_EQ_REGS]; +}; struct wm8994_pdata { int gpio_base; @@ -73,6 +124,7 @@ struct wm8994_pdata { struct wm8994_ldo_pdata ldo[WM8994_NUM_LDO]; + int irq_base; /** Base IRQ number for WM8994, required for IRQs */ int num_drc_cfgs; struct wm8994_drc_cfg *drc_cfgs; @@ -80,6 +132,18 @@ struct wm8994_pdata { int num_retune_mobile_cfgs; struct wm8994_retune_mobile_cfg *retune_mobile_cfgs; + int num_mbc_cfgs; + struct wm8958_mbc_cfg *mbc_cfgs; + + int num_vss_cfgs; + struct wm8958_vss_cfg *vss_cfgs; + + int num_vss_hpf_cfgs; + struct wm8958_vss_hpf_cfg *vss_hpf_cfgs; + + int num_enh_eq_cfgs; + struct wm8958_enh_eq_cfg *enh_eq_cfgs; + /* LINEOUT can be differential or single ended */ unsigned int lineout1_diff:1; unsigned int lineout2_diff:1; @@ -88,42 +152,21 @@ struct wm8994_pdata { unsigned int lineout1fb:1; unsigned int lineout2fb:1; - /* Microphone biases: 0=0.9*AVDD1 1=0.65*AVVD1 */ + /* IRQ for microphone detection if brought out directly as a + * signal. + */ + int micdet_irq; + + /* WM8994 microphone biases: 0=0.9*AVDD1 1=0.65*AVVD1 */ unsigned int micbias1_lvl:1; unsigned int micbias2_lvl:1; - /* Jack detect threashold levels, see datasheet for values */ + /* WM8994 jack detect threashold levels, see datasheet for values */ unsigned int jd_scthr:2; unsigned int jd_thr:2; - //for phonepad - unsigned int no_earpiece:1; // =1 don't have a earpiece, =0 has a earpiece - unsigned int sp_hp_same_channel:1; - - //BB input can be differential or single ended - unsigned int BB_input_diff:1; // =0 single ended =1 differential - unsigned int BB_class:1;//PCM_BB= 1 NO_PCM_BB=0 - - //If an external amplifier speakers wm8994 enable>0 disable=0 - unsigned int PA_control_pin; - - //wm8994 LDO1_ENA and LDO2_ENA - unsigned int Power_EN_Pin; - char PowerEN_iomux_name[50]; - int PowerEN_iomux_mode; - - //volume - int speaker_incall_vol; //max = 6, min = -21 - int speaker_incall_mic_vol; //max = 30, min = -22 - int speaker_normal_vol; //max = 6, min = -57 - int earpiece_incall_vol; //max = 6, min = -21 - int headset_incall_vol; //max = 6, min = -12 - int headset_incall_mic_vol; //max = 30, min = -22 - int headset_normal_vol; //max = 6, min = -57 - int BT_incall_vol; //max = 30, min = -16 - int BT_incall_mic_vol; //max = 6, min = -57 - int recorder_vol; //max = 60 , min = -16 - + /* WM8958 microphone bias configuration */ + int micbias[2]; }; #endif diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 9f50f4f54a67..9dfeb311759f 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -74,7 +74,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_WM8776 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8804 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8900 if I2C - select SND_SOC_alc5621 if I2C + select SND_SOC_RT5621 if I2C select SND_SOC_RT5631 if I2C select SND_SOC_RT5625 if I2C select SND_SOC_WM8903 if I2C @@ -312,7 +312,7 @@ config SND_SOC_WM8804 config SND_SOC_WM8900 tristate -config SND_SOC_alc5621 +config SND_SOC_RT5621 tristate config SND_SOC_RT5631 diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index b4bf7e53b76b..b5c69a02e817 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -57,7 +57,7 @@ snd-soc-wm8770-objs := wm8770.o snd-soc-wm8776-objs := wm8776.o snd-soc-wm8804-objs := wm8804.o snd-soc-wm8900-objs := wm8900.o -snd-soc-alc5621-objs := alc5621.o +snd-soc-rt5621-objs := rt5621.o snd-soc-rt5631-objs := rt5631.o snd-soc-rt5625-objs := rt5625.o snd-soc-cs42l52-objs := cs42l52.o @@ -154,7 +154,7 @@ obj-$(CONFIG_SND_SOC_WM8770) += snd-soc-wm8770.o obj-$(CONFIG_SND_SOC_WM8776) += snd-soc-wm8776.o obj-$(CONFIG_SND_SOC_WM8804) += snd-soc-wm8804.o obj-$(CONFIG_SND_SOC_WM8900) += snd-soc-wm8900.o -obj-$(CONFIG_SND_SOC_alc5621) += snd-soc-alc5621.o +obj-$(CONFIG_SND_SOC_RT5621) += snd-soc-rt5621.o obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o obj-$(CONFIG_SND_SOC_RT5625) += snd-soc-rt5625.o obj-$(CONFIG_SND_SOC_CS42L52) += snd-soc-cs42l52.o diff --git a/sound/soc/codecs/rt5621.c b/sound/soc/codecs/rt5621.c new file mode 100644 index 000000000000..c0ee91b3f40e --- /dev/null +++ b/sound/soc/codecs/rt5621.c @@ -0,0 +1,1221 @@ +/* + * rt5621.c -- RT5621 ALSA SoC audio codec driver + * + * Copyright 2011 Realtek Semiconductor Corp. + * Author: Johnny Hsu + * + * This program 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt5621.h" + +#if REALTEK_HWDEP +#include +#include +#endif + +#if 0 +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +#define RT5621_VERSION "0.01 alsa 1.0.24" + +static int caps_charge = 500; +module_param(caps_charge, int, 0); +MODULE_PARM_DESC(caps_charge, "RT5621 cap charge time (msecs)"); + +struct snd_soc_codec *rt5621_codec; + +static void rt5621_work(struct work_struct *work); + +static struct workqueue_struct *rt5621_workq; +static DECLARE_DELAYED_WORK(delayed_work, rt5621_work); + +#define ENABLE_EQ_HREQ 1 + +struct rt5621_priv { + unsigned int sysclk; +}; + +enum { + NORMAL, + CLUB, + DANCE, + LIVE, + POP, + ROCK, + OPPO, + TREBLE, + BASS, + HFREQ, + EQTEST, + SPK_FR +}; + +typedef struct _HW_EQ_PRESET +{ + u16 HwEqType; + u16 EqValue[14]; + u16 HwEQCtrl; + +}HW_EQ_PRESET; + +HW_EQ_PRESET HwEq_Preset[]={ + /* 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x 0x9 0xa 0x 0xc 0x62*/ + {NORMAL,{0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000},0x0000}, + {CLUB ,{0x1C10,0x0000,0xC1CC,0x1E5D,0x0699,0xCD48,0x188D,0x0699,0xC3B6,0x1CD0,0x0699,0x0436,0x0000},0x800E}, + {DANCE ,{0x1F2C,0x095B,0xC071,0x1F95,0x0616,0xC96E,0x1B11,0xFC91,0xDCF2,0x1194,0xFAF2,0x0436,0x0000},0x800F}, + {LIVE ,{0x1EB5,0xFCB6,0xC24A,0x1DF8,0x0E7C,0xC883,0x1C10,0x0699,0xDA41,0x1561,0x0295,0x0436,0x0000},0x800F}, + {POP ,{0x1E98,0xFCB6,0xC340,0x1D60,0x095B,0xC6AC,0x1BBC,0x0556,0x0689,0x0F33,0x0000,0xEDD1,0xF805},0x801F}, + {ROCK ,{0x1EB5,0xFCB6,0xC071,0x1F95,0x0424,0xC30A,0x1D27,0xF900,0x0C5D,0x0FC7,0x0E23,0x0436,0x0000},0x800F}, + {OPPO ,{0x0000,0x0000,0xCA4A,0x17F8,0x0FEC,0xCA4A,0x17F8,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000},0x800F}, + {TREBLE,{0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x188D,0x1699},0x8010}, + {BASS ,{0x1A43,0x0C00,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000},0x8001}, + {EQTEST,{0x1F8C,0x1830,0xC118,0x1EEF,0xFD77,0xD8CB,0x1BBC,0x0556,0x0689,0x0F33,0x0000,0xF17F,0x0FEC},0x8003}, +}; + +static const u16 rt5621_reg[RT5621_VENDOR_ID2 + 1] = { + [RT5621_RESET] = 0x59b4, + [RT5621_SPK_OUT_VOL] = 0x8080, + [RT5621_HP_OUT_VOL] = 0x8080, + [RT5621_MONO_AUX_OUT_VOL] = 0x8080, + [RT5621_AUXIN_VOL] = 0xe808, + [RT5621_LINE_IN_VOL] = 0xe808, + [RT5621_STEREO_DAC_VOL] = 0xe808, + [RT5621_MIC_VOL] = 0x0808, + [RT5621_MIC_ROUTING_CTRL] = 0xe0e0, + [RT5621_ADC_REC_GAIN] = 0xf58b, + [RT5621_ADC_REC_MIXER] = 0x7f7f, + [RT5621_SOFT_VOL_CTRL_TIME] = 0x000a, + [RT5621_OUTPUT_MIXER_CTRL] = 0xc000, + [RT5621_AUDIO_INTERFACE] = 0x8000, + [RT5621_STEREO_AD_DA_CLK_CTRL] = 0x166d, + [RT5621_ADD_CTRL_REG] = 0x5300, + [RT5621_GPIO_PIN_CONFIG] = 0x1c0e, + [RT5621_GPIO_PIN_POLARITY] = 0x1c0e, + [RT5621_GPIO_PIN_STATUS] = 0x0002, + [RT5621_OVER_TEMP_CURR_STATUS] = 0x003c, + [RT5621_PSEDUEO_SPATIAL_CTRL] = 0x0497, + [RT5621_AVC_CTRL] = 0x000b, + [RT5621_VENDOR_ID1] = 0x10ec, + [RT5621_VENDOR_ID2] = 0x2003, +}; + +#define rt5621_write_mask(c, reg, value, mask) snd_soc_update_bits(c, reg, mask, value) + +#define rt5621_write_index_reg(c, addr, data) \ +{ \ + snd_soc_write(c, 0x6a, addr); \ + snd_soc_write(c, 0x6c, data); \ +} + +static int rt5621_reset(struct snd_soc_codec *codec) +{ + return snd_soc_write(codec, RT5621_RESET, 0); +} + +static int rt5621_volatile_register( + struct snd_soc_codec *codec, unsigned int reg) +{ + switch (reg) { + case RT5621_RESET: + case RT5621_HID_CTRL_DATA: + case RT5621_GPIO_PIN_STATUS: + case RT5621_OVER_TEMP_CURR_STATUS: + return 1; + default: + return 0; + } +} + +static int rt5621_readable_register( + struct snd_soc_codec *codec, unsigned int reg) +{ + switch (reg) { + case RT5621_RESET: + case RT5621_SPK_OUT_VOL: + case RT5621_HP_OUT_VOL: + case RT5621_MONO_AUX_OUT_VOL: + case RT5621_AUXIN_VOL: + case RT5621_LINE_IN_VOL: + case RT5621_STEREO_DAC_VOL: + case RT5621_MIC_VOL: + case RT5621_MIC_ROUTING_CTRL: + case RT5621_ADC_REC_GAIN: + case RT5621_ADC_REC_MIXER: + case RT5621_SOFT_VOL_CTRL_TIME: + case RT5621_OUTPUT_MIXER_CTRL: + case RT5621_MIC_CTRL: + case RT5621_AUDIO_INTERFACE: + case RT5621_STEREO_AD_DA_CLK_CTRL: + case RT5621_COMPANDING_CTRL: + case RT5621_PWR_MANAG_ADD1: + case RT5621_PWR_MANAG_ADD2: + case RT5621_PWR_MANAG_ADD3: + case RT5621_ADD_CTRL_REG: + case RT5621_GLOBAL_CLK_CTRL_REG: + case RT5621_PLL_CTRL: + case RT5621_GPIO_OUTPUT_PIN_CTRL: + case RT5621_GPIO_PIN_CONFIG: + case RT5621_GPIO_PIN_POLARITY: + case RT5621_GPIO_PIN_STICKY: + case RT5621_GPIO_PIN_WAKEUP: + case RT5621_GPIO_PIN_STATUS: + case RT5621_GPIO_PIN_SHARING: + case RT5621_OVER_TEMP_CURR_STATUS: + case RT5621_JACK_DET_CTRL: + case RT5621_MISC_CTRL: + case RT5621_PSEDUEO_SPATIAL_CTRL: + case RT5621_EQ_CTRL: + case RT5621_EQ_MODE_ENABLE: + case RT5621_AVC_CTRL: + case RT5621_HID_CTRL_INDEX: + case RT5621_HID_CTRL_DATA: + case RT5621_VENDOR_ID1: + case RT5621_VENDOR_ID2: + return 1; + default: + return 0; + } +} + + +//static const char *rt5621_spkl_pga[] = {"Vmid","HPL mixer","SPK mixer","Mono Mixer"}; +static const char *rt5621_spkn_source_sel[] = {"RN", "RP", "LN"}; +static const char *rt5621_spk_pga[] = {"Vmid","HP mixer","SPK mixer","Mono Mixer"}; +static const char *rt5621_hpl_pga[] = {"Vmid","HPL mixer"}; +static const char *rt5621_hpr_pga[] = {"Vmid","HPR mixer"}; +static const char *rt5621_mono_pga[] = {"Vmid","HP mixer","SPK mixer","Mono Mixer"}; +static const char *rt5621_amp_type_sel[] = {"Class AB","Class D"}; +static const char *rt5621_mic_boost_sel[] = {"Bypass","20db","30db","40db"}; + +static const struct soc_enum rt5621_enum[] = { +SOC_ENUM_SINGLE(RT5621_OUTPUT_MIXER_CTRL, 14, 3, rt5621_spkn_source_sel), /* spkn source from hp mixer */ +SOC_ENUM_SINGLE(RT5621_OUTPUT_MIXER_CTRL, 10, 4, rt5621_spk_pga), /* spk input sel 1 */ +SOC_ENUM_SINGLE(RT5621_OUTPUT_MIXER_CTRL, 9, 2, rt5621_hpl_pga), /* hp left input sel 2 */ +SOC_ENUM_SINGLE(RT5621_OUTPUT_MIXER_CTRL, 8, 2, rt5621_hpr_pga), /* hp right input sel 3 */ +SOC_ENUM_SINGLE(RT5621_OUTPUT_MIXER_CTRL, 6, 4, rt5621_mono_pga), /* mono input sel 4 */ +SOC_ENUM_SINGLE(RT5621_MIC_CTRL, 10,4, rt5621_mic_boost_sel), /*Mic1 boost sel 5 */ +SOC_ENUM_SINGLE(RT5621_MIC_CTRL, 8,4,rt5621_mic_boost_sel), /*Mic2 boost sel 6 */ +SOC_ENUM_SINGLE(RT5621_OUTPUT_MIXER_CTRL,13,2,rt5621_amp_type_sel), /*Speaker AMP sel 7 */ +}; + +static int rt5621_amp_sel_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned short val; + unsigned short mask, bitmask; + + for (bitmask = 1; bitmask < e->max; bitmask <<= 1); + + if (ucontrol->value.enumerated.item[0] > e->max - 1) + return -EINVAL; + val = ucontrol->value.enumerated.item[0] << e->shift_l; + mask = (bitmask - 1) << e->shift_l; + if (e->shift_l != e->shift_r) { + if (ucontrol->value.enumerated.item[1] > e->max - 1) + return -EINVAL; + val |= ucontrol->value.enumerated.item[1] << e->shift_r; + mask |= (bitmask - 1) << e->shift_r; + } + + snd_soc_update_bits(codec, e->reg, mask, val); + val &= (0x1 << 13); + + if (val == 0) + { + snd_soc_update_bits(codec, 0x3c, 0x0000, 0x4000); /*power off classd*/ + snd_soc_update_bits(codec, 0x3c, 0x8000, 0x8000); /*power on classab*/ + } else { + snd_soc_update_bits(codec, 0x3c, 0x0000, 0x8000); /*power off classab*/ + snd_soc_update_bits(codec, 0x3c, 0x4000, 0x4000); /*power on classd*/ + } + + return 0; +} + +//***************************************************************************** +// +//function:Change audio codec power status +// +//***************************************************************************** +static int rt5621_ChangeCodecPowerStatus(struct snd_soc_codec *codec,int power_state) +{ + unsigned short int PowerDownState=0; + + switch(power_state) { + case POWER_STATE_D0: //FULL ON-----power on all power + + snd_soc_write(codec,RT5621_PWR_MANAG_ADD1,~PowerDownState); + snd_soc_write(codec,RT5621_PWR_MANAG_ADD2,~PowerDownState); + snd_soc_write(codec,RT5621_PWR_MANAG_ADD3,~PowerDownState); + break; + case POWER_STATE_D1: //LOW ON----- + rt5621_write_mask(codec, RT5621_PWR_MANAG_ADD2 ,PWR_VREF | + PWR_DAC_REF_CIR | PWR_L_DAC_CLK | PWR_R_DAC_CLK | + PWR_L_HP_MIXER | PWR_R_HP_MIXER | PWR_L_ADC_CLK_GAIN | + PWR_R_ADC_CLK_GAIN | PWR_L_ADC_REC_MIXER | PWR_R_ADC_REC_MIXER | + PWR_CLASS_AB, PWR_VREF | PWR_DAC_REF_CIR | PWR_L_DAC_CLK | + PWR_R_DAC_CLK | PWR_L_HP_MIXER | PWR_R_HP_MIXER | + PWR_L_ADC_CLK_GAIN | PWR_R_ADC_CLK_GAIN | + PWR_L_ADC_REC_MIXER | PWR_R_ADC_REC_MIXER | PWR_CLASS_AB); + + rt5621_write_mask(codec, RT5621_PWR_MANAG_ADD3 ,PWR_MAIN_BIAS | + PWR_HP_R_OUT_VOL | PWR_HP_L_OUT_VOL | PWR_SPK_OUT | + PWR_MIC1_FUN_CTRL | PWR_MIC1_BOOST_MIXER, PWR_MAIN_BIAS | + PWR_HP_R_OUT_VOL | PWR_HP_L_OUT_VOL | PWR_SPK_OUT | + PWR_MIC1_FUN_CTRL | PWR_MIC1_BOOST_MIXER); + + rt5621_write_mask(codec, RT5621_PWR_MANAG_ADD1 ,PWR_MAIN_I2S_EN | + PWR_HP_OUT_ENH_AMP | PWR_HP_OUT_AMP | PWR_MIC1_BIAS_EN, + PWR_MAIN_I2S_EN | PWR_HP_OUT_ENH_AMP | PWR_HP_OUT_AMP | + PWR_MIC1_BIAS_EN); + break; + + case POWER_STATE_D1_PLAYBACK: //Low on of Playback + rt5621_write_mask(codec, RT5621_PWR_MANAG_ADD2, PWR_VREF | PWR_DAC_REF_CIR | + PWR_L_DAC_CLK | PWR_R_DAC_CLK | PWR_L_HP_MIXER | PWR_R_HP_MIXER | + PWR_CLASS_AB | PWR_CLASS_D, PWR_VREF | PWR_DAC_REF_CIR | + PWR_L_DAC_CLK | PWR_R_DAC_CLK | PWR_L_HP_MIXER | PWR_R_HP_MIXER | + PWR_CLASS_AB | PWR_CLASS_D); + + rt5621_write_mask(codec, RT5621_PWR_MANAG_ADD3, PWR_MAIN_BIAS | + PWR_HP_R_OUT_VOL | PWR_HP_L_OUT_VOL | PWR_SPK_OUT, + PWR_MAIN_BIAS | PWR_HP_R_OUT_VOL | PWR_HP_L_OUT_VOL | PWR_SPK_OUT); + + rt5621_write_mask(codec, RT5621_PWR_MANAG_ADD1, PWR_MAIN_I2S_EN | + PWR_HP_OUT_ENH_AMP | PWR_HP_OUT_AMP, PWR_MAIN_I2S_EN | + PWR_HP_OUT_ENH_AMP | PWR_HP_OUT_AMP); + break; + + case POWER_STATE_D1_RECORD: //Low on of Record + rt5621_write_mask(codec, RT5621_PWR_MANAG_ADD1, PWR_MAIN_I2S_EN | + PWR_MIC1_BIAS_EN, PWR_MAIN_I2S_EN | PWR_MIC1_BIAS_EN); + rt5621_write_mask(codec, RT5621_PWR_MANAG_ADD2, PWR_VREF | + PWR_L_ADC_CLK_GAIN | PWR_R_ADC_CLK_GAIN | PWR_L_ADC_REC_MIXER | + PWR_R_ADC_REC_MIXER, PWR_VREF | PWR_L_ADC_CLK_GAIN | + PWR_R_ADC_CLK_GAIN | PWR_L_ADC_REC_MIXER | PWR_R_ADC_REC_MIXER); + rt5621_write_mask(codec, RT5621_PWR_MANAG_ADD3, PWR_MAIN_BIAS | + PWR_MIC2_BOOST_MIXER | PWR_MIC1_BOOST_MIXER, PWR_MAIN_BIAS | + PWR_MIC2_BOOST_MIXER | PWR_MIC1_BOOST_MIXER); + break; + + case POWER_STATE_D2: //STANDBY---- + rt5621_write_mask(codec, RT5621_PWR_MANAG_ADD1, 0, PWR_MAIN_I2S_EN | + PWR_HP_OUT_ENH_AMP | PWR_HP_OUT_AMP | PWR_MIC1_BIAS_EN); + rt5621_write_mask(codec, RT5621_PWR_MANAG_ADD3, 0, PWR_HP_R_OUT_VOL | + PWR_HP_L_OUT_VOL | PWR_SPK_OUT | PWR_MIC1_FUN_CTRL | + PWR_MIC1_BOOST_MIXER); + rt5621_write_mask(codec, RT5621_PWR_MANAG_ADD2, 0, PWR_DAC_REF_CIR | + PWR_L_DAC_CLK | PWR_R_DAC_CLK | PWR_L_HP_MIXER | PWR_R_HP_MIXER| + PWR_L_ADC_CLK_GAIN | PWR_R_ADC_CLK_GAIN | PWR_L_ADC_REC_MIXER | + PWR_R_ADC_REC_MIXER | PWR_CLASS_AB | PWR_CLASS_D); + break; + + case POWER_STATE_D2_PLAYBACK: //STANDBY of playback + rt5621_write_mask(codec, RT5621_PWR_MANAG_ADD3, 0, /*PWR_HP_R_OUT_VOL | + PWR_HP_L_OUT_VOL |*/ PWR_SPK_OUT); + rt5621_write_mask(codec, RT5621_PWR_MANAG_ADD1, 0, PWR_HP_OUT_ENH_AMP | + PWR_HP_OUT_AMP); + rt5621_write_mask(codec, RT5621_PWR_MANAG_ADD2, 0, PWR_DAC_REF_CIR | + PWR_L_DAC_CLK | PWR_R_DAC_CLK | PWR_L_HP_MIXER | PWR_R_HP_MIXER | + PWR_CLASS_AB | PWR_CLASS_D); + break; + + case POWER_STATE_D2_RECORD: //STANDBY of record + rt5621_write_mask(codec, RT5621_PWR_MANAG_ADD1, 0, PWR_MIC1_BIAS_EN); + rt5621_write_mask(codec, RT5621_PWR_MANAG_ADD2, 0, PWR_L_ADC_CLK_GAIN | + PWR_R_ADC_CLK_GAIN | PWR_L_ADC_REC_MIXER | PWR_R_ADC_REC_MIXER); + rt5621_write_mask(codec, RT5621_PWR_MANAG_ADD3, 0, PWR_MIC2_BOOST_MIXER | + PWR_MIC1_BOOST_MIXER); + break; + + case POWER_STATE_D3: //SLEEP + case POWER_STATE_D4: //OFF----power off all power + rt5621_write_mask(codec, RT5621_PWR_MANAG_ADD1, 0, PWR_HP_OUT_ENH_AMP | + PWR_HP_OUT_AMP); + snd_soc_write(codec, RT5621_PWR_MANAG_ADD3, 0); + snd_soc_write(codec, RT5621_PWR_MANAG_ADD1, 0); + snd_soc_write(codec, RT5621_PWR_MANAG_ADD2, 0); + break; + + default: + break; + } + + return 0; +} + + +//***************************************************************************** +// +//function AudioOutEnable:Mute/Unmute audio out channel +//WavOutPath:output channel +//Mute :Mute/Unmute output channel +// +//***************************************************************************** +static int rt5621_AudioOutEnable(struct snd_soc_codec *codec, + unsigned short int WavOutPath, int Mute) +{ + int RetVal=0; + + if(Mute) { + switch(WavOutPath) { + case RT_WAVOUT_ALL_ON: + RetVal = rt5621_write_mask(codec, RT5621_SPK_OUT_VOL, RT_L_MUTE | RT_R_MUTE, + RT_L_MUTE | RT_R_MUTE); //Mute Speaker right/left channel + RetVal = rt5621_write_mask(codec, RT5621_HP_OUT_VOL, RT_L_MUTE | RT_R_MUTE, + RT_L_MUTE | RT_R_MUTE); //Mute headphone right/left channel + RetVal = rt5621_write_mask(codec, RT5621_MONO_AUX_OUT_VOL, RT_L_MUTE | + RT_R_MUTE, RT_L_MUTE | RT_R_MUTE); //Mute Aux/Mono right/left channel + RetVal = rt5621_write_mask(codec, RT5621_STEREO_DAC_VOL, RT_M_HP_MIXER | + RT_M_SPK_MIXER | RT_M_MONO_MIXER, RT_M_HP_MIXER | + RT_M_SPK_MIXER | RT_M_MONO_MIXER); //Mute DAC to HP,Speaker,Mono Mixer + break; + + case RT_WAVOUT_HP: + RetVal = rt5621_write_mask(codec, RT5621_HP_OUT_VOL, RT_L_MUTE | RT_R_MUTE, + RT_L_MUTE | RT_R_MUTE); //Mute headphone right/left channel + break; + + case RT_WAVOUT_SPK: + RetVal = rt5621_write_mask(codec, RT5621_SPK_OUT_VOL, RT_L_MUTE | RT_R_MUTE, + RT_L_MUTE | RT_R_MUTE); //Mute Speaker right/left channel + break; + + case RT_WAVOUT_AUXOUT: + RetVal = rt5621_write_mask(codec, RT5621_MONO_AUX_OUT_VOL, RT_L_MUTE | + RT_R_MUTE, RT_L_MUTE | RT_R_MUTE); //Mute AuxOut right/left channel + break; + + case RT_WAVOUT_MONO: + + RetVal = rt5621_write_mask(codec, RT5621_MONO_AUX_OUT_VOL, RT_L_MUTE, + RT_L_MUTE); //Mute MonoOut channel + break; + + case RT_WAVOUT_DAC: + RetVal = rt5621_write_mask(codec, RT5621_STEREO_DAC_VOL, RT_M_HP_MIXER | + RT_M_SPK_MIXER | RT_M_MONO_MIXER, RT_M_HP_MIXER | RT_M_SPK_MIXER | + RT_M_MONO_MIXER); //Mute DAC to HP,Speaker,Mono Mixer + break; + + default: + return 0; + } + } else { + switch(WavOutPath) { + case RT_WAVOUT_ALL_ON: + RetVal = rt5621_write_mask(codec, RT5621_SPK_OUT_VOL, 0, RT_L_MUTE | + RT_R_MUTE); //Mute Speaker right/left channel + RetVal = rt5621_write_mask(codec, RT5621_HP_OUT_VOL, 0, RT_L_MUTE | + RT_R_MUTE); //Mute headphone right/left channel + RetVal = rt5621_write_mask(codec, RT5621_MONO_AUX_OUT_VOL, 0, RT_L_MUTE | + RT_R_MUTE); //Mute Aux/Mono right/left channel + RetVal = rt5621_write_mask(codec, RT5621_STEREO_DAC_VOL, 0, RT_M_HP_MIXER | + RT_M_SPK_MIXER | RT_M_MONO_MIXER); //Mute DAC to HP,Speaker,Mono Mixer + break; + case RT_WAVOUT_HP: + RetVal = rt5621_write_mask(codec, RT5621_HP_OUT_VOL, 0, RT_L_MUTE | + RT_R_MUTE); //UnMute headphone right/left channel + break; + + case RT_WAVOUT_SPK: + RetVal = rt5621_write_mask(codec, RT5621_SPK_OUT_VOL, 0, RT_L_MUTE | + RT_R_MUTE); //unMute Speaker right/left channel + break; + case RT_WAVOUT_AUXOUT: + RetVal = rt5621_write_mask(codec, RT5621_MONO_AUX_OUT_VOL, 0, RT_L_MUTE | + RT_R_MUTE); //unMute AuxOut right/left channel + break; + + case RT_WAVOUT_MONO: + RetVal = rt5621_write_mask(codec, RT5621_MONO_AUX_OUT_VOL, 0, + RT_L_MUTE); //unMute MonoOut channel + break; + + case RT_WAVOUT_DAC: + RetVal = rt5621_write_mask(codec, RT5621_STEREO_DAC_VOL, 0, RT_M_HP_MIXER | + RT_M_SPK_MIXER | RT_M_MONO_MIXER); //unMute DAC to HP,Speaker,Mono Mixer + break; + default: + return 0; + } + + } + + return RetVal; +} + + +//***************************************************************************** +// +//function:Enable/Disable ADC input source control +// +//***************************************************************************** +static int Enable_ADC_Input_Source(struct snd_soc_codec *codec,unsigned short int ADC_Input_Sour,int Enable) +{ + int bRetVal=0; + + if(Enable) { + //Enable ADC source + bRetVal=rt5621_write_mask(codec,RT5621_ADC_REC_MIXER,0,ADC_Input_Sour); + } else { + //Disable ADC source + bRetVal=rt5621_write_mask(codec,RT5621_ADC_REC_MIXER,ADC_Input_Sour,ADC_Input_Sour); + } + + return bRetVal; +} + +static void rt5621_update_eqmode(struct snd_soc_codec *codec, int mode) +{ + u16 HwEqIndex=0; + + if (mode == NORMAL) { + /*clear EQ parameter*/ + for (HwEqIndex=0; HwEqIndex<=0x0C; HwEqIndex++) { + + rt5621_write_index_reg(codec, HwEqIndex, HwEq_Preset[mode].EqValue[HwEqIndex]) + } + + snd_soc_write(codec, 0x62, 0x0); /*disable EQ block*/ + } else { + snd_soc_write(codec, 0x62, HwEq_Preset[mode].HwEQCtrl); + + /*Fill EQ parameter*/ + for (HwEqIndex=0; HwEqIndex<=0x0C; HwEqIndex++) { + + rt5621_write_index_reg(codec, HwEqIndex, HwEq_Preset[mode].EqValue[HwEqIndex]) + } + //update EQ parameter + snd_soc_write(codec, 0x66, 0x1f); + schedule_timeout_uninterruptible(msecs_to_jiffies(1)); + snd_soc_write(codec, 0x66, 0x0); + } +} + +static const struct snd_kcontrol_new rt5621_snd_controls[] = { +SOC_DOUBLE("Speaker Playback Volume", RT5621_SPK_OUT_VOL, 8, 0, 31, 1), +SOC_DOUBLE("Speaker Playback Switch", RT5621_SPK_OUT_VOL, 15, 7, 1, 1), +SOC_DOUBLE("Headphone Playback Volume", RT5621_HP_OUT_VOL, 8, 0, 31, 1), +SOC_DOUBLE("Headphone Playback Switch", RT5621_HP_OUT_VOL,15, 7, 1, 1), +SOC_DOUBLE("AUX Playback Volume", RT5621_MONO_AUX_OUT_VOL, 8, 0, 31, 1), +SOC_DOUBLE("AUX Playback Switch", RT5621_MONO_AUX_OUT_VOL, 15, 7, 1, 1), +SOC_DOUBLE("PCM Playback Volume", RT5621_STEREO_DAC_VOL, 8, 0, 31, 1), +SOC_DOUBLE("Line In Volume", RT5621_LINE_IN_VOL, 8, 0, 31, 1), +SOC_SINGLE("Mic 1 Volume", RT5621_MIC_VOL, 8, 31, 1), +SOC_SINGLE("Mic 2 Volume", RT5621_MIC_VOL, 0, 31, 1), +SOC_ENUM("Mic 1 Boost", rt5621_enum[5]), +SOC_ENUM("Mic 2 Boost", rt5621_enum[6]), +SOC_ENUM_EXT("Speaker Amp Type", rt5621_enum[7], snd_soc_get_enum_double, rt5621_amp_sel_put), +SOC_DOUBLE("AUX In Volume", RT5621_AUXIN_VOL, 8, 0, 31, 1), +SOC_DOUBLE("Capture Volume", RT5621_ADC_REC_GAIN, 7, 0, 31, 0), +}; + +void hp_depop_mode2(struct snd_soc_codec *codec) +{ + snd_soc_update_bits(codec, 0x3e, 0x8000, 0x8000); + snd_soc_update_bits(codec, 0x04, 0x8080, 0x8080); + snd_soc_update_bits(codec, 0x3a, 0x0100, 0x0100); + snd_soc_update_bits(codec, 0x3c, 0x2000, 0x2000); + snd_soc_update_bits(codec, 0x3e, 0x0600, 0x0600); + snd_soc_update_bits(codec, 0x5e, 0x0200, 0x0200); + schedule_timeout_uninterruptible(msecs_to_jiffies(300)); +} + +void aux_depop_mode2(struct snd_soc_codec *codec) +{ + snd_soc_update_bits(codec, 0x3e, 0x8000, 0x8000); + snd_soc_update_bits(codec, 0x06, 0x8080, 0x8080); + snd_soc_update_bits(codec, 0x3a, 0x0100, 0x0100); + snd_soc_update_bits(codec, 0x3c, 0x2000, 0x2000); + snd_soc_update_bits(codec, 0x3e, 0x6000, 0x6000); + snd_soc_update_bits(codec, 0x5e, 0x0020, 0x0200); + schedule_timeout_uninterruptible(msecs_to_jiffies(300)); + snd_soc_update_bits(codec, 0x3a, 0x0002, 0x0002); + snd_soc_update_bits(codec, 0x3a, 0x0001, 0x0001); +} + +static int rt5621_pcm_hw_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *codec_dai) +{ + + struct snd_soc_codec *codec = codec_dai->codec; + int stream = substream->stream; + + switch (stream) + { + case SNDRV_PCM_STREAM_PLAYBACK: + + rt5621_ChangeCodecPowerStatus(codec,POWER_STATE_D1_PLAYBACK); //power on dac to hp and speaker out + + rt5621_AudioOutEnable(codec,RT_WAVOUT_SPK,0); //unmute speaker out + + rt5621_AudioOutEnable(codec,RT_WAVOUT_HP,0); //unmute hp + + rt5621_AudioOutEnable(codec,RT_WAVOUT_AUXOUT,0); //unmute auxout out + + break; + case SNDRV_PCM_STREAM_CAPTURE: + + rt5621_ChangeCodecPowerStatus(codec,POWER_STATE_D1_RECORD); //power on input to adc + + Enable_ADC_Input_Source(codec,RT_WAVIN_L_MIC1|RT_WAVIN_R_MIC1,1); //enable record source from mic1 + + break; + } + + return 0; +} + +/* PLL divisors */ +struct _pll_div { + u32 pll_in; + u32 pll_out; + u16 regvalue; +}; + +static const struct _pll_div codec_pll_div[] = { + + { 2048000, 8192000, 0x0ea0}, + { 3686400, 8192000, 0x4e27}, + { 12000000, 8192000, 0x456b}, + { 13000000, 8192000, 0x495f}, + { 13100000, 8192000, 0x0320}, + { 2048000, 11289600, 0xf637}, + { 3686400, 11289600, 0x2f22}, + { 12000000, 11289600, 0x3e2f}, + { 13000000, 11289600, 0x4d5b}, + { 13100000, 11289600, 0x363b}, + { 2048000, 16384000, 0x1ea0}, + { 3686400, 16384000, 0x9e27}, + { 12000000, 16384000, 0x452b}, + { 13000000, 16384000, 0x542f}, + { 13100000, 16384000, 0x03a0}, + { 2048000, 16934400, 0xe625}, + { 3686400, 16934400, 0x9126}, + { 12000000, 16934400, 0x4d2c}, + { 13000000, 16934400, 0x742f}, + { 13100000, 16934400, 0x3c27}, + { 2048000, 22579200, 0x2aa0}, + { 3686400, 22579200, 0x2f20}, + { 12000000, 22579200, 0x7e2f}, + { 13000000, 22579200, 0x742f}, + { 13100000, 22579200, 0x3c27}, + { 2048000, 24576000, 0x2ea0}, + { 3686400, 24576000, 0xee27}, + { 12000000, 24576000, 0x2915}, + { 13000000, 24576000, 0x772e}, + { 13100000, 24576000, 0x0d20}, +}; + +static const struct _pll_div codec_bclk_pll_div[] = { + + { 1536000, 24576000, 0x3ea0}, + { 3072000, 24576000, 0x1ea0}, + { 512000, 24576000, 0x8e90}, + { 256000, 24576000, 0xbe80}, + { 2822400, 11289600, 0x1ee0}, + { 3072000, 12288000, 0x1ee0}, +}; + + +static int rt5621_set_dai_pll(struct snd_soc_dai *dai, + int pll_id,int source, unsigned int freq_in, unsigned int freq_out) +{ + int i; + int ret = -EINVAL; + struct snd_soc_codec *codec = dai->codec; + + if (pll_id < RT5621_PLL_FR_MCLK || pll_id > RT5621_PLL_FR_BCLK) + return -EINVAL; + + //rt5621_write_mask(codec,RT5621_PWR_MANAG_ADD2, 0x0000,0x1000); //disable PLL power + + if (!freq_in || !freq_out) { + + return 0; + } + + if (RT5621_PLL_FR_MCLK == pll_id) { + for (i = 0; i < ARRAY_SIZE(codec_pll_div); i++) { + + if (codec_pll_div[i].pll_in == freq_in && codec_pll_div[i].pll_out == freq_out) + { + snd_soc_update_bits(codec, RT5621_GLOBAL_CLK_CTRL_REG, 0x0000, 0x4000); + snd_soc_write(codec,RT5621_PLL_CTRL,codec_pll_div[i].regvalue);//set PLL parameter + snd_soc_update_bits(codec,RT5621_PWR_MANAG_ADD2, 0x1000,0x1000); //enable PLL power + ret = 0; + } + } + } + else if (RT5621_PLL_FR_BCLK == pll_id) + { + for (i = 0; i < ARRAY_SIZE(codec_bclk_pll_div); i++) + { + if ((freq_in == codec_bclk_pll_div[i].pll_in) && (freq_out == codec_bclk_pll_div[i].pll_out)) + { + snd_soc_update_bits(codec, RT5621_GLOBAL_CLK_CTRL_REG, 0x4000, 0x4000); + snd_soc_write(codec,RT5621_PLL_CTRL,codec_bclk_pll_div[i].regvalue);//set PLL parameter + snd_soc_update_bits(codec,RT5621_PWR_MANAG_ADD2, 0x1000,0x1000); //enable PLL power + ret = 0; + } + } + } + + snd_soc_update_bits(codec,RT5621_GLOBAL_CLK_CTRL_REG,0x8000,0x8000);//Codec sys-clock from PLL + return ret; +} + + +struct _coeff_div { + u32 mclk; + u32 rate; + u16 fs; + u16 regvalue; +}; + +/* codec hifi mclk (after PLL) clock divider coefficients */ +static const struct _coeff_div coeff_div[] = { + /* 8k */ + { 8192000, 8000, 256*4, 0x2a2d}, + {12288000, 8000, 384*4, 0x2c2f}, + + /* 11.025k */ + {11289600, 11025, 256*4, 0x2a2d}, + {16934400, 11025, 384*4, 0x2c2f}, + + /* 16k */ + {12288000, 16000, 384*2, 0x1c2f}, + {16384000, 16000, 256*4, 0x2a2d}, + {24576000, 16000, 384*4, 0x2c2f}, + + /* 22.05k */ + {11289600, 22050, 256*2, 0x1a2d}, + {16934400, 22050, 384*2, 0x1c2f}, + + /* 32k */ + {12288000, 32000, 384 , 0x0c2f}, + {16384000, 32000, 256*2, 0x1a2d}, + {24576000, 32000, 384*2, 0x1c2f}, + + /* 44.1k */ + {11289600, 44100, 256*1, 0x0a2d}, + {22579200, 44100, 256*2, 0x1a2d}, + {45158400, 44100, 256*4, 0x2a2d}, + + /* 48k */ + {12288000, 48000, 256*1, 0x0a2d}, + {24576000, 48000, 256*2, 0x1a2d}, + {49152000, 48000, 256*4, 0x2a2d}, + + //MCLK is 24.576Mhz(for 8k,16k,32k) + {24576000, 8000, 384*8, 0x3c6b}, + {24576000, 16000, 384*4, 0x2c6b}, + {24576000, 32000, 384*2, 0x1c6b}, + + //MCLK is 22.5792mHz(for 11k,22k) + {22579200, 11025, 256*8, 0x3a2d}, + {22579200, 22050, 256*4, 0x2a2d}, +}; + + + +static int get_coeff(int mclk, int rate) +{ + int i; + + DBG("get_coeff mclk=%d,rate=%d\n",mclk,rate); + + for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { + if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) + return i; + } + return -EINVAL; +} + + +/* + * Clock after PLL and dividers + */ + /*in this driver, you have to set sysclk to be 24576000, + * but you don't need to give a clk to be 24576000, our + * internal pll will generate this clock! so it won't make + * you any difficult. + */ +static int rt5621_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt5621_priv *rt5621 = snd_soc_codec_get_drvdata(codec); + + if ((freq >= (256 * 8000)) && (freq <= (512 * 48000))) { + rt5621->sysclk = freq; + return 0; + } + + printk("unsupported sysclk freq %u for audio i2s\n", freq); + + return -EINVAL; +} + + +static int rt5621_set_dai_fmt(struct snd_soc_dai *dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = dai->codec; + u16 iface = 0; + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + iface = 0x0000; + break; + case SND_SOC_DAIFMT_CBS_CFS: + iface = 0x8000; + break; + default: + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + iface |= 0x0000; + break; + case SND_SOC_DAIFMT_RIGHT_J: + iface |= 0x0001; + break; + case SND_SOC_DAIFMT_LEFT_J: + iface |= 0x0002; + break; + case SND_SOC_DAIFMT_DSP_A: + iface |= 0x0003; + break; + case SND_SOC_DAIFMT_DSP_B: + iface |= 0x4003; + break; + default: + return -EINVAL; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + iface |= 0x0000; + break; + case SND_SOC_DAIFMT_IB_NF: + iface |= 0x0100; + break; + default: + return -EINVAL; + } + + snd_soc_write(codec,RT5621_AUDIO_INTERFACE,iface); + return 0; +} + + +static int rt5621_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt5621_priv *rt5621 = snd_soc_codec_get_drvdata(codec); + u16 iface = snd_soc_read(codec,RT5621_AUDIO_INTERFACE)&0xfff3; + int coeff = get_coeff(rt5621->sysclk, params_rate(params)); + + DBG("rt5621_pcm_hw_params\n"); + if (coeff < 0) + coeff = get_coeff(24576000, params_rate(params)); /*if not set sysclk, default to be 24.576MHz*/ + + /* bit size */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + iface |= 0x0000; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + iface |= 0x0004; + break; + case SNDRV_PCM_FORMAT_S24_LE: + iface |= 0x0008; + break; + case SNDRV_PCM_FORMAT_S32_LE: + iface |= 0x000c; + break; + } + + /* set iface & srate */ + snd_soc_write(codec, RT5621_AUDIO_INTERFACE, iface); + + if (coeff >= 0) + snd_soc_write(codec, RT5621_STEREO_AD_DA_CLK_CTRL, coeff_div[coeff].regvalue); + else + { + printk(KERN_ERR "cant find matched sysclk and rate config\n"); + return -EINVAL; + + } + return 0; +} + +static int rt5621_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_ON: + snd_soc_update_bits(codec, RT5621_SPK_OUT_VOL, RT_L_MUTE | RT_R_MUTE, 0); + snd_soc_update_bits(codec, RT5621_HP_OUT_VOL, RT_L_MUTE | RT_R_MUTE, 0); + break; + + case SND_SOC_BIAS_PREPARE: + break; + + case SND_SOC_BIAS_STANDBY: + snd_soc_update_bits(codec, RT5621_SPK_OUT_VOL, RT_L_MUTE | RT_R_MUTE, RT_L_MUTE | RT_R_MUTE); + snd_soc_update_bits(codec, RT5621_HP_OUT_VOL, RT_L_MUTE | RT_R_MUTE, RT_L_MUTE | RT_R_MUTE); + if (SND_SOC_BIAS_OFF == codec->dapm.bias_level) { + snd_soc_write(codec, RT5621_PWR_MANAG_ADD3, 0x8000);//enable Main bias + snd_soc_write(codec, RT5621_PWR_MANAG_ADD2, 0x2000);//enable Vref + codec->cache_only = false; + snd_soc_cache_sync(codec); + } + break; + + case SND_SOC_BIAS_OFF: + snd_soc_update_bits(codec, RT5621_SPK_OUT_VOL, RT_L_MUTE | RT_R_MUTE, RT_L_MUTE | RT_R_MUTE); + snd_soc_update_bits(codec, RT5621_HP_OUT_VOL, RT_L_MUTE | RT_R_MUTE, RT_L_MUTE | RT_R_MUTE); + snd_soc_write(codec, RT5621_PWR_MANAG_ADD3, 0x0000); + snd_soc_write(codec, RT5621_PWR_MANAG_ADD2, 0x0000); + snd_soc_write(codec, RT5621_PWR_MANAG_ADD1, 0x0000); + break; + + default: + break; + } + codec->dapm.bias_level = level; + + return 0; +} + + + +struct rt5621_init_reg{ + + u8 reg_index; + u16 reg_value; +}; + +static struct rt5621_init_reg init_data[] = { + {RT5621_AUDIO_INTERFACE, 0x8000}, //set I2S codec to slave mode + {RT5621_STEREO_DAC_VOL, 0x0505}, //default stereo DAC volume to 0db + {RT5621_OUTPUT_MIXER_CTRL, 0x2b40}, //default output mixer control + {RT5621_ADC_REC_MIXER, 0x3f3f}, //set record source is Mic1 by default + {RT5621_MIC_CTRL, 0x0a00}, //set Mic1,Mic2 boost 20db + {RT5621_SPK_OUT_VOL, 0x8080}, //default speaker volume to 0db + {RT5621_HP_OUT_VOL, 0x8080}, //default HP volume to -12db + {RT5621_ADD_CTRL_REG, 0x4b00}, //Class AB/D speaker ratio is 1.25VDD + {RT5621_STEREO_AD_DA_CLK_CTRL, 0x066d}, //set Dac filter to 256fs + {RT5621_ADC_REC_GAIN, 0xfa95}, //set ADC boost to 15db + {RT5621_HID_CTRL_INDEX, 0x46}, //Class D setting + {RT5621_MIC_VOL, 0x1f08}, + {RT5621_HID_CTRL_DATA, 0xFFFF}, //power on Class D Internal register + {RT5621_JACK_DET_CTRL, 0x4810}, //power on Class D Internal register +}; +#define RT5621_INIT_REG_NUM ARRAY_SIZE(init_data) + +static int rt5621_reg_init(struct snd_soc_codec *codec) +{ + int i; + + for (i = 0; i < RT5621_INIT_REG_NUM; i++) + snd_soc_write(codec, init_data[i].reg_index, init_data[i].reg_value); + + return 0; +} + +void codec_set_spk(bool on) +{ + struct snd_soc_codec *codec = rt5621_codec; + + DBG("%s: %d\n", __func__, on); + + if(!codec) + return; + + if(on){ + DBG("snd_soc_dapm_enable_pin\n"); + snd_soc_dapm_enable_pin(&codec->dapm, "Headphone Jack"); + snd_soc_dapm_enable_pin(&codec->dapm, "Ext Spk"); + } + else{ + + DBG("snd_soc_dapm_disable_pin\n"); + snd_soc_dapm_disable_pin(&codec->dapm, "Headphone Jack"); + snd_soc_dapm_disable_pin(&codec->dapm, "Ext Spk"); + } + + snd_soc_dapm_sync(&codec->dapm); + + return; +} + +static void rt5621_work(struct work_struct *work) +{ + struct snd_soc_codec *codec = rt5621_codec; + + rt5621_set_bias_level(codec, codec->dapm.bias_level); +} + +static int rt5621_probe(struct snd_soc_codec *codec) +{ + int ret; + + ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C); + if (ret != 0) { + dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); + return ret; + } + codec->cache_bypass = 1; + + rt5621_reset(codec); + snd_soc_write(codec, RT5621_PWR_MANAG_ADD3, 0x8000);//enable Main bias + snd_soc_write(codec, RT5621_PWR_MANAG_ADD2, 0x2000);//enable Vref + hp_depop_mode2(codec); + rt5621_reg_init(codec); + +#if ENABLE_EQ_HREQ + + rt5621_write_index_reg(codec, 0x11,0x1); + rt5621_write_index_reg(codec, 0x12,0x1); + rt5621_update_eqmode(codec, HFREQ); + +#endif + rt5621_workq = create_freezable_workqueue("rt5621"); + if (rt5621_workq == NULL) { + printk("wm8900_probe::create_freezeable_workqueue ERROR !"); + kfree(codec); + return -ENOMEM; + } + + rt5621_set_bias_level(codec, SND_SOC_BIAS_PREPARE); + codec->dapm.bias_level = SND_SOC_BIAS_STANDBY; + + queue_delayed_work(rt5621_workq, &delayed_work, + msecs_to_jiffies(caps_charge)); + + codec->dapm.bias_level = SND_SOC_BIAS_STANDBY; + + rt5621_codec = codec; + + return 0; +} + +static int rt5621_remove(struct snd_soc_codec *codec) +{ + rt5621_set_bias_level(codec, SND_SOC_BIAS_OFF); + + cancel_delayed_work_sync(&delayed_work); + return 0; +} + +#ifdef CONFIG_PM +static int rt5621_suspend(struct snd_soc_codec *codec, pm_message_t state) +{ + rt5621_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +static int rt5621_resume(struct snd_soc_codec *codec) +{ + + rt5621_reset(codec); + snd_soc_write(codec, RT5621_PWR_MANAG_ADD3, 0x8000);//enable Main bias + snd_soc_write(codec, RT5621_PWR_MANAG_ADD2, 0x2000);//enable Vref + + hp_depop_mode2(codec); + + rt5621_reg_init(codec); + +#if ENABLE_EQ_HREQ + + rt5621_write_index_reg(codec, 0x11,0x1); + rt5621_write_index_reg(codec, 0x12,0x1); + rt5621_update_eqmode(codec, HFREQ); +#endif + + rt5621_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + if (codec->dapm.suspend_bias_level == SND_SOC_BIAS_ON) { + rt5621_set_bias_level(codec, SND_SOC_BIAS_PREPARE); + codec->dapm.bias_level = SND_SOC_BIAS_ON; + queue_delayed_work(rt5621_workq, &delayed_work, + msecs_to_jiffies(caps_charge)); + } + return 0; +} +#else +#define rt5621_suspend NULL +#define rt5621_resume NULL +#endif + +static void rt5621_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *codec_dai) +{ + struct snd_soc_codec *codec = codec_dai->codec; + int stream = substream->stream; + + switch (stream) + { + case SNDRV_PCM_STREAM_PLAYBACK: + + rt5621_AudioOutEnable(codec,RT_WAVOUT_SPK,1); //mute speaker out + + rt5621_AudioOutEnable(codec,RT_WAVOUT_HP,1); //mute hp out + + rt5621_AudioOutEnable(codec,RT_WAVOUT_AUXOUT,1); //mute auxout + + rt5621_ChangeCodecPowerStatus(codec,POWER_STATE_D2_PLAYBACK); //power off dac to hp and speaker out and auxout + + + + break; + case SNDRV_PCM_STREAM_CAPTURE: + + Enable_ADC_Input_Source(codec,RT_WAVIN_L_MIC1|RT_WAVIN_R_MIC1,0); //disable record source from mic1 + + rt5621_ChangeCodecPowerStatus(codec,POWER_STATE_D2_RECORD); + + + break; + } +} + +//#define RT5621_HIFI_RATES SNDRV_PCM_RATE_8000_48000 +#define RT5621_HIFI_RATES (SNDRV_PCM_RATE_44100) // zyy 20110704, playback and record use same sample rate + +#define RT5621_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +struct snd_soc_dai_ops rt5621_hifi_ops = { + .hw_params = rt5621_pcm_hw_params, + .set_fmt = rt5621_set_dai_fmt, + .set_sysclk = rt5621_set_dai_sysclk, + .set_pll = rt5621_set_dai_pll, + .prepare = rt5621_pcm_hw_prepare, + .shutdown = rt5621_shutdown, +}; + +struct snd_soc_dai_driver rt5621_dai = { + .name = "RT5621 HiFi", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5621_HIFI_RATES, + .formats = RT5621_FORMATS, + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5621_HIFI_RATES, + .formats = RT5621_FORMATS, + }, + .ops = &rt5621_hifi_ops, +}; + +static struct snd_soc_codec_driver soc_codec_dev_rt5621 = { + .probe = rt5621_probe, + .remove = rt5621_remove, + .suspend = rt5621_suspend, + .resume = rt5621_resume, + .set_bias_level = rt5621_set_bias_level, + .reg_cache_size = RT5621_VENDOR_ID2 + 1, + .reg_word_size = sizeof(u16), + .reg_cache_default = rt5621_reg, + .volatile_register = rt5621_volatile_register, + .readable_register = rt5621_readable_register, + .reg_cache_step = 1, + .controls = rt5621_snd_controls, + .num_controls = ARRAY_SIZE(rt5621_snd_controls), +}; + +static const struct i2c_device_id rt5621_i2c_id[] = { + { "rt5621", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rt5621_i2c_id); + +static int rt5621_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct rt5621_priv *rt5621; + int ret; + + rt5621 = kzalloc(sizeof(struct rt5621_priv), GFP_KERNEL); + if (NULL == rt5621) + return -ENOMEM; + + i2c_set_clientdata(i2c, rt5621); + + ret = snd_soc_register_codec(&i2c->dev, + &soc_codec_dev_rt5621, &rt5621_dai, 1); + if (ret < 0) + kfree(rt5621); + + return ret; +} + +static __devexit int rt5621_i2c_remove(struct i2c_client *i2c) +{ + snd_soc_unregister_codec(&i2c->dev); + kfree(i2c_get_clientdata(i2c)); + return 0; +} + +struct i2c_driver rt5621_i2c_driver = { + .driver = { + .name = "RT5621", + .owner = THIS_MODULE, + }, + .probe = rt5621_i2c_probe, + .remove = __devexit_p(rt5621_i2c_remove), + .id_table = rt5621_i2c_id, +}; + +static int __init rt5621_modinit(void) +{ + return i2c_add_driver(&rt5621_i2c_driver); +} +module_init(rt5621_modinit); + +static void __exit rt5621_modexit(void) +{ + i2c_del_driver(&rt5621_i2c_driver); +} +module_exit(rt5621_modexit); + +MODULE_DESCRIPTION("ASoC RT5621 driver"); +MODULE_AUTHOR("Johnny Hsu "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt5621.h b/sound/soc/codecs/rt5621.h new file mode 100644 index 000000000000..9c1459c89360 --- /dev/null +++ b/sound/soc/codecs/rt5621.h @@ -0,0 +1,515 @@ +/* + * rt5621.h -- RT5621 ALSA SoC audio driver + * + * Copyright 2011 Realtek Microelectronics + * Author: Johnny Hsu + * + * This program 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. + */ + +#ifndef __RT5621_H__ +#define __RT5621_H__ + +#define RT5621_RESET 0X00 //RESET CODEC TO DEFAULT +#define RT5621_SPK_OUT_VOL 0X02 //SPEAKER OUT VOLUME +#define RT5621_HP_OUT_VOL 0X04 //HEADPHONE OUTPUT VOLUME +#define RT5621_MONO_AUX_OUT_VOL 0X06 //MONO OUTPUT/AUXOUT VOLUME +#define RT5621_AUXIN_VOL 0X08 //AUXIN VOLUME +#define RT5621_LINE_IN_VOL 0X0A //LINE IN VOLUME +#define RT5621_STEREO_DAC_VOL 0X0C //STEREO DAC VOLUME +#define RT5621_MIC_VOL 0X0E //MICROPHONE VOLUME +#define RT5621_MIC_ROUTING_CTRL 0X10 //MIC ROUTING CONTROL +#define RT5621_ADC_REC_GAIN 0X12 //ADC RECORD GAIN +#define RT5621_ADC_REC_MIXER 0X14 //ADC RECORD MIXER CONTROL +#define RT5621_SOFT_VOL_CTRL_TIME 0X16 //SOFT VOLUME CONTROL TIME +#define RT5621_OUTPUT_MIXER_CTRL 0X1C //OUTPUT MIXER CONTROL +#define RT5621_MIC_CTRL 0X22 //MICROPHONE CONTROL +#define RT5621_AUDIO_INTERFACE 0X34 //AUDIO INTERFACE +#define RT5621_STEREO_AD_DA_CLK_CTRL 0X36 //STEREO AD/DA CLOCK CONTROL +#define RT5621_COMPANDING_CTRL 0X38 //COMPANDING CONTROL +#define RT5621_PWR_MANAG_ADD1 0X3A //POWER MANAGMENT ADDITION 1 +#define RT5621_PWR_MANAG_ADD2 0X3C //POWER MANAGMENT ADDITION 2 +#define RT5621_PWR_MANAG_ADD3 0X3E //POWER MANAGMENT ADDITION 3 +#define RT5621_ADD_CTRL_REG 0X40 //ADDITIONAL CONTROL REGISTER +#define RT5621_GLOBAL_CLK_CTRL_REG 0X42 //GLOBAL CLOCK CONTROL REGISTER +#define RT5621_PLL_CTRL 0X44 //PLL CONTROL +#define RT5621_GPIO_OUTPUT_PIN_CTRL 0X4A //GPIO OUTPUT PIN CONTROL +#define RT5621_GPIO_PIN_CONFIG 0X4C //GPIO PIN CONFIGURATION +#define RT5621_GPIO_PIN_POLARITY 0X4E //GPIO PIN POLARITY/TYPE +#define RT5621_GPIO_PIN_STICKY 0X50 //GPIO PIN STICKY +#define RT5621_GPIO_PIN_WAKEUP 0X52 //GPIO PIN WAKE UP +#define RT5621_GPIO_PIN_STATUS 0X54 //GPIO PIN STATUS +#define RT5621_GPIO_PIN_SHARING 0X56 //GPIO PIN SHARING +#define RT5621_OVER_TEMP_CURR_STATUS 0X58 //OVER TEMPERATURE AND CURRENT STATUS +#define RT5621_JACK_DET_CTRL 0X5A //JACK DETECT CONTROL REGISTER +#define RT5621_MISC_CTRL 0X5E //MISC CONTROL +#define RT5621_PSEDUEO_SPATIAL_CTRL 0X60 //PSEDUEO STEREO & SPATIAL EFFECT BLOCK CONTROL +#define RT5621_EQ_CTRL 0X62 //EQ CONTROL +#define RT5621_EQ_MODE_ENABLE 0X66 //EQ MODE CHANGE ENABLE +#define RT5621_AVC_CTRL 0X68 //AVC CONTROL +#define RT5621_HID_CTRL_INDEX 0X6A //HIDDEN CONTROL INDEX PORT +#define RT5621_HID_CTRL_DATA 0X6C //HIDDEN CONTROL DATA PORT +#define RT5621_VENDOR_ID1 0x7C //VENDOR ID1 +#define RT5621_VENDOR_ID2 0x7E //VENDOR ID2 + + +//global definition +#define RT_L_MUTE (0x1<<15) //MUTE LEFT CONTROL BIT +#define RT_L_ZC (0x1<<14) //LEFT ZERO CROSS CONTROL BIT +#define RT_L_SM (0x1<<13) //LEFT SOFTMUTE CONTROL BIT +#define RT_R_MUTE (0x1<<7) //MUTE RIGHT CONTROL BIT +#define RT_R_ZC (0x1<<6) //RIGHT ZERO CROSS CONTROL BIT +#define RT_R_SM (0x1<<5) //RIGHT SOFTMUTE CONTROL BIT +#define RT_M_HP_MIXER (0x1<<15) //Mute source to HP Mixer +#define RT_M_SPK_MIXER (0x1<<14) //Mute source to Speaker Mixer +#define RT_M_MONO_MIXER (0x1<<13) //Mute source to Mono Mixer +#define SPK_CLASS_AB 0 +#define SPK_CLASS_D 1 + +//Mic Routing Control(0x10) +#define M_MIC1_TO_HP_MIXER (0x1<<15) //Mute MIC1 to HP mixer +#define M_MIC1_TO_SPK_MIXER (0x1<<14) //Mute MiC1 to SPK mixer +#define M_MIC1_TO_MONO_MIXER (0x1<<13) //Mute MIC1 to MONO mixer +#define MIC1_DIFF_INPUT_CTRL (0x1<<12) //MIC1 different input control +#define M_MIC2_TO_HP_MIXER (0x1<<7) //Mute MIC2 to HP mixer +#define M_MIC2_TO_SPK_MIXER (0x1<<6) //Mute MiC2 to SPK mixer +#define M_MIC2_TO_MONO_MIXER (0x1<<5) //Mute MIC2 to MONO mixer +#define MIC2_DIFF_INPUT_CTRL (0x1<<4) //MIC2 different input control + +//ADC Record Gain(0x12) +#define M_ADC_L_TO_HP_MIXER (0x1<<15) //Mute left of ADC to HP Mixer +#define M_ADC_R_TO_HP_MIXER (0x1<<14) //Mute right of ADC to HP Mixer +#define M_ADC_L_TO_MONO_MIXER (0x1<<13) //Mute left of ADC to MONO Mixer +#define M_ADC_R_TO_MONO_MIXER (0x1<<12) //Mute right of ADC to MONO Mixer +#define ADC_L_GAIN_MASK (0x1f<<7) //ADC Record Gain Left channel Mask +#define ADC_L_ZC_DET (0x1<<6) //ADC Zero-Cross Detector Control +#define ADC_R_ZC_DET (0x1<<5) //ADC Zero-Cross Detector Control +#define ADC_R_GAIN_MASK (0x1f<<0) //ADC Record Gain Right channel Mask + +//ADC Input Mixer Control(0x14) +#define M_MIC1_TO_ADC_L_MIXER (0x1<<14) //Mute mic1 to left channel of ADC mixer +#define M_MIC2_TO_ADC_L_MIXER (0x1<<13) //Mute mic2 to left channel of ADC mixer +#define M_LINEIN_L_TO_ADC_L_MIXER (0x1<<12) //Mute line In left channel to left channel of ADC mixer +#define M_AUXIN_L_TO_ADC_L_MIXER (0x1<<11) //Mute aux In left channel to left channel of ADC mixer +#define M_HPMIXER_L_TO_ADC_L_MIXER (0x1<<10) //Mute HP mixer left channel to left channel of ADC mixer +#define M_SPKMIXER_L_TO_ADC_L_MIXER (0x1<<9) //Mute SPK mixer left channel to left channel of ADC mixer +#define M_MONOMIXER_L_TO_ADC_L_MIXER (0x1<<8) //Mute MONO mixer left channel to left channel of ADC mixer +#define M_MIC1_TO_ADC_R_MIXER (0x1<<6) //Mute mic1 to right channel of ADC mixer +#define M_MIC2_TO_ADC_R_MIXER (0x1<<5) //Mute mic2 to right channel of ADC mixer +#define M_LINEIN_R_TO_ADC_R_MIXER (0x1<<4) //Mute lineIn right channel to right channel of ADC mixer +#define M_AUXIN_R_TO_ADC_R_MIXER (0x1<<3) //Mute aux In right channel to right channel of ADC mixer +#define M_HPMIXER_R_TO_ADC_R_MIXER (0x1<<2) //Mute HP mixer right channel to right channel of ADC mixer +#define M_SPKMIXER_R_TO_ADC_R_MIXER (0x1<<1) //Mute SPK mixer right channel to right channel of ADC mixer +#define M_MONOMIXER_R_TO_ADC_R_MIXER (0x1<<0) //Mute MONO mixer right channel to right channel of ADC mixer + +//Output Mixer Control(0x1C) +#define SPKOUT_N_SOUR_MASK (0x3<<14) +#define SPKOUT_N_SOUR_LN (0x2<<14) +#define SPKOUT_N_SOUR_RP (0x1<<14) +#define SPKOUT_N_SOUR_RN (0x0<<14) +#define SPK_OUTPUT_CLASS_AB (0x0<<13) +#define SPK_OUTPUT_CLASS_D (0x1<<13) +#define SPK_CLASS_AB_S_AMP (0x0<<12) +#define SPK_CALSS_AB_W_AMP (0x1<<12) +#define SPKOUT_INPUT_SEL_MASK (0x3<<10) +#define SPKOUT_INPUT_SEL_MONOMIXER (0x3<<10) +#define SPKOUT_INPUT_SEL_SPKMIXER (0x2<<10) +#define SPKOUT_INPUT_SEL_HPMIXER (0x1<<10) +#define SPKOUT_INPUT_SEL_VMID (0x0<<10) +#define HPL_INPUT_SEL_HPLMIXER (0x1<<9) +#define HPR_INPUT_SEL_HPRMIXER (0x1<<8) +#define MONO_AUX_INPUT_SEL_MASK (0x3<<6) +#define MONO_AUX_INPUT_SEL_MONO (0x3<<6) +#define MONO_AUX_INPUT_SEL_SPK (0x2<<6) +#define MONO_AUX_INPUT_SEL_HP (0x1<<6) +#define MONO_AUX_INPUT_SEL_VMID (0x0<<6) + +//Micphone Control define(0x22) +#define MIC1 1 +#define MIC2 2 +#define MIC_BIAS_90_PRECNET_AVDD 1 +#define MIC_BIAS_75_PRECNET_AVDD 2 + +#define MIC1_BOOST_CTRL_MASK (0x3<<10) +#define MIC1_BOOST_CTRL_BYPASS (0x0<<10) +#define MIC1_BOOST_CTRL_20DB (0x1<<10) +#define MIC1_BOOST_CTRL_30DB (0x2<<10) +#define MIC1_BOOST_CTRL_40DB (0x3<<10) + +#define MIC2_BOOST_CTRL_MASK (0x3<<8) +#define MIC2_BOOST_CTRL_BYPASS (0x0<<8) +#define MIC2_BOOST_CTRL_20DB (0x1<<8) +#define MIC2_BOOST_CTRL_30DB (0x2<<8) +#define MIC2_BOOST_CTRL_40DB (0x3<<8) + +#define MICBIAS_VOLT_CTRL_MASK (0x1<<5) +#define MICBIAS_VOLT_CTRL_90P (0x0<<5) +#define MICBIAS_VOLT_CTRL_75P (0x1<<5) + +#define MICBIAS_SHORT_CURR_DET_MASK (0x3) +#define MICBIAS_SHORT_CURR_DET_600UA (0x0) +#define MICBIAS_SHORT_CURR_DET_1200UA (0x1) +#define MICBIAS_SHORT_CURR_DET_1800UA (0x2) + +//Audio Interface(0x34) +#define SDP_MASTER_MODE (0x0<<15) //Main I2S interface select Master mode +#define SDP_SLAVE_MODE (0x1<<15) //Main I2S interface select Slave mode +#define I2S_PCM_MODE (0x1<<14) //PCM 0:mode A ,1:mode B +#define MAIN_I2S_BCLK_POL_CTRL (0x1<<7) //0:Normal 1:Invert +#define ADC_DATA_L_R_SWAP (0x1<<5) //0:ADC data appear at left phase of LRCK + //1:ADC data appear at right phase of LRCK +#define DAC_DATA_L_R_SWAP (0x1<<4) //0:DAC data appear at left phase of LRCK + //1:DAC data appear at right phase of LRCK +//Data Length Slection +#define I2S_DL_MASK (0x3<<2) //main i2s Data Length mask +#define I2S_DL_16 (0x0<<2) //16 bits +#define I2S_DL_20 (0x1<<2) //20 bits +#define I2S_DL_24 (0x2<<2) //24 bits +#define I2S_DL_32 (0x3<<2) //32 bits + +//PCM Data Format Selection +#define I2S_DF_MASK (0x3) //main i2s Data Format mask +#define I2S_DF_I2S (0x0) //I2S FORMAT +#define I2S_DF_RIGHT (0x1) //RIGHT JUSTIFIED format +#define I2S_DF_LEFT (0x2) //LEFT JUSTIFIED format +#define I2S_DF_PCM (0x3) //PCM format + +//Stereo AD/DA Clock Control(0x36h) +#define I2S_PRE_DIV_MASK (0x7<<12) +#define I2S_PRE_DIV_1 (0x0<<12) //DIV 1 +#define I2S_PRE_DIV_2 (0x1<<12) //DIV 2 +#define I2S_PRE_DIV_4 (0x2<<12) //DIV 4 +#define I2S_PRE_DIV_8 (0x3<<12) //DIV 8 +#define I2S_PRE_DIV_16 (0x4<<12) //DIV 16 +#define I2S_PRE_DIV_32 (0x5<<12) //DIV 32 + +#define I2S_SCLK_DIV_MASK (0x7<<9) +#define I2S_SCLK_DIV_1 (0x0<<9) //DIV 1 +#define I2S_SCLK_DIV_2 (0x1<<9) //DIV 2 +#define I2S_SCLK_DIV_3 (0x2<<9) //DIV 3 +#define I2S_SCLK_DIV_4 (0x3<<9) //DIV 4 +#define I2S_SCLK_DIV_6 (0x4<<9) //DIV 6 +#define I2S_SCLK_DIV_8 (0x5<<9) //DIV 8 +#define I2S_SCLK_DIV_12 (0x6<<9) //DIV 12 +#define I2S_SCLK_DIV_16 (0x7<<9) //DIV 16 + +#define I2S_WCLK_DIV_PRE_MASK (0xF<<5) +#define I2S_WCLK_PRE_DIV_1 (0x0<<5) //DIV 1 +#define I2S_WCLK_PRE_DIV_2 (0x1<<5) //DIV 2 +#define I2S_WCLK_PRE_DIV_3 (0x2<<5) //DIV 3 +#define I2S_WCLK_PRE_DIV_4 (0x3<<5) //DIV 4 +#define I2S_WCLK_PRE_DIV_5 (0x4<<5) //DIV 5 +#define I2S_WCLK_PRE_DIV_6 (0x5<<5) //DIV 6 +#define I2S_WCLK_PRE_DIV_7 (0x6<<5) //DIV 7 +#define I2S_WCLK_PRE_DIV_8 (0x7<<5) //DIV 8 +//........................ + +#define I2S_WCLK_DIV_MASK (0x7<<2) +#define I2S_WCLK_DIV_2 (0x0<<2) //DIV 2 +#define I2S_WCLK_DIV_4 (0x1<<2) //DIV 4 +#define I2S_WCLK_DIV_8 (0x2<<2) //DIV 8 +#define I2S_WCLK_DIV_16 (0x3<<2) //DIV 16 +#define I2S_WCLK_DIV_32 (0x4<<2) //DIV 32 + +#define ADDA_FILTER_CLK_SEL_256FS (0<<1) //256FS +#define ADDA_FILTER_CLK_SEL_384FS (1<<1) //384FS + +#define ADDA_OSR_SEL_64FS (0) //64FS +#define ADDA_OSR_SEL_128FS (1) //128FS + +//Power managment addition 1 (0x3A),0:Disable,1:Enable +#define PWR_MAIN_I2S_EN (0x1<<15) +#define PWR_ZC_DET_PD_EN (0x1<<14) +#define PWR_MIC1_BIAS_EN (0x1<<11) +#define PWR_SHORT_CURR_DET_EN (0x1<<10) +#define PWR_SOFTGEN_EN (0x1<<8) +#define PWR_DEPOP_BUF_HP (0x1<<6) +#define PWR_HP_OUT_AMP (0x1<<5) +#define PWR_HP_OUT_ENH_AMP (0x1<<4) +#define PWR_DEPOP_BUF_AUX (0x1<<2) +#define PWR_AUX_OUT_AMP (0x1<<1) +#define PWR_AUX_OUT_ENH_AMP (0x1) + + +//Power managment addition 2(0x3C),0:Disable,1:Enable +#define PWR_CLASS_AB (0x1<<15) +#define PWR_CLASS_D (0x1<<14) +#define PWR_VREF (0x1<<13) +#define PWR_PLL (0x1<<12) +#define PWR_DAC_REF_CIR (0x1<<10) +#define PWR_L_DAC_CLK (0x1<<9) +#define PWR_R_DAC_CLK (0x1<<8) +#define PWR_L_ADC_CLK_GAIN (0x1<<7) +#define PWR_R_ADC_CLK_GAIN (0x1<<6) +#define PWR_L_HP_MIXER (0x1<<5) +#define PWR_R_HP_MIXER (0x1<<4) +#define PWR_SPK_MIXER (0x1<<3) +#define PWR_MONO_MIXER (0x1<<2) +#define PWR_L_ADC_REC_MIXER (0x1<<1) +#define PWR_R_ADC_REC_MIXER (0x1) + +//Power managment addition 3(0x3E),0:Disable,1:Enable +#define PWR_MAIN_BIAS (0x1<<15) +#define PWR_AUXOUT_L_VOL_AMP (0x1<<14) +#define PWR_AUXOUT_R_VOL_AMP (0x1<<13) +#define PWR_SPK_OUT (0x1<<12) +#define PWR_HP_L_OUT_VOL (0x1<<10) +#define PWR_HP_R_OUT_VOL (0x1<<9) +#define PWR_LINEIN_L_VOL (0x1<<7) +#define PWR_LINEIN_R_VOL (0x1<<6) +#define PWR_AUXIN_L_VOL (0x1<<5) +#define PWR_AUXIN_R_VOL (0x1<<4) +#define PWR_MIC1_FUN_CTRL (0x1<<3) +#define PWR_MIC2_FUN_CTRL (0x1<<2) +#define PWR_MIC1_BOOST_MIXER (0x1<<1) +#define PWR_MIC2_BOOST_MIXER (0x1) + + +//Additional Control Register(0x40) +#define AUXOUT_SEL_DIFF (0x1<<15) //Differential Mode +#define AUXOUT_SEL_SE (0x1<<15) //Single-End Mode + +#define SPK_AB_AMP_CTRL_MASK (0x7<<12) +#define SPK_AB_AMP_CTRL_RATIO_225 (0x0<<12) //2.25 Vdd +#define SPK_AB_AMP_CTRL_RATIO_200 (0x1<<12) //2.00 Vdd +#define SPK_AB_AMP_CTRL_RATIO_175 (0x2<<12) //1.75 Vdd +#define SPK_AB_AMP_CTRL_RATIO_150 (0x3<<12) //1.50 Vdd +#define SPK_AB_AMP_CTRL_RATIO_125 (0x4<<12) //1.25 Vdd +#define SPK_AB_AMP_CTRL_RATIO_100 (0x5<<12) //1.00 Vdd + +#define SPK_D_AMP_CTRL_MASK (0x3<<10) +#define SPK_D_AMP_CTRL_RATIO_175 (0x0<<10) //1.75 Vdd +#define SPK_D_AMP_CTRL_RATIO_150 (0x1<<10) //1.50 Vdd +#define SPK_D_AMP_CTRL_RATIO_125 (0x2<<10) //1.25 Vdd +#define SPK_D_AMP_CTRL_RATIO_100 (0x3<<10) //1.00 Vdd + +#define STEREO_DAC_HI_PASS_FILTER_EN (0x1<<9) //Stereo DAC high pass filter enable +#define STEREO_ADC_HI_PASS_FILTER_EN (0x1<<8) //Stereo ADC high pass filter enable + +#define DIG_VOL_BOOST_MASK (0x3<<4) //Digital volume Boost mask +#define DIG_VOL_BOOST_0DB (0x0<<4) //Digital volume Boost 0DB +#define DIG_VOL_BOOST_6DB (0x1<<4) //Digital volume Boost 6DB +#define DIG_VOL_BOOST_12DB (0x2<<4) //Digital volume Boost 12DB +#define DIG_VOL_BOOST_18DB (0x3<<4) //Digital volume Boost 18DB + + +//Global Clock Control Register(0x42) +#define SYSCLK_SOUR_SEL_MASK (0x1<<15) +#define SYSCLK_SOUR_SEL_MCLK (0x0<<15) //system Clock source from MCLK +#define SYSCLK_SOUR_SEL_PLL (0x1<<15) //system Clock source from PLL +#define PLLCLK_SOUR_SEL_MCLK (0x0<<14) //PLL clock source from MCLK +#define PLLCLK_SOUR_SEL_BITCLK (0x1<<14) //PLL clock source from BITCLK + +#define PLLCLK_DIV_RATIO_MASK (0x3<<1) +#define PLLCLK_DIV_RATIO_DIV1 (0x0<<1) //DIV 1 +#define PLLCLK_DIV_RATIO_DIV2 (0x1<<1) //DIV 2 +#define PLLCLK_DIV_RATIO_DIV4 (0x2<<1) //DIV 4 +#define PLLCLK_DIV_RATIO_DIV8 (0x3<<1) //DIV 8 + +#define PLLCLK_PRE_DIV1 (0x0) //DIV 1 +#define PLLCLK_PRE_DIV2 (0x1) //DIV 2 + +//PLL Control(0x44) + +#define PLL_CTRL_M_VAL(m) ((m)&0xf) +#define PLL_CTRL_K_VAL(k) (((k)&0x7)<<4) +#define PLL_CTRL_N_VAL(n) (((n)&0xff)<<8) + +//GPIO Pin Configuration(0x4C) +#define GPIO_PIN_MASK (0x1<<1) +#define GPIO_PIN_SET_INPUT (0x1<<1) +#define GPIO_PIN_SET_OUTPUT (0x0<<1) + +//Pin Sharing(0x56) +#define LINEIN_L_PIN_SHARING (0x1<<15) +#define LINEIN_L_PIN_AS_LINEIN_L (0x0<<15) +#define LINEIN_L_PIN_AS_JD1 (0x1<<15) + +#define LINEIN_R_PIN_SHARING (0x1<<14) +#define LINEIN_R_PIN_AS_LINEIN_R (0x0<<14) +#define LINEIN_R_PIN_AS_JD2 (0x1<<14) + +#define GPIO_PIN_SHARING (0x3) +#define GPIO_PIN_AS_GPIO (0x0) +#define GPIO_PIN_AS_IRQOUT (0x1) +#define GPIO_PIN_AS_PLLOUT (0x3) + +//Jack Detect Control Register(0x5A) +#define JACK_DETECT_MASK (0x3<<14) +#define JACK_DETECT_USE_JD2 (0x3<<14) +#define JACK_DETECT_USE_JD1 (0x2<<14) +#define JACK_DETECT_USE_GPIO (0x1<<14) +#define JACK_DETECT_OFF (0x0<<14) + +#define SPK_EN_IN_HI (0x1<<11) +#define AUX_R_EN_IN_HI (0x1<<10) +#define AUX_L_EN_IN_HI (0x1<<9) +#define HP_EN_IN_HI (0x1<<8) +#define SPK_EN_IN_LO (0x1<<7) +#define AUX_R_EN_IN_LO (0x1<<6) +#define AUX_L_EN_IN_LO (0x1<<5) +#define HP_EN_IN_LO (0x1<<4) + +////MISC CONTROL(0x5E) +#define DISABLE_FAST_VREG (0x1<<15) +#define SPK_CLASS_AB_OC_PD (0x1<<13) +#define SPK_CLASS_AB_OC_DET (0x1<<12) +#define HP_DEPOP_MODE3_EN (0x1<<10) +#define HP_DEPOP_MODE2_EN (0x1<<9) +#define HP_DEPOP_MODE1_EN (0x1<<8) +#define AUXOUT_DEPOP_MODE3_EN (0x1<<6) +#define AUXOUT_DEPOP_MODE2_EN (0x1<<5) +#define AUXOUT_DEPOP_MODE1_EN (0x1<<4) +#define M_DAC_L_INPUT (0x1<<3) +#define M_DAC_R_INPUT (0x1<<2) +#define IRQOUT_INV_CTRL (0x1<<0) + +//Psedueo Stereo & Spatial Effect Block Control(0x60) +#define SPATIAL_CTRL_EN (0x1<<15) +#define ALL_PASS_FILTER_EN (0x1<<14) +#define PSEUDO_STEREO_EN (0x1<<13) +#define STEREO_EXPENSION_EN (0x1<<12) + +#define GAIN_3D_PARA_L_MASK (0x7<<9) +#define GAIN_3D_PARA_L_1_00 (0x0<<9) +#define GAIN_3D_PARA_L_1_25 (0x1<<9) +#define GAIN_3D_PARA_L_1_50 (0x2<<9) +#define GAIN_3D_PARA_L_1_75 (0x3<<9) +#define GAIN_3D_PARA_L_2_00 (0x4<<9) + +#define GAIN_3D_PARA_R_MASK (0x7<<6) +#define GAIN_3D_PARA_R_1_00 (0x0<<6) +#define GAIN_3D_PARA_R_1_25 (0x1<<6) +#define GAIN_3D_PARA_R_1_50 (0x2<<6) +#define GAIN_3D_PARA_R_1_75 (0x3<<6) +#define GAIN_3D_PARA_R_2_00 (0x4<<6) + +#define RATIO_3D_L_MASK (0x3<<4) +#define RATIO_3D_L_0_0 (0x0<<4) +#define RATIO_3D_L_0_66 (0x1<<4) +#define RATIO_3D_L_1_0 (0x2<<4) + +#define RATIO_3D_R_MASK (0x3<<2) +#define RATIO_3D_R_0_0 (0x0<<2) +#define RATIO_3D_R_0_66 (0x1<<2) +#define RATIO_3D_R_1_0 (0x2<<2) + +#define APF_MASK (0x3) +#define APF_FOR_48K (0x3) +#define APF_FOR_44_1K (0x2) +#define APF_FOR_32K (0x1) + +//EQ CONTROL(0x62) + +#define EN_HW_EQ_BLK (0x1<<15) //HW EQ block control +#define EN_HW_EQ_HPF_MODE (0x1<<14) //High Frequency shelving filter mode +#define EN_HW_EQ_SOUR (0x1<<11) //0:DAC PATH,1:ADC PATH +#define EN_HW_EQ_HPF (0x1<<4) //EQ High Pass Filter Control +#define EN_HW_EQ_BP3 (0x1<<3) //EQ Band-3 Control +#define EN_HW_EQ_BP2 (0x1<<2) //EQ Band-2 Control +#define EN_HW_EQ_BP1 (0x1<<1) //EQ Band-1 Control +#define EN_HW_EQ_LPF (0x1<<0) //EQ Low Pass Filter Control + +//EQ Mode Change Enable(0x66) +#define EQ_HPF_CHANGE_EN (0x1<<4) //EQ High Pass Filter Mode Change Enable +#define EQ_BP3_CHANGE_EN (0x1<<3) //EQ Band-3 Pass Filter Mode Change Enable +#define EQ_BP2_CHANGE_EN (0x1<<2) //EQ Band-2 Pass Filter Mode Change Enable +#define EQ_BP1_CHANGE_EN (0x1<<1) //EQ Band-1 Pass Filter Mode Change Enable +#define EQ_LPF_CHANGE_EN (0x1<<0) //EQ Low Pass Filter Mode Change Enable + + +//AVC Control(0x68) +#define AVC_ENABLE (0x1<<15) +#define AVC_TARTGET_SEL_MASK (0x1<<14) +#define AVC_TARTGET_SEL_R (0x1<<14) +#define AVC_TARTGET_SEL_L (0x0<<14) + + +#define RT5621_PLL_FR_MCLK 0 +#define RT5621_PLL_FR_BCLK 1 + + +#define REALTEK_HWDEP 0 + +//WaveOut channel for realtek codec +enum +{ + RT_WAVOUT_SPK =(0x1<<0), + RT_WAVOUT_SPK_R =(0x1<<1), + RT_WAVOUT_SPK_L =(0x1<<2), + RT_WAVOUT_HP =(0x1<<3), + RT_WAVOUT_HP_R =(0x1<<4), + RT_WAVOUT_HP_L =(0x1<<5), + RT_WAVOUT_MONO =(0x1<<6), + RT_WAVOUT_AUXOUT =(0x1<<7), + RT_WAVOUT_AUXOUT_R =(0x1<<8), + RT_WAVOUT_AUXOUT_L =(0x1<<9), + RT_WAVOUT_LINEOUT =(0x1<<10), + RT_WAVOUT_LINEOUT_R =(0x1<<11), + RT_WAVOUT_LINEOUT_L =(0x1<<12), + RT_WAVOUT_DAC =(0x1<<13), + RT_WAVOUT_ALL_ON =(0x1<<14), +}; + +//WaveIn channel for realtek codec +enum +{ + RT_WAVIN_R_MONO_MIXER =(0x1<<0), + RT_WAVIN_R_SPK_MIXER =(0x1<<1), + RT_WAVIN_R_HP_MIXER =(0x1<<2), + RT_WAVIN_R_PHONE =(0x1<<3), + RT_WAVIN_R_AUXIN =(0x1<<3), + RT_WAVIN_R_LINE_IN =(0x1<<4), + RT_WAVIN_R_MIC2 =(0x1<<5), + RT_WAVIN_R_MIC1 =(0x1<<6), + + RT_WAVIN_L_MONO_MIXER =(0x1<<8), + RT_WAVIN_L_SPK_MIXER =(0x1<<9), + RT_WAVIN_L_HP_MIXER =(0x1<<10), + RT_WAVIN_L_PHONE =(0x1<<11), + RT_WAVIN_L_AUXIN =(0x1<<11), + RT_WAVIN_L_LINE_IN =(0x1<<12), + RT_WAVIN_L_MIC2 =(0x1<<13), + RT_WAVIN_L_MIC1 =(0x1<<14), +}; + +enum +{ + POWER_STATE_D0=0, + POWER_STATE_D1, + POWER_STATE_D1_PLAYBACK, + POWER_STATE_D1_RECORD, + POWER_STATE_D2, + POWER_STATE_D2_PLAYBACK, + POWER_STATE_D2_RECORD, + POWER_STATE_D3, + POWER_STATE_D4 + +}; + +#if REALTEK_HWDEP + +struct rt56xx_reg_state +{ + unsigned int reg_index; + unsigned int reg_value; +}; + +struct rt56xx_cmd +{ + size_t number; + struct rt56xx_reg_state __user *buf; +}; + +enum +{ + RT_READ_CODEC_REG_IOCTL = _IOR('R', 0x01, struct rt56xx_cmd), + RT_READ_ALL_CODEC_REG_IOCTL = _IOR('R', 0x02, struct rt56xx_cmd), + RT_WRITE_CODEC_REG_IOCTL = _IOW('R', 0x03, struct rt56xx_cmd), +}; + +#endif + +#endif /* __RT5621_H__ */ diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 83014a7c2e14..8da1cc17b79c 100755 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -10,7 +10,7 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ - +#define DEBUG #include #include #include @@ -3228,7 +3228,7 @@ static int __devexit wm8994_remove(struct platform_device *pdev) static struct platform_driver wm8994_codec_driver = { .driver = { - .name = "wm8994-codec", + .name = "WM8994", .owner = THIS_MODULE, }, .probe = wm8994_probe, diff --git a/sound/soc/rk29/Kconfig b/sound/soc/rk29/Kconfig index 433fb2ba9732..9474a30ea572 100755 --- a/sound/soc/rk29/Kconfig +++ b/sound/soc/rk29/Kconfig @@ -52,14 +52,14 @@ config SND_RK29_SOC_WM8900 help Say Y if you want to add support for SoC audio on rockchip with the WM8900. -config SND_RK29_SOC_alc5621 - tristate "SoC I2S Audio support for rockchip - alc5621" +config SND_RK29_SOC_RT5621 + tristate "SoC I2S Audio support for rockchip - rt5621" depends on SND_RK29_SOC && I2C_RK29 select SND_RK29_SOC_I2S - select SND_SOC_alc5621 + select SND_SOC_RT5621 help Say Y if you want to add support for SoC audio on rockchip - with the alc5621. + with the rt5621. config SND_RK29_SOC_RT5631 tristate "SoC I2S Audio support for rockchip - RT5631" depends on SND_RK29_SOC && I2C_RK29 @@ -105,7 +105,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_alc5621 || 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 choice prompt "Set i2s type" config SND_RK29_CODEC_SOC_MASTER diff --git a/sound/soc/rk29/Makefile b/sound/soc/rk29/Makefile index 657cdab91331..cc0c09b0b40d 100644 --- a/sound/soc/rk29/Makefile +++ b/sound/soc/rk29/Makefile @@ -7,7 +7,7 @@ obj-$(CONFIG_SND_RK29_SOC_I2S) += snd-soc-rockchip-i2s.o # ROCKCHIP Machine Support snd-soc-wm8900-objs := rk29_wm8900.o -snd-soc-alc5621-objs := rk29_alc5621.o +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 @@ -18,7 +18,7 @@ snd-soc-wm8994-objs := rk29_wm8994.o obj-$(CONFIG_SND_RK29_SOC_WM8994) += snd-soc-wm8994.o obj-$(CONFIG_SND_RK29_SOC_WM8988) += snd-soc-wm8988.o obj-$(CONFIG_SND_RK29_SOC_WM8900) += snd-soc-wm8900.o -obj-$(CONFIG_SND_RK29_SOC_alc5621) += snd-soc-alc5621.o +obj-$(CONFIG_SND_RK29_SOC_RT5621) += snd-soc-rt5621.o 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 diff --git a/sound/soc/rk29/rk29_rt5621.c b/sound/soc/rk29/rk29_rt5621.c new file mode 100644 index 000000000000..92c221e6f317 --- /dev/null +++ b/sound/soc/rk29/rk29_rt5621.c @@ -0,0 +1,229 @@ +/* + * rk29_rt5621.c -- SoC audio for rockchip + * + * Driver for rockchip rt5621 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 "../codecs/rt5621.h" +#include "rk29_pcm.h" +#include "rk29_i2s.h" + +#if 0 +#define DBG(x...) printk(KERN_INFO x) +#else +#define DBG(x...) +#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; + unsigned int lrclk = 0; + int ret; + + 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); //by Vincent + 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: + DBG("Enter:%s, %d, Error rate=%d\n",__FUNCTION__,__LINE__,params_rate(params)); + return -EINVAL; + break; + } + DBG("Enter:%s, %d, rate=%d\n",__FUNCTION__,__LINE__,params_rate(params)); + +#if defined (CONFIG_SND_RK29_CODEC_SOC_SLAVE) +#if 0 //use pll from blck + /*Set the pll of rt5621,the Pll source from BITCLK on CPU is master mode*/ + //bitclk is 64fs + ret=snd_soc_dai_set_pll(codec_dai,RT5621_PLL_FR_BCLK,params_rate(params)*64,pll_out); + if (ret < 0) { + DBG("rk29_hw_params_rt5621:failed to set the pll for codec side\n"); + return ret; + } +#endif + /*Set the system clk for codec*/ + ret=snd_soc_dai_set_sysclk(codec_dai, 0,pll_out,SND_SOC_CLOCK_IN); + if (ret < 0) { + DBG("rk29_hw_params_rt5621:failed to set the sysclk for codec side\n"); + return ret; + } +#endif + + + #if defined (CONFIG_SND_RK29_CODEC_SOC_MASTER) + + if((24576000%params_rate(params))==0) //for 8k,16k,32k,48k + { + snd_soc_dai_set_pll(codec_dai,RT5621_PLL_FR_MCLK,pll_out, 24576000); + snd_soc_dai_set_sysclk(codec_dai,0, 24576000, SND_SOC_CLOCK_IN); + } + else if((22579200%params_rate(params))==0) //for 11k,22k,44k + { + snd_soc_dai_set_pll(codec_dai,RT5621_PLL_FR_MCLK,pll_out, 22579200); + snd_soc_dai_set_sysclk(codec_dai,0, 22579200, SND_SOC_CLOCK_IN); + } + +#endif + + +#if defined (CONFIG_SND_RK29_CODEC_SOC_SLAVE) + snd_soc_dai_set_sysclk(cpu_dai, 0, pll_out, 0); + snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_BCLK, (pll_out/4)/params_rate(params)-1); + snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_MCLK, 3); +#endif + + DBG("Enter:%s, %d, LRCK=%d\n",__FUNCTION__,__LINE__,(pll_out/4)/params_rate(params)); + + return 0; +} + +static const struct snd_soc_dapm_widget rt5621_dapm_widgets[] = { + + SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_SPK("Ext Spk", NULL), + SND_SOC_DAPM_HP("Headphone Jack", NULL), + +}; + +static const struct snd_soc_dapm_route audio_map[]={ + + /* Mic Jack --> MIC_IN*/ + {"Mic Bias1", NULL, "Mic Jack"}, + {"MIC1", NULL, "Mic Bias1"}, + /* HP_OUT --> Headphone Jack */ + {"Headphone Jack", NULL, "HPOL"}, + {"Headphone Jack", NULL, "HPOR"}, + /* LINE_OUT --> Ext Speaker */ + {"Ext Spk", NULL, "SPOL"}, + {"Ext Spk", NULL, "SPOR"}, + +} ; + +/* + * Logic for a rt5621 as connected on a rockchip board. + */ +static int rk29_wm8988_init(struct snd_soc_pcm_runtime *rtd) +{ + return 0; +} + +static struct snd_soc_ops rk29_ops = { + .hw_params = rk29_hw_params, +}; + +static struct snd_soc_dai_link rk29_dai = { + .name = "RT5621", + .stream_name = "RT5621 PCM", + .codec_name = "RT5621.0-001a", + .platform_name = "rockchip-audio", + .cpu_dai_name = "rk29_i2s.0", + .codec_dai_name = "RT5621 HiFi", + .init = rk29_wm8988_init, + .ops = &rk29_ops, +}; + +static struct snd_soc_card snd_soc_card_rk29 = { + .name = "RK29_RT5621", + .dai_link = &rk29_dai, + .num_links = 1, +}; + +static struct platform_device *rk29_snd_device; + +static int __init audio_card_init(void) +{ + int ret =0; + + //rk29_speaker = rk29_speaker_init(RK29_PIN6_PB6, GPIO_HIGH, 2, (200*1000*1000)); + + DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + rk29_snd_device = platform_device_alloc("soc-audio", -1); + if (!rk29_snd_device) { + 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) { + DBG("platform device add failed\n"); + platform_device_put(rk29_snd_device); + return ret; + } + 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"); diff --git a/sound/soc/rk29/rk29_wm8994.c b/sound/soc/rk29/rk29_wm8994.c index b666b0c20906..84222e918eef 100755 --- a/sound/soc/rk29/rk29_wm8994.c +++ b/sound/soc/rk29/rk29_wm8994.c @@ -26,79 +26,95 @@ #include "rk29_i2s.h" #include -#if 0 +#if 1 #define DBG(x...) printk(KERN_INFO x) #else #define DBG(x...) #endif -static int rk29_hw_params(struct snd_pcm_substream *substream, +#define HW_PARAMS_FLAG_EQVOL_ON 0x21 +#define HW_PARAMS_FLAG_EQVOL_OFF 0x22 + +static int rk29_aif1_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->dai->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + 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 div_bclk,div_mclk; int ret; struct clk *general_pll; - - DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + + DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + + if ((params->flags == HW_PARAMS_FLAG_EQVOL_ON)||(params->flags == HW_PARAMS_FLAG_EQVOL_OFF)) + { + DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + + if (codec_dai->driver->ops->hw_params) + ret = codec_dai->driver->ops->hw_params(substream, params, codec_dai); //by Vincent + + return 0; + } + /* set codec DAI configuration */ #if defined (CONFIG_SND_RK29_CODEC_SOC_SLAVE) - DBG("Set codec_dai slave\n"); + DBG("Set codec_dai slave\n"); ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); + 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); - DBG("Set codec_dai master\n"); + ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); + DBG("Set codec_dai master\n"); #endif - if (ret < 0) - return ret; + if (ret < 0) + return ret; /* set cpu DAI configuration */ #if defined (CONFIG_SND_RK29_CODEC_SOC_SLAVE) - DBG("Set cpu_dai slave\n"); + DBG("Set cpu_dai slave\n"); ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); + 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); - DBG("Set cpu_dai master\n"); + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); + DBG("Set cpu_dai master\n"); #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: - DBG("Enter:%s, %d, Error rate=%d\n",__FUNCTION__,__LINE__,params_rate(params)); - return -EINVAL; - break; - } - DBG("Enter:%s, %d, rate=%d\n",__FUNCTION__,__LINE__,params_rate(params)); + 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: + DBG("Enter:%s, %d, Error rate=%d\n",__FUNCTION__,__LINE__,params_rate(params)); + return -EINVAL; + break; + } + + DBG("Enter:%s, %d, rate=%d\n",__FUNCTION__,__LINE__,params_rate(params)); + #if defined (CONFIG_SND_RK29_CODEC_SOC_MASTER) snd_soc_dai_set_sysclk(cpu_dai, 0, pll_out, 0); -#endif - +#endif + #if defined (CONFIG_SND_RK29_CODEC_SOC_SLAVE) general_pll=clk_get(NULL, "general_pll"); - if(clk_get_rate(general_pll)>260000000) - { + + if(clk_get_rate(general_pll)>260000000) { div_bclk=(pll_out/4)/params_rate(params)-1; div_mclk=3; } @@ -113,21 +129,174 @@ static int rk29_hw_params(struct snd_pcm_substream *substream, div_bclk=(pll_out)/params_rate(params)-1; div_mclk=0; } - DBG("func is%s,gpll=%ld,pll_out=%ld,div_mclk=%ld\n", - __FUNCTION__,clk_get_rate(general_pll),pll_out,div_mclk); + DBG("func is%s,gpll=%ld,pll_out=%d,div_mclk=%d\n", + __FUNCTION__,clk_get_rate(general_pll),pll_out,div_mclk); snd_soc_dai_set_sysclk(cpu_dai, 0, pll_out, 0); snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_BCLK,div_bclk); snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_MCLK, div_mclk); + if(div_mclk == 3) + snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_MCLK1, pll_out, 0); + else + snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL1, pll_out, 0); +#endif + + return 0; +} + +static int rk29_aif2_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 div_bclk,div_mclk; + int ret; + struct clk *general_pll; + + DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + + /* set codec DAI configuration */ +#if defined (CONFIG_SND_RK29_CODEC_SOC_SLAVE) + DBG("Set codec_dai slave\n"); + 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); + DBG("Set codec_dai master\n"); +#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: + DBG("Enter:%s, %d, Error rate=%d\n",__FUNCTION__,__LINE__,params_rate(params)); + return -EINVAL; + break; + } + + DBG("Enter:%s, %d, rate=%d\n",__FUNCTION__,__LINE__,params_rate(params)); + +#if defined (CONFIG_SND_RK29_CODEC_SOC_MASTER) + snd_soc_dai_set_sysclk(cpu_dai, 0, pll_out, 0); +#endif + +#if defined (CONFIG_SND_RK29_CODEC_SOC_SLAVE) + general_pll=clk_get(NULL, "general_pll"); + + if(clk_get_rate(general_pll)>260000000) { + div_bclk=(pll_out/4)/params_rate(params)-1; + div_mclk=3; + } + else if(clk_get_rate(general_pll)>130000000) + { + div_bclk=(pll_out/2)/params_rate(params)-1; + div_mclk=1; + } + else + { + pll_out=pll_out/4; + div_bclk=(pll_out)/params_rate(params)-1; + div_mclk=0; + } + DBG("func is%s,gpll=%ld,pll_out=%d,div_mclk=%d\n", + __FUNCTION__,clk_get_rate(general_pll),pll_out,div_mclk); + snd_soc_dai_set_sysclk(cpu_dai, 0, pll_out, 0); + if(div_mclk == 3) snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_MCLK1, pll_out, 0); else snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL1, pll_out, 0); - DBG("Enter:%s, %d, LRCK=%d\n",__FUNCTION__,__LINE__,(pll_out/4)/params_rate(params)); #endif - return 0; + return 0; +} + +static int rk29_aif3_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 div_bclk,div_mclk; + int ret; + struct clk *general_pll; + + DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + + /* set codec DAI configuration */ + ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); + DBG("Set codec_dai master\n"); + + 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: + DBG("Enter:%s, %d, Error rate=%d\n",__FUNCTION__,__LINE__,params_rate(params)); + return -EINVAL; + break; + } + + DBG("Enter:%s, %d, rate=%d\n",__FUNCTION__,__LINE__,params_rate(params)); + + snd_soc_dai_set_sysclk(cpu_dai, 0, pll_out, 0); + + general_pll=clk_get(NULL, "general_pll"); + + if(clk_get_rate(general_pll)>260000000) { + div_bclk=(pll_out/4)/params_rate(params)-1; + div_mclk=3; + } + else if(clk_get_rate(general_pll)>130000000) + { + div_bclk=(pll_out/2)/params_rate(params)-1; + div_mclk=1; + } + else + { + pll_out=pll_out/4; + div_bclk=(pll_out)/params_rate(params)-1; + div_mclk=0; + } + + if(div_mclk == 3) + snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_MCLK1, pll_out, 0); + else + snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL1, pll_out, 0); + + return 0; } + /* static const struct snd_soc_dapm_widget rk2818_dapm_widgets[] = { SND_SOC_DAPM_LINE("Audio Out", NULL), @@ -149,80 +318,108 @@ static const struct snd_soc_dapm_route audio_map[]= { /* * Logic for a wm8994 as connected on a rockchip board. */ -static int rk29_wm8994_init(struct snd_soc_codec *codec) +static int rk29_wm8994_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_dai *codec_dai = &codec->dai[0]; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; int ret; - DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); - ret = snd_soc_dai_set_sysclk(codec_dai, 0, + ret = snd_soc_dai_set_sysclk(codec_dai, 0, 12000000, SND_SOC_CLOCK_IN); if (ret < 0) { printk(KERN_ERR "Failed to set WM8994 SYSCLK: %d\n", ret); return ret; } - /* Add specific widgets */ -// snd_soc_dapm_new_controls(codec, rk2818_dapm_widgets, -// ARRAY_SIZE(rk2818_dapm_widgets)); - /* Set up specific audio path audio_mapnects */ -// snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); -// snd_soc_dapm_sync(codec); + /* Add specific widgets */ +// snd_soc_dapm_new_controls(dapm, rk2818_dapm_widgets, +// ARRAY_SIZE(rk2818_dapm_widgets)); + /* Set up specific audio path audio_mapnects */ +// snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); +// snd_soc_dapm_sync(codec); return 0; } -static struct snd_soc_ops rk29_ops = { - .hw_params = rk29_hw_params, +static struct snd_soc_ops rk29_aif1_ops = { + .hw_params = rk29_aif1_hw_params, }; -static struct snd_soc_dai_link rk29_dai = { - .name = "WM8994", - .stream_name = "WM8994 PCM", -#ifdef CONFIG_MACH_RK29_PHONEPADSDK - .cpu_dai = &rk29_i2s_dai[1], -#else - .cpu_dai = &rk29_i2s_dai[0], -#endif - .codec_dai = &wm8994_dai, - .init = rk29_wm8994_init, - .ops = &rk29_ops, +static struct snd_soc_ops rk29_aif2_ops = { + .hw_params = rk29_aif2_hw_params, }; -static struct snd_soc_card snd_soc_card_rk29 = { - .name = "RK29_WM8994", - .platform = &rk29_soc_platform, - .dai_link = &rk29_dai, - .num_links = 1, +static struct snd_soc_ops rk29_aif3_ops = { + .hw_params = rk29_aif3_hw_params, }; +static struct snd_soc_dai_link rk29_dai[] = { + { + .name = "WM8994", + .stream_name = "WM8994 I2S1", + .codec_name = "WM8994.0-001a", + .platform_name = "rockchip-audio", + .cpu_dai_name = "rk29_i2s.0", + .codec_dai_name = "wm8994-aif1", + .init = rk29_wm8994_init, + .ops = &rk29_aif1_ops, + }, + { + .name = "WM8994", + .stream_name = "WM8994 I2S2", + .codec_name = "WM8994.1-001a", + .platform_name = "rockchip-audio", + .cpu_dai_name = "rk29_i2s.0", + .codec_dai_name = "wm8994-aif2", + .init = rk29_wm8994_init, + .ops = &rk29_aif2_ops, + }, + { + .name = "WM8994", + .stream_name = "WM8994 I2S3", + .codec_name = "WM8994.2-001a", + .platform_name = "rockchip-audio", + .cpu_dai_name = "rk29_i2s.0", + .codec_dai_name = "wm8994-aif3", + .init = rk29_wm8994_init, + .ops = &rk29_aif3_ops, + }, +}; -static struct snd_soc_device rk29_snd_devdata = { - .card = &snd_soc_card_rk29, - .codec_dev = &soc_codec_dev_wm8994, +static struct snd_soc_card snd_soc_card_rk29 = { + .name = "RK29_WM8994", + .dai_link = rk29_dai, + .num_links = 3, }; static struct platform_device *rk29_snd_device; static int __init audio_card_init(void) { - int ret =0; - DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + int ret =0; + + DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + rk29_snd_device = platform_device_alloc("soc-audio", -1); if (!rk29_snd_device) { DBG("platform device allocation failed\n"); ret = -ENOMEM; return ret; } - platform_set_drvdata(rk29_snd_device, &rk29_snd_devdata); - rk29_snd_devdata.dev = &rk29_snd_device->dev; + + platform_set_drvdata(rk29_snd_device, &snd_soc_card_rk29); ret = platform_device_add(rk29_snd_device); if (ret) { - DBG("platform device add failed\n"); - platform_device_put(rk29_snd_device); + DBG("platform device add failed\n"); + + platform_device_put(rk29_snd_device); + return ret; } - return ret; + + return ret; } static void __exit audio_card_exit(void) -- 2.34.1