mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-25 16:31:13 +00:00
e2e2fc3cd0
Add updated patches for 6.6. DMA/cache-handling patches have been reworked / backported from upstream. Signed-off-by: Zoltan HERPAI <wigyori@uid0.hu>
4749 lines
132 KiB
Diff
4749 lines
132 KiB
Diff
From cd2254c6be9441ebacaa35693ecb5ce116b90622 Mon Sep 17 00:00:00 2001
|
|
From: Xingyu Wu <xingyu.wu@starfivetech.com>
|
|
Date: Fri, 16 Jun 2023 16:27:46 +0800
|
|
Subject: [PATCH 079/116] ASoC: codecs: Add AC108 Codec driver
|
|
|
|
Add AC108 Codec driver and AC101 driver for AC10x.
|
|
|
|
Signed-off-by: Xingyu Wu <xingyu.wu@starfivetech.com>
|
|
Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
|
|
---
|
|
sound/soc/codecs/Kconfig | 5 +
|
|
sound/soc/codecs/Makefile | 2 +
|
|
sound/soc/codecs/ac101.c | 1716 +++++++++++++++++++++++++++++++++
|
|
sound/soc/codecs/ac101_regs.h | 431 +++++++++
|
|
sound/soc/codecs/ac108.c | 1622 +++++++++++++++++++++++++++++++
|
|
sound/soc/codecs/ac108.h | 749 ++++++++++++++
|
|
sound/soc/codecs/ac10x.h | 152 +++
|
|
7 files changed, 4677 insertions(+)
|
|
create mode 100644 sound/soc/codecs/ac101.c
|
|
create mode 100644 sound/soc/codecs/ac101_regs.h
|
|
create mode 100644 sound/soc/codecs/ac108.c
|
|
create mode 100644 sound/soc/codecs/ac108.h
|
|
create mode 100644 sound/soc/codecs/ac10x.h
|
|
|
|
--- a/sound/soc/codecs/Kconfig
|
|
+++ b/sound/soc/codecs/Kconfig
|
|
@@ -16,6 +16,7 @@ config SND_SOC_ALL_CODECS
|
|
depends on COMPILE_TEST
|
|
imply SND_SOC_88PM860X
|
|
imply SND_SOC_AB8500_CODEC
|
|
+ imply SND_SOC_AC108
|
|
imply SND_SOC_AC97_CODEC
|
|
imply SND_SOC_AD1836
|
|
imply SND_SOC_AD193X_SPI
|
|
@@ -397,6 +398,10 @@ config SND_SOC_AB8500_CODEC
|
|
tristate
|
|
depends on ABX500_CORE
|
|
|
|
+config SND_SOC_AC108
|
|
+ tristate "AC108"
|
|
+ depends on I2C
|
|
+
|
|
config SND_SOC_AC97_CODEC
|
|
tristate "Build generic ASoC AC97 CODEC driver"
|
|
select SND_AC97_CODEC
|
|
--- a/sound/soc/codecs/Makefile
|
|
+++ b/sound/soc/codecs/Makefile
|
|
@@ -2,6 +2,7 @@
|
|
snd-soc-88pm860x-objs := 88pm860x-codec.o
|
|
snd-soc-ab8500-codec-objs := ab8500-codec.o
|
|
snd-soc-ac97-objs := ac97.o
|
|
+snd-soc-ac108-objs := ac108.o ac101.o
|
|
snd-soc-ad1836-objs := ad1836.o
|
|
snd-soc-ad193x-objs := ad193x.o
|
|
snd-soc-ad193x-spi-objs := ad193x-spi.o
|
|
@@ -386,6 +387,7 @@ snd-soc-simple-mux-objs := simple-mux.o
|
|
|
|
obj-$(CONFIG_SND_SOC_88PM860X) += snd-soc-88pm860x.o
|
|
obj-$(CONFIG_SND_SOC_AB8500_CODEC) += snd-soc-ab8500-codec.o
|
|
+obj-$(CONFIG_SND_SOC_AC108) += snd-soc-ac108.o
|
|
obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o
|
|
obj-$(CONFIG_SND_SOC_AD1836) += snd-soc-ad1836.o
|
|
obj-$(CONFIG_SND_SOC_AD193X) += snd-soc-ad193x.o
|
|
--- /dev/null
|
|
+++ b/sound/soc/codecs/ac101.c
|
|
@@ -0,0 +1,1716 @@
|
|
+/*
|
|
+ * ac101.c
|
|
+ *
|
|
+ * (C) Copyright 2017-2018
|
|
+ * Seeed Technology Co., Ltd. <www.seeedstudio.com>
|
|
+ *
|
|
+ * PeterYang <linsheng.yang@seeed.cc>
|
|
+ *
|
|
+ * (C) Copyright 2014-2017
|
|
+ * Reuuimlla Technology Co., Ltd. <www.reuuimllatech.com>
|
|
+ *
|
|
+ * huangxin <huangxin@Reuuimllatech.com>
|
|
+ * liushaohua <liushaohua@allwinnertech.com>
|
|
+ *
|
|
+ * X-Powers AC101 codec driver
|
|
+ *
|
|
+ * 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 <linux/clk.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/gpio/consumer.h>
|
|
+#include <linux/i2c.h>
|
|
+#include <linux/input.h>
|
|
+#include <linux/irq.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/regmap.h>
|
|
+#include <sound/tlv.h>
|
|
+#include <linux/workqueue.h>
|
|
+#include <sound/pcm.h>
|
|
+#include <sound/pcm_params.h>
|
|
+#include <sound/soc.h>
|
|
+#include <sound/soc-dapm.h>
|
|
+
|
|
+#include "ac101_regs.h"
|
|
+#include "ac10x.h"
|
|
+
|
|
+/* #undef AC101_DEBG
|
|
+ * use 'make DEBUG=1' to enable debugging
|
|
+ */
|
|
+
|
|
+/*
|
|
+ * *** To sync channels ***
|
|
+ *
|
|
+ * 1. disable clock in codec hw_params()
|
|
+ * 2. clear fifo in bcm2835 hw_params()
|
|
+ * 3. clear fifo in bcm2385 prepare()
|
|
+ * 4. enable RX in bcm2835 trigger()
|
|
+ * 5. enable clock in machine trigger()
|
|
+ */
|
|
+
|
|
+/*Default initialize configuration*/
|
|
+static bool speaker_double_used = 1;
|
|
+static int double_speaker_val = 0x1B;
|
|
+static int single_speaker_val = 0x19;
|
|
+static int headset_val = 0x3B;
|
|
+static int mainmic_val = 0x4;
|
|
+static int headsetmic_val = 0x4;
|
|
+static bool dmic_used = 0;
|
|
+static int adc_digital_val = 0xb0b0;
|
|
+static bool drc_used = false;
|
|
+
|
|
+#define AC101_RATES (SNDRV_PCM_RATE_8000_96000 & \
|
|
+ ~(SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_64000 | \
|
|
+ SNDRV_PCM_RATE_88200))
|
|
+#define AC101_FORMATS (/*SNDRV_PCM_FMTBIT_S16_LE | \
|
|
+ SNDRV_PCM_FMTBIT_S24_LE |*/ \
|
|
+ SNDRV_PCM_FMTBIT_S32_LE | \
|
|
+ 0)
|
|
+
|
|
+static struct ac10x_priv* static_ac10x;
|
|
+
|
|
+
|
|
+int ac101_read(struct snd_soc_codec *codec, unsigned reg) {
|
|
+ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
|
+ int r, v = 0;
|
|
+
|
|
+ if ((r = regmap_read(ac10x->regmap101, reg, &v)) < 0) {
|
|
+ dev_err(codec->dev, "read reg %02X fail\n",
|
|
+ reg);
|
|
+ return r;
|
|
+ }
|
|
+ return v;
|
|
+}
|
|
+
|
|
+int ac101_write(struct snd_soc_codec *codec, unsigned reg, unsigned val) {
|
|
+ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
|
+ int v;
|
|
+
|
|
+ v = regmap_write(ac10x->regmap101, reg, val);
|
|
+ return v;
|
|
+}
|
|
+
|
|
+int ac101_update_bits(struct snd_soc_codec *codec, unsigned reg,
|
|
+ unsigned mask, unsigned value
|
|
+) {
|
|
+ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
|
+ int v;
|
|
+
|
|
+ v = regmap_update_bits(ac10x->regmap101, reg, mask, value);
|
|
+ return v;
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+#ifdef CONFIG_AC101_SWITCH_DETECT
|
|
+/******************************************************************************/
|
|
+/********************************switch****************************************/
|
|
+/******************************************************************************/
|
|
+#define KEY_HEADSETHOOK 226 /* key define */
|
|
+#define HEADSET_FILTER_CNT (10)
|
|
+
|
|
+/*
|
|
+ * switch_hw_config:config the 53 codec register
|
|
+ */
|
|
+static void switch_hw_config(struct snd_soc_codec *codec)
|
|
+{
|
|
+ int r;
|
|
+
|
|
+ AC101_DBG();
|
|
+
|
|
+ /*HMIC/MMIC BIAS voltage level select:2.5v*/
|
|
+ ac101_update_bits(codec, OMIXER_BST1_CTRL, (0xf<<BIASVOLTAGE), (0xf<<BIASVOLTAGE));
|
|
+ /*debounce when Key down or keyup*/
|
|
+ ac101_update_bits(codec, HMIC_CTRL1, (0xf<<HMIC_M), (0x0<<HMIC_M));
|
|
+ /*debounce when earphone plugin or pullout*/
|
|
+ ac101_update_bits(codec, HMIC_CTRL1, (0xf<<HMIC_N), (0x0<<HMIC_N));
|
|
+ /*Down Sample Setting Select: Downby 4,32Hz*/
|
|
+ ac101_update_bits(codec, HMIC_CTRL2, (0x3<<HMIC_SAMPLE_SELECT),
|
|
+ (0x02<<HMIC_SAMPLE_SELECT));
|
|
+ /*Hmic_th2 for detecting Keydown or Keyup.*/
|
|
+ ac101_update_bits(codec, HMIC_CTRL2, (0x1f<<HMIC_TH2), (0x8<<HMIC_TH2));
|
|
+ /*Hmic_th1[4:0],detecting eraphone plugin or pullout*/
|
|
+ ac101_update_bits(codec, HMIC_CTRL2, (0x1f<<HMIC_TH1), (0x1<<HMIC_TH1));
|
|
+ /*Headset microphone BIAS working mode: when HBIASEN = 1 */
|
|
+ ac101_update_bits(codec, ADC_APC_CTRL, (0x1<<HBIASMOD), (0x1<<HBIASMOD));
|
|
+ /*Headset microphone BIAS Enable*/
|
|
+ ac101_update_bits(codec, ADC_APC_CTRL, (0x1<<HBIASEN), (0x1<<HBIASEN));
|
|
+ /*Headset microphone BIAS Current sensor & ADC Enable*/
|
|
+ ac101_update_bits(codec, ADC_APC_CTRL, (0x1<<HBIASADCEN), (0x1<<HBIASADCEN));
|
|
+ /*Earphone Plugin/out Irq Enable*/
|
|
+ ac101_update_bits(codec, HMIC_CTRL1, (0x1<<HMIC_PULLOUT_IRQ), (0x1<<HMIC_PULLOUT_IRQ));
|
|
+ ac101_update_bits(codec, HMIC_CTRL1, (0x1<<HMIC_PLUGIN_IRQ), (0x1<<HMIC_PLUGIN_IRQ));
|
|
+
|
|
+ /*Hmic KeyUp/key down Irq Enable*/
|
|
+ ac101_update_bits(codec, HMIC_CTRL1, (0x1<<HMIC_KEYDOWN_IRQ), (0x1<<HMIC_KEYDOWN_IRQ));
|
|
+ ac101_update_bits(codec, HMIC_CTRL1, (0x1<<HMIC_KEYUP_IRQ), (0x1<<HMIC_KEYUP_IRQ));
|
|
+
|
|
+ /*headphone calibration clock frequency select*/
|
|
+ ac101_update_bits(codec, SPKOUT_CTRL, (0x7<<HPCALICKS), (0x7<<HPCALICKS));
|
|
+
|
|
+ /*clear hmic interrupt */
|
|
+ r = HMIC_PEND_ALL;
|
|
+ ac101_write(codec, HMIC_STS, r);
|
|
+
|
|
+ return;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * switch_status_update: update the switch state.
|
|
+ */
|
|
+static void switch_status_update(struct ac10x_priv *ac10x)
|
|
+{
|
|
+ AC101_DBG("ac10x->state:%d\n", ac10x->state);
|
|
+
|
|
+ input_report_switch(ac10x->inpdev, SW_HEADPHONE_INSERT, ac10x->state);
|
|
+ input_sync(ac10x->inpdev);
|
|
+ return;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * work_cb_clear_irq: clear audiocodec pending and Record the interrupt.
|
|
+ */
|
|
+static void work_cb_clear_irq(struct work_struct *work)
|
|
+{
|
|
+ int reg_val = 0;
|
|
+ struct ac10x_priv *ac10x = container_of(work, struct ac10x_priv, work_clear_irq);
|
|
+ struct snd_soc_codec *codec = ac10x->codec;
|
|
+
|
|
+ ac10x->irq_cntr++;
|
|
+
|
|
+ reg_val = ac101_read(codec, HMIC_STS);
|
|
+ if (BIT(HMIC_PULLOUT_PEND) & reg_val) {
|
|
+ ac10x->pullout_cntr++;
|
|
+ AC101_DBG("ac10x->pullout_cntr: %d\n", ac10x->pullout_cntr);
|
|
+ }
|
|
+
|
|
+ reg_val |= HMIC_PEND_ALL;
|
|
+ ac101_write(codec, HMIC_STS, reg_val);
|
|
+
|
|
+ reg_val = ac101_read(codec, HMIC_STS);
|
|
+ if ((reg_val & HMIC_PEND_ALL) != 0){
|
|
+ reg_val |= HMIC_PEND_ALL;
|
|
+ ac101_write(codec, HMIC_STS, reg_val);
|
|
+ }
|
|
+
|
|
+ if (cancel_work_sync(&ac10x->work_switch) != 0) {
|
|
+ ac10x->irq_cntr--;
|
|
+ }
|
|
+
|
|
+ if (0 == schedule_work(&ac10x->work_switch)) {
|
|
+ ac10x->irq_cntr--;
|
|
+ AC101_DBG("[work_cb_clear_irq] add work struct failed!\n");
|
|
+ }
|
|
+}
|
|
+
|
|
+enum {
|
|
+ HBIAS_LEVEL_1 = 0x02,
|
|
+ HBIAS_LEVEL_2 = 0x0B,
|
|
+ HBIAS_LEVEL_3 = 0x13,
|
|
+ HBIAS_LEVEL_4 = 0x17,
|
|
+ HBIAS_LEVEL_5 = 0x19,
|
|
+};
|
|
+
|
|
+static int __ac101_get_hmic_data(struct snd_soc_codec *codec) {
|
|
+ #ifdef AC101_DEBG
|
|
+ static long counter;
|
|
+ #endif
|
|
+ int r, d;
|
|
+
|
|
+ d = GET_HMIC_DATA(ac101_read(codec, HMIC_STS));
|
|
+
|
|
+ r = 0x1 << HMIC_DATA_PEND;
|
|
+ ac101_write(codec, HMIC_STS, r);
|
|
+
|
|
+ /* prevent i2c accessing too frequently */
|
|
+ usleep_range(1500, 3000);
|
|
+
|
|
+ AC101_DBG("HMIC_DATA(%3ld): %02X\n", counter++, d);
|
|
+ return d;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * work_cb_earphone_switch: judge the status of the headphone
|
|
+ */
|
|
+static void work_cb_earphone_switch(struct work_struct *work)
|
|
+{
|
|
+ struct ac10x_priv *ac10x = container_of(work, struct ac10x_priv, work_switch);
|
|
+ struct snd_soc_codec *codec = ac10x->codec;
|
|
+
|
|
+ static int hook_flag1 = 0, hook_flag2 = 0;
|
|
+ static int KEY_VOLUME_FLAG = 0;
|
|
+
|
|
+ unsigned filter_buf = 0;
|
|
+ int filt_index = 0;
|
|
+ int t = 0;
|
|
+
|
|
+ ac10x->irq_cntr--;
|
|
+
|
|
+ /* read HMIC_DATA */
|
|
+ t = __ac101_get_hmic_data(codec);
|
|
+
|
|
+ if ((t >= HBIAS_LEVEL_2) && (ac10x->mode == FOUR_HEADPHONE_PLUGIN)) {
|
|
+ t = __ac101_get_hmic_data(codec);
|
|
+
|
|
+ if (t >= HBIAS_LEVEL_5){
|
|
+ msleep(150);
|
|
+ t = __ac101_get_hmic_data(codec);
|
|
+ if (((t < HBIAS_LEVEL_2 && t >= HBIAS_LEVEL_1 - 1) || t >= HBIAS_LEVEL_5)
|
|
+ && (ac10x->pullout_cntr == 0)) {
|
|
+ input_report_key(ac10x->inpdev, KEY_HEADSETHOOK, 1);
|
|
+ input_sync(ac10x->inpdev);
|
|
+
|
|
+ AC101_DBG("KEY_HEADSETHOOK1\n");
|
|
+
|
|
+ if (hook_flag1 != hook_flag2)
|
|
+ hook_flag1 = hook_flag2 = 0;
|
|
+ hook_flag1++;
|
|
+ }
|
|
+ if (ac10x->pullout_cntr)
|
|
+ ac10x->pullout_cntr--;
|
|
+ } else if (t >= HBIAS_LEVEL_4) {
|
|
+ msleep(80);
|
|
+ t = __ac101_get_hmic_data(codec);
|
|
+ if (t < HBIAS_LEVEL_5 && t >= HBIAS_LEVEL_4 && (ac10x->pullout_cntr == 0)) {
|
|
+ KEY_VOLUME_FLAG = 1;
|
|
+ input_report_key(ac10x->inpdev, KEY_VOLUMEUP, 1);
|
|
+ input_sync(ac10x->inpdev);
|
|
+ input_report_key(ac10x->inpdev, KEY_VOLUMEUP, 0);
|
|
+ input_sync(ac10x->inpdev);
|
|
+
|
|
+ AC101_DBG("HMIC_DATA: %d KEY_VOLUMEUP\n", t);
|
|
+ }
|
|
+ if (ac10x->pullout_cntr)
|
|
+ ac10x->pullout_cntr--;
|
|
+ } else if (t >= HBIAS_LEVEL_3){
|
|
+ msleep(80);
|
|
+ t = __ac101_get_hmic_data(codec);
|
|
+ if (t < HBIAS_LEVEL_4 && t >= HBIAS_LEVEL_3 && (ac10x->pullout_cntr == 0)) {
|
|
+ KEY_VOLUME_FLAG = 1;
|
|
+ input_report_key(ac10x->inpdev, KEY_VOLUMEDOWN, 1);
|
|
+ input_sync(ac10x->inpdev);
|
|
+ input_report_key(ac10x->inpdev, KEY_VOLUMEDOWN, 0);
|
|
+ input_sync(ac10x->inpdev);
|
|
+ AC101_DBG("KEY_VOLUMEDOWN\n");
|
|
+ }
|
|
+ if (ac10x->pullout_cntr)
|
|
+ ac10x->pullout_cntr--;
|
|
+ }
|
|
+ } else if ((t < HBIAS_LEVEL_2 && t >= HBIAS_LEVEL_1) &&
|
|
+ (ac10x->mode == FOUR_HEADPHONE_PLUGIN)) {
|
|
+ t = __ac101_get_hmic_data(codec);
|
|
+ if (t < HBIAS_LEVEL_2 && t >= HBIAS_LEVEL_1) {
|
|
+ if (KEY_VOLUME_FLAG) {
|
|
+ KEY_VOLUME_FLAG = 0;
|
|
+ }
|
|
+ if (hook_flag1 == (++hook_flag2)) {
|
|
+ hook_flag1 = hook_flag2 = 0;
|
|
+ input_report_key(ac10x->inpdev, KEY_HEADSETHOOK, 0);
|
|
+ input_sync(ac10x->inpdev);
|
|
+
|
|
+ AC101_DBG("KEY_HEADSETHOOK0\n");
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ while (ac10x->irq_cntr == 0 && ac10x->irq != 0) {
|
|
+ msleep(20);
|
|
+
|
|
+ t = __ac101_get_hmic_data(codec);
|
|
+
|
|
+ if (filt_index <= HEADSET_FILTER_CNT) {
|
|
+ if (filt_index++ == 0) {
|
|
+ filter_buf = t;
|
|
+ } else if (filter_buf != t) {
|
|
+ filt_index = 0;
|
|
+ }
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ filt_index = 0;
|
|
+ if (filter_buf >= HBIAS_LEVEL_2) {
|
|
+ ac10x->mode = THREE_HEADPHONE_PLUGIN;
|
|
+ ac10x->state = 2;
|
|
+ } else if (filter_buf >= HBIAS_LEVEL_1 - 1) {
|
|
+ ac10x->mode = FOUR_HEADPHONE_PLUGIN;
|
|
+ ac10x->state = 1;
|
|
+ } else {
|
|
+ ac10x->mode = HEADPHONE_IDLE;
|
|
+ ac10x->state = 0;
|
|
+ }
|
|
+ switch_status_update(ac10x);
|
|
+ ac10x->pullout_cntr = 0;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * audio_hmic_irq: the interrupt handlers
|
|
+ */
|
|
+static irqreturn_t audio_hmic_irq(int irq, void *para)
|
|
+{
|
|
+ struct ac10x_priv *ac10x = (struct ac10x_priv *)para;
|
|
+ if (ac10x == NULL) {
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (0 == schedule_work(&ac10x->work_clear_irq)){
|
|
+ AC101_DBG("[audio_hmic_irq] work already in queue_codec_irq, adding failed!\n");
|
|
+ }
|
|
+ return IRQ_HANDLED;
|
|
+}
|
|
+
|
|
+static int ac101_switch_probe(struct ac10x_priv *ac10x) {
|
|
+ struct i2c_client *i2c = ac10x->i2c101;
|
|
+ long ret;
|
|
+
|
|
+ ac10x->gpiod_irq = devm_gpiod_get_optional(&i2c->dev, "switch-irq", GPIOD_IN);
|
|
+ if (IS_ERR(ac10x->gpiod_irq)) {
|
|
+ ac10x->gpiod_irq = NULL;
|
|
+ dev_err(&i2c->dev, "failed get switch-irq in device tree\n");
|
|
+ goto _err_irq;
|
|
+ }
|
|
+
|
|
+ gpiod_direction_input(ac10x->gpiod_irq);
|
|
+
|
|
+ ac10x->irq = gpiod_to_irq(ac10x->gpiod_irq);
|
|
+ if (IS_ERR_VALUE(ac10x->irq)) {
|
|
+ pr_info("[ac101] map gpio to irq failed, errno = %ld\n", ac10x->irq);
|
|
+ ac10x->irq = 0;
|
|
+ goto _err_irq;
|
|
+ }
|
|
+
|
|
+ /* request irq, set irq type to falling edge trigger */
|
|
+ ret = devm_request_irq(ac10x->codec->dev, ac10x->irq, audio_hmic_irq,
|
|
+ IRQF_TRIGGER_FALLING, "SWTICH_EINT", ac10x);
|
|
+ if (IS_ERR_VALUE(ret)) {
|
|
+ pr_info("[ac101] request virq %ld failed, errno = %ld\n", ac10x->irq, ret);
|
|
+ goto _err_irq;
|
|
+ }
|
|
+
|
|
+ ac10x->mode = HEADPHONE_IDLE;
|
|
+ ac10x->state = -1;
|
|
+
|
|
+ /*use for judge the state of switch*/
|
|
+ INIT_WORK(&ac10x->work_switch, work_cb_earphone_switch);
|
|
+ INIT_WORK(&ac10x->work_clear_irq, work_cb_clear_irq);
|
|
+
|
|
+ /********************create input device************************/
|
|
+ ac10x->inpdev = devm_input_allocate_device(ac10x->codec->dev);
|
|
+ if (!ac10x->inpdev) {
|
|
+ AC101_DBG("input_allocate_device: not enough memory for input device\n");
|
|
+ ret = -ENOMEM;
|
|
+ goto _err_input_allocate_device;
|
|
+ }
|
|
+
|
|
+ ac10x->inpdev->name = "seed-voicecard-headset";
|
|
+ ac10x->inpdev->phys = dev_name(ac10x->codec->dev);
|
|
+ ac10x->inpdev->id.bustype = BUS_I2C;
|
|
+ ac10x->inpdev->dev.parent = ac10x->codec->dev;
|
|
+ input_set_drvdata(ac10x->inpdev, ac10x->codec);
|
|
+
|
|
+ ac10x->inpdev->evbit[0] = BIT_MASK(EV_KEY) | BIT(EV_SW);
|
|
+
|
|
+ set_bit(KEY_HEADSETHOOK, ac10x->inpdev->keybit);
|
|
+ set_bit(KEY_VOLUMEUP, ac10x->inpdev->keybit);
|
|
+ set_bit(KEY_VOLUMEDOWN, ac10x->inpdev->keybit);
|
|
+ input_set_capability(ac10x->inpdev, EV_SW, SW_HEADPHONE_INSERT);
|
|
+
|
|
+ ret = input_register_device(ac10x->inpdev);
|
|
+ if (ret) {
|
|
+ AC101_DBG("input_register_device: input_register_device failed\n");
|
|
+ goto _err_input_register_device;
|
|
+ }
|
|
+
|
|
+ /* the first headset state checking */
|
|
+ switch_hw_config(ac10x->codec);
|
|
+ ac10x->irq_cntr = 1;
|
|
+ schedule_work(&ac10x->work_switch);
|
|
+
|
|
+ return 0;
|
|
+
|
|
+_err_input_register_device:
|
|
+_err_input_allocate_device:
|
|
+
|
|
+ if (ac10x->irq) {
|
|
+ devm_free_irq(&i2c->dev, ac10x->irq, ac10x);
|
|
+ ac10x->irq = 0;
|
|
+ }
|
|
+_err_irq:
|
|
+ return ret;
|
|
+}
|
|
+/******************************************************************************/
|
|
+/********************************switch****************************************/
|
|
+/******************************************************************************/
|
|
+#endif
|
|
+
|
|
+
|
|
+
|
|
+void drc_config(struct snd_soc_codec *codec)
|
|
+{
|
|
+ int reg_val;
|
|
+ reg_val = ac101_read(codec, 0xa3);
|
|
+ reg_val &= ~(0x7ff<<0);
|
|
+ reg_val |= 1<<0;
|
|
+ ac101_write(codec, 0xa3, reg_val);
|
|
+ ac101_write(codec, 0xa4, 0x2baf);
|
|
+
|
|
+ reg_val = ac101_read(codec, 0xa5);
|
|
+ reg_val &= ~(0x7ff<<0);
|
|
+ reg_val |= 1<<0;
|
|
+ ac101_write(codec, 0xa5, reg_val);
|
|
+ ac101_write(codec, 0xa6, 0x2baf);
|
|
+
|
|
+ reg_val = ac101_read(codec, 0xa7);
|
|
+ reg_val &= ~(0x7ff<<0);
|
|
+ ac101_write(codec, 0xa7, reg_val);
|
|
+ ac101_write(codec, 0xa8, 0x44a);
|
|
+
|
|
+ reg_val = ac101_read(codec, 0xa9);
|
|
+ reg_val &= ~(0x7ff<<0);
|
|
+ ac101_write(codec, 0xa9, reg_val);
|
|
+ ac101_write(codec, 0xaa, 0x1e06);
|
|
+
|
|
+ reg_val = ac101_read(codec, 0xab);
|
|
+ reg_val &= ~(0x7ff<<0);
|
|
+ reg_val |= (0x352<<0);
|
|
+ ac101_write(codec, 0xab, reg_val);
|
|
+ ac101_write(codec, 0xac, 0x6910);
|
|
+
|
|
+ reg_val = ac101_read(codec, 0xad);
|
|
+ reg_val &= ~(0x7ff<<0);
|
|
+ reg_val |= (0x77a<<0);
|
|
+ ac101_write(codec, 0xad, reg_val);
|
|
+ ac101_write(codec, 0xae, 0xaaaa);
|
|
+
|
|
+ reg_val = ac101_read(codec, 0xaf);
|
|
+ reg_val &= ~(0x7ff<<0);
|
|
+ reg_val |= (0x2de<<0);
|
|
+ ac101_write(codec, 0xaf, reg_val);
|
|
+ ac101_write(codec, 0xb0, 0xc982);
|
|
+
|
|
+ ac101_write(codec, 0x16, 0x9f9f);
|
|
+
|
|
+}
|
|
+
|
|
+void drc_enable(struct snd_soc_codec *codec,bool on)
|
|
+{
|
|
+ int reg_val;
|
|
+ if (on) {
|
|
+ ac101_write(codec, 0xb5, 0xA080);
|
|
+ reg_val = ac101_read(codec, MOD_CLK_ENA);
|
|
+ reg_val |= (0x1<<6);
|
|
+ ac101_write(codec, MOD_CLK_ENA, reg_val);
|
|
+ reg_val = ac101_read(codec, MOD_RST_CTRL);
|
|
+ reg_val |= (0x1<<6);
|
|
+ ac101_write(codec, MOD_RST_CTRL, reg_val);
|
|
+
|
|
+ reg_val = ac101_read(codec, 0xa0);
|
|
+ reg_val |= (0x7<<0);
|
|
+ ac101_write(codec, 0xa0, reg_val);
|
|
+ } else {
|
|
+ ac101_write(codec, 0xb5, 0x0);
|
|
+ reg_val = ac101_read(codec, MOD_CLK_ENA);
|
|
+ reg_val &= ~(0x1<<6);
|
|
+ ac101_write(codec, MOD_CLK_ENA, reg_val);
|
|
+ reg_val = ac101_read(codec, MOD_RST_CTRL);
|
|
+ reg_val &= ~(0x1<<6);
|
|
+ ac101_write(codec, MOD_RST_CTRL, reg_val);
|
|
+
|
|
+ reg_val = ac101_read(codec, 0xa0);
|
|
+ reg_val &= ~(0x7<<0);
|
|
+ ac101_write(codec, 0xa0, reg_val);
|
|
+ }
|
|
+}
|
|
+
|
|
+void set_configuration(struct snd_soc_codec *codec)
|
|
+{
|
|
+ if (speaker_double_used) {
|
|
+ ac101_update_bits(codec, SPKOUT_CTRL, (0x1f<<SPK_VOL),
|
|
+ (double_speaker_val<<SPK_VOL));
|
|
+ } else {
|
|
+ ac101_update_bits(codec, SPKOUT_CTRL, (0x1f<<SPK_VOL),
|
|
+ (single_speaker_val<<SPK_VOL));
|
|
+ }
|
|
+ ac101_update_bits(codec, HPOUT_CTRL, (0x3f<<HP_VOL), (headset_val<<HP_VOL));
|
|
+ ac101_update_bits(codec, ADC_SRCBST_CTRL, (0x7<<ADC_MIC1G), (mainmic_val<<ADC_MIC1G));
|
|
+ ac101_update_bits(codec, ADC_SRCBST_CTRL, (0x7<<ADC_MIC2G), (headsetmic_val<<ADC_MIC2G));
|
|
+ if (dmic_used) {
|
|
+ ac101_write(codec, ADC_VOL_CTRL, adc_digital_val);
|
|
+ }
|
|
+ if (drc_used) {
|
|
+ drc_config(codec);
|
|
+ }
|
|
+ /*headphone calibration clock frequency select*/
|
|
+ ac101_update_bits(codec, SPKOUT_CTRL, (0x7<<HPCALICKS), (0x7<<HPCALICKS));
|
|
+
|
|
+ /* I2S1 DAC Timeslot 0 data <- I2S1 DAC channel 0 */
|
|
+ // "AIF1IN0L Mux" <= "AIF1DACL"
|
|
+ // "AIF1IN0R Mux" <= "AIF1DACR"
|
|
+ ac101_update_bits(codec, AIF1_DACDAT_CTRL, 0x3 << AIF1_DA0L_SRC, 0x0 << AIF1_DA0L_SRC);
|
|
+ ac101_update_bits(codec, AIF1_DACDAT_CTRL, 0x3 << AIF1_DA0R_SRC, 0x0 << AIF1_DA0R_SRC);
|
|
+ /* Timeslot 0 Left & Right Channel enable */
|
|
+ ac101_update_bits(codec, AIF1_DACDAT_CTRL, 0x3 << AIF1_DA0R_ENA, 0x3 << AIF1_DA0R_ENA);
|
|
+
|
|
+ /* DAC Digital Mixer Source Select <- I2S1 DA0 */
|
|
+ // "DACL Mixer" += "AIF1IN0L Mux"
|
|
+ // "DACR Mixer" += "AIF1IN0R Mux"
|
|
+ ac101_update_bits(codec, DAC_MXR_SRC, 0xF << DACL_MXR_ADCL, 0x8 << DACL_MXR_ADCL);
|
|
+ ac101_update_bits(codec, DAC_MXR_SRC, 0xF << DACR_MXR_ADCR, 0x8 << DACR_MXR_ADCR);
|
|
+ /* Internal DAC Analog Left & Right Channel enable */
|
|
+ ac101_update_bits(codec, OMIXER_DACA_CTRL, 0x3 << DACALEN, 0x3 << DACALEN);
|
|
+
|
|
+ /* Output Mixer Source Select */
|
|
+ // "Left Output Mixer" += "DACL Mixer"
|
|
+ // "Right Output Mixer" += "DACR Mixer"
|
|
+ ac101_update_bits(codec, OMIXER_SR, 0x1 << LMIXMUTEDACL, 0x1 << LMIXMUTEDACL);
|
|
+ ac101_update_bits(codec, OMIXER_SR, 0x1 << RMIXMUTEDACR, 0x1 << RMIXMUTEDACR);
|
|
+ /* Left & Right Analog Output Mixer enable */
|
|
+ ac101_update_bits(codec, OMIXER_DACA_CTRL, 0x3 << LMIXEN, 0x3 << LMIXEN);
|
|
+
|
|
+ /* Headphone Ouput Control */
|
|
+ // "HP_R Mux" <= "DACR Mixer"
|
|
+ // "HP_L Mux" <= "DACL Mixer"
|
|
+ ac101_update_bits(codec, HPOUT_CTRL, 0x1 << LHPS, 0x0 << LHPS);
|
|
+ ac101_update_bits(codec, HPOUT_CTRL, 0x1 << RHPS, 0x0 << RHPS);
|
|
+
|
|
+ /* Speaker Output Control */
|
|
+ // "SPK_L Mux" <= "SPK_LR Adder"
|
|
+ // "SPK_R Mux" <= "SPK_LR Adder"
|
|
+ ac101_update_bits(codec, SPKOUT_CTRL, (0x1 << LSPKS) | (0x1 << RSPKS),
|
|
+ (0x1 << LSPKS) | (0x1 << RSPKS));
|
|
+ /* Enable Left & Right Speaker */
|
|
+ ac101_update_bits(codec, SPKOUT_CTRL, (0x1 << LSPK_EN) | (0x1 << RSPK_EN),
|
|
+ (0x1 << LSPK_EN) | (0x1 << RSPK_EN));
|
|
+ return;
|
|
+}
|
|
+
|
|
+static int late_enable_dac(struct snd_soc_codec* codec, int event) {
|
|
+ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
|
+
|
|
+ mutex_lock(&ac10x->dac_mutex);
|
|
+ switch (event) {
|
|
+ case SND_SOC_DAPM_PRE_PMU:
|
|
+ AC101_DBG();
|
|
+ if (ac10x->dac_enable == 0){
|
|
+ /*enable dac module clk*/
|
|
+ ac101_update_bits(codec, MOD_CLK_ENA, (0x1<<MOD_CLK_DAC_DIG),
|
|
+ (0x1<<MOD_CLK_DAC_DIG));
|
|
+ ac101_update_bits(codec, MOD_RST_CTRL, (0x1<<MOD_RESET_DAC_DIG),
|
|
+ (0x1<<MOD_RESET_DAC_DIG));
|
|
+ ac101_update_bits(codec, DAC_DIG_CTRL, (0x1<<ENDA), (0x1<<ENDA));
|
|
+ ac101_update_bits(codec, DAC_DIG_CTRL, (0x1<<ENHPF),(0x1<<ENHPF));
|
|
+ }
|
|
+ ac10x->dac_enable++;
|
|
+ break;
|
|
+ case SND_SOC_DAPM_POST_PMD:
|
|
+ if (ac10x->dac_enable != 0){
|
|
+ ac10x->dac_enable = 0;
|
|
+
|
|
+ ac101_update_bits(codec, DAC_DIG_CTRL, (0x1<<ENHPF),(0x0<<ENHPF));
|
|
+ ac101_update_bits(codec, DAC_DIG_CTRL, (0x1<<ENDA), (0x0<<ENDA));
|
|
+ /*disable dac module clk*/
|
|
+ ac101_update_bits(codec, MOD_CLK_ENA, (0x1<<MOD_CLK_DAC_DIG),
|
|
+ (0x0<<MOD_CLK_DAC_DIG));
|
|
+ ac101_update_bits(codec, MOD_RST_CTRL, (0x1<<MOD_RESET_DAC_DIG),
|
|
+ (0x0<<MOD_RESET_DAC_DIG));
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ mutex_unlock(&ac10x->dac_mutex);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int ac101_headphone_event(struct snd_soc_codec* codec, int event) {
|
|
+ switch (event) {
|
|
+ case SND_SOC_DAPM_POST_PMU:
|
|
+ /*open*/
|
|
+ AC101_DBG("post:open\n");
|
|
+ ac101_update_bits(codec, OMIXER_DACA_CTRL, (0xf<<HPOUTPUTENABLE),
|
|
+ (0xf<<HPOUTPUTENABLE));
|
|
+ msleep(10);
|
|
+ ac101_update_bits(codec, HPOUT_CTRL, (0x1<<HPPA_EN), (0x1<<HPPA_EN));
|
|
+ ac101_update_bits(codec, HPOUT_CTRL, (0x3<<LHPPA_MUTE), (0x3<<LHPPA_MUTE));
|
|
+ break;
|
|
+ case SND_SOC_DAPM_PRE_PMD:
|
|
+ /*close*/
|
|
+ AC101_DBG("pre:close\n");
|
|
+ ac101_update_bits(codec, HPOUT_CTRL, (0x3<<LHPPA_MUTE), (0x0<<LHPPA_MUTE));
|
|
+ msleep(10);
|
|
+ ac101_update_bits(codec, OMIXER_DACA_CTRL, (0xf<<HPOUTPUTENABLE),
|
|
+ (0x0<<HPOUTPUTENABLE));
|
|
+ ac101_update_bits(codec, HPOUT_CTRL, (0x1<<HPPA_EN), (0x0<<HPPA_EN));
|
|
+ break;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int ac101_sysclk_started(void) {
|
|
+ int reg_val;
|
|
+
|
|
+ reg_val = ac101_read(static_ac10x->codec, SYSCLK_CTRL);
|
|
+ return (reg_val & (0x1<<SYSCLK_ENA));
|
|
+}
|
|
+
|
|
+static int ac101_aif1clk(struct snd_soc_codec* codec, int event, int quick) {
|
|
+ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
|
+ int ret = 0;
|
|
+
|
|
+ switch (event) {
|
|
+ case SND_SOC_DAPM_PRE_PMU:
|
|
+ if (ac10x->aif1_clken == 0){
|
|
+ ret = ac101_update_bits(codec, SYSCLK_CTRL, (0x1<<AIF1CLK_ENA),
|
|
+ (0x1<<AIF1CLK_ENA));
|
|
+ if(!quick || _MASTER_MULTI_CODEC != _MASTER_AC101) {
|
|
+ /* enable aif1clk & sysclk */
|
|
+ ret = ret || ac101_update_bits(codec, MOD_CLK_ENA,
|
|
+ (0x1<<MOD_CLK_AIF1),
|
|
+ (0x1<<MOD_CLK_AIF1));
|
|
+ ret = ret || ac101_update_bits(codec, MOD_RST_CTRL,
|
|
+ (0x1<<MOD_RESET_AIF1),
|
|
+ (0x1<<MOD_RESET_AIF1));
|
|
+ }
|
|
+ ret = ret || ac101_update_bits(codec, SYSCLK_CTRL, (0x1<<SYSCLK_ENA),
|
|
+ (0x1<<SYSCLK_ENA));
|
|
+
|
|
+ if (ret) {
|
|
+ AC101_DBG("start sysclk failed\n");
|
|
+ } else {
|
|
+ AC101_DBG("hw sysclk enable\n");
|
|
+ ac10x->aif1_clken++;
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+ case SND_SOC_DAPM_POST_PMD:
|
|
+ if (ac10x->aif1_clken != 0) {
|
|
+ /* disable aif1clk & sysclk */
|
|
+ ret = ac101_update_bits(codec, SYSCLK_CTRL, (0x1<<AIF1CLK_ENA),
|
|
+ (0x0<<AIF1CLK_ENA));
|
|
+ ret = ret || ac101_update_bits(codec, MOD_CLK_ENA, (0x1<<MOD_CLK_AIF1),
|
|
+ (0x0<<MOD_CLK_AIF1));
|
|
+ ret = ret || ac101_update_bits(codec, MOD_RST_CTRL, (0x1<<MOD_RESET_AIF1),
|
|
+ (0x0<<MOD_RESET_AIF1));
|
|
+ ret = ret || ac101_update_bits(codec, SYSCLK_CTRL, (0x1<<SYSCLK_ENA),
|
|
+ (0x0<<SYSCLK_ENA));
|
|
+
|
|
+ if (ret) {
|
|
+ AC101_DBG("stop sysclk failed\n");
|
|
+ } else {
|
|
+ AC101_DBG("hw sysclk disable\n");
|
|
+ ac10x->aif1_clken = 0;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ AC101_DBG("event=%d pre_up/%d post_down/%d\n", event, SND_SOC_DAPM_PRE_PMU,
|
|
+ SND_SOC_DAPM_POST_PMD);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * snd_ac101_get_volsw - single mixer get callback
|
|
+ * @kcontrol: mixer control
|
|
+ * @ucontrol: control element information
|
|
+ *
|
|
+ * Callback to get the value of a single mixer control, or a double mixer
|
|
+ * control that spans 2 registers.
|
|
+ *
|
|
+ * Returns 0 for success.
|
|
+ */
|
|
+static int snd_ac101_get_volsw(struct snd_kcontrol *kcontrol,
|
|
+ struct snd_ctl_elem_value *ucontrol
|
|
+){
|
|
+ struct soc_mixer_control *mc =
|
|
+ (struct soc_mixer_control *)kcontrol->private_value;
|
|
+ unsigned int val, mask = (1 << fls(mc->max)) - 1;
|
|
+ unsigned int invert = mc->invert;
|
|
+ int ret;
|
|
+
|
|
+ if ((ret = ac101_read(static_ac10x->codec, mc->reg)) < 0)
|
|
+ return ret;
|
|
+
|
|
+ val = (ret >> mc->shift) & mask;
|
|
+ ucontrol->value.integer.value[0] = val - mc->min;
|
|
+ if (invert) {
|
|
+ ucontrol->value.integer.value[0] =
|
|
+ mc->max - ucontrol->value.integer.value[0];
|
|
+ }
|
|
+
|
|
+ if (snd_soc_volsw_is_stereo(mc)) {
|
|
+ val = (ret >> mc->rshift) & mask;
|
|
+ ucontrol->value.integer.value[1] = val - mc->min;
|
|
+ if (invert) {
|
|
+ ucontrol->value.integer.value[1] =
|
|
+ mc->max - ucontrol->value.integer.value[1];
|
|
+ }
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * snd_ac101_put_volsw - single mixer put callback
|
|
+ * @kcontrol: mixer control
|
|
+ * @ucontrol: control element information
|
|
+ *
|
|
+ * Callback to set the value of a single mixer control, or a double mixer
|
|
+ * control that spans 2 registers.
|
|
+ *
|
|
+ * Returns 0 for success.
|
|
+ */
|
|
+static int snd_ac101_put_volsw(struct snd_kcontrol *kcontrol,
|
|
+ struct snd_ctl_elem_value *ucontrol
|
|
+){
|
|
+ struct soc_mixer_control *mc =
|
|
+ (struct soc_mixer_control *)kcontrol->private_value;
|
|
+ unsigned int sign_bit = mc->sign_bit;
|
|
+ unsigned int val, mask = (1 << fls(mc->max)) - 1;
|
|
+ unsigned int invert = mc->invert;
|
|
+ int ret;
|
|
+
|
|
+ if (sign_bit)
|
|
+ mask = BIT(sign_bit + 1) - 1;
|
|
+
|
|
+ val = ((ucontrol->value.integer.value[0] + mc->min) & mask);
|
|
+ if (invert) {
|
|
+ val = mc->max - val;
|
|
+ }
|
|
+
|
|
+ ret = ac101_update_bits(static_ac10x->codec, mc->reg, mask << mc->shift, val << mc->shift);
|
|
+
|
|
+ if (! snd_soc_volsw_is_stereo(mc)) {
|
|
+ return ret;
|
|
+ }
|
|
+ val = ((ucontrol->value.integer.value[1] + mc->min) & mask);
|
|
+ if (invert) {
|
|
+ val = mc->max - val;
|
|
+ }
|
|
+
|
|
+ ret = ac101_update_bits(static_ac10x->codec, mc->reg, mask << mc->rshift,
|
|
+ val << mc->rshift);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+
|
|
+static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -11925, 75, 0);
|
|
+static const DECLARE_TLV_DB_SCALE(dac_mix_vol_tlv, -600, 600, 0);
|
|
+static const DECLARE_TLV_DB_SCALE(dig_vol_tlv, -7308, 116, 0);
|
|
+static const DECLARE_TLV_DB_SCALE(speaker_vol_tlv, -4800, 150, 0);
|
|
+static const DECLARE_TLV_DB_SCALE(headphone_vol_tlv, -6300, 100, 0);
|
|
+
|
|
+static struct snd_kcontrol_new ac101_controls[] = {
|
|
+ /*DAC*/
|
|
+ SOC_DOUBLE_TLV("DAC volume", DAC_VOL_CTRL, DAC_VOL_L, DAC_VOL_R, 0xff, 0, dac_vol_tlv),
|
|
+ SOC_DOUBLE_TLV("DAC mixer gain", DAC_MXR_GAIN, DACL_MXR_GAIN, DACR_MXR_GAIN,
|
|
+ 0xf, 0, dac_mix_vol_tlv),
|
|
+ SOC_SINGLE_TLV("digital volume", DAC_DBG_CTRL, DVC, 0x3f, 1, dig_vol_tlv),
|
|
+ SOC_SINGLE_TLV("speaker volume", SPKOUT_CTRL, SPK_VOL, 0x1f, 0, speaker_vol_tlv),
|
|
+ SOC_SINGLE_TLV("headphone volume", HPOUT_CTRL, HP_VOL, 0x3f, 0, headphone_vol_tlv),
|
|
+};
|
|
+
|
|
+/* PLL divisors */
|
|
+struct pll_div {
|
|
+ unsigned int pll_in;
|
|
+ unsigned int pll_out;
|
|
+ int m;
|
|
+ int n_i;
|
|
+ int n_f;
|
|
+};
|
|
+
|
|
+struct aif1_fs {
|
|
+ unsigned samp_rate;
|
|
+ int bclk_div;
|
|
+ int srbit;
|
|
+ #define _SERIES_24_576K 0
|
|
+ #define _SERIES_22_579K 1
|
|
+ int series;
|
|
+};
|
|
+
|
|
+struct kv_map {
|
|
+ int val;
|
|
+ int bit;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Note : pll code from original tdm/i2s driver.
|
|
+ * freq_out = freq_in * N/(M*(2k+1)) , k=1,N=N_i+N_f,N_f=factor*0.2;
|
|
+ * N_i[0,1023], N_f_factor[0,7], m[1,64]=REG_VAL[1-63,0]
|
|
+ */
|
|
+static const struct pll_div codec_pll_div[] = {
|
|
+ {128000, _FREQ_22_579K, 1, 529, 1},
|
|
+ {192000, _FREQ_22_579K, 1, 352, 4},
|
|
+ {256000, _FREQ_22_579K, 1, 264, 3},
|
|
+ {384000, _FREQ_22_579K, 1, 176, 2}, /*((176+2*0.2)*6000000)/(38*(2*1+1))*/
|
|
+ {1411200, _FREQ_22_579K, 1, 48, 0},
|
|
+ {2822400, _FREQ_22_579K, 1, 24, 0}, /* accurate, 11025 * 256 */
|
|
+ {5644800, _FREQ_22_579K, 1, 12, 0}, /* accurate, 22050 * 256 */
|
|
+ {6000000, _FREQ_22_579K, 38, 429, 0}, /*((429+0*0.2)*6000000)/(38*(2*1+1))*/
|
|
+ {11289600, _FREQ_22_579K, 1, 6, 0}, /* accurate, 44100 * 256 */
|
|
+ {13000000, _FREQ_22_579K, 19, 99, 0},
|
|
+ {19200000, _FREQ_22_579K, 25, 88, 1},
|
|
+ {24000000, _FREQ_22_579K, 63, 177, 4}, /* 22577778 Hz */
|
|
+
|
|
+ {128000, _FREQ_24_576K, 1, 576, 0},
|
|
+ {192000, _FREQ_24_576K, 1, 384, 0},
|
|
+ {256000, _FREQ_24_576K, 1, 288, 0},
|
|
+ {384000, _FREQ_24_576K, 1, 192, 0},
|
|
+ {2048000, _FREQ_24_576K, 1, 36, 0}, /* accurate, 8000 * 256 */
|
|
+ {3072000, _FREQ_24_576K, 1, 24, 0}, /* accurate, 12000 * 256 */
|
|
+ {4096000, _FREQ_24_576K, 1, 18, 0}, /* accurate, 16000 * 256 */
|
|
+ {6000000, _FREQ_24_576K, 25, 307, 1},
|
|
+ {6144000, _FREQ_24_576K, 4, 48, 0}, /* accurate, 24000 * 256 */
|
|
+ {12288000, _FREQ_24_576K, 8, 48, 0}, /* accurate, 48000 * 256 */
|
|
+ {13000000, _FREQ_24_576K, 42, 238, 1},
|
|
+ {19200000, _FREQ_24_576K, 25, 96, 0},
|
|
+ {24000000, _FREQ_24_576K, 25, 76, 4}, /* accurate */
|
|
+
|
|
+ {_FREQ_22_579K, _FREQ_22_579K, 8, 24, 0}, /* accurate, 88200 * 256 */
|
|
+ {_FREQ_24_576K, _FREQ_24_576K, 8, 24, 0}, /* accurate, 96000 * 256 */
|
|
+};
|
|
+
|
|
+static const struct aif1_fs codec_aif1_fs[] = {
|
|
+ {8000, 12, 0},
|
|
+ {11025, 8, 1, _SERIES_22_579K},
|
|
+ {12000, 8, 2},
|
|
+ {16000, 6, 3},
|
|
+ {22050, 4, 4, _SERIES_22_579K},
|
|
+ {24000, 4, 5},
|
|
+ /* {32000, 3, 6}, dividing by 3 is not support */
|
|
+ {44100, 2, 7, _SERIES_22_579K},
|
|
+ {48000, 2, 8},
|
|
+ {96000, 1, 9},
|
|
+};
|
|
+
|
|
+static const struct kv_map codec_aif1_lrck[] = {
|
|
+ {16, 0},
|
|
+ {32, 1},
|
|
+ {64, 2},
|
|
+ {128, 3},
|
|
+ {256, 4},
|
|
+};
|
|
+
|
|
+static const struct kv_map codec_aif1_wsize[] = {
|
|
+ {8, 0},
|
|
+ {16, 1},
|
|
+ {20, 2},
|
|
+ {24, 3},
|
|
+ {32, 3},
|
|
+};
|
|
+
|
|
+static const unsigned ac101_bclkdivs[] = {
|
|
+ 1, 2, 4, 6,
|
|
+ 8, 12, 16, 24,
|
|
+ 32, 48, 64, 96,
|
|
+ 128, 192, 0, 0,
|
|
+};
|
|
+
|
|
+static int ac101_aif_play(struct ac10x_priv* ac10x) {
|
|
+ struct snd_soc_codec * codec = ac10x->codec;
|
|
+
|
|
+ late_enable_dac(codec, SND_SOC_DAPM_PRE_PMU);
|
|
+ ac101_headphone_event(codec, SND_SOC_DAPM_POST_PMU);
|
|
+ if (drc_used) {
|
|
+ drc_enable(codec, 1);
|
|
+ }
|
|
+
|
|
+ /* Enable Left & Right Speaker */
|
|
+ ac101_update_bits(codec, SPKOUT_CTRL, (0x1 << LSPK_EN) | (0x1 << RSPK_EN),
|
|
+ (0x1 << LSPK_EN) | (0x1 << RSPK_EN));
|
|
+ if (ac10x->gpiod_spk_amp_gate) {
|
|
+ gpiod_set_value(ac10x->gpiod_spk_amp_gate, 1);
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void ac10x_work_aif_play(struct work_struct *work) {
|
|
+ struct ac10x_priv *ac10x = container_of(work, struct ac10x_priv, dlywork.work);
|
|
+
|
|
+ ac101_aif_play(ac10x);
|
|
+ return;
|
|
+}
|
|
+
|
|
+int ac101_aif_mute(struct snd_soc_dai *codec_dai, int mute)
|
|
+{
|
|
+ struct snd_soc_codec *codec = codec_dai->codec;
|
|
+ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
|
+
|
|
+ AC101_DBG("mute=%d\n", mute);
|
|
+
|
|
+ ac101_write(codec, DAC_VOL_CTRL, mute? 0: 0xA0A0);
|
|
+
|
|
+ if (!mute) {
|
|
+ #if _MASTER_MULTI_CODEC != _MASTER_AC101
|
|
+ /* enable global clock */
|
|
+ ac10x->aif1_clken = 0;
|
|
+ ac101_aif1clk(codec, SND_SOC_DAPM_PRE_PMU, 0);
|
|
+ ac101_aif_play(ac10x);
|
|
+ #else
|
|
+ schedule_delayed_work(&ac10x->dlywork, msecs_to_jiffies(50));
|
|
+ #endif
|
|
+ } else {
|
|
+ #if _MASTER_MULTI_CODEC == _MASTER_AC101
|
|
+ cancel_delayed_work_sync(&ac10x->dlywork);
|
|
+ #endif
|
|
+
|
|
+ if (ac10x->gpiod_spk_amp_gate) {
|
|
+ gpiod_set_value(ac10x->gpiod_spk_amp_gate, 0);
|
|
+ }
|
|
+ /* Disable Left & Right Speaker */
|
|
+ ac101_update_bits(codec, SPKOUT_CTRL, (0x1 << LSPK_EN) | (0x1 << RSPK_EN),
|
|
+ (0x0 << LSPK_EN) | (0x0 << RSPK_EN));
|
|
+ if (drc_used) {
|
|
+ drc_enable(codec, 0);
|
|
+ }
|
|
+ ac101_headphone_event(codec, SND_SOC_DAPM_PRE_PMD);
|
|
+ late_enable_dac(codec, SND_SOC_DAPM_POST_PMD);
|
|
+
|
|
+ #if _MASTER_MULTI_CODEC != _MASTER_AC101
|
|
+ ac10x->aif1_clken = 1;
|
|
+ ac101_aif1clk(codec, SND_SOC_DAPM_POST_PMD, 0);
|
|
+ #endif
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void ac101_aif_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai)
|
|
+{
|
|
+ struct snd_soc_codec *codec = codec_dai->codec;
|
|
+ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
|
+
|
|
+ AC101_DBG("stream = %s, play: %d, capt: %d, active: %d\n",
|
|
+ snd_pcm_stream_str(substream),
|
|
+ codec_dai->stream[SNDRV_PCM_STREAM_PLAYBACK].active,
|
|
+ codec_dai->stream[SNDRV_PCM_STREAM_CAPTURE].active,
|
|
+ snd_soc_dai_active(codec_dai));
|
|
+
|
|
+ if (!snd_soc_dai_active(codec_dai)) {
|
|
+ ac10x->aif1_clken = 1;
|
|
+ ac101_aif1clk(codec, SND_SOC_DAPM_POST_PMD, 0);
|
|
+ } else {
|
|
+ ac101_aif1clk(codec, SND_SOC_DAPM_PRE_PMU, 0);
|
|
+ }
|
|
+}
|
|
+
|
|
+static int ac101_set_pll(struct snd_soc_dai *codec_dai, int pll_id, int source,
|
|
+ unsigned int freq_in, unsigned int freq_out)
|
|
+{
|
|
+ struct snd_soc_codec *codec = codec_dai->codec;
|
|
+ int i, m, n_i, n_f;
|
|
+
|
|
+ AC101_DBG("pll_id:%d\n", pll_id);
|
|
+
|
|
+ /* clear volatile reserved bits*/
|
|
+ ac101_update_bits(codec, SYSCLK_CTRL, 0xFF & ~(0x1 << SYSCLK_ENA), 0x0);
|
|
+
|
|
+ /* select aif1 clk srouce from mclk1 */
|
|
+ ac101_update_bits(codec, SYSCLK_CTRL, (0x3<<AIF1CLK_SRC), (0x0<<AIF1CLK_SRC));
|
|
+ /* disable pll */
|
|
+ ac101_update_bits(codec, PLL_CTRL2, (0x1<<PLL_EN), (0<<PLL_EN));
|
|
+
|
|
+ if (!freq_out)
|
|
+ return 0;
|
|
+ if ((freq_in < 128000) || (freq_in > _FREQ_24_576K)) {
|
|
+ return -EINVAL;
|
|
+ } else if ((freq_in == _FREQ_24_576K) || (freq_in == _FREQ_22_579K)) {
|
|
+ if (pll_id == AC101_MCLK1) {
|
|
+ /*select aif1 clk source from mclk1*/
|
|
+ ac101_update_bits(codec, SYSCLK_CTRL, (0x3<<AIF1CLK_SRC),
|
|
+ (0x0<<AIF1CLK_SRC));
|
|
+ return 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ switch (pll_id) {
|
|
+ case AC101_MCLK1:
|
|
+ /*pll source from MCLK1*/
|
|
+ ac101_update_bits(codec, SYSCLK_CTRL, (0x3<<PLLCLK_SRC), (0x0<<PLLCLK_SRC));
|
|
+ break;
|
|
+ case AC101_BCLK1:
|
|
+ /*pll source from BCLK1*/
|
|
+ ac101_update_bits(codec, SYSCLK_CTRL, (0x3<<PLLCLK_SRC), (0x2<<PLLCLK_SRC));
|
|
+ break;
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ /* freq_out = freq_in * n/(m*(2k+1)) , k=1,N=N_i+N_f */
|
|
+ for (i = m = n_i = n_f = 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)) {
|
|
+ m = codec_pll_div[i].m;
|
|
+ n_i = codec_pll_div[i].n_i;
|
|
+ n_f = codec_pll_div[i].n_f;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ /* config pll m */
|
|
+ if (m == 64) m = 0;
|
|
+ ac101_update_bits(codec, PLL_CTRL1, (0x3f<<PLL_POSTDIV_M), (m<<PLL_POSTDIV_M));
|
|
+ /* config pll n */
|
|
+ ac101_update_bits(codec, PLL_CTRL2, (0x3ff<<PLL_PREDIV_NI), (n_i<<PLL_PREDIV_NI));
|
|
+ ac101_update_bits(codec, PLL_CTRL2, (0x7<<PLL_POSTDIV_NF), (n_f<<PLL_POSTDIV_NF));
|
|
+ /* enable pll */
|
|
+ ac101_update_bits(codec, PLL_CTRL2, (0x1<<PLL_EN), (1<<PLL_EN));
|
|
+ ac101_update_bits(codec, SYSCLK_CTRL, (0x1<<PLLCLK_ENA), (0x1<<PLLCLK_ENA));
|
|
+ ac101_update_bits(codec, SYSCLK_CTRL, (0x3<<AIF1CLK_SRC), (0x3<<AIF1CLK_SRC));
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int ac101_hw_params(struct snd_pcm_substream *substream,
|
|
+ struct snd_pcm_hw_params *params,
|
|
+ struct snd_soc_dai *codec_dai)
|
|
+{
|
|
+ int i = 0;
|
|
+ int AIF_CLK_CTRL = AIF1_CLK_CTRL;
|
|
+ int aif1_word_size = 24;
|
|
+ int aif1_slot_size = 32;
|
|
+ int aif1_lrck_div;
|
|
+ struct snd_soc_codec *codec = codec_dai->codec;
|
|
+ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
|
+ int reg_val, freq_out;
|
|
+ unsigned channels;
|
|
+
|
|
+ AC101_DBG("+++\n");
|
|
+
|
|
+ if (_MASTER_MULTI_CODEC == _MASTER_AC101 && ac101_sysclk_started()) {
|
|
+ /* not configure hw_param twice if stream is playback, tell the caller it's started */
|
|
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
|
+ return 1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* get channels count & slot size */
|
|
+ channels = params_channels(params);
|
|
+
|
|
+ switch (params_format(params)) {
|
|
+ case SNDRV_PCM_FORMAT_S24_LE:
|
|
+ case SNDRV_PCM_FORMAT_S32_LE:
|
|
+ aif1_slot_size = 32;
|
|
+ break;
|
|
+ case SNDRV_PCM_FORMAT_S16_LE:
|
|
+ default:
|
|
+ aif1_slot_size = 16;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* set LRCK/BCLK ratio */
|
|
+ aif1_lrck_div = aif1_slot_size * channels;
|
|
+ for (i = 0; i < ARRAY_SIZE(codec_aif1_lrck); i++) {
|
|
+ if (codec_aif1_lrck[i].val == aif1_lrck_div) {
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ ac101_update_bits(codec, AIF_CLK_CTRL, (0x7 << AIF1_LRCK_DIV),
|
|
+ codec_aif1_lrck[i].bit << AIF1_LRCK_DIV);
|
|
+
|
|
+ /* set PLL output freq */
|
|
+ freq_out = _FREQ_24_576K;
|
|
+ for (i = 0; i < ARRAY_SIZE(codec_aif1_fs); i++) {
|
|
+ if (codec_aif1_fs[i].samp_rate == params_rate(params)) {
|
|
+ if (codec_dai->stream[SNDRV_PCM_STREAM_CAPTURE].active && dmic_used &&
|
|
+ codec_aif1_fs[i].samp_rate == 44100) {
|
|
+ ac101_update_bits(codec, AIF_SR_CTRL, (0xf<<AIF1_FS),
|
|
+ (0x4<<AIF1_FS));
|
|
+ } else {
|
|
+ ac101_update_bits(codec, AIF_SR_CTRL, (0xf<<AIF1_FS),
|
|
+ ((codec_aif1_fs[i].srbit)<<AIF1_FS));
|
|
+ }
|
|
+ if (codec_aif1_fs[i].series == _SERIES_22_579K)
|
|
+ freq_out = _FREQ_22_579K;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* set I2S word size */
|
|
+ for (i = 0; i < ARRAY_SIZE(codec_aif1_wsize); i++) {
|
|
+ if (codec_aif1_wsize[i].val == aif1_word_size) {
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ ac101_update_bits(codec, AIF_CLK_CTRL, (0x3<<AIF1_WORK_SIZ),
|
|
+ ((codec_aif1_wsize[i].bit)<<AIF1_WORK_SIZ));
|
|
+
|
|
+ /* set TDM slot size */
|
|
+ if ((reg_val = codec_aif1_wsize[i].bit) > 2) reg_val = 2;
|
|
+ ac101_update_bits(codec, AIF1_ADCDAT_CTRL, 0x3 << AIF1_SLOT_SIZ, reg_val << AIF1_SLOT_SIZ);
|
|
+
|
|
+ /* setting pll if it's master mode */
|
|
+ reg_val = ac101_read(codec, AIF_CLK_CTRL);
|
|
+ if ((reg_val & (0x1 << AIF1_MSTR_MOD)) == 0) {
|
|
+ unsigned bclkdiv;
|
|
+
|
|
+ ac101_set_pll(codec_dai, AC101_MCLK1, 0, ac10x->sysclk, freq_out);
|
|
+
|
|
+ bclkdiv = freq_out / (aif1_lrck_div * params_rate(params));
|
|
+ for (i = 0; i < ARRAY_SIZE(ac101_bclkdivs) - 1; i++) {
|
|
+ if (ac101_bclkdivs[i] >= bclkdiv) {
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ ac101_update_bits(codec, AIF_CLK_CTRL, (0xf<<AIF1_BCLK_DIV), i<<AIF1_BCLK_DIV);
|
|
+ } else {
|
|
+ /* set pll clock source to BCLK if slave mode */
|
|
+ ac101_set_pll(codec_dai, AC101_BCLK1, 0, aif1_lrck_div * params_rate(params),
|
|
+ freq_out);
|
|
+ }
|
|
+
|
|
+ #if _MASTER_MULTI_CODEC == _MASTER_AC101
|
|
+ /* Master mode, to clear cpu_dai fifos, disable output bclk & lrck */
|
|
+ ac101_aif1clk(codec, SND_SOC_DAPM_POST_PMD, 0);
|
|
+ #endif
|
|
+
|
|
+ AC101_DBG("rate: %d , channels: %d , samp_res: %d",
|
|
+ params_rate(params), channels, aif1_slot_size);
|
|
+
|
|
+ AC101_DBG("---\n");
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int ac101_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
|
|
+{
|
|
+ int reg_val;
|
|
+ int AIF_CLK_CTRL = AIF1_CLK_CTRL;
|
|
+ struct snd_soc_codec *codec = codec_dai->codec;
|
|
+
|
|
+ AC101_DBG();
|
|
+
|
|
+ /*
|
|
+ * master or slave selection
|
|
+ * 0 = Master mode
|
|
+ * 1 = Slave mode
|
|
+ */
|
|
+ reg_val = ac101_read(codec, AIF_CLK_CTRL);
|
|
+ reg_val &= ~(0x1<<AIF1_MSTR_MOD);
|
|
+ switch(fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
|
+ case SND_SOC_DAIFMT_CBM_CFM: /* codec clk & frm master, ap is slave*/
|
|
+ #if _MASTER_MULTI_CODEC == _MASTER_AC101
|
|
+ pr_warn("AC101 as Master\n");
|
|
+ reg_val |= (0x0<<AIF1_MSTR_MOD);
|
|
+ break;
|
|
+ #else
|
|
+ pr_warn("AC108 as Master\n");
|
|
+ #endif
|
|
+ case SND_SOC_DAIFMT_CBS_CFS: /* codec clk & frm slave, ap is master*/
|
|
+ pr_warn("AC101 as Slave\n");
|
|
+ reg_val |= (0x1<<AIF1_MSTR_MOD);
|
|
+ break;
|
|
+ default:
|
|
+ pr_err("unknwon master/slave format\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Enable TDM mode
|
|
+ */
|
|
+ reg_val |= (0x1 << AIF1_TDMM_ENA);
|
|
+ ac101_write(codec, AIF_CLK_CTRL, reg_val);
|
|
+
|
|
+ /* i2s mode selection */
|
|
+ reg_val = ac101_read(codec, AIF_CLK_CTRL);
|
|
+ reg_val&=~(3<<AIF1_DATA_FMT);
|
|
+ switch(fmt & SND_SOC_DAIFMT_FORMAT_MASK){
|
|
+ case SND_SOC_DAIFMT_I2S: /* I2S1 mode */
|
|
+ reg_val |= (0x0<<AIF1_DATA_FMT);
|
|
+ break;
|
|
+ case SND_SOC_DAIFMT_RIGHT_J: /* Right Justified mode */
|
|
+ reg_val |= (0x2<<AIF1_DATA_FMT);
|
|
+ break;
|
|
+ case SND_SOC_DAIFMT_LEFT_J: /* Left Justified mode */
|
|
+ reg_val |= (0x1<<AIF1_DATA_FMT);
|
|
+ break;
|
|
+ case SND_SOC_DAIFMT_DSP_A: /* L reg_val msb after FRM LRC */
|
|
+ reg_val |= (0x3<<AIF1_DATA_FMT);
|
|
+ break;
|
|
+ case SND_SOC_DAIFMT_DSP_B:
|
|
+ /* TODO: data offset set to 0 */
|
|
+ reg_val |= (0x3<<AIF1_DATA_FMT);
|
|
+ break;
|
|
+ default:
|
|
+ pr_err("%s, line:%d\n", __func__, __LINE__);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ ac101_write(codec, AIF_CLK_CTRL, reg_val);
|
|
+
|
|
+ /* DAI signal inversions */
|
|
+ reg_val = ac101_read(codec, AIF_CLK_CTRL);
|
|
+ switch(fmt & SND_SOC_DAIFMT_INV_MASK){
|
|
+ case SND_SOC_DAIFMT_NB_NF: /* normal bit clock + nor frame */
|
|
+ reg_val &= ~(0x1<<AIF1_LRCK_INV);
|
|
+ reg_val &= ~(0x1<<AIF1_BCLK_INV);
|
|
+ break;
|
|
+ case SND_SOC_DAIFMT_NB_IF: /* normal bclk + inv frm */
|
|
+ reg_val |= (0x1<<AIF1_LRCK_INV);
|
|
+ reg_val &= ~(0x1<<AIF1_BCLK_INV);
|
|
+ break;
|
|
+ case SND_SOC_DAIFMT_IB_NF: /* invert bclk + nor frm */
|
|
+ reg_val &= ~(0x1<<AIF1_LRCK_INV);
|
|
+ reg_val |= (0x1<<AIF1_BCLK_INV);
|
|
+ break;
|
|
+ case SND_SOC_DAIFMT_IB_IF: /* invert bclk + inv frm */
|
|
+ reg_val |= (0x1<<AIF1_LRCK_INV);
|
|
+ reg_val |= (0x1<<AIF1_BCLK_INV);
|
|
+ break;
|
|
+ }
|
|
+ ac101_write(codec, AIF_CLK_CTRL, reg_val);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int ac101_audio_startup(struct snd_pcm_substream *substream,
|
|
+ struct snd_soc_dai *codec_dai)
|
|
+{
|
|
+ // struct snd_soc_codec *codec = codec_dai->codec;
|
|
+
|
|
+ AC101_DBG("\n\n\n");
|
|
+
|
|
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int ac101_trigger(struct snd_pcm_substream *substream, int cmd,
|
|
+ struct snd_soc_dai *dai)
|
|
+{
|
|
+ struct snd_soc_codec *codec = dai->codec;
|
|
+ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
|
+ int ret = 0;
|
|
+
|
|
+ AC101_DBG("stream=%s cmd=%d\n",
|
|
+ snd_pcm_stream_str(substream),
|
|
+ cmd);
|
|
+
|
|
+ switch (cmd) {
|
|
+ case SNDRV_PCM_TRIGGER_START:
|
|
+ case SNDRV_PCM_TRIGGER_RESUME:
|
|
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
|
+ #if _MASTER_MULTI_CODEC == _MASTER_AC101
|
|
+ if (ac10x->aif1_clken == 0){
|
|
+ /*
|
|
+ * enable aif1clk, it' here due to reduce time between 'AC108 Sysclk Enable' and 'AC101 Sysclk Enable'
|
|
+ * Or else the two AC108 chips lost the sync.
|
|
+ */
|
|
+ ret = 0;
|
|
+ ret = ret || ac101_update_bits(codec, MOD_CLK_ENA,
|
|
+ (0x1<<MOD_CLK_AIF1), (0x1<<MOD_CLK_AIF1));
|
|
+ ret = ret || ac101_update_bits(codec, MOD_RST_CTRL, (0x1<<MOD_RESET_AIF1),
|
|
+ (0x1<<MOD_RESET_AIF1));
|
|
+ }
|
|
+ #endif
|
|
+ break;
|
|
+ case SNDRV_PCM_TRIGGER_STOP:
|
|
+ case SNDRV_PCM_TRIGGER_SUSPEND:
|
|
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
|
+ break;
|
|
+ default:
|
|
+ ret = -EINVAL;
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+#if 0
|
|
+static int ac101_set_dai_sysclk(struct snd_soc_dai *codec_dai,
|
|
+ int clk_id, unsigned int freq, int dir)
|
|
+{
|
|
+ struct snd_soc_codec *codec = codec_dai->codec;
|
|
+ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
|
+
|
|
+ AC101_DBG("id=%d freq=%d, dir=%d\n",
|
|
+ clk_id, freq, dir);
|
|
+
|
|
+ ac10x->sysclk = freq;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct snd_soc_dai_ops ac101_aif1_dai_ops = {
|
|
+ //.startup = ac101_audio_startup,
|
|
+ //.shutdown = ac101_aif_shutdown,
|
|
+ //.set_sysclk = ac101_set_dai_sysclk,
|
|
+ //.set_pll = ac101_set_pll,
|
|
+ //.set_fmt = ac101_set_dai_fmt,
|
|
+ //.hw_params = ac101_hw_params,
|
|
+ //.trigger = ac101_trigger,
|
|
+ //.digital_mute = ac101_aif_mute,
|
|
+};
|
|
+
|
|
+static struct snd_soc_dai_driver ac101_dai[] = {
|
|
+ {
|
|
+ .name = "ac10x-aif1",
|
|
+ .id = AIF1_CLK,
|
|
+ .playback = {
|
|
+ .stream_name = "Playback",
|
|
+ .channels_min = 1,
|
|
+ .channels_max = 8,
|
|
+ .rates = AC101_RATES,
|
|
+ .formats = AC101_FORMATS,
|
|
+ },
|
|
+ #if 0
|
|
+ .capture = {
|
|
+ .stream_name = "Capture",
|
|
+ .channels_min = 1,
|
|
+ .channels_max = 8,
|
|
+ .rates = AC101_RATES,
|
|
+ .formats = AC101_FORMATS,
|
|
+ },
|
|
+ #endif
|
|
+ .ops = &ac101_aif1_dai_ops,
|
|
+ }
|
|
+};
|
|
+#endif
|
|
+
|
|
+static void codec_resume_work(struct work_struct *work)
|
|
+{
|
|
+ struct ac10x_priv *ac10x = container_of(work, struct ac10x_priv, codec_resume);
|
|
+ struct snd_soc_codec *codec = ac10x->codec;
|
|
+
|
|
+ AC101_DBG("+++\n");
|
|
+
|
|
+ set_configuration(codec);
|
|
+ if (drc_used) {
|
|
+ drc_config(codec);
|
|
+ }
|
|
+ /*enable this bit to prevent leakage from ldoin*/
|
|
+ ac101_update_bits(codec, ADDA_TUNE3, (0x1<<OSCEN), (0x1<<OSCEN));
|
|
+
|
|
+ AC101_DBG("---\n");
|
|
+ return;
|
|
+}
|
|
+
|
|
+int ac101_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level)
|
|
+{
|
|
+ switch (level) {
|
|
+ case SND_SOC_BIAS_ON:
|
|
+ AC101_DBG("SND_SOC_BIAS_ON\n");
|
|
+ break;
|
|
+ case SND_SOC_BIAS_PREPARE:
|
|
+ AC101_DBG("SND_SOC_BIAS_PREPARE\n");
|
|
+ break;
|
|
+ case SND_SOC_BIAS_STANDBY:
|
|
+ AC101_DBG("SND_SOC_BIAS_STANDBY\n");
|
|
+ #ifdef CONFIG_AC101_SWITCH_DETECT
|
|
+ switch_hw_config(codec);
|
|
+ #endif
|
|
+ break;
|
|
+ case SND_SOC_BIAS_OFF:
|
|
+ #ifdef CONFIG_AC101_SWITCH_DETECT
|
|
+ ac101_update_bits(codec, ADC_APC_CTRL, (0x1<<HBIASEN), (0<<HBIASEN));
|
|
+ ac101_update_bits(codec, ADC_APC_CTRL, (0x1<<HBIASADCEN), (0<<HBIASADCEN));
|
|
+ #endif
|
|
+ ac101_update_bits(codec, OMIXER_DACA_CTRL, (0xf<<HPOUTPUTENABLE),
|
|
+ (0<<HPOUTPUTENABLE));
|
|
+ ac101_update_bits(codec, ADDA_TUNE3, (0x1<<OSCEN), (0<<OSCEN));
|
|
+ AC101_DBG("SND_SOC_BIAS_OFF\n");
|
|
+ break;
|
|
+ }
|
|
+ snd_soc_codec_get_dapm(codec)->bias_level = level;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int ac101_codec_probe(struct snd_soc_codec *codec)
|
|
+{
|
|
+ int ret = 0;
|
|
+ struct ac10x_priv *ac10x;
|
|
+
|
|
+ ac10x = dev_get_drvdata(codec->dev);
|
|
+ if (ac10x == NULL) {
|
|
+ AC101_DBG("not set client data!\n");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ ac10x->codec = codec;
|
|
+
|
|
+ INIT_DELAYED_WORK(&ac10x->dlywork, ac10x_work_aif_play);
|
|
+ INIT_WORK(&ac10x->codec_resume, codec_resume_work);
|
|
+ ac10x->dac_enable = 0;
|
|
+ ac10x->aif1_clken = 0;
|
|
+ mutex_init(&ac10x->dac_mutex);
|
|
+
|
|
+ set_configuration(ac10x->codec);
|
|
+
|
|
+ /*enable this bit to prevent leakage from ldoin*/
|
|
+ ac101_update_bits(codec, ADDA_TUNE3, (0x1<<OSCEN), (0x1<<OSCEN));
|
|
+ ac101_write(codec, DAC_VOL_CTRL, 0);
|
|
+
|
|
+ /* customized get/put inteface */
|
|
+ for (ret = 0; ret < ARRAY_SIZE(ac101_controls); ret++) {
|
|
+ struct snd_kcontrol_new* skn = &ac101_controls[ret];
|
|
+
|
|
+ skn->get = snd_ac101_get_volsw;
|
|
+ skn->put = snd_ac101_put_volsw;
|
|
+ }
|
|
+ ret = snd_soc_add_codec_controls(codec, ac101_controls, ARRAY_SIZE(ac101_controls));
|
|
+ if (ret) {
|
|
+ pr_err("[ac10x] Failed to register audio mode control, "
|
|
+ "will continue without it.\n");
|
|
+ }
|
|
+
|
|
+ #ifdef CONFIG_AC101_SWITCH_DETECT
|
|
+ ret = ac101_switch_probe(ac10x);
|
|
+ if (ret) {
|
|
+ // not care the switch return value
|
|
+ }
|
|
+ #endif
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* power down chip */
|
|
+int ac101_codec_remove(struct snd_soc_codec *codec)
|
|
+{
|
|
+ #ifdef CONFIG_AC101_SWITCH_DETECT
|
|
+ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
|
+
|
|
+ if (ac10x->irq) {
|
|
+ devm_free_irq(codec->dev, ac10x->irq, ac10x);
|
|
+ ac10x->irq = 0;
|
|
+ }
|
|
+
|
|
+ if (cancel_work_sync(&ac10x->work_switch) != 0) {
|
|
+ }
|
|
+
|
|
+ if (cancel_work_sync(&ac10x->work_clear_irq) != 0) {
|
|
+ }
|
|
+
|
|
+ if (ac10x->inpdev) {
|
|
+ input_unregister_device(ac10x->inpdev);
|
|
+ ac10x->inpdev = NULL;
|
|
+ }
|
|
+ #endif
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int ac101_codec_suspend(struct snd_soc_codec *codec)
|
|
+{
|
|
+ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
|
+
|
|
+ AC101_DBG("[codec]:suspend\n");
|
|
+ regcache_cache_only(ac10x->regmap101, true);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int ac101_codec_resume(struct snd_soc_codec *codec)
|
|
+{
|
|
+ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
|
+ int ret;
|
|
+
|
|
+ AC101_DBG("[codec]:resume");
|
|
+
|
|
+ /* Sync reg_cache with the hardware */
|
|
+ regcache_cache_only(ac10x->regmap101, false);
|
|
+ ret = regcache_sync(ac10x->regmap101);
|
|
+ if (ret != 0) {
|
|
+ dev_err(codec->dev, "Failed to sync register cache: %d\n", ret);
|
|
+ regcache_cache_only(ac10x->regmap101, true);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ #ifdef CONFIG_AC101_SWITCH_DETECT
|
|
+ ac10x->mode = HEADPHONE_IDLE;
|
|
+ ac10x->state = -1;
|
|
+ #endif
|
|
+
|
|
+ ac101_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
|
+ schedule_work(&ac10x->codec_resume);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/***************************************************************************/
|
|
+static ssize_t ac101_debug_store(struct device *dev,
|
|
+ struct device_attribute *attr, const char *buf, size_t count)
|
|
+{
|
|
+ struct ac10x_priv *ac10x = dev_get_drvdata(dev);
|
|
+ int val = 0, flag = 0;
|
|
+ u16 value_w, value_r;
|
|
+ u8 reg, num, i=0;
|
|
+
|
|
+ val = simple_strtol(buf, NULL, 16);
|
|
+ flag = (val >> 24) & 0xF;
|
|
+ if (flag) {
|
|
+ reg = (val >> 16) & 0xFF;
|
|
+ value_w = val & 0xFFFF;
|
|
+ ac101_write(ac10x->codec, reg, value_w);
|
|
+ printk("write 0x%x to reg:0x%x\n", value_w, reg);
|
|
+ } else {
|
|
+ reg = (val >> 8) & 0xFF;
|
|
+ num = val & 0xff;
|
|
+ printk("\n");
|
|
+ printk("read:start add:0x%x,count:0x%x\n", reg, num);
|
|
+
|
|
+ regcache_cache_bypass(ac10x->regmap101, true);
|
|
+ do {
|
|
+ value_r = ac101_read(ac10x->codec, reg);
|
|
+ printk("0x%x: 0x%04x ", reg++, value_r);
|
|
+ if (++i % 4 == 0 || i == num)
|
|
+ printk("\n");
|
|
+ } while (i < num);
|
|
+ regcache_cache_bypass(ac10x->regmap101, false);
|
|
+ }
|
|
+ return count;
|
|
+}
|
|
+static ssize_t ac101_debug_show(struct device *dev,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ printk("echo flag|reg|val > ac10x\n");
|
|
+ printk("eg read star addres=0x06,count 0x10:echo 0610 >ac10x\n");
|
|
+ printk("eg write value:0x13fe to address:0x06 :echo 10613fe > ac10x\n");
|
|
+ return 0;
|
|
+}
|
|
+static DEVICE_ATTR(ac10x, 0644, ac101_debug_show, ac101_debug_store);
|
|
+
|
|
+static struct attribute *audio_debug_attrs[] = {
|
|
+ &dev_attr_ac10x.attr,
|
|
+ NULL,
|
|
+};
|
|
+
|
|
+static struct attribute_group audio_debug_attr_group = {
|
|
+ .name = "ac101_debug",
|
|
+ .attrs = audio_debug_attrs,
|
|
+};
|
|
+/***************************************************************************/
|
|
+
|
|
+/************************************************************/
|
|
+static bool ac101_volatile_reg(struct device *dev, unsigned int reg)
|
|
+{
|
|
+ switch (reg) {
|
|
+ case PLL_CTRL2:
|
|
+ case HMIC_STS:
|
|
+ return true;
|
|
+ }
|
|
+ return false;
|
|
+}
|
|
+
|
|
+static const struct regmap_config ac101_regmap = {
|
|
+ .reg_bits = 8,
|
|
+ .val_bits = 16,
|
|
+ .reg_stride = 1,
|
|
+ .max_register = 0xB5,
|
|
+ .cache_type = REGCACHE_FLAT,
|
|
+ .volatile_reg = ac101_volatile_reg,
|
|
+};
|
|
+
|
|
+/* Sync reg_cache from the hardware */
|
|
+int ac10x_fill_regcache(struct device* dev, struct regmap* map) {
|
|
+ int r, i, n;
|
|
+ int v;
|
|
+
|
|
+ n = regmap_get_max_register(map);
|
|
+ for (i = 0; i < n; i++) {
|
|
+ regcache_cache_bypass(map, true);
|
|
+ r = regmap_read(map, i, &v);
|
|
+ if (r) {
|
|
+ dev_dbg(dev, "failed to read register %d\n", i);
|
|
+ continue;
|
|
+ }
|
|
+ regcache_cache_bypass(map, false);
|
|
+
|
|
+ regcache_cache_only(map, true);
|
|
+ r = regmap_write(map, i, v);
|
|
+ regcache_cache_only(map, false);
|
|
+ }
|
|
+ regcache_cache_bypass(map, false);
|
|
+ regcache_cache_only(map, false);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int ac101_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
|
|
+{
|
|
+ struct ac10x_priv *ac10x = i2c_get_clientdata(i2c);
|
|
+ int ret = 0;
|
|
+ unsigned v = 0;
|
|
+
|
|
+ AC101_DBG();
|
|
+
|
|
+ static_ac10x = ac10x;
|
|
+
|
|
+ ac10x->regmap101 = devm_regmap_init_i2c(i2c, &ac101_regmap);
|
|
+ if (IS_ERR(ac10x->regmap101)) {
|
|
+ ret = PTR_ERR(ac10x->regmap101);
|
|
+ dev_err(&i2c->dev, "Fail to initialize I/O: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ /* Chip reset */
|
|
+ regcache_cache_only(ac10x->regmap101, false);
|
|
+ ret = regmap_write(ac10x->regmap101, CHIP_AUDIO_RST, 0);
|
|
+ msleep(50);
|
|
+
|
|
+ /* sync regcache for FLAT type */
|
|
+ ac10x_fill_regcache(&i2c->dev, ac10x->regmap101);
|
|
+
|
|
+ ret = regmap_read(ac10x->regmap101, CHIP_AUDIO_RST, &v);
|
|
+ if (ret < 0) {
|
|
+ dev_err(&i2c->dev, "failed to read vendor ID: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ if (v != AC101_CHIP_ID) {
|
|
+ dev_err(&i2c->dev, "chip is not AC101 (%X)\n", v);
|
|
+ dev_err(&i2c->dev, "Expected %X\n", AC101_CHIP_ID);
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
+ ret = sysfs_create_group(&i2c->dev.kobj, &audio_debug_attr_group);
|
|
+ if (ret) {
|
|
+ pr_err("failed to create attr group\n");
|
|
+ }
|
|
+
|
|
+ ac10x->gpiod_spk_amp_gate = devm_gpiod_get_optional(&i2c->dev, "spk-amp-switch",
|
|
+ GPIOD_OUT_LOW);
|
|
+ if (IS_ERR(ac10x->gpiod_spk_amp_gate)) {
|
|
+ ac10x->gpiod_spk_amp_gate = NULL;
|
|
+ dev_err(&i2c->dev, "failed get spk-amp-switch in device tree\n");
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void ac101_shutdown(struct i2c_client *i2c)
|
|
+{
|
|
+ struct ac10x_priv *ac10x = i2c_get_clientdata(i2c);
|
|
+ struct snd_soc_codec *codec = ac10x->codec;
|
|
+ int reg_val;
|
|
+
|
|
+ if (codec == NULL) {
|
|
+ pr_err(": no sound card.\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /*set headphone volume to 0*/
|
|
+ reg_val = ac101_read(codec, HPOUT_CTRL);
|
|
+ reg_val &= ~(0x3f<<HP_VOL);
|
|
+ ac101_write(codec, HPOUT_CTRL, reg_val);
|
|
+
|
|
+ /*disable pa*/
|
|
+ reg_val = ac101_read(codec, HPOUT_CTRL);
|
|
+ reg_val &= ~(0x1<<HPPA_EN);
|
|
+ ac101_write(codec, HPOUT_CTRL, reg_val);
|
|
+
|
|
+ /*hardware xzh support*/
|
|
+ reg_val = ac101_read(codec, OMIXER_DACA_CTRL);
|
|
+ reg_val &= ~(0xf<<HPOUTPUTENABLE);
|
|
+ ac101_write(codec, OMIXER_DACA_CTRL, reg_val);
|
|
+
|
|
+ /*unmute l/r headphone pa*/
|
|
+ reg_val = ac101_read(codec, HPOUT_CTRL);
|
|
+ reg_val &= ~((0x1<<RHPPA_MUTE)|(0x1<<LHPPA_MUTE));
|
|
+ ac101_write(codec, HPOUT_CTRL, reg_val);
|
|
+ return;
|
|
+}
|
|
+
|
|
+int ac101_remove(struct i2c_client *i2c)
|
|
+{
|
|
+ sysfs_remove_group(&i2c->dev.kobj, &audio_debug_attr_group);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+MODULE_DESCRIPTION("ASoC ac10x driver");
|
|
+MODULE_AUTHOR("huangxin,liushaohua");
|
|
+MODULE_AUTHOR("PeterYang<linsheng.yang@seeed.cc>");
|
|
--- /dev/null
|
|
+++ b/sound/soc/codecs/ac101_regs.h
|
|
@@ -0,0 +1,431 @@
|
|
+/*
|
|
+ * ac101_regs.h
|
|
+ *
|
|
+ * (C) Copyright 2017-2018
|
|
+ * Seeed Technology Co., Ltd. <www.seeedstudio.com>
|
|
+ *
|
|
+ * PeterYang <linsheng.yang@seeed.cc>
|
|
+ *
|
|
+ * (C) Copyright 2010-2017
|
|
+ * Reuuimlla Technology Co., Ltd. <www.reuuimllatech.com>
|
|
+ * huangxin <huangxin@reuuimllatech.com>
|
|
+ *
|
|
+ * some simple description for this code
|
|
+ *
|
|
+ * 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.
|
|
+ *
|
|
+ */
|
|
+#ifndef __AC101_REGS_H__
|
|
+#define __AC101_REGS_H__
|
|
+
|
|
+/*pll source*/
|
|
+#define AC101_MCLK1 1
|
|
+#define AC101_MCLK2 2
|
|
+#define AC101_BCLK1 3
|
|
+#define AC101_BCLK2 4
|
|
+
|
|
+#define AIF1_CLK 1
|
|
+#define AIF2_CLK 2
|
|
+
|
|
+#define CHIP_AUDIO_RST 0x0
|
|
+#define PLL_CTRL1 0x1
|
|
+#define PLL_CTRL2 0x2
|
|
+#define SYSCLK_CTRL 0x3
|
|
+#define MOD_CLK_ENA 0x4
|
|
+#define MOD_RST_CTRL 0x5
|
|
+#define AIF_SR_CTRL 0x6
|
|
+
|
|
+#define AIF1_CLK_CTRL 0x10
|
|
+#define AIF1_ADCDAT_CTRL 0x11
|
|
+#define AIF1_DACDAT_CTRL 0x12
|
|
+#define AIF1_MXR_SRC 0x13
|
|
+#define AIF1_VOL_CTRL1 0x14
|
|
+#define AIF1_VOL_CTRL2 0x15
|
|
+#define AIF1_VOL_CTRL3 0x16
|
|
+#define AIF1_VOL_CTRL4 0x17
|
|
+#define AIF1_MXR_GAIN 0x18
|
|
+#define AIF1_RXD_CTRL 0x19
|
|
+#define ADC_DIG_CTRL 0x40
|
|
+#define ADC_VOL_CTRL 0x41
|
|
+#define ADC_DBG_CTRL 0x42
|
|
+
|
|
+#define HMIC_CTRL1 0x44
|
|
+#define HMIC_CTRL2 0x45
|
|
+#define HMIC_STS 0x46
|
|
+
|
|
+#define DAC_DIG_CTRL 0x48
|
|
+#define DAC_VOL_CTRL 0x49
|
|
+#define DAC_DBG_CTRL 0x4a
|
|
+#define DAC_MXR_SRC 0x4c
|
|
+#define DAC_MXR_GAIN 0x4d
|
|
+
|
|
+#define ADC_APC_CTRL 0x50
|
|
+#define ADC_SRC 0x51
|
|
+#define ADC_SRCBST_CTRL 0x52
|
|
+#define OMIXER_DACA_CTRL 0x53
|
|
+#define OMIXER_SR 0x54
|
|
+#define OMIXER_BST1_CTRL 0x55
|
|
+#define HPOUT_CTRL 0x56
|
|
+#define ESPKOUT_CTRL 0x57
|
|
+#define SPKOUT_CTRL 0x58
|
|
+#define LOUT_CTRL 0x59
|
|
+#define ADDA_TUNE1 0x5a
|
|
+#define ADDA_TUNE2 0x5b
|
|
+#define ADDA_TUNE3 0x5c
|
|
+#define HPOUT_STR 0x5d
|
|
+
|
|
+/*CHIP_AUDIO_RST*/
|
|
+#define AC101_CHIP_ID 0x0101
|
|
+
|
|
+/*PLL_CTRL1*/
|
|
+#define DPLL_DAC_BIAS 14
|
|
+#define PLL_POSTDIV_M 8
|
|
+#define CLOSE_LOOP 6
|
|
+#define INT 0
|
|
+
|
|
+/*PLL_CTRL2*/
|
|
+#define PLL_EN 15
|
|
+#define PLL_LOCK_STATUS 14
|
|
+#define PLL_PREDIV_NI 4
|
|
+#define PLL_POSTDIV_NF 0
|
|
+
|
|
+/*SYSCLK_CTRL*/
|
|
+#define PLLCLK_ENA 15
|
|
+#define PLLCLK_SRC 12
|
|
+#define AIF1CLK_ENA 11
|
|
+#define AIF1CLK_SRC 8
|
|
+#define AIF2CLK_ENA 7
|
|
+#define AIF2CLK_SRC 4
|
|
+#define SYSCLK_ENA 3
|
|
+#define SYSCLK_SRC 0
|
|
+
|
|
+/*MOD_CLK_ENA*/
|
|
+#define MOD_CLK_AIF1 15
|
|
+#define MOD_CLK_AIF2 14
|
|
+#define MOD_CLK_AIF3 13
|
|
+#define MOD_CLK_SRC1 11
|
|
+#define MOD_CLK_SRC2 10
|
|
+#define MOD_CLK_HPF_AGC 7
|
|
+#define MOD_CLK_HPF_DRC 6
|
|
+#define MOD_CLK_ADC_DIG 3
|
|
+#define MOD_CLK_DAC_DIG 2
|
|
+
|
|
+/*MOD_RST_CTRL*/
|
|
+#define MOD_RESET_CTL 0
|
|
+#define MOD_RESET_AIF1 15
|
|
+#define MOD_RESET_AIF2 14
|
|
+#define MOD_RESET_AIF3 13
|
|
+#define MOD_RESET_SRC1 11
|
|
+#define MOD_RESET_SRC2 10
|
|
+#define MOD_RESET_HPF_AGC 7
|
|
+#define MOD_RESET_HPF_DRC 6
|
|
+#define MOD_RESET_ADC_DIG 3
|
|
+#define MOD_RESET_DAC_DIG 2
|
|
+
|
|
+/*AIF_SR_CTRL*/
|
|
+#define AIF1_FS 12 //AIF1 Sample Rate
|
|
+#define AIF2_FS 8 //AIF2 Sample Rate
|
|
+#define SRC1_ENA 3
|
|
+#define SRC1_SRC 2
|
|
+#define SRC2_ENA 1
|
|
+#define SRC2_SRC 0
|
|
+
|
|
+/*AIF1LCK_CTRL*/
|
|
+#define AIF1_MSTR_MOD 15
|
|
+#define AIF1_BCLK_INV 14
|
|
+#define AIF1_LRCK_INV 13
|
|
+#define AIF1_BCLK_DIV 9
|
|
+#define AIF1_LRCK_DIV 6
|
|
+#define AIF1_WORK_SIZ 4
|
|
+#define AIF1_DATA_FMT 2
|
|
+#define DSP_MONO_PCM 1
|
|
+#define AIF1_TDMM_ENA 0
|
|
+
|
|
+/*AIF1_ADCDAT_CTRL*/
|
|
+#define AIF1_AD0L_ENA 15
|
|
+#define AIF1_AD0R_ENA 14
|
|
+#define AIF1_AD1L_ENA 13
|
|
+#define AIF1_AD1R_ENA 12
|
|
+#define AIF1_AD0L_SRC 10
|
|
+#define AIF1_AD0R_SRC 8
|
|
+#define AIF1_AD1L_SRC 6
|
|
+#define AIF1_AD1R_SRC 4
|
|
+#define AIF1_ADCP_ENA 3
|
|
+#define AIF1_ADUL_ENA 2
|
|
+#define AIF1_SLOT_SIZ 0
|
|
+
|
|
+/*AIF1_DACDAT_CTRL*/
|
|
+#define AIF1_DA0L_ENA 15
|
|
+#define AIF1_DA0R_ENA 14
|
|
+#define AIF1_DA1L_ENA 13
|
|
+#define AIF1_DA1R_ENA 12
|
|
+#define AIF1_DA0L_SRC 10
|
|
+#define AIF1_DA0R_SRC 8
|
|
+#define AIF1_DA1L_SRC 6
|
|
+#define AIF1_DA1R_SRC 4
|
|
+#define AIF1_DACP_ENA 3
|
|
+#define AIF1_DAUL_ENA 2
|
|
+#define AIF1_SLOT_SIZ 0
|
|
+
|
|
+/*AIF1_MXR_SRC*/
|
|
+#define AIF1_AD0L_AIF1_DA0L_MXR 15
|
|
+#define AIF1_AD0L_AIF2_DACL_MXR 14
|
|
+#define AIF1_AD0L_ADCL_MXR 13
|
|
+#define AIF1_AD0L_AIF2_DACR_MXR 12
|
|
+#define AIF1_AD0R_AIF1_DA0R_MXR 11
|
|
+#define AIF1_AD0R_AIF2_DACR_MXR 10
|
|
+#define AIF1_AD0R_ADCR_MXR 9
|
|
+#define AIF1_AD0R_AIF2_DACL_MXR 8
|
|
+#define AIF1_AD1L_AIF2_DACL_MXR 7
|
|
+#define AIF1_AD1L_ADCL_MXR 6
|
|
+#define AIF1_AD1L_MXR_SRC 6
|
|
+#define AIF1_AD1R_AIF2_DACR_MXR 3
|
|
+#define AIF1_AD1R_ADCR_MXR 2
|
|
+#define AIF1_AD1R_MXR_SRC 2
|
|
+
|
|
+/*AIF1_VOL_CTRL1*/
|
|
+#define AIF1_AD0L_VOL 8
|
|
+#define AIF1_AD0R_VOL 0
|
|
+
|
|
+/*AIF1_VOL_CTRL2*/
|
|
+#define AIF1_AD1L_VOL 8
|
|
+#define AIF1_AD1R_VOL 0
|
|
+
|
|
+/*AIF1_VOL_CTRL3*/
|
|
+#define AIF1_DA0L_VOL 8
|
|
+#define AIF1_DA0R_VOL 0
|
|
+
|
|
+/*AIF1_VOL_CTRL4*/
|
|
+#define AIF1_DA1L_VOL 8
|
|
+#define AIF1_DA1R_VOL 0
|
|
+
|
|
+/*AIF1_MXR_GAIN*/
|
|
+#define AIF1_AD0L_MXR_GAIN 12
|
|
+#define AIF1_AD0R_MXR_GAIN 8
|
|
+#define AIF1_AD1L_MXR_GAIN 6
|
|
+#define AIF1_AD1R_MXR_GAIN 2
|
|
+
|
|
+/*AIF1_RXD_CTRL*/
|
|
+#define AIF1_N_DATA_DISCARD 8
|
|
+
|
|
+/*ADC_DIG_CTRL*/
|
|
+#define ENAD 15
|
|
+#define ENDM 14
|
|
+#define ADFIR32 13
|
|
+#define ADOUT_DTS 2
|
|
+#define ADOUT_DLY 1
|
|
+
|
|
+/*ADC_VOL_CTRL*/
|
|
+#define ADC_VOL_L 8
|
|
+#define ADC_VOL_R 0
|
|
+
|
|
+/*ADC_DBG_CTRL*/
|
|
+#define ADSW 15
|
|
+#define DMIC_CLK_PIN_CTRL 12
|
|
+
|
|
+/*HMIC_CTRL1*/
|
|
+#define HMIC_M 12
|
|
+#define HMIC_N 8
|
|
+#define HMIC_DATA_IRQ_MODE 7
|
|
+#define HMIC_TH1_HYSTERESIS 5
|
|
+#define HMIC_PULLOUT_IRQ 4
|
|
+#define HMIC_PLUGIN_IRQ 3
|
|
+#define HMIC_KEYUP_IRQ 2
|
|
+#define HMIC_KEYDOWN_IRQ 1
|
|
+#define HMIC_DATA_IRQ_EN 0
|
|
+
|
|
+/*HMIC_CTRL2*/
|
|
+#define HMIC_SAMPLE_SELECT 14
|
|
+#define HMIC_TH2_HYSTERESIS 13
|
|
+#define HMIC_TH2 8
|
|
+#define HMIC_SF 6
|
|
+#define KEYUP_CLEAR 5
|
|
+#define HMIC_TH1 0
|
|
+
|
|
+/*HMIC_STS*/
|
|
+#define HMIC_DATA 8
|
|
+#define GET_HMIC_DATA(r) (((r) >> HMIC_DATA) & 0x1F)
|
|
+#define HMIC_PULLOUT_PEND 4
|
|
+#define HMIC_PLUGIN_PEND 3
|
|
+#define HMIC_KEYUP_PEND 2
|
|
+#define HMKC_KEYDOWN_PEND 1
|
|
+#define HMIC_DATA_PEND 0
|
|
+#define HMIC_PEND_ALL (0x1F)
|
|
+
|
|
+/*DAC_DIG_CTRL*/
|
|
+#define ENDA 15
|
|
+#define ENHPF 14
|
|
+#define DAFIR32 13
|
|
+#define MODQU 8
|
|
+
|
|
+/*DAC_VOL_CTRL*/
|
|
+#define DAC_VOL_L 8
|
|
+#define DAC_VOL_R 0
|
|
+
|
|
+/*DAC_DBG_CTRL*/
|
|
+#define DASW 15
|
|
+#define ENDWA_N 14
|
|
+#define DAC_MOD_DBG 13
|
|
+#define DAC_PTN_SEL 6
|
|
+#define DVC 0
|
|
+
|
|
+/*DAC_MXR_SRC*/
|
|
+#define DACL_MXR_AIF1_DA0L 15
|
|
+#define DACL_MXR_AIF1_DA1L 14
|
|
+#define DACL_MXR_AIF2_DACL 13
|
|
+#define DACL_MXR_ADCL 12
|
|
+#define DACL_MXR_SRC 12
|
|
+#define DACR_MXR_AIF1_DA0R 11
|
|
+#define DACR_MXR_AIF1_DA1R 10
|
|
+#define DACR_MXR_AIF2_DACR 9
|
|
+#define DACR_MXR_ADCR 8
|
|
+#define DACR_MXR_SRC 8
|
|
+
|
|
+/*DAC_MXR_GAIN*/
|
|
+#define DACL_MXR_GAIN 12
|
|
+#define DACR_MXR_GAIN 8
|
|
+
|
|
+/*ADC_APC_CTRL*/
|
|
+#define ADCREN 15
|
|
+#define ADCRG 12
|
|
+#define ADCLEN 11
|
|
+#define ADCLG 8
|
|
+#define MBIASEN 7
|
|
+#define MMIC_BIAS_CHOP_EN 6
|
|
+#define MMIC_BIAS_CHOP_CKS 4
|
|
+#define HBIASMOD 2
|
|
+#define HBIASEN 1
|
|
+#define HBIASADCEN 0
|
|
+
|
|
+/*ADC_SRC*/
|
|
+#define RADCMIXMUTEMIC1BOOST (13)
|
|
+#define RADCMIXMUTEMIC2BOOST (12)
|
|
+#define RADCMIXMUTELINEINLR (11)
|
|
+#define RADCMIXMUTELINEINR (10)
|
|
+#define RADCMIXMUTEAUXINR (9)
|
|
+#define RADCMIXMUTEROUTPUT (8)
|
|
+#define RADCMIXMUTELOUTPUT (7)
|
|
+#define LADCMIXMUTEMIC1BOOST (6)
|
|
+#define LADCMIXMUTEMIC2BOOST (5)
|
|
+#define LADCMIXMUTELINEINLR (4)
|
|
+#define LADCMIXMUTELINEINL (3)
|
|
+#define LADCMIXMUTEAUXINL (2)
|
|
+#define LADCMIXMUTELOUTPUT (1)
|
|
+#define LADCMIXMUTEROUTPUT (0)
|
|
+
|
|
+/*ADC_SRCBST_CTRL*/
|
|
+#define MIC1AMPEN 15
|
|
+#define ADC_MIC1G 12
|
|
+#define MIC2AMPEN 11
|
|
+#define ADC_MIC2G 8
|
|
+#define MIC2SLT 7
|
|
+#define LINEIN_PREG 4
|
|
+#define AUXI_PREG 0
|
|
+
|
|
+/*OMIXER_DACA_CTRL*/
|
|
+#define DACAREN 15
|
|
+#define DACALEN 14
|
|
+#define RMIXEN 13
|
|
+#define LMIXEN 12
|
|
+#define HPOUTPUTENABLE 8
|
|
+
|
|
+/*OMIXER_SR*/
|
|
+#define RMIXMUTEMIC1BOOST (13)
|
|
+#define RMIXMUTEMIC2BOOST (12)
|
|
+#define RMIXMUTELINEINLR (11)
|
|
+#define RMIXMUTELINEINR (10)
|
|
+#define RMIXMUTEAUXINR (9)
|
|
+#define RMIXMUTEDACR (8)
|
|
+#define RMIXMUTEDACL (7)
|
|
+#define LMIXMUTEMIC1BOOST (6)
|
|
+#define LMIXMUTEMIC2BOOST (5)
|
|
+#define LMIXMUTELINEINLR (4)
|
|
+#define LMIXMUTELINEINL (3)
|
|
+#define LMIXMUTEAUXINL (2)
|
|
+#define LMIXMUTEDACL (1)
|
|
+#define LMIXMUTEDACR (0)
|
|
+
|
|
+/*OMIXER_BST1_CTRL*/
|
|
+#define BIASVOLTAGE 12
|
|
+#define AXG 9
|
|
+#define OMIXER_MIC1G 6
|
|
+#define OMIXER_MIC2G 3
|
|
+#define LINEING 0
|
|
+
|
|
+/*HPOUT_CTRL*/
|
|
+#define RHPS 15
|
|
+#define LHPS 14
|
|
+#define RHPPA_MUTE 13
|
|
+#define LHPPA_MUTE 12
|
|
+#define HPPA_EN 11
|
|
+#define HP_VOL 4
|
|
+#define HPPA_DEL 2
|
|
+#define HPPA_IS 0
|
|
+
|
|
+/*ESPKOUT_CTRL*/
|
|
+#define EAR_RAMP_TIME 11
|
|
+#define ESPA_OUT_CURRENT 9
|
|
+#define ESPSR 7
|
|
+#define ESPPA_MUTE 6
|
|
+#define ESPPA_EN 5
|
|
+#define ESP_VOL 0
|
|
+
|
|
+/*SPKOUT_CTRL*/
|
|
+#define HPCALICKS 13
|
|
+#define RSPKS 12
|
|
+#define RSPKINVEN 11
|
|
+#define RSPK_EN 9
|
|
+#define LSPKS 8
|
|
+#define LSPKINVEN 7
|
|
+#define LSPK_EN 5
|
|
+#define SPK_VOL 0
|
|
+
|
|
+/*LOUT_CTRL*/
|
|
+#define LINEOUTG 5
|
|
+#define LINEOUTEN 4
|
|
+#define LINEOUTS0 3
|
|
+#define LINEOUTS1 2
|
|
+#define LINEOUTS2 1
|
|
+#define LINEOUTS3 0
|
|
+
|
|
+/*ADDA_TUNE1*/
|
|
+#define CURRENT_TEST_SELECT 14
|
|
+#define BIHE_CTRL 12
|
|
+#define DITHER 11
|
|
+#define DITHER_CLK 9
|
|
+#define ZERO_CROSSOVER_EN 8
|
|
+#define ZERO_CROSSOVER_TIME 7
|
|
+#define EAR_SPEED_SELECT 6
|
|
+#define REF_CHOPPEN_CKS 4
|
|
+#define OPMIC_BIAS_CUR 0
|
|
+
|
|
+/*ADDA_TUNE2*/
|
|
+#define OPDAC_BIAS_CUR 14
|
|
+#define OPDRV_BIAS_CUR 12
|
|
+#define OPMIX_BIAS_CUR 10
|
|
+#define OPEAR_BIAS_CUR 8
|
|
+#define OPVR_BIAS_CUR 6
|
|
+#define OPAAF_BIAS_CUR 4
|
|
+#define OPADC1_BIAS_CUR 2
|
|
+#define OPADC2_BIAS_CUR 0
|
|
+
|
|
+/*ADDA_TUNE3*/
|
|
+#define LDOEN 15
|
|
+#define LDO_SEL 12
|
|
+#define BIASCALIVERIFY 11
|
|
+#define BIASMODE 10
|
|
+#define BIASCALIDATA 9
|
|
+#define OSCS 1
|
|
+#define OSCEN 0
|
|
+
|
|
+/*HPOUT_STR*/
|
|
+#define HPVL_SOFT_MOD 14
|
|
+#define HPVL_STEP_CTRL 8
|
|
+#define DACA_CHND_ENA 7
|
|
+#define HPPA_MXRD_ENA 6
|
|
+#define HPVL_CTRL_OUT 0
|
|
+
|
|
+#endif//__AC101_REGS_H__
|
|
--- /dev/null
|
|
+++ b/sound/soc/codecs/ac108.c
|
|
@@ -0,0 +1,1622 @@
|
|
+/*
|
|
+ * ac10x.c -- ac10x ALSA SoC Audio driver
|
|
+ *
|
|
+ *
|
|
+ * Author: Baozhu Zuo<zuobaozhu@gmail.com>
|
|
+ *
|
|
+ * 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 <linux/clk.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/gpio/consumer.h>
|
|
+#include <linux/i2c.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/moduleparam.h>
|
|
+#include <linux/pm.h>
|
|
+#include <linux/regmap.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/workqueue.h>
|
|
+#include <sound/core.h>
|
|
+#include <sound/initval.h>
|
|
+#include <sound/pcm.h>
|
|
+#include <sound/pcm_params.h>
|
|
+#include <sound/soc.h>
|
|
+#include <sound/tlv.h>
|
|
+
|
|
+#include "ac108.h"
|
|
+#include "ac10x.h"
|
|
+
|
|
+#define _USE_CAPTURE 1
|
|
+#define _MASTER_INDEX 0
|
|
+
|
|
+/* #undef DEBUG
|
|
+ * use 'make DEBUG=1' to enable debugging
|
|
+ */
|
|
+
|
|
+/**
|
|
+ * TODO:
|
|
+ * 1, add PM API: ac108_suspend,ac108_resume
|
|
+ * 2,0x65-0x6a
|
|
+ * 3,0x76-0x79 high 4bit
|
|
+ */
|
|
+struct pll_div {
|
|
+ unsigned int freq_in;
|
|
+ unsigned int freq_out;
|
|
+ unsigned int m1;
|
|
+ unsigned int m2;
|
|
+ unsigned int n;
|
|
+ unsigned int k1;
|
|
+ unsigned int k2;
|
|
+};
|
|
+
|
|
+static struct ac10x_priv *ac10x;
|
|
+
|
|
+struct real_val_to_reg_val {
|
|
+ unsigned int real_val;
|
|
+ unsigned int reg_val;
|
|
+};
|
|
+
|
|
+static const struct real_val_to_reg_val ac108_sample_rate[] = {
|
|
+ { 8000, 0 },
|
|
+ { 11025, 1 },
|
|
+ { 12000, 2 },
|
|
+ { 16000, 3 },
|
|
+ { 22050, 4 },
|
|
+ { 24000, 5 },
|
|
+ { 32000, 6 },
|
|
+ { 44100, 7 },
|
|
+ { 48000, 8 },
|
|
+ { 96000, 9 },
|
|
+};
|
|
+
|
|
+/* Sample resolution */
|
|
+static const struct real_val_to_reg_val ac108_samp_res[] = {
|
|
+ { 8, 1 },
|
|
+ { 12, 2 },
|
|
+ { 16, 3 },
|
|
+ { 20, 4 },
|
|
+ { 24, 5 },
|
|
+ { 28, 6 },
|
|
+ { 32, 7 },
|
|
+};
|
|
+
|
|
+static const unsigned int ac108_bclkdivs[] = {
|
|
+ 0, 1, 2, 4,
|
|
+ 6, 8, 12, 16,
|
|
+ 24, 32, 48, 64,
|
|
+ 96, 128, 176, 192,
|
|
+};
|
|
+
|
|
+/* FOUT =(FIN * N) / [(M1+1) * (M2+1)*(K1+1)*(K2+1)] ; M1[0,31], M2[0,1], N[0,1023], K1[0,31], K2[0,1] */
|
|
+static const struct pll_div ac108_pll_div_list[] = {
|
|
+ { 400000, _FREQ_24_576K, 0, 0, 614, 4, 1 },
|
|
+ { 512000, _FREQ_24_576K, 0, 0, 960, 9, 1 }, /* _FREQ_24_576K/48 */
|
|
+ { 768000, _FREQ_24_576K, 0, 0, 640, 9, 1 }, /* _FREQ_24_576K/32 */
|
|
+ { 800000, _FREQ_24_576K, 0, 0, 614, 9, 1 },
|
|
+ { 1024000, _FREQ_24_576K, 0, 0, 480, 9, 1 }, /* _FREQ_24_576K/24 */
|
|
+ { 1600000, _FREQ_24_576K, 0, 0, 307, 9, 1 },
|
|
+ { 2048000, _FREQ_24_576K, 0, 0, 240, 9, 1 }, /* accurate, 8000 * 256 */
|
|
+ { 3072000, _FREQ_24_576K, 0, 0, 160, 9, 1 }, /* accurate, 12000 * 256 */
|
|
+ { 4096000, _FREQ_24_576K, 2, 0, 360, 9, 1 }, /* accurate, 16000 * 256 */
|
|
+ { 6000000, _FREQ_24_576K, 4, 0, 410, 9, 1 },
|
|
+ { 12000000, _FREQ_24_576K, 9, 0, 410, 9, 1 },
|
|
+ { 13000000, _FREQ_24_576K, 8, 0, 340, 9, 1 },
|
|
+ { 15360000, _FREQ_24_576K, 12, 0, 415, 9, 1 },
|
|
+ { 16000000, _FREQ_24_576K, 12, 0, 400, 9, 1 },
|
|
+ { 19200000, _FREQ_24_576K, 15, 0, 410, 9, 1 },
|
|
+ { 19680000, _FREQ_24_576K, 15, 0, 400, 9, 1 },
|
|
+ { 24000000, _FREQ_24_576K, 9, 0, 256,24, 0 }, /* accurate, 24M -> 24.576M */
|
|
+
|
|
+ { 400000, _FREQ_22_579K, 0, 0, 566, 4, 1 },
|
|
+ { 512000, _FREQ_22_579K, 0, 0, 880, 9, 1 },
|
|
+ { 768000, _FREQ_22_579K, 0, 0, 587, 9, 1 },
|
|
+ { 800000, _FREQ_22_579K, 0, 0, 567, 9, 1 },
|
|
+ { 1024000, _FREQ_22_579K, 0, 0, 440, 9, 1 },
|
|
+ { 1600000, _FREQ_22_579K, 1, 0, 567, 9, 1 },
|
|
+ { 2048000, _FREQ_22_579K, 0, 0, 220, 9, 1 },
|
|
+ { 3072000, _FREQ_22_579K, 0, 0, 148, 9, 1 },
|
|
+ { 4096000, _FREQ_22_579K, 2, 0, 330, 9, 1 },
|
|
+ { 6000000, _FREQ_22_579K, 2, 0, 227, 9, 1 },
|
|
+ { 12000000, _FREQ_22_579K, 8, 0, 340, 9, 1 },
|
|
+ { 13000000, _FREQ_22_579K, 9, 0, 350, 9, 1 },
|
|
+ { 15360000, _FREQ_22_579K, 10, 0, 325, 9, 1 },
|
|
+ { 16000000, _FREQ_22_579K, 11, 0, 340, 9, 1 },
|
|
+ { 19200000, _FREQ_22_579K, 13, 0, 330, 9, 1 },
|
|
+ { 19680000, _FREQ_22_579K, 14, 0, 345, 9, 1 },
|
|
+ { 24000000, _FREQ_22_579K, 24, 0, 588,24, 0 }, /* accurate, 24M -> 22.5792M */
|
|
+
|
|
+
|
|
+ { _FREQ_24_576K / 1, _FREQ_24_576K, 9, 0, 200, 9, 1 }, /* _FREQ_24_576K */
|
|
+ { _FREQ_24_576K / 2, _FREQ_24_576K, 9, 0, 400, 9, 1 }, /* 12288000,accurate, 48000 * 256 */
|
|
+ { _FREQ_24_576K / 4, _FREQ_24_576K, 4, 0, 400, 9, 1 }, /* 6144000, accurate, 24000 * 256 */
|
|
+ { _FREQ_24_576K / 16, _FREQ_24_576K, 0, 0, 320, 9, 1 }, /* 1536000 */
|
|
+ { _FREQ_24_576K / 64, _FREQ_24_576K, 0, 0, 640, 4, 1 }, /* 384000 */
|
|
+ { _FREQ_24_576K / 96, _FREQ_24_576K, 0, 0, 960, 4, 1 }, /* 256000 */
|
|
+ { _FREQ_24_576K / 128, _FREQ_24_576K, 0, 0, 512, 1, 1 }, /* 192000 */
|
|
+ { _FREQ_24_576K / 176, _FREQ_24_576K, 0, 0, 880, 4, 0 }, /* 140000 */
|
|
+ { _FREQ_24_576K / 192, _FREQ_24_576K, 0, 0, 960, 4, 0 }, /* 128000 */
|
|
+
|
|
+ { _FREQ_22_579K / 1, _FREQ_22_579K, 9, 0, 200, 9, 1 }, /* _FREQ_22_579K */
|
|
+ { _FREQ_22_579K / 2, _FREQ_22_579K, 9, 0, 400, 9, 1 }, /* 11289600,accurate, 44100 * 256 */
|
|
+ { _FREQ_22_579K / 4, _FREQ_22_579K, 4, 0, 400, 9, 1 }, /* 5644800, accurate, 22050 * 256 */
|
|
+ { _FREQ_22_579K / 16, _FREQ_22_579K, 0, 0, 320, 9, 1 }, /* 1411200 */
|
|
+ { _FREQ_22_579K / 64, _FREQ_22_579K, 0, 0, 640, 4, 1 }, /* 352800 */
|
|
+ { _FREQ_22_579K / 96, _FREQ_22_579K, 0, 0, 960, 4, 1 }, /* 235200 */
|
|
+ { _FREQ_22_579K / 128, _FREQ_22_579K, 0, 0, 512, 1, 1 }, /* 176400 */
|
|
+ { _FREQ_22_579K / 176, _FREQ_22_579K, 0, 0, 880, 4, 0 }, /* 128290 */
|
|
+ { _FREQ_22_579K / 192, _FREQ_22_579K, 0, 0, 960, 4, 0 }, /* 117600 */
|
|
+
|
|
+ { _FREQ_22_579K / 6, _FREQ_22_579K, 2, 0, 360, 9, 1 }, /* 3763200 */
|
|
+ { _FREQ_22_579K / 8, _FREQ_22_579K, 0, 0, 160, 9, 1 }, /* 2822400, accurate, 11025 * 256 */
|
|
+ { _FREQ_22_579K / 12, _FREQ_22_579K, 0, 0, 240, 9, 1 }, /* 1881600 */
|
|
+ { _FREQ_22_579K / 24, _FREQ_22_579K, 0, 0, 480, 9, 1 }, /* 940800 */
|
|
+ { _FREQ_22_579K / 32, _FREQ_22_579K, 0, 0, 640, 9, 1 }, /* 705600 */
|
|
+ { _FREQ_22_579K / 48, _FREQ_22_579K, 0, 0, 960, 9, 1 }, /* 470400 */
|
|
+};
|
|
+
|
|
+
|
|
+/* AC108 definition */
|
|
+#define AC108_CHANNELS_MAX 8 /* range[1, 16] */
|
|
+#define AC108_RATES (SNDRV_PCM_RATE_8000_96000 & \
|
|
+ ~(SNDRV_PCM_RATE_64000 | \
|
|
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000))
|
|
+#define AC108_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
|
|
+ /*SNDRV_PCM_FMTBIT_S20_3LE | \
|
|
+ SNDRV_PCM_FMTBIT_S24_LE |*/ \
|
|
+ SNDRV_PCM_FMTBIT_S32_LE)
|
|
+
|
|
+static const DECLARE_TLV_DB_SCALE(tlv_adc_pga_gain, 0, 100, 0);
|
|
+static const DECLARE_TLV_DB_SCALE(tlv_ch_digital_vol, -11925, 75, 0);
|
|
+
|
|
+int ac10x_read(u8 reg, u8* rt_val, struct regmap* i2cm)
|
|
+{
|
|
+ int r, v = 0;
|
|
+
|
|
+ if ((r = regmap_read(i2cm, reg, &v)) < 0)
|
|
+ pr_info("ac10x_read info->[REG-0x%02x]\n", reg);
|
|
+ else
|
|
+ *rt_val = v;
|
|
+ return r;
|
|
+}
|
|
+
|
|
+int ac10x_write(u8 reg, u8 val, struct regmap* i2cm)
|
|
+{
|
|
+ int r;
|
|
+
|
|
+ if ((r = regmap_write(i2cm, reg, val)) < 0)
|
|
+ pr_info("ac10x_write info->[REG-0x%02x,val-0x%02x]\n", reg, val);
|
|
+ return r;
|
|
+}
|
|
+
|
|
+int ac10x_update_bits(u8 reg, u8 mask, u8 val, struct regmap* i2cm)
|
|
+{
|
|
+ int r;
|
|
+
|
|
+ if ((r = regmap_update_bits(i2cm, reg, mask, val)) < 0)
|
|
+ pr_info("%s() info->[REG-0x%02x,val-0x%02x]\n", __func__, reg, val);
|
|
+ return r;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * snd_ac108_get_volsw - single mixer get callback
|
|
+ * @kcontrol: mixer control
|
|
+ * @ucontrol: control element information
|
|
+ *
|
|
+ * Callback to get the value of a single mixer control, or a double mixer
|
|
+ * control that spans 2 registers.
|
|
+ *
|
|
+ * Returns 0 for success.
|
|
+ */
|
|
+static int snd_ac108_get_volsw(struct snd_kcontrol *kcontrol,
|
|
+ struct snd_ctl_elem_value *ucontrol)
|
|
+{
|
|
+ struct soc_mixer_control *mc =
|
|
+ (struct soc_mixer_control *)kcontrol->private_value;
|
|
+ unsigned int mask = (1 << fls(mc->max)) - 1;
|
|
+ unsigned int invert = mc->invert;
|
|
+ int ret, chip = mc->autodisable;
|
|
+ u8 val;
|
|
+
|
|
+ if ((ret = ac10x_read(mc->reg, &val, ac10x->i2cmap[chip])) < 0)
|
|
+ return ret;
|
|
+
|
|
+ val = ((val >> mc->shift) & mask) - mc->min;
|
|
+ if (invert) {
|
|
+ val = mc->max - val;
|
|
+ }
|
|
+ ucontrol->value.integer.value[0] = val;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * snd_ac108_put_volsw - single mixer put callback
|
|
+ * @kcontrol: mixer control
|
|
+ * @ucontrol: control element information
|
|
+ *
|
|
+ * Callback to set the value of a single mixer control, or a double mixer
|
|
+ * control that spans 2 registers.
|
|
+ *
|
|
+ * Returns 0 for success.
|
|
+ */
|
|
+static int snd_ac108_put_volsw(struct snd_kcontrol *kcontrol,
|
|
+ struct snd_ctl_elem_value *ucontrol)
|
|
+{
|
|
+ struct soc_mixer_control *mc =
|
|
+ (struct soc_mixer_control *)kcontrol->private_value;
|
|
+ unsigned int sign_bit = mc->sign_bit;
|
|
+ unsigned int val, mask = (1 << fls(mc->max)) - 1;
|
|
+ unsigned int invert = mc->invert;
|
|
+ int ret, chip = mc->autodisable;
|
|
+
|
|
+ if (sign_bit)
|
|
+ mask = BIT(sign_bit + 1) - 1;
|
|
+
|
|
+ val = ((ucontrol->value.integer.value[0] + mc->min) & mask);
|
|
+ if (invert) {
|
|
+ val = mc->max - val;
|
|
+ }
|
|
+
|
|
+ mask = mask << mc->shift;
|
|
+ val = val << mc->shift;
|
|
+
|
|
+ ret = ac10x_update_bits(mc->reg, mask, val, ac10x->i2cmap[chip]);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+#define SOC_AC108_SINGLE_TLV(xname, reg, shift, max, invert, chip, tlv_array) \
|
|
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
|
|
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
|
|
+ SNDRV_CTL_ELEM_ACCESS_READWRITE,\
|
|
+ .tlv.p = (tlv_array), \
|
|
+ .info = snd_soc_info_volsw, .get = snd_ac108_get_volsw,\
|
|
+ .put = snd_ac108_put_volsw, \
|
|
+ .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, chip) }
|
|
+
|
|
+/* single ac108 */
|
|
+static const struct snd_kcontrol_new ac108_snd_controls[] = {
|
|
+ /* ### chip 0 ### */
|
|
+ /*0x70: ADC1 Digital Channel Volume Control Register*/
|
|
+ SOC_AC108_SINGLE_TLV("CH1 digital volume", ADC1_DVOL_CTRL,
|
|
+ 0, 0xff, 0, 0, tlv_ch_digital_vol),
|
|
+ /*0x71: ADC2 Digital Channel Volume Control Register*/
|
|
+ SOC_AC108_SINGLE_TLV("CH2 digital volume", ADC2_DVOL_CTRL,
|
|
+ 0, 0xff, 0, 0, tlv_ch_digital_vol),
|
|
+ /*0x72: ADC3 Digital Channel Volume Control Register*/
|
|
+ SOC_AC108_SINGLE_TLV("CH3 digital volume", ADC3_DVOL_CTRL,
|
|
+ 0, 0xff, 0, 0, tlv_ch_digital_vol),
|
|
+ /*0x73: ADC4 Digital Channel Volume Control Register*/
|
|
+ SOC_AC108_SINGLE_TLV("CH4 digital volume", ADC4_DVOL_CTRL,
|
|
+ 0, 0xff, 0, 0, tlv_ch_digital_vol),
|
|
+
|
|
+ /*0x90: Analog PGA1 Control Register*/
|
|
+ SOC_AC108_SINGLE_TLV("ADC1 PGA gain", ANA_PGA1_CTRL,
|
|
+ ADC1_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain),
|
|
+ /*0x91: Analog PGA2 Control Register*/
|
|
+ SOC_AC108_SINGLE_TLV("ADC2 PGA gain", ANA_PGA2_CTRL,
|
|
+ ADC2_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain),
|
|
+ /*0x92: Analog PGA3 Control Register*/
|
|
+ SOC_AC108_SINGLE_TLV("ADC3 PGA gain", ANA_PGA3_CTRL,
|
|
+ ADC3_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain),
|
|
+ /*0x93: Analog PGA4 Control Register*/
|
|
+ SOC_AC108_SINGLE_TLV("ADC4 PGA gain", ANA_PGA4_CTRL,
|
|
+ ADC4_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain),
|
|
+};
|
|
+/* multiple ac108s */
|
|
+static const struct snd_kcontrol_new ac108tdm_snd_controls[] = {
|
|
+ /* ### chip 1 ### */
|
|
+ /*0x70: ADC1 Digital Channel Volume Control Register*/
|
|
+ SOC_AC108_SINGLE_TLV("CH1 digital volume", ADC1_DVOL_CTRL,
|
|
+ 0, 0xff, 0, 1, tlv_ch_digital_vol),
|
|
+ /*0x71: ADC2 Digital Channel Volume Control Register*/
|
|
+ SOC_AC108_SINGLE_TLV("CH2 digital volume", ADC2_DVOL_CTRL,
|
|
+ 0, 0xff, 0, 1, tlv_ch_digital_vol),
|
|
+ /*0x72: ADC3 Digital Channel Volume Control Register*/
|
|
+ SOC_AC108_SINGLE_TLV("CH3 digital volume", ADC3_DVOL_CTRL,
|
|
+ 0, 0xff, 0, 1, tlv_ch_digital_vol),
|
|
+ /*0x73: ADC4 Digital Channel Volume Control Register*/
|
|
+ SOC_AC108_SINGLE_TLV("CH4 digital volume", ADC4_DVOL_CTRL,
|
|
+ 0, 0xff, 0, 1, tlv_ch_digital_vol),
|
|
+
|
|
+ /*0x90: Analog PGA1 Control Register*/
|
|
+ SOC_AC108_SINGLE_TLV("ADC1 PGA gain", ANA_PGA1_CTRL,
|
|
+ ADC1_ANALOG_PGA, 0x1f, 0, 1, tlv_adc_pga_gain),
|
|
+ /*0x91: Analog PGA2 Control Register*/
|
|
+ SOC_AC108_SINGLE_TLV("ADC2 PGA gain", ANA_PGA2_CTRL,
|
|
+ ADC2_ANALOG_PGA, 0x1f, 0, 1, tlv_adc_pga_gain),
|
|
+ /*0x92: Analog PGA3 Control Register*/
|
|
+ SOC_AC108_SINGLE_TLV("ADC3 PGA gain", ANA_PGA3_CTRL,
|
|
+ ADC3_ANALOG_PGA, 0x1f, 0, 1, tlv_adc_pga_gain),
|
|
+ /*0x93: Analog PGA4 Control Register*/
|
|
+ SOC_AC108_SINGLE_TLV("ADC4 PGA gain", ANA_PGA4_CTRL,
|
|
+ ADC4_ANALOG_PGA, 0x1f, 0, 1, tlv_adc_pga_gain),
|
|
+
|
|
+ /* ### chip 0 ### */
|
|
+ /*0x70: ADC1 Digital Channel Volume Control Register*/
|
|
+ SOC_AC108_SINGLE_TLV("CH5 digital volume", ADC1_DVOL_CTRL,
|
|
+ 0, 0xff, 0, 0, tlv_ch_digital_vol),
|
|
+ /*0x71: ADC2 Digital Channel Volume Control Register*/
|
|
+ SOC_AC108_SINGLE_TLV("CH6 digital volume", ADC2_DVOL_CTRL,
|
|
+ 0, 0xff, 0, 0, tlv_ch_digital_vol),
|
|
+ /*0x72: ADC3 Digital Channel Volume Control Register*/
|
|
+ SOC_AC108_SINGLE_TLV("CH7 digital volume", ADC3_DVOL_CTRL,
|
|
+ 0, 0xff, 0, 0, tlv_ch_digital_vol),
|
|
+ /*0x73: ADC4 Digital Channel Volume Control Register*/
|
|
+ SOC_AC108_SINGLE_TLV("CH8 digital volume", ADC4_DVOL_CTRL,
|
|
+ 0, 0xff, 0, 0, tlv_ch_digital_vol),
|
|
+
|
|
+ /*0x90: Analog PGA1 Control Register*/
|
|
+ SOC_AC108_SINGLE_TLV("ADC5 PGA gain", ANA_PGA1_CTRL,
|
|
+ ADC1_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain),
|
|
+ /*0x91: Analog PGA2 Control Register*/
|
|
+ SOC_AC108_SINGLE_TLV("ADC6 PGA gain", ANA_PGA2_CTRL,
|
|
+ ADC2_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain),
|
|
+ /*0x92: Analog PGA3 Control Register*/
|
|
+ SOC_AC108_SINGLE_TLV("ADC7 PGA gain", ANA_PGA3_CTRL,
|
|
+ ADC3_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain),
|
|
+ /*0x93: Analog PGA4 Control Register*/
|
|
+ SOC_AC108_SINGLE_TLV("ADC8 PGA gain", ANA_PGA4_CTRL,
|
|
+ ADC4_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain),
|
|
+};
|
|
+
|
|
+
|
|
+static const struct snd_soc_dapm_widget ac108_dapm_widgets[] = {
|
|
+ /* input widgets */
|
|
+ SND_SOC_DAPM_INPUT("MIC1P"),
|
|
+ SND_SOC_DAPM_INPUT("MIC1N"),
|
|
+
|
|
+ SND_SOC_DAPM_INPUT("MIC2P"),
|
|
+ SND_SOC_DAPM_INPUT("MIC2N"),
|
|
+
|
|
+ SND_SOC_DAPM_INPUT("MIC3P"),
|
|
+ SND_SOC_DAPM_INPUT("MIC3N"),
|
|
+
|
|
+ SND_SOC_DAPM_INPUT("MIC4P"),
|
|
+ SND_SOC_DAPM_INPUT("MIC4N"),
|
|
+
|
|
+ SND_SOC_DAPM_INPUT("DMIC1"),
|
|
+ SND_SOC_DAPM_INPUT("DMIC2"),
|
|
+
|
|
+ /*0xa0: ADC1 Analog Control 1 Register*/
|
|
+ /*0xa1-0xa6:use the defualt value*/
|
|
+ SND_SOC_DAPM_AIF_IN("Channel 1 AAF", "Capture", 0, ANA_ADC1_CTRL1, ADC1_DSM_ENABLE, 1),
|
|
+ SND_SOC_DAPM_SUPPLY("Channel 1 EN", ANA_ADC1_CTRL1, ADC1_PGA_ENABLE, 1, NULL, 0),
|
|
+ SND_SOC_DAPM_MICBIAS("MIC1BIAS", ANA_ADC1_CTRL1, ADC1_MICBIAS_EN, 1),
|
|
+
|
|
+ /*0xa7: ADC2 Analog Control 1 Register*/
|
|
+ /*0xa8-0xad:use the defualt value*/
|
|
+ SND_SOC_DAPM_AIF_IN("Channel 2 AAF", "Capture", 0, ANA_ADC2_CTRL1, ADC2_DSM_ENABLE, 1),
|
|
+ SND_SOC_DAPM_SUPPLY("Channel 2 EN", ANA_ADC2_CTRL1, ADC2_PGA_ENABLE, 1, NULL, 0),
|
|
+ SND_SOC_DAPM_MICBIAS("MIC2BIAS", ANA_ADC2_CTRL1, ADC2_MICBIAS_EN, 1),
|
|
+
|
|
+ /*0xae: ADC3 Analog Control 1 Register*/
|
|
+ /*0xaf-0xb4:use the defualt value*/
|
|
+ SND_SOC_DAPM_AIF_IN("Channel 3 AAF", "Capture", 0, ANA_ADC3_CTRL1, ADC3_DSM_ENABLE, 1),
|
|
+ SND_SOC_DAPM_SUPPLY("Channel 3 EN", ANA_ADC3_CTRL1, ADC3_PGA_ENABLE, 1, NULL, 0),
|
|
+ SND_SOC_DAPM_MICBIAS("MIC3BIAS", ANA_ADC3_CTRL1, ADC3_MICBIAS_EN, 1),
|
|
+
|
|
+ /*0xb5: ADC4 Analog Control 1 Register*/
|
|
+ /*0xb6-0xbb:use the defualt value*/
|
|
+ SND_SOC_DAPM_AIF_IN("Channel 4 AAF", "Capture", 0, ANA_ADC4_CTRL1, ADC4_DSM_ENABLE, 1),
|
|
+ SND_SOC_DAPM_SUPPLY("Channel 4 EN", ANA_ADC4_CTRL1, ADC4_PGA_ENABLE, 1, NULL, 0),
|
|
+ SND_SOC_DAPM_MICBIAS("MIC4BIAS", ANA_ADC4_CTRL1, ADC4_MICBIAS_EN, 1),
|
|
+
|
|
+
|
|
+ /*0x61: ADC Digital Part Enable Register*/
|
|
+ SND_SOC_DAPM_SUPPLY("ADC EN", ADC_DIG_EN, 4, 1, NULL, 0),
|
|
+ SND_SOC_DAPM_ADC("ADC1", "Capture", ADC_DIG_EN, 0, 1),
|
|
+ SND_SOC_DAPM_ADC("ADC2", "Capture", ADC_DIG_EN, 1, 1),
|
|
+ SND_SOC_DAPM_ADC("ADC3", "Capture", ADC_DIG_EN, 2, 1),
|
|
+ SND_SOC_DAPM_ADC("ADC4", "Capture", ADC_DIG_EN, 3, 1),
|
|
+
|
|
+ SND_SOC_DAPM_SUPPLY("ADC1 CLK", ANA_ADC4_CTRL7, ADC1_CLK_GATING, 1, NULL, 0),
|
|
+ SND_SOC_DAPM_SUPPLY("ADC2 CLK", ANA_ADC4_CTRL7, ADC2_CLK_GATING, 1, NULL, 0),
|
|
+ SND_SOC_DAPM_SUPPLY("ADC3 CLK", ANA_ADC4_CTRL7, ADC3_CLK_GATING, 1, NULL, 0),
|
|
+ SND_SOC_DAPM_SUPPLY("ADC4 CLK", ANA_ADC4_CTRL7, ADC4_CLK_GATING, 1, NULL, 0),
|
|
+
|
|
+ SND_SOC_DAPM_SUPPLY("DSM EN", ANA_ADC4_CTRL6, DSM_DEMOFF, 1, NULL, 0),
|
|
+
|
|
+ /*0x62:Digital MIC Enable Register*/
|
|
+ SND_SOC_DAPM_MICBIAS("DMIC1 enable", DMIC_EN, 0, 0),
|
|
+ SND_SOC_DAPM_MICBIAS("DMIC2 enable", DMIC_EN, 1, 0),
|
|
+};
|
|
+
|
|
+static const struct snd_soc_dapm_route ac108_dapm_routes[] = {
|
|
+
|
|
+ { "ADC1", NULL, "Channel 1 AAF" },
|
|
+ { "ADC2", NULL, "Channel 2 AAF" },
|
|
+ { "ADC3", NULL, "Channel 3 AAF" },
|
|
+ { "ADC4", NULL, "Channel 4 AAF" },
|
|
+
|
|
+ { "Channel 1 AAF", NULL, "MIC1BIAS" },
|
|
+ { "Channel 2 AAF", NULL, "MIC2BIAS" },
|
|
+ { "Channel 3 AAF", NULL, "MIC3BIAS" },
|
|
+ { "Channel 4 AAF", NULL, "MIC4BIAS" },
|
|
+
|
|
+ { "MIC1BIAS", NULL, "ADC1 CLK" },
|
|
+ { "MIC2BIAS", NULL, "ADC2 CLK" },
|
|
+ { "MIC3BIAS", NULL, "ADC3 CLK" },
|
|
+ { "MIC4BIAS", NULL, "ADC4 CLK" },
|
|
+
|
|
+
|
|
+ { "ADC1 CLK", NULL, "DSM EN" },
|
|
+ { "ADC2 CLK", NULL, "DSM EN" },
|
|
+ { "ADC3 CLK", NULL, "DSM EN" },
|
|
+ { "ADC4 CLK", NULL, "DSM EN" },
|
|
+
|
|
+
|
|
+ { "DSM EN", NULL, "ADC EN" },
|
|
+
|
|
+ { "Channel 1 EN", NULL, "DSM EN" },
|
|
+ { "Channel 2 EN", NULL, "DSM EN" },
|
|
+ { "Channel 3 EN", NULL, "DSM EN" },
|
|
+ { "Channel 4 EN", NULL, "DSM EN" },
|
|
+
|
|
+
|
|
+ { "MIC1P", NULL, "Channel 1 EN" },
|
|
+ { "MIC1N", NULL, "Channel 1 EN" },
|
|
+
|
|
+ { "MIC2P", NULL, "Channel 2 EN" },
|
|
+ { "MIC2N", NULL, "Channel 2 EN" },
|
|
+
|
|
+ { "MIC3P", NULL, "Channel 3 EN" },
|
|
+ { "MIC3N", NULL, "Channel 3 EN" },
|
|
+
|
|
+ { "MIC4P", NULL, "Channel 4 EN" },
|
|
+ { "MIC4N", NULL, "Channel 4 EN" },
|
|
+
|
|
+};
|
|
+
|
|
+static int ac108_multi_write(u8 reg, u8 val, struct ac10x_priv *ac10x)
|
|
+{
|
|
+ u8 i;
|
|
+ for (i = 0; i < ac10x->codec_cnt; i++)
|
|
+ ac10x_write(reg, val, ac10x->i2cmap[i]);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int ac108_multi_update_bits(u8 reg, u8 mask, u8 val, struct ac10x_priv *ac10x)
|
|
+{
|
|
+ int r = 0;
|
|
+ u8 i;
|
|
+ for (i = 0; i < ac10x->codec_cnt; i++)
|
|
+ r |= ac10x_update_bits(reg, mask, val, ac10x->i2cmap[i]);
|
|
+ return r;
|
|
+}
|
|
+
|
|
+static unsigned int ac108_codec_read(struct snd_soc_codec *codec, unsigned int reg)
|
|
+{
|
|
+ unsigned char val_r;
|
|
+ struct ac10x_priv *ac10x = dev_get_drvdata(codec->dev);
|
|
+ /*read one chip is fine*/
|
|
+ ac10x_read(reg, &val_r, ac10x->i2cmap[_MASTER_INDEX]);
|
|
+ return val_r;
|
|
+}
|
|
+
|
|
+static int ac108_codec_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int val)
|
|
+{
|
|
+ struct ac10x_priv *ac10x = dev_get_drvdata(codec->dev);
|
|
+ ac108_multi_write(reg, val, ac10x);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * The Power management related registers are Reg01h~Reg09h
|
|
+ * 0x01-0x05,0x08,use the default value
|
|
+ * @author baozhu (17-6-21)
|
|
+ *
|
|
+ * @param ac10x
|
|
+ */
|
|
+static void ac108_configure_power(struct ac10x_priv *ac10x)
|
|
+{
|
|
+ /**
|
|
+ * 0x06:Enable Analog LDO
|
|
+ */
|
|
+ ac108_multi_update_bits(PWR_CTRL6, 0x01 << LDO33ANA_ENABLE, 0x01 << LDO33ANA_ENABLE, ac10x);
|
|
+ /**
|
|
+ * 0x07:
|
|
+ * Control VREF output and micbias voltage ?
|
|
+ * REF faststart disable, enable Enable VREF (needed for Analog
|
|
+ * LDO and MICBIAS)
|
|
+ */
|
|
+ ac108_multi_update_bits(PWR_CTRL7,
|
|
+ 0x1f << VREF_SEL | 0x01 << VREF_FASTSTART_ENABLE |
|
|
+ 0x01 << VREF_ENABLE,
|
|
+ 0x13 << VREF_SEL | 0x00 << VREF_FASTSTART_ENABLE |
|
|
+ 0x01 << VREF_ENABLE,
|
|
+ ac10x);
|
|
+ /**
|
|
+ * 0x09:
|
|
+ * Disable fast-start circuit on VREFP
|
|
+ * VREFP_RESCTRL=00=1 MOhm
|
|
+ * IGEN_TRIM=100=+25%
|
|
+ * Enable VREFP (needed by all audio input channels)
|
|
+ */
|
|
+ ac108_multi_update_bits(PWR_CTRL9,
|
|
+ 0x01 << VREFP_FASTSTART_ENABLE | 0x03 << VREFP_RESCTRL |
|
|
+ 0x07 << IGEN_TRIM | 0x01 << VREFP_ENABLE,
|
|
+ 0x00 << VREFP_FASTSTART_ENABLE | 0x00 << VREFP_RESCTRL |
|
|
+ 0x04 << IGEN_TRIM | 0x01 << VREFP_ENABLE,
|
|
+ ac10x);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * The clock management related registers are Reg20h~Reg25h
|
|
+ * The PLL management related registers are Reg10h~Reg18h.
|
|
+ * @author baozhu (17-6-20)
|
|
+ *
|
|
+ * @param ac10x
|
|
+ * @param rate : sample rate
|
|
+ *
|
|
+ * @return int : fail or success
|
|
+ */
|
|
+static int ac108_config_pll(struct ac10x_priv *ac10x, unsigned rate, unsigned lrck_ratio)
|
|
+{
|
|
+ unsigned int i = 0;
|
|
+ struct pll_div ac108_pll_div = { 0 };
|
|
+
|
|
+ if (ac10x->clk_id == SYSCLK_SRC_PLL) {
|
|
+ unsigned pll_src, pll_freq_in;
|
|
+
|
|
+ if (lrck_ratio == 0) {
|
|
+ /* PLL clock source from MCLK */
|
|
+ pll_freq_in = ac10x->sysclk;
|
|
+ pll_src = 0x0;
|
|
+ } else {
|
|
+ /* PLL clock source from BCLK */
|
|
+ pll_freq_in = rate * lrck_ratio;
|
|
+ pll_src = 0x1;
|
|
+ }
|
|
+
|
|
+ /* FOUT =(FIN * N) / [(M1+1) * (M2+1)*(K1+1)*(K2+1)] */
|
|
+ for (i = 0; i < ARRAY_SIZE(ac108_pll_div_list); i++) {
|
|
+ if (ac108_pll_div_list[i].freq_in == pll_freq_in &&
|
|
+ ac108_pll_div_list[i].freq_out % rate == 0) {
|
|
+ ac108_pll_div = ac108_pll_div_list[i];
|
|
+ dev_info(&ac10x->i2c[_MASTER_INDEX]->dev,
|
|
+ "AC108 PLL freq_in match:%u, freq_out:%u\n\n",
|
|
+ ac108_pll_div.freq_in, ac108_pll_div.freq_out);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ /* 0x11,0x12,0x13,0x14: Config PLL DIV param M1/M2/N/K1/K2 */
|
|
+ ac108_multi_update_bits(PLL_CTRL5,
|
|
+ 0x1f << PLL_POSTDIV1 | 0x01 << PLL_POSTDIV2,
|
|
+ ac108_pll_div.k1 << PLL_POSTDIV1 |
|
|
+ ac108_pll_div.k2 << PLL_POSTDIV2, ac10x);
|
|
+ ac108_multi_update_bits(PLL_CTRL4, 0xff << PLL_LOOPDIV_LSB,
|
|
+ (unsigned char)ac108_pll_div.n << PLL_LOOPDIV_LSB, ac10x);
|
|
+ ac108_multi_update_bits(PLL_CTRL3, 0x03 << PLL_LOOPDIV_MSB,
|
|
+ (ac108_pll_div.n >> 8) << PLL_LOOPDIV_MSB, ac10x);
|
|
+ ac108_multi_update_bits(PLL_CTRL2, 0x1f << PLL_PREDIV1 | 0x01 << PLL_PREDIV2,
|
|
+ ac108_pll_div.m1 << PLL_PREDIV1 |
|
|
+ ac108_pll_div.m2 << PLL_PREDIV2, ac10x);
|
|
+
|
|
+ /*0x18: PLL clk lock enable*/
|
|
+ ac108_multi_update_bits(PLL_LOCK_CTRL, 0x1 << PLL_LOCK_EN,
|
|
+ 0x1 << PLL_LOCK_EN, ac10x);
|
|
+
|
|
+ /**
|
|
+ * 0x20: enable pll, pll source from mclk/bclk, sysclk source from pll, enable sysclk
|
|
+ */
|
|
+ ac108_multi_update_bits(SYSCLK_CTRL,
|
|
+ 0x01 << PLLCLK_EN | 0x03 << PLLCLK_SRC |
|
|
+ 0x01 << SYSCLK_SRC | 0x01 << SYSCLK_EN,
|
|
+ 0x01 << PLLCLK_EN | pll_src << PLLCLK_SRC |
|
|
+ 0x01 << SYSCLK_SRC | 0x01 << SYSCLK_EN, ac10x);
|
|
+ ac10x->mclk = ac108_pll_div.freq_out;
|
|
+ }
|
|
+ if (ac10x->clk_id == SYSCLK_SRC_MCLK) {
|
|
+ /**
|
|
+ *0x20: sysclk source from mclk, enable sysclk
|
|
+ */
|
|
+ ac108_multi_update_bits(SYSCLK_CTRL,
|
|
+ 0x01 << PLLCLK_EN | 0x01 << SYSCLK_SRC | 0x01 << SYSCLK_EN,
|
|
+ 0x00 << PLLCLK_EN | 0x00 << SYSCLK_SRC | 0x01 << SYSCLK_EN,
|
|
+ ac10x);
|
|
+ ac10x->mclk = ac10x->sysclk;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * support no more than 16 slots.
|
|
+ */
|
|
+static int ac108_multi_chips_slots(struct ac10x_priv *ac, int slots)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ /*
|
|
+ * codec0 enable slots 2,3,0,1 when 1 codec
|
|
+ *
|
|
+ * codec0 enable slots 6,7,0,1 when 2 codec
|
|
+ * codec1 enable slots 2,3,4,5
|
|
+ *
|
|
+ * ...
|
|
+ */
|
|
+ for (i = 0; i < ac->codec_cnt; i++) {
|
|
+ /* rotate map, due to channels rotated by CPU_DAI */
|
|
+ const unsigned vec_mask[] = {
|
|
+ 0x3 << 6 | 0x3, // slots 6,7,0,1
|
|
+ 0xF << 2, // slots 2,3,4,5
|
|
+ 0,
|
|
+ 0,
|
|
+ };
|
|
+ const unsigned vec_maps[] = {
|
|
+ /*
|
|
+ * chip 0,
|
|
+ * mic 0 sample -> slot 6
|
|
+ * mic 1 sample -> slot 7
|
|
+ * mic 2 sample -> slot 0
|
|
+ * mic 3 sample -> slot 1
|
|
+ */
|
|
+ 0x0 << 12 | 0x1 << 14 | 0x2 << 0 | 0x3 << 2,
|
|
+ /*
|
|
+ * chip 1,
|
|
+ * mic 0 sample -> slot 2
|
|
+ * mic 1 sample -> slot 3
|
|
+ * mic 2 sample -> slot 4
|
|
+ * mic 3 sample -> slot 5
|
|
+ */
|
|
+ 0x0 << 4 | 0x1 << 6 | 0x2 << 8 | 0x3 << 10,
|
|
+ 0,
|
|
+ 0,
|
|
+ };
|
|
+ unsigned vec;
|
|
+
|
|
+ /* 0x38-0x3A I2S_TX1_CTRLx */
|
|
+ if (ac->codec_cnt == 1) {
|
|
+ vec = 0xFUL;
|
|
+ } else {
|
|
+ vec = vec_mask[i];
|
|
+ }
|
|
+ ac10x_write(I2S_TX1_CTRL1, slots - 1, ac->i2cmap[i]);
|
|
+ ac10x_write(I2S_TX1_CTRL2, (vec >> 0) & 0xFF, ac->i2cmap[i]);
|
|
+ ac10x_write(I2S_TX1_CTRL3, (vec >> 8) & 0xFF, ac->i2cmap[i]);
|
|
+
|
|
+ /* 0x3C-0x3F I2S_TX1_CHMP_CTRLx */
|
|
+ if (ac->codec_cnt == 1) {
|
|
+ vec = (0x2 << 0 | 0x3 << 2 | 0x0 << 4 | 0x1 << 6);
|
|
+ } else if (ac->codec_cnt == 2) {
|
|
+ vec = vec_maps[i];
|
|
+ }
|
|
+
|
|
+ ac10x_write(I2S_TX1_CHMP_CTRL1, (vec >> 0) & 0xFF, ac->i2cmap[i]);
|
|
+ ac10x_write(I2S_TX1_CHMP_CTRL2, (vec >> 8) & 0xFF, ac->i2cmap[i]);
|
|
+ ac10x_write(I2S_TX1_CHMP_CTRL3, (vec >> 16) & 0xFF, ac->i2cmap[i]);
|
|
+ ac10x_write(I2S_TX1_CHMP_CTRL4, (vec >> 24) & 0xFF, ac->i2cmap[i]);
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int ac108_hw_params(struct snd_pcm_substream *substream,
|
|
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
|
|
+{
|
|
+ unsigned int i, channels, samp_res, rate;
|
|
+ struct snd_soc_codec *codec = dai->codec;
|
|
+ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
|
+ unsigned bclkdiv;
|
|
+ int ret = 0;
|
|
+ u8 v;
|
|
+
|
|
+ dev_dbg(dai->dev, "%s() stream=%s play:%d capt:%d +++\n", __func__,
|
|
+ snd_pcm_stream_str(substream),
|
|
+ dai->stream[SNDRV_PCM_STREAM_PLAYBACK].active,
|
|
+ dai->stream[SNDRV_PCM_STREAM_CAPTURE].active);
|
|
+
|
|
+ if (ac10x->i2c101) {
|
|
+ ret = ac101_hw_params(substream, params, dai);
|
|
+ if (ret > 0) {
|
|
+ dev_dbg(dai->dev, "%s() L%d returned\n", __func__, __LINE__);
|
|
+ /* not configure hw_param twice */
|
|
+ return 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE &&
|
|
+ dai->stream[SNDRV_PCM_STREAM_PLAYBACK].active)
|
|
+ || (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
|
|
+ dai->stream[SNDRV_PCM_STREAM_CAPTURE].active)) {
|
|
+ /* not configure hw_param twice */
|
|
+ /* return 0; */
|
|
+ }
|
|
+
|
|
+ channels = params_channels(params);
|
|
+
|
|
+ /* Master mode, to clear cpu_dai fifos, output bclk without lrck */
|
|
+ ac10x_read(I2S_CTRL, &v, ac10x->i2cmap[_MASTER_INDEX]);
|
|
+ if (v & (0x01 << BCLK_IOEN)) {
|
|
+ ac10x_update_bits(I2S_CTRL, 0x1 << LRCK_IOEN,
|
|
+ 0x0 << LRCK_IOEN, ac10x->i2cmap[_MASTER_INDEX]);
|
|
+ }
|
|
+
|
|
+ switch (params_format(params)) {
|
|
+ case SNDRV_PCM_FORMAT_S8:
|
|
+ samp_res = 0;
|
|
+ break;
|
|
+ case SNDRV_PCM_FORMAT_S16_LE:
|
|
+ samp_res = 2;
|
|
+ break;
|
|
+ case SNDRV_PCM_FORMAT_S20_3LE:
|
|
+ samp_res = 3;
|
|
+ break;
|
|
+ case SNDRV_PCM_FORMAT_S24_LE:
|
|
+ samp_res = 4;
|
|
+ break;
|
|
+ case SNDRV_PCM_FORMAT_S32_LE:
|
|
+ samp_res = 6;
|
|
+ break;
|
|
+ default:
|
|
+ dev_err(dai->dev, "AC108 don't supported the sample resolution: %u\n",
|
|
+ params_format(params));
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(ac108_sample_rate); i++) {
|
|
+ if (ac108_sample_rate[i].real_val == params_rate(params) /
|
|
+ (ac10x->data_protocol + 1UL)) {
|
|
+ rate = i;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if (i >= ARRAY_SIZE(ac108_sample_rate)) {
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (channels == 8 && ac108_sample_rate[rate].real_val == 96000) {
|
|
+ /* 24.576M bit clock is not support by ac108 */
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ dev_dbg(dai->dev, "rate: %d , channels: %d , samp_res: %d",
|
|
+ ac108_sample_rate[rate].real_val,
|
|
+ channels,
|
|
+ ac108_samp_res[samp_res].real_val);
|
|
+
|
|
+ /**
|
|
+ * 0x33:
|
|
+ * The 8-Low bit of LRCK period value. It is used to program
|
|
+ * the number of BCLKs per channel of sample frame. This value
|
|
+ * is interpreted as follow:
|
|
+ * The 8-Low bit of LRCK period value. It is used to program
|
|
+ * the number of BCLKs per channel of sample frame. This value
|
|
+ * is interpreted as follow: PCM mode: Number of BCLKs within
|
|
+ * (Left + Right) channel width I2S / Left-Justified /
|
|
+ * Right-Justified mode: Number of BCLKs within each individual
|
|
+ * channel width (Left or Right) N+1
|
|
+ * For example:
|
|
+ * n = 7: 8 BCLK width
|
|
+ * …
|
|
+ * n = 1023: 1024 BCLKs width
|
|
+ * 0X32[0:1]:
|
|
+ * The 2-High bit of LRCK period value.
|
|
+ */
|
|
+ if (ac10x->i2s_mode != PCM_FORMAT) {
|
|
+ if (ac10x->data_protocol) {
|
|
+ ac108_multi_write(I2S_LRCK_CTRL2, ac108_samp_res[samp_res].real_val - 1,
|
|
+ ac10x);
|
|
+ /*encoding mode, the max LRCK period value < 32,so the 2-High bit is zero*/
|
|
+ ac108_multi_update_bits(I2S_LRCK_CTRL1, 0x03 << 0, 0x00, ac10x);
|
|
+ } else {
|
|
+ /*TDM mode or normal mode*/
|
|
+ //ac108_multi_update_bits(I2S_LRCK_CTRL1, 0x03 << 0, 0x00, ac10x);
|
|
+ ac108_multi_write(I2S_LRCK_CTRL2, ac108_samp_res[samp_res].real_val - 1,
|
|
+ ac10x);
|
|
+ ac108_multi_update_bits(I2S_LRCK_CTRL1, 0x03 << 0, 0x00, ac10x);
|
|
+ }
|
|
+ } else {
|
|
+ unsigned div;
|
|
+
|
|
+ /*TDM mode or normal mode*/
|
|
+ div = ac108_samp_res[samp_res].real_val * channels - 1;
|
|
+ ac108_multi_write(I2S_LRCK_CTRL2, (div & 0xFF), ac10x);
|
|
+ ac108_multi_update_bits(I2S_LRCK_CTRL1, 0x03 << 0, (div >> 8) << 0, ac10x);
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * 0x35:
|
|
+ * TX Encoding mode will add 4bits to mark channel number
|
|
+ * TODO: need a chat to explain this
|
|
+ */
|
|
+ ac108_multi_update_bits(I2S_FMT_CTRL2, 0x07 << SAMPLE_RESOLUTION | 0x07 << SLOT_WIDTH_SEL,
|
|
+ ac108_samp_res[samp_res].reg_val << SAMPLE_RESOLUTION |
|
|
+ ac108_samp_res[samp_res].reg_val << SLOT_WIDTH_SEL, ac10x);
|
|
+
|
|
+ /**
|
|
+ * 0x60:
|
|
+ * ADC Sample Rate synchronised with I2S1 clock zone
|
|
+ */
|
|
+ ac108_multi_update_bits(ADC_SPRC, 0x0f << ADC_FS_I2S1,
|
|
+ ac108_sample_rate[rate].reg_val << ADC_FS_I2S1, ac10x);
|
|
+ ac108_multi_write(HPF_EN, 0x0F, ac10x);
|
|
+
|
|
+ if (ac10x->i2c101 && _MASTER_MULTI_CODEC == _MASTER_AC101) {
|
|
+ ac108_config_pll(ac10x, ac108_sample_rate[rate].real_val,
|
|
+ ac108_samp_res[samp_res].real_val * channels);
|
|
+ } else {
|
|
+ ac108_config_pll(ac10x, ac108_sample_rate[rate].real_val, 0);
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * master mode only
|
|
+ */
|
|
+ bclkdiv = ac10x->mclk / (ac108_sample_rate[rate].real_val * channels *
|
|
+ ac108_samp_res[samp_res].real_val);
|
|
+ for (i = 0; i < ARRAY_SIZE(ac108_bclkdivs) - 1; i++) {
|
|
+ if (ac108_bclkdivs[i] >= bclkdiv) {
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ ac108_multi_update_bits(I2S_BCLK_CTRL, 0x0F << BCLKDIV, i << BCLKDIV, ac10x);
|
|
+
|
|
+ /*
|
|
+ * slots allocation for each chip
|
|
+ */
|
|
+ ac108_multi_chips_slots(ac10x, channels);
|
|
+
|
|
+ /*0x21: Module clock enable<I2S, ADC digital, MIC offset Calibration, ADC analog>*/
|
|
+ ac108_multi_write(MOD_CLK_EN, 1 << I2S | 1 << ADC_DIGITAL |
|
|
+ 1 << MIC_OFFSET_CALIBRATION | 1 << ADC_ANALOG, ac10x);
|
|
+ /*0x22: Module reset de-asserted<I2S, ADC digital, MIC offset Calibration, ADC analog>*/
|
|
+ ac108_multi_write(MOD_RST_CTRL, 1 << I2S | 1 << ADC_DIGITAL |
|
|
+ 1 << MIC_OFFSET_CALIBRATION | 1 << ADC_ANALOG, ac10x);
|
|
+
|
|
+ ac108_multi_write(I2S_TX1_CHMP_CTRL1, 0xE4, ac10x);
|
|
+ ac108_multi_write(I2S_TX1_CHMP_CTRL2, 0xE4, ac10x);
|
|
+ ac108_multi_write(I2S_TX1_CHMP_CTRL3, 0xE4, ac10x);
|
|
+ ac108_multi_write(I2S_TX1_CHMP_CTRL4, 0xE4, ac10x);
|
|
+
|
|
+ ac108_multi_write(I2S_TX2_CHMP_CTRL1, 0xE4, ac10x);
|
|
+ ac108_multi_write(I2S_TX2_CHMP_CTRL2, 0xE4, ac10x);
|
|
+ ac108_multi_write(I2S_TX2_CHMP_CTRL3, 0xE4, ac10x);
|
|
+ ac108_multi_write(I2S_TX2_CHMP_CTRL4, 0xE4, ac10x);
|
|
+
|
|
+ dev_dbg(dai->dev, "%s() stream=%s ---\n", __func__,
|
|
+ snd_pcm_stream_str(substream));
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int ac108_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir)
|
|
+{
|
|
+
|
|
+ struct ac10x_priv *ac10x = snd_soc_dai_get_drvdata(dai);
|
|
+
|
|
+ freq = 24000000;
|
|
+ clk_id = SYSCLK_SRC_PLL;
|
|
+
|
|
+ switch (clk_id) {
|
|
+ case SYSCLK_SRC_MCLK:
|
|
+ ac108_multi_update_bits(SYSCLK_CTRL, 0x1 << SYSCLK_SRC,
|
|
+ SYSCLK_SRC_MCLK << SYSCLK_SRC, ac10x);
|
|
+ break;
|
|
+ case SYSCLK_SRC_PLL:
|
|
+ ac108_multi_update_bits(SYSCLK_CTRL, 0x1 << SYSCLK_SRC,
|
|
+ SYSCLK_SRC_PLL << SYSCLK_SRC, ac10x);
|
|
+ break;
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ ac10x->sysclk = freq;
|
|
+ ac10x->clk_id = clk_id;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * The i2s format management related registers are Reg
|
|
+ * 30h~Reg36h
|
|
+ * 33h,35h will be set in ac108_hw_params, It's BCLK width and
|
|
+ * Sample Resolution.
|
|
+ * @author baozhu (17-6-20)
|
|
+ *
|
|
+ * @param dai
|
|
+ * @param fmt
|
|
+ *
|
|
+ * @return int
|
|
+ */
|
|
+static int ac108_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
|
+{
|
|
+ unsigned char tx_offset, lrck_polarity, brck_polarity;
|
|
+ struct ac10x_priv *ac10x = dev_get_drvdata(dai->dev);
|
|
+
|
|
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
|
+ case SND_SOC_DAIFMT_CBM_CFM: /* AC108 Master */
|
|
+ if (! ac10x->i2c101 || _MASTER_MULTI_CODEC == _MASTER_AC108) {
|
|
+ /**
|
|
+ * 0x30:chip is master mode ,BCLK & LRCK output
|
|
+ */
|
|
+ ac108_multi_update_bits(I2S_CTRL,
|
|
+ 0x03 << LRCK_IOEN | 0x03 << SDO1_EN |
|
|
+ 0x1 << TXEN | 0x1 << GEN,
|
|
+ 0x03 << LRCK_IOEN | 0x01 << SDO1_EN |
|
|
+ 0x1 << TXEN | 0x1 << GEN, ac10x);
|
|
+ /* multi_chips: only one chip set as Master, and the others also need to set as Slave */
|
|
+ ac10x_update_bits(I2S_CTRL, 0x3 << LRCK_IOEN, 0x01 << BCLK_IOEN,
|
|
+ ac10x->i2cmap[_MASTER_INDEX]);
|
|
+ } else {
|
|
+ /* TODO: Both cpu_dai and codec_dai(AC108) be set as slave in DTS */
|
|
+ dev_err(dai->dev, "used as slave when AC101 is master\n");
|
|
+ }
|
|
+ break;
|
|
+ case SND_SOC_DAIFMT_CBS_CFS: /* AC108 Slave */
|
|
+ /**
|
|
+ * 0x30:chip is slave mode, BCLK & LRCK input,enable SDO1_EN and
|
|
+ * SDO2_EN, Transmitter Block Enable, Globe Enable
|
|
+ */
|
|
+ ac108_multi_update_bits(I2S_CTRL,
|
|
+ 0x03 << LRCK_IOEN | 0x03 << SDO1_EN |
|
|
+ 0x1 << TXEN | 0x1 << GEN,
|
|
+ 0x00 << LRCK_IOEN | 0x03 << SDO1_EN |
|
|
+ 0x0 << TXEN | 0x0 << GEN, ac10x);
|
|
+ break;
|
|
+ default:
|
|
+ dev_err(dai->dev, "AC108 Master/Slave mode config error:%u\n\n",
|
|
+ (fmt & SND_SOC_DAIFMT_MASTER_MASK) >> 12);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ /*AC108 config I2S/LJ/RJ/PCM format*/
|
|
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
|
+ case SND_SOC_DAIFMT_I2S:
|
|
+ ac10x->i2s_mode = LEFT_JUSTIFIED_FORMAT;
|
|
+ tx_offset = 1;
|
|
+ break;
|
|
+ case SND_SOC_DAIFMT_RIGHT_J:
|
|
+ ac10x->i2s_mode = RIGHT_JUSTIFIED_FORMAT;
|
|
+ tx_offset = 0;
|
|
+ break;
|
|
+ case SND_SOC_DAIFMT_LEFT_J:
|
|
+ ac10x->i2s_mode = LEFT_JUSTIFIED_FORMAT;
|
|
+ tx_offset = 0;
|
|
+ break;
|
|
+ case SND_SOC_DAIFMT_DSP_A:
|
|
+ ac10x->i2s_mode = PCM_FORMAT;
|
|
+ tx_offset = 1;
|
|
+ break;
|
|
+ case SND_SOC_DAIFMT_DSP_B:
|
|
+ ac10x->i2s_mode = PCM_FORMAT;
|
|
+ tx_offset = 0;
|
|
+ break;
|
|
+ default:
|
|
+ dev_err(dai->dev, "AC108 I2S format config error:%u\n\n",
|
|
+ fmt & SND_SOC_DAIFMT_FORMAT_MASK);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ /*AC108 config BCLK&LRCK polarity*/
|
|
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
|
+ case SND_SOC_DAIFMT_NB_NF:
|
|
+ brck_polarity = BCLK_NORMAL_DRIVE_N_SAMPLE_P;
|
|
+ lrck_polarity = LRCK_LEFT_HIGH_RIGHT_LOW;
|
|
+ break;
|
|
+ case SND_SOC_DAIFMT_NB_IF:
|
|
+ brck_polarity = BCLK_NORMAL_DRIVE_N_SAMPLE_P;
|
|
+ lrck_polarity = LRCK_LEFT_LOW_RIGHT_HIGH;
|
|
+ break;
|
|
+ case SND_SOC_DAIFMT_IB_NF:
|
|
+ brck_polarity = BCLK_INVERT_DRIVE_P_SAMPLE_N;
|
|
+ lrck_polarity = LRCK_LEFT_HIGH_RIGHT_LOW;
|
|
+ break;
|
|
+ case SND_SOC_DAIFMT_IB_IF:
|
|
+ brck_polarity = BCLK_INVERT_DRIVE_P_SAMPLE_N;
|
|
+ lrck_polarity = LRCK_LEFT_LOW_RIGHT_HIGH;
|
|
+ break;
|
|
+ default:
|
|
+ dev_err(dai->dev, "AC108 config BCLK/LRCLK polarity error:%u\n\n",
|
|
+ (fmt & SND_SOC_DAIFMT_INV_MASK) >> 8);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ ac108_configure_power(ac10x);
|
|
+
|
|
+ /**
|
|
+ *0x31: 0: normal mode, negative edge drive and positive edge sample
|
|
+ 1: invert mode, positive edge drive and negative edge sample
|
|
+ */
|
|
+ ac108_multi_update_bits(I2S_BCLK_CTRL, 0x01 << BCLK_POLARITY,
|
|
+ brck_polarity << BCLK_POLARITY, ac10x);
|
|
+ /**
|
|
+ * 0x32: same as 0x31
|
|
+ */
|
|
+ ac108_multi_update_bits(I2S_LRCK_CTRL1, 0x01 << LRCK_POLARITY,
|
|
+ lrck_polarity << LRCK_POLARITY, ac10x);
|
|
+ /**
|
|
+ * 0x34:Encoding Mode Selection,Mode
|
|
+ * Selection,data is offset by 1 BCLKs to LRCK
|
|
+ * normal mode for the last half cycle of BCLK in the slot ?
|
|
+ * turn to hi-z state (TDM) when not transferring slot ?
|
|
+ */
|
|
+ ac108_multi_update_bits(I2S_FMT_CTRL1,
|
|
+ 0x01 << ENCD_SEL | 0x03 << MODE_SEL | 0x01 << TX2_OFFSET |
|
|
+ 0x01 << TX1_OFFSET | 0x01 << TX_SLOT_HIZ | 0x01 << TX_STATE,
|
|
+ ac10x->data_protocol << ENCD_SEL | ac10x->i2s_mode << MODE_SEL |
|
|
+ tx_offset << TX2_OFFSET | tx_offset << TX1_OFFSET |
|
|
+ 0x00 << TX_SLOT_HIZ | 0x01 << TX_STATE, ac10x);
|
|
+
|
|
+ /**
|
|
+ * 0x60:
|
|
+ * MSB / LSB First Select: This driver only support MSB First Select .
|
|
+ * OUT2_MUTE,OUT1_MUTE shoule be set in widget.
|
|
+ * LRCK = 1 BCLK width
|
|
+ * Linear PCM
|
|
+ *
|
|
+ * TODO:pcm mode, bit[0:1] and bit[2] is special
|
|
+ */
|
|
+ ac108_multi_update_bits(I2S_FMT_CTRL3,
|
|
+ 0x01 << TX_MLS | 0x03 << SEXT |
|
|
+ 0x01 << LRCK_WIDTH | 0x03 << TX_PDM,
|
|
+ 0x00 << TX_MLS | 0x03 << SEXT |
|
|
+ 0x00 << LRCK_WIDTH | 0x00 << TX_PDM, ac10x);
|
|
+
|
|
+ ac108_multi_write(HPF_EN, 0x00, ac10x);
|
|
+
|
|
+ if (ac10x->i2c101) {
|
|
+ return ac101_set_dai_fmt(dai, fmt);
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * due to miss channels order in cpu_dai, we meed defer the clock starting.
|
|
+ */
|
|
+static int ac108_set_clock(int y_start_n_stop)
|
|
+{
|
|
+ u8 reg;
|
|
+ int ret = 0;
|
|
+
|
|
+ dev_dbg(ac10x->codec->dev, "%s() L%d cmd:%d\n", __func__, __LINE__, y_start_n_stop);
|
|
+
|
|
+ /* spin_lock move to machine trigger */
|
|
+
|
|
+ if (y_start_n_stop && ac10x->sysclk_en == 0) {
|
|
+ /* enable lrck clock */
|
|
+ ac10x_read(I2S_CTRL, ®, ac10x->i2cmap[_MASTER_INDEX]);
|
|
+ if (reg & (0x01 << BCLK_IOEN)) {
|
|
+ ret = ret || ac10x_update_bits(I2S_CTRL, 0x03 << LRCK_IOEN,
|
|
+ 0x03 << LRCK_IOEN,
|
|
+ ac10x->i2cmap[_MASTER_INDEX]);
|
|
+ }
|
|
+
|
|
+ /*0x10: PLL Common voltage enable, PLL enable */
|
|
+ ret = ret || ac108_multi_update_bits(PLL_CTRL1,
|
|
+ 0x01 << PLL_EN | 0x01 << PLL_COM_EN,
|
|
+ 0x01 << PLL_EN | 0x01 << PLL_COM_EN, ac10x);
|
|
+ /* enable global clock */
|
|
+ ret = ret || ac108_multi_update_bits(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN,
|
|
+ 0x1 << TXEN | 0x1 << GEN, ac10x);
|
|
+
|
|
+ ac10x->sysclk_en = 1UL;
|
|
+ } else if (!y_start_n_stop && ac10x->sysclk_en != 0) {
|
|
+ /* disable global clock */
|
|
+ ret = ret || ac108_multi_update_bits(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN,
|
|
+ 0x0 << TXEN | 0x0 << GEN, ac10x);
|
|
+
|
|
+ /*0x10: PLL Common voltage disable, PLL disable */
|
|
+ ret = ret || ac108_multi_update_bits(PLL_CTRL1, 0x01 << PLL_EN | 0x01 << PLL_COM_EN,
|
|
+ 0x00 << PLL_EN | 0x00 << PLL_COM_EN, ac10x);
|
|
+
|
|
+ /* disable lrck clock if it's enabled */
|
|
+ ac10x_read(I2S_CTRL, ®, ac10x->i2cmap[_MASTER_INDEX]);
|
|
+ if (reg & (0x01 << LRCK_IOEN)) {
|
|
+ ret = ret || ac10x_update_bits(I2S_CTRL, 0x03 << LRCK_IOEN,
|
|
+ 0x01 << BCLK_IOEN,
|
|
+ ac10x->i2cmap[_MASTER_INDEX]);
|
|
+ }
|
|
+ if (!ret) {
|
|
+ ac10x->sysclk_en = 0UL;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int ac108_prepare(struct snd_pcm_substream *substream,
|
|
+ struct snd_soc_dai *dai)
|
|
+{
|
|
+ u8 r;
|
|
+
|
|
+ dev_dbg(dai->dev, "%s() stream=%s\n",
|
|
+ __func__,
|
|
+ snd_pcm_stream_str(substream));
|
|
+
|
|
+ if (ac10x->i2c101 && _MASTER_MULTI_CODEC == _MASTER_AC101) {
|
|
+ ac101_trigger(substream, SNDRV_PCM_TRIGGER_START, dai);
|
|
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
|
+ return 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ ac10x_read(I2S_CTRL, &r, ac10x->i2cmap[_MASTER_INDEX]);
|
|
+ if ((r & (0x01 << BCLK_IOEN)) && (r & (0x01 << LRCK_IOEN)) == 0) {
|
|
+ /* disable global clock */
|
|
+ ac108_multi_update_bits(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN,
|
|
+ 0x0 << TXEN | 0x0 << GEN, ac10x);
|
|
+ }
|
|
+
|
|
+ /* delayed clock starting, move to machine trigger() */
|
|
+ ac108_set_clock(1);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int ac108_audio_startup(struct snd_pcm_substream *substream,
|
|
+ struct snd_soc_dai *dai)
|
|
+{
|
|
+ struct snd_soc_codec *codec = dai->codec;
|
|
+ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
|
+
|
|
+ if (ac10x->i2c101) {
|
|
+ return ac101_audio_startup(substream, dai);
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void ac108_aif_shutdown(struct snd_pcm_substream *substream,
|
|
+ struct snd_soc_dai *dai)
|
|
+{
|
|
+ struct snd_soc_codec *codec = dai->codec;
|
|
+ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
|
+
|
|
+ ac108_set_clock(0);
|
|
+
|
|
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
|
|
+ /*0x21: Module clock disable <I2S, ADC digital, MIC offset Calibration, ADC analog>*/
|
|
+ ac108_multi_write(MOD_CLK_EN, 0x0, ac10x);
|
|
+ /*0x22: Module reset asserted <I2S, ADC digital, MIC offset Calibration, ADC analog>*/
|
|
+ ac108_multi_write(MOD_RST_CTRL, 0x0, ac10x);
|
|
+ }
|
|
+
|
|
+ if (ac10x->i2c101) {
|
|
+ ac101_aif_shutdown(substream, dai);
|
|
+ }
|
|
+}
|
|
+
|
|
+int ac108_aif_mute(struct snd_soc_dai *dai, int mute, int stream)
|
|
+{
|
|
+ struct snd_soc_codec *codec = dai->codec;
|
|
+ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
|
+
|
|
+ if (ac10x->i2c101) {
|
|
+ return ac101_aif_mute(dai, mute);
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct snd_soc_dai_ops ac108_dai_ops = {
|
|
+ .startup = ac108_audio_startup,
|
|
+ .shutdown = ac108_aif_shutdown,
|
|
+
|
|
+ /*DAI clocking configuration*/
|
|
+ .set_sysclk = ac108_set_sysclk,
|
|
+
|
|
+ /*ALSA PCM audio operations*/
|
|
+ .hw_params = ac108_hw_params,
|
|
+ .prepare = ac108_prepare,
|
|
+ .mute_stream = ac108_aif_mute,
|
|
+
|
|
+ /*DAI format configuration*/
|
|
+ .set_fmt = ac108_set_fmt,
|
|
+};
|
|
+
|
|
+static struct snd_soc_dai_driver ac108_dai0 = {
|
|
+ .name = "ac10x-codec0",
|
|
+ #if _USE_CAPTURE
|
|
+ .playback = {
|
|
+ .stream_name = "Playback",
|
|
+ .channels_min = 1,
|
|
+ .channels_max = AC108_CHANNELS_MAX,
|
|
+ .rates = AC108_RATES,
|
|
+ .formats = AC108_FORMATS,
|
|
+ },
|
|
+ #endif
|
|
+ .capture = {
|
|
+ .stream_name = "Capture",
|
|
+ .channels_min = 1,
|
|
+ .channels_max = AC108_CHANNELS_MAX,
|
|
+ .rates = AC108_RATES,
|
|
+ .formats = AC108_FORMATS,
|
|
+ },
|
|
+ .ops = &ac108_dai_ops,
|
|
+};
|
|
+
|
|
+static struct snd_soc_dai_driver ac108_dai1 = {
|
|
+ .name = "ac10x-codec1",
|
|
+ #if _USE_CAPTURE
|
|
+ .playback = {
|
|
+ .stream_name = "Playback",
|
|
+ .channels_min = 1,
|
|
+ .channels_max = AC108_CHANNELS_MAX,
|
|
+ .rates = AC108_RATES,
|
|
+ .formats = AC108_FORMATS,
|
|
+ },
|
|
+ #endif
|
|
+ .capture = {
|
|
+ .stream_name = "Capture",
|
|
+ .channels_min = 1,
|
|
+ .channels_max = AC108_CHANNELS_MAX,
|
|
+ .rates = AC108_RATES,
|
|
+ .formats = AC108_FORMATS,
|
|
+ },
|
|
+ .ops = &ac108_dai_ops,
|
|
+};
|
|
+
|
|
+static struct snd_soc_dai_driver *ac108_dai[] = {
|
|
+ &ac108_dai0,
|
|
+ &ac108_dai1,
|
|
+};
|
|
+
|
|
+static int ac108_add_widgets(struct snd_soc_codec *codec)
|
|
+{
|
|
+ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
|
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
|
|
+ const struct snd_kcontrol_new* snd_kcntl = ac108_snd_controls;
|
|
+ int ctrl_cnt = ARRAY_SIZE(ac108_snd_controls);
|
|
+
|
|
+ /* only register controls correspond to exist chips */
|
|
+ if (ac10x->tdm_chips_cnt >= 2) {
|
|
+ snd_kcntl = ac108tdm_snd_controls;
|
|
+ ctrl_cnt = ARRAY_SIZE(ac108tdm_snd_controls);
|
|
+ }
|
|
+ snd_soc_add_codec_controls(codec, snd_kcntl, ctrl_cnt);
|
|
+
|
|
+ snd_soc_dapm_new_controls(dapm, ac108_dapm_widgets,ARRAY_SIZE(ac108_dapm_widgets));
|
|
+ snd_soc_dapm_add_routes(dapm, ac108_dapm_routes, ARRAY_SIZE(ac108_dapm_routes));
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int ac108_codec_probe(struct snd_soc_codec *codec)
|
|
+{
|
|
+ spin_lock_init(&ac10x->lock);
|
|
+
|
|
+ ac10x->codec = codec;
|
|
+ dev_set_drvdata(codec->dev, ac10x);
|
|
+ ac108_add_widgets(codec);
|
|
+
|
|
+ if (ac10x->i2c101) {
|
|
+ ac101_codec_probe(codec);
|
|
+ }
|
|
+
|
|
+ /* change default volume */
|
|
+ ac108_multi_update_bits(ADC1_DVOL_CTRL, 0xff, 0xc8, ac10x);
|
|
+ ac108_multi_update_bits(ADC2_DVOL_CTRL, 0xff, 0xc8, ac10x);
|
|
+ ac108_multi_update_bits(ADC3_DVOL_CTRL, 0xff, 0xc8, ac10x);
|
|
+ ac108_multi_update_bits(ADC4_DVOL_CTRL, 0xff, 0xc8, ac10x);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int ac108_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level)
|
|
+{
|
|
+ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
|
+
|
|
+ dev_dbg(codec->dev, "AC108 level:%d\n", level);
|
|
+
|
|
+ switch (level) {
|
|
+ case SND_SOC_BIAS_ON:
|
|
+ ac108_multi_update_bits(ANA_ADC1_CTRL1, 0x01 << ADC1_MICBIAS_EN,
|
|
+ 0x01 << ADC1_MICBIAS_EN, ac10x);
|
|
+ ac108_multi_update_bits(ANA_ADC2_CTRL1, 0x01 << ADC2_MICBIAS_EN,
|
|
+ 0x01 << ADC2_MICBIAS_EN, ac10x);
|
|
+ ac108_multi_update_bits(ANA_ADC3_CTRL1, 0x01 << ADC3_MICBIAS_EN,
|
|
+ 0x01 << ADC3_MICBIAS_EN, ac10x);
|
|
+ ac108_multi_update_bits(ANA_ADC4_CTRL1, 0x01 << ADC4_MICBIAS_EN,
|
|
+ 0x01 << ADC4_MICBIAS_EN, ac10x);
|
|
+ break;
|
|
+
|
|
+ case SND_SOC_BIAS_PREPARE:
|
|
+ /* Put the MICBIASes into regulating mode */
|
|
+ break;
|
|
+
|
|
+ case SND_SOC_BIAS_STANDBY:
|
|
+ break;
|
|
+
|
|
+ case SND_SOC_BIAS_OFF:
|
|
+ ac108_multi_update_bits(ANA_ADC1_CTRL1, 0x01 << ADC1_MICBIAS_EN,
|
|
+ 0x00 << ADC1_MICBIAS_EN, ac10x);
|
|
+ ac108_multi_update_bits(ANA_ADC2_CTRL1, 0x01 << ADC2_MICBIAS_EN,
|
|
+ 0x00 << ADC2_MICBIAS_EN, ac10x);
|
|
+ ac108_multi_update_bits(ANA_ADC3_CTRL1, 0x01 << ADC3_MICBIAS_EN,
|
|
+ 0x00 << ADC3_MICBIAS_EN, ac10x);
|
|
+ ac108_multi_update_bits(ANA_ADC4_CTRL1, 0x01 << ADC4_MICBIAS_EN,
|
|
+ 0x00 << ADC4_MICBIAS_EN, ac10x);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (ac10x->i2c101) {
|
|
+ ac101_set_bias_level(codec, level);
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int ac108_codec_remove(struct snd_soc_codec *codec) {
|
|
+ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
|
+
|
|
+ if (! ac10x->i2c101) {
|
|
+ return 0;
|
|
+ }
|
|
+ return ac101_codec_remove(codec);
|
|
+}
|
|
+#if __NO_SND_SOC_CODEC_DRV
|
|
+void ac108_codec_remove_void(struct snd_soc_codec *codec)
|
|
+{
|
|
+ ac108_codec_remove(codec);
|
|
+}
|
|
+#define ac108_codec_remove ac108_codec_remove_void
|
|
+#endif
|
|
+
|
|
+int ac108_codec_suspend(struct snd_soc_codec *codec)
|
|
+{
|
|
+ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < ac10x->codec_cnt; i++) {
|
|
+ regcache_cache_only(ac10x->i2cmap[i], true);
|
|
+ }
|
|
+
|
|
+ if (! ac10x->i2c101) {
|
|
+ return 0;
|
|
+ }
|
|
+ return ac101_codec_suspend(codec);
|
|
+}
|
|
+
|
|
+int ac108_codec_resume(struct snd_soc_codec *codec)
|
|
+{
|
|
+ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
|
+ int i, ret;
|
|
+
|
|
+ /* Sync reg_cache with the hardware */
|
|
+ for (i = 0; i < ac10x->codec_cnt; i++) {
|
|
+ regcache_cache_only(ac10x->i2cmap[i], false);
|
|
+ ret = regcache_sync(ac10x->i2cmap[i]);
|
|
+ if (ret != 0) {
|
|
+ dev_err(codec->dev, "Failed to sync i2cmap%d register cache: %d\n",
|
|
+ i, ret);
|
|
+ regcache_cache_only(ac10x->i2cmap[i], true);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (! ac10x->i2c101) {
|
|
+ return 0;
|
|
+ }
|
|
+ return ac101_codec_resume(codec);
|
|
+}
|
|
+
|
|
+static struct snd_soc_codec_driver ac10x_soc_codec_driver = {
|
|
+ .probe = ac108_codec_probe,
|
|
+ .remove = ac108_codec_remove,
|
|
+ .suspend = ac108_codec_suspend,
|
|
+ .resume = ac108_codec_resume,
|
|
+ .set_bias_level = ac108_set_bias_level,
|
|
+ .read = ac108_codec_read,
|
|
+ .write = ac108_codec_write,
|
|
+};
|
|
+
|
|
+static ssize_t ac108_store(struct device *dev, struct device_attribute *attr,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+ int val = 0, flag = 0;
|
|
+ u8 i = 0, reg, num, value_w, value_r[4];
|
|
+
|
|
+ val = simple_strtol(buf, NULL, 16);
|
|
+ flag = (val >> 16) & 0xF;
|
|
+
|
|
+ if (flag) {
|
|
+ reg = (val >> 8) & 0xFF;
|
|
+ value_w = val & 0xFF;
|
|
+ ac108_multi_write(reg, value_w, ac10x);
|
|
+ dev_info(dev, "Write 0x%02x to REG:0x%02x\n", value_w, reg);
|
|
+ } else {
|
|
+ int k;
|
|
+
|
|
+ reg = (val >> 8) & 0xFF;
|
|
+ num = val & 0xff;
|
|
+ dev_info(dev, "\nRead: start REG:0x%02x,count:0x%02x\n", reg, num);
|
|
+
|
|
+ for (k = 0; k < ac10x->codec_cnt; k++)
|
|
+ regcache_cache_bypass(ac10x->i2cmap[k], true);
|
|
+
|
|
+ do {
|
|
+ memset(value_r, 0, sizeof value_r);
|
|
+
|
|
+ for (k = 0; k < ac10x->codec_cnt; k++)
|
|
+ ac10x_read(reg, &value_r[k], ac10x->i2cmap[k]);
|
|
+
|
|
+ if (ac10x->codec_cnt >= 2)
|
|
+ dev_info(dev, "REG[0x%02x]: 0x%02x 0x%02x", reg,
|
|
+ value_r[0], value_r[1]);
|
|
+ else
|
|
+ dev_info(dev, "REG[0x%02x]: 0x%02x", reg, value_r[0]);
|
|
+ reg++;
|
|
+
|
|
+ if ((++i == num) || (i % 4 == 0))
|
|
+ dev_info(dev, "\n");
|
|
+ } while (i < num);
|
|
+
|
|
+ for (k = 0; k < ac10x->codec_cnt; k++)
|
|
+ regcache_cache_bypass(ac10x->i2cmap[k], false);
|
|
+ }
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static ssize_t ac108_show(struct device *dev, struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ dev_info(dev, "echo flag|reg|val > ac108\n");
|
|
+ dev_info(dev, "eg read star addres=0x06,count 0x10:echo 0610 >ac108\n");
|
|
+ dev_info(dev, "eg write value:0xfe to address:0x06 :echo 106fe > ac108\n");
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static DEVICE_ATTR(ac108, 0644, ac108_show, ac108_store);
|
|
+static struct attribute *ac108_debug_attrs[] = {
|
|
+ &dev_attr_ac108.attr,
|
|
+ NULL,
|
|
+};
|
|
+static struct attribute_group ac108_debug_attr_group = {
|
|
+ .name = "ac108_debug",
|
|
+ .attrs = ac108_debug_attrs,
|
|
+};
|
|
+
|
|
+static const struct i2c_device_id ac108_i2c_id[] = {
|
|
+ { "ac108_0", 0 },
|
|
+ { "ac108_1", 1 },
|
|
+ { "ac108_2", 2 },
|
|
+ { "ac108_3", 3 },
|
|
+ { "ac101", AC101_I2C_ID },
|
|
+ { }
|
|
+};
|
|
+MODULE_DEVICE_TABLE(i2c, ac108_i2c_id);
|
|
+
|
|
+static const struct regmap_config ac108_regmap = {
|
|
+ .reg_bits = 8,
|
|
+ .val_bits = 8,
|
|
+ .reg_stride = 1,
|
|
+ .max_register = 0xDF,
|
|
+ .cache_type = REGCACHE_FLAT,
|
|
+};
|
|
+static int ac108_i2c_probe(struct i2c_client *i2c)
|
|
+{
|
|
+ struct device_node *np = i2c->dev.of_node;
|
|
+ const struct i2c_device_id *i2c_id;
|
|
+ unsigned int val = 0;
|
|
+ int ret = 0, index;
|
|
+
|
|
+ if (ac10x == NULL) {
|
|
+ ac10x = kzalloc(sizeof(struct ac10x_priv), GFP_KERNEL);
|
|
+ if (ac10x == NULL) {
|
|
+ dev_err(&i2c->dev, "Unable to allocate ac10x private data\n");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ i2c_id = i2c_match_id(ac108_i2c_id, i2c);
|
|
+ index = (int)i2c_id->driver_data;
|
|
+ if (index == AC101_I2C_ID) {
|
|
+ ac10x->i2c101 = i2c;
|
|
+ i2c_set_clientdata(i2c, ac10x);
|
|
+ ret = ac101_probe(i2c, i2c_id);
|
|
+ if (ret) {
|
|
+ ac10x->i2c101 = NULL;
|
|
+ return ret;
|
|
+ }
|
|
+ goto __ret;
|
|
+ }
|
|
+
|
|
+ ret = of_property_read_u32(np, "data-protocol", &val);
|
|
+ if (ret) {
|
|
+ dev_err(&i2c->dev, "Please set data-protocol.\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ ac10x->data_protocol = val;
|
|
+
|
|
+ if (of_property_read_u32(np, "tdm-chips-count", &val)) val = 1;
|
|
+ ac10x->tdm_chips_cnt = val;
|
|
+
|
|
+ dev_info(&i2c->dev, " ac10x i2c_id number: %d\n", index);
|
|
+ dev_info(&i2c->dev, " ac10x data protocol: %d\n", ac10x->data_protocol);
|
|
+
|
|
+ ac10x->i2c[index] = i2c;
|
|
+ ac10x->i2cmap[index] = devm_regmap_init_i2c(i2c, &ac108_regmap);
|
|
+ if (IS_ERR(ac10x->i2cmap[index])) {
|
|
+ ret = PTR_ERR(ac10x->i2cmap[index]);
|
|
+ dev_err(&i2c->dev, "Fail to initialize i2cmap%d I/O: %d\n", index, ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Writing this register with 0x12
|
|
+ * will resets all register to their default state.
|
|
+ */
|
|
+ regcache_cache_only(ac10x->i2cmap[index], false);
|
|
+
|
|
+ ret = regmap_write(ac10x->i2cmap[index], CHIP_RST, CHIP_RST_VAL);
|
|
+ msleep(1);
|
|
+
|
|
+ /* sync regcache for FLAT type */
|
|
+ ac10x_fill_regcache(&i2c->dev, ac10x->i2cmap[index]);
|
|
+
|
|
+ ac10x->codec_cnt++;
|
|
+ dev_info(&i2c->dev, " ac10x codec count : %d\n", ac10x->codec_cnt);
|
|
+
|
|
+ ret = sysfs_create_group(&i2c->dev.kobj, &ac108_debug_attr_group);
|
|
+ if (ret) {
|
|
+ dev_err(&i2c->dev, "failed to create attr group\n");
|
|
+ }
|
|
+
|
|
+__ret:
|
|
+ /* It's time to bind codec to i2c[_MASTER_INDEX] when all i2c are ready */
|
|
+ if ((ac10x->codec_cnt != 0 && ac10x->tdm_chips_cnt < 2)
|
|
+ || (ac10x->i2c[0] && ac10x->i2c[1] && ac10x->i2c101)) {
|
|
+ /* no playback stream */
|
|
+ if (! ac10x->i2c101) {
|
|
+ memset(&ac108_dai[_MASTER_INDEX]->playback, '\0',
|
|
+ sizeof ac108_dai[_MASTER_INDEX]->playback);
|
|
+ }
|
|
+ ret = snd_soc_register_codec(&ac10x->i2c[_MASTER_INDEX]->dev,
|
|
+ &ac10x_soc_codec_driver,
|
|
+ ac108_dai[_MASTER_INDEX], 1);
|
|
+ if (ret < 0) {
|
|
+ dev_err(&i2c->dev, "Failed to register ac10x codec: %d\n", ret);
|
|
+ }
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void ac108_i2c_remove(struct i2c_client *i2c)
|
|
+{
|
|
+ if (ac10x->codec != NULL) {
|
|
+ snd_soc_unregister_codec(&ac10x->i2c[_MASTER_INDEX]->dev);
|
|
+ ac10x->codec = NULL;
|
|
+ }
|
|
+ if (i2c == ac10x->i2c101) {
|
|
+ ac101_remove(ac10x->i2c101);
|
|
+ ac10x->i2c101 = NULL;
|
|
+ goto __ret;
|
|
+ }
|
|
+
|
|
+ if (i2c == ac10x->i2c[0]) {
|
|
+ ac10x->i2c[0] = NULL;
|
|
+ }
|
|
+ if (i2c == ac10x->i2c[1]) {
|
|
+ ac10x->i2c[1] = NULL;
|
|
+ }
|
|
+
|
|
+ sysfs_remove_group(&i2c->dev.kobj, &ac108_debug_attr_group);
|
|
+
|
|
+__ret:
|
|
+ if (!ac10x->i2c[0] && !ac10x->i2c[1] && !ac10x->i2c101) {
|
|
+ kfree(ac10x);
|
|
+ ac10x = NULL;
|
|
+ }
|
|
+}
|
|
+
|
|
+static const struct of_device_id ac108_of_match[] = {
|
|
+ { .compatible = "x-power,ac108_0", },
|
|
+ { .compatible = "x-power,ac108_1", },
|
|
+ { .compatible = "x-power,ac108_2", },
|
|
+ { .compatible = "x-power,ac108_3", },
|
|
+ { .compatible = "x-power,ac101", },
|
|
+ { }
|
|
+};
|
|
+MODULE_DEVICE_TABLE(of, ac108_of_match);
|
|
+
|
|
+static struct i2c_driver ac108_i2c_driver = {
|
|
+ .driver = {
|
|
+ .name = "ac10x-codec",
|
|
+ .of_match_table = ac108_of_match,
|
|
+ },
|
|
+ .probe = ac108_i2c_probe,
|
|
+ .remove = ac108_i2c_remove,
|
|
+ .id_table = ac108_i2c_id,
|
|
+};
|
|
+
|
|
+module_i2c_driver(ac108_i2c_driver);
|
|
+
|
|
+MODULE_DESCRIPTION("ASoC AC108 driver");
|
|
+MODULE_AUTHOR("Baozhu Zuo<zuobaozhu@gmail.com>");
|
|
+MODULE_LICENSE("GPL");
|
|
--- /dev/null
|
|
+++ b/sound/soc/codecs/ac108.h
|
|
@@ -0,0 +1,749 @@
|
|
+/*
|
|
+ * ac108.h -- ac108 ALSA Soc Audio driver
|
|
+ *
|
|
+ * Author: panjunwen
|
|
+ *
|
|
+ * 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 _AC108_H
|
|
+#define _AC108_H
|
|
+
|
|
+/*** AC108 Codec Register Define***/
|
|
+
|
|
+//Chip Reset
|
|
+#define CHIP_RST 0x00
|
|
+#define CHIP_RST_VAL 0x12
|
|
+
|
|
+//Power Control
|
|
+#define PWR_CTRL1 0x01
|
|
+#define PWR_CTRL2 0x02
|
|
+#define PWR_CTRL3 0x03
|
|
+#define PWR_CTRL4 0x04
|
|
+#define PWR_CTRL5 0x05
|
|
+#define PWR_CTRL6 0x06
|
|
+#define PWR_CTRL7 0x07
|
|
+#define PWR_CTRL8 0x08
|
|
+#define PWR_CTRL9 0x09
|
|
+
|
|
+//PLL Configure Control
|
|
+#define PLL_CTRL1 0x10
|
|
+#define PLL_CTRL2 0x11
|
|
+#define PLL_CTRL3 0x12
|
|
+#define PLL_CTRL4 0x13
|
|
+#define PLL_CTRL5 0x14
|
|
+#define PLL_CTRL6 0x16
|
|
+#define PLL_CTRL7 0x17
|
|
+#define PLL_LOCK_CTRL 0x18
|
|
+
|
|
+//System Clock Control
|
|
+#define SYSCLK_CTRL 0x20
|
|
+#define MOD_CLK_EN 0x21
|
|
+#define MOD_RST_CTRL 0x22
|
|
+#define DSM_CLK_CTRL 0x25
|
|
+
|
|
+//I2S Common Control
|
|
+#define I2S_CTRL 0x30
|
|
+#define I2S_BCLK_CTRL 0x31
|
|
+#define I2S_LRCK_CTRL1 0x32
|
|
+#define I2S_LRCK_CTRL2 0x33
|
|
+#define I2S_FMT_CTRL1 0x34
|
|
+#define I2S_FMT_CTRL2 0x35
|
|
+#define I2S_FMT_CTRL3 0x36
|
|
+
|
|
+//I2S TX1 Control
|
|
+#define I2S_TX1_CTRL1 0x38
|
|
+#define I2S_TX1_CTRL2 0x39
|
|
+#define I2S_TX1_CTRL3 0x3A
|
|
+#define I2S_TX1_CHMP_CTRL1 0x3C
|
|
+#define I2S_TX1_CHMP_CTRL2 0x3D
|
|
+#define I2S_TX1_CHMP_CTRL3 0x3E
|
|
+#define I2S_TX1_CHMP_CTRL4 0x3F
|
|
+
|
|
+//I2S TX2 Control
|
|
+#define I2S_TX2_CTRL1 0x40
|
|
+#define I2S_TX2_CTRL2 0x41
|
|
+#define I2S_TX2_CTRL3 0x42
|
|
+#define I2S_TX2_CHMP_CTRL1 0x44
|
|
+#define I2S_TX2_CHMP_CTRL2 0x45
|
|
+#define I2S_TX2_CHMP_CTRL3 0x46
|
|
+#define I2S_TX2_CHMP_CTRL4 0x47
|
|
+
|
|
+//I2S RX1 Control
|
|
+#define I2S_RX1_CTRL1 0x50
|
|
+#define I2S_RX1_CHMP_CTRL1 0x54
|
|
+#define I2S_RX1_CHMP_CTRL2 0x55
|
|
+#define I2S_RX1_CHMP_CTRL3 0x56
|
|
+#define I2S_RX1_CHMP_CTRL4 0x57
|
|
+
|
|
+//I2S Loopback Debug
|
|
+#define I2S_LPB_DEBUG 0x58
|
|
+
|
|
+//ADC Common Control
|
|
+#define ADC_SPRC 0x60
|
|
+#define ADC_DIG_EN 0x61
|
|
+#define DMIC_EN 0x62
|
|
+#define ADC_DSR 0x63
|
|
+#define ADC_FIR 0x64
|
|
+#define ADC_DDT_CTRL 0x65
|
|
+
|
|
+//HPF Control
|
|
+#define HPF_EN 0x66
|
|
+#define HPF_COEF_REGH1 0x67
|
|
+#define HPF_COEF_REGH2 0x68
|
|
+#define HPF_COEF_REGL1 0x69
|
|
+#define HPF_COEF_REGL2 0x6A
|
|
+#define HPF_GAIN_REGH1 0x6B
|
|
+#define HPF_GAIN_REGH2 0x6C
|
|
+#define HPF_GAIN_REGL1 0x6D
|
|
+#define HPF_GAIN_REGL2 0x6E
|
|
+
|
|
+//ADC Digital Channel Volume Control
|
|
+#define ADC1_DVOL_CTRL 0x70
|
|
+#define ADC2_DVOL_CTRL 0x71
|
|
+#define ADC3_DVOL_CTRL 0x72
|
|
+#define ADC4_DVOL_CTRL 0x73
|
|
+
|
|
+//ADC Digital Mixer Source and Gain Control
|
|
+#define ADC1_DMIX_SRC 0x76
|
|
+#define ADC2_DMIX_SRC 0x77
|
|
+#define ADC3_DMIX_SRC 0x78
|
|
+#define ADC4_DMIX_SRC 0x79
|
|
+
|
|
+//ADC Digital Debug Control
|
|
+#define ADC_DIG_DEBUG 0x7F
|
|
+
|
|
+//I2S Pad Drive Control
|
|
+#define I2S_DAT_PADDRV_CTRL 0x80
|
|
+#define I2S_CLK_PADDRV_CTRL 0x81
|
|
+
|
|
+//Analog PGA Control
|
|
+#define ANA_PGA1_CTRL 0x90
|
|
+#define ANA_PGA2_CTRL 0x91
|
|
+#define ANA_PGA3_CTRL 0x92
|
|
+#define ANA_PGA4_CTRL 0x93
|
|
+
|
|
+//MIC Offset Control
|
|
+#define MIC_OFFSET_CTRL1 0x96
|
|
+#define MIC_OFFSET_CTRL2 0x97
|
|
+#define MIC1_OFFSET_STATU1 0x98
|
|
+#define MIC1_OFFSET_STATU2 0x99
|
|
+#define MIC2_OFFSET_STATU1 0x9A
|
|
+#define MIC2_OFFSET_STATU2 0x9B
|
|
+#define MIC3_OFFSET_STATU1 0x9C
|
|
+#define MIC3_OFFSET_STATU2 0x9D
|
|
+#define MIC4_OFFSET_STATU1 0x9E
|
|
+#define MIC4_OFFSET_STATU2 0x9F
|
|
+
|
|
+//ADC1 Analog Control
|
|
+#define ANA_ADC1_CTRL1 0xA0
|
|
+#define ANA_ADC1_CTRL2 0xA1
|
|
+#define ANA_ADC1_CTRL3 0xA2
|
|
+#define ANA_ADC1_CTRL4 0xA3
|
|
+#define ANA_ADC1_CTRL5 0xA4
|
|
+#define ANA_ADC1_CTRL6 0xA5
|
|
+#define ANA_ADC1_CTRL7 0xA6
|
|
+
|
|
+//ADC2 Analog Control
|
|
+#define ANA_ADC2_CTRL1 0xA7
|
|
+#define ANA_ADC2_CTRL2 0xA8
|
|
+#define ANA_ADC2_CTRL3 0xA9
|
|
+#define ANA_ADC2_CTRL4 0xAA
|
|
+#define ANA_ADC2_CTRL5 0xAB
|
|
+#define ANA_ADC2_CTRL6 0xAC
|
|
+#define ANA_ADC2_CTRL7 0xAD
|
|
+
|
|
+//ADC3 Analog Control
|
|
+#define ANA_ADC3_CTRL1 0xAE
|
|
+#define ANA_ADC3_CTRL2 0xAF
|
|
+#define ANA_ADC3_CTRL3 0xB0
|
|
+#define ANA_ADC3_CTRL4 0xB1
|
|
+#define ANA_ADC3_CTRL5 0xB2
|
|
+#define ANA_ADC3_CTRL6 0xB3
|
|
+#define ANA_ADC3_CTRL7 0xB4
|
|
+
|
|
+//ADC4 Analog Control
|
|
+#define ANA_ADC4_CTRL1 0xB5
|
|
+#define ANA_ADC4_CTRL2 0xB6
|
|
+#define ANA_ADC4_CTRL3 0xB7
|
|
+#define ANA_ADC4_CTRL4 0xB8
|
|
+#define ANA_ADC4_CTRL5 0xB9
|
|
+#define ANA_ADC4_CTRL6 0xBA
|
|
+#define ANA_ADC4_CTRL7 0xBB
|
|
+
|
|
+//GPIO Configure
|
|
+#define GPIO_CFG1 0xC0
|
|
+#define GPIO_CFG2 0xC1
|
|
+#define GPIO_DAT 0xC2
|
|
+#define GPIO_DRV 0xC3
|
|
+#define GPIO_PULL 0xC4
|
|
+#define GPIO_INT_CFG 0xC5
|
|
+#define GPIO_INT_EN 0xC6
|
|
+#define GPIO_INT_STATUS 0xC7
|
|
+
|
|
+//Misc
|
|
+#define BGTC_DAT 0xD1
|
|
+#define BGVC_DAT 0xD2
|
|
+#define PRNG_CLK_CTRL 0xDF
|
|
+
|
|
+/*** AC108 Codec Register Bit Define***/
|
|
+
|
|
+/*PWR_CTRL1*/
|
|
+#define CP12_CTRL 4
|
|
+#define CP12_SENSE_SELECT 3
|
|
+
|
|
+/*PWR_CTRL2*/
|
|
+#define CP12_SENSE_FILT 6
|
|
+#define CP12_COMP_FF_EN 3
|
|
+#define CP12_FORCE_ENABLE 2
|
|
+#define CP12_FORCE_RSTB 1
|
|
+
|
|
+/*PWR_CTRL3*/
|
|
+#define LDO33DIG_CTRL 0
|
|
+
|
|
+/*PWR_CTRL6*/
|
|
+#define LDO33ANA_2XHDRM 2
|
|
+#define LDO33ANA_ENABLE 0
|
|
+
|
|
+/*PWR_CTRL7*/
|
|
+#define VREF_SEL 3
|
|
+#define VREF_FASTSTART_ENABLE 1
|
|
+#define VREF_ENABLE 0
|
|
+
|
|
+/*PWR_CTRL9*/
|
|
+#define VREFP_FASTSTART_ENABLE 7
|
|
+#define VREFP_RESCTRL 5
|
|
+#define VREFP_LPMODE 4
|
|
+#define IGEN_TRIM 1
|
|
+#define VREFP_ENABLE 0
|
|
+
|
|
+/*PLL_CTRL1*/
|
|
+#define PLL_IBIAS 4
|
|
+#define PLL_NDET 3
|
|
+#define PLL_LOCKED_STATUS 2
|
|
+#define PLL_COM_EN 1
|
|
+#define PLL_EN 0
|
|
+
|
|
+/*PLL_CTRL2*/
|
|
+#define PLL_PREDIV2 5
|
|
+#define PLL_PREDIV1 0
|
|
+
|
|
+/*PLL_CTRL3*/
|
|
+#define PLL_LOOPDIV_MSB 0
|
|
+
|
|
+/*PLL_CTRL4*/
|
|
+#define PLL_LOOPDIV_LSB 0
|
|
+
|
|
+/*PLL_CTRL5*/
|
|
+#define PLL_POSTDIV2 5
|
|
+#define PLL_POSTDIV1 0
|
|
+
|
|
+/*PLL_CTRL6*/
|
|
+#define PLL_LDO 6
|
|
+#define PLL_CP 0
|
|
+
|
|
+/*PLL_CTRL7*/
|
|
+#define PLL_CAP 6
|
|
+#define PLL_RES 4
|
|
+#define PLL_TEST_EN 0
|
|
+
|
|
+/*PLL_LOCK_CTRL*/
|
|
+#define LOCK_LEVEL1 2
|
|
+#define LOCK_LEVEL2 1
|
|
+#define PLL_LOCK_EN 0
|
|
+
|
|
+/*SYSCLK_CTRL*/
|
|
+#define PLLCLK_EN 7
|
|
+#define PLLCLK_SRC 4
|
|
+#define SYSCLK_SRC 3
|
|
+#define SYSCLK_EN 0
|
|
+
|
|
+/*MOD_CLK_EN & MOD_RST_CTRL*/
|
|
+#define I2S 7
|
|
+#define ADC_DIGITAL 4
|
|
+#define MIC_OFFSET_CALIBRATION 1
|
|
+#define ADC_ANALOG 0
|
|
+
|
|
+/*DSM_CLK_CTRL*/
|
|
+#define MIC_OFFSET_DIV 4
|
|
+#define DSM_CLK_SEL 0
|
|
+
|
|
+/*I2S_CTRL*/
|
|
+#define BCLK_IOEN 7
|
|
+#define LRCK_IOEN 6
|
|
+#define SDO2_EN 5
|
|
+#define SDO1_EN 4
|
|
+#define TXEN 2
|
|
+#define RXEN 1
|
|
+#define GEN 0
|
|
+
|
|
+/*I2S_BCLK_CTRL*/
|
|
+#define EDGE_TRANSFER 5
|
|
+#define BCLK_POLARITY 4
|
|
+#define BCLKDIV 0
|
|
+
|
|
+/*I2S_LRCK_CTRL1*/
|
|
+#define LRCK_POLARITY 4
|
|
+#define LRCK_PERIODH 0
|
|
+
|
|
+/*I2S_LRCK_CTRL2*/
|
|
+#define LRCK_PERIODL 0
|
|
+
|
|
+/*I2S_FMT_CTRL1*/
|
|
+#define ENCD_SEL 6
|
|
+#define MODE_SEL 4
|
|
+#define TX2_OFFSET 3
|
|
+#define TX1_OFFSET 2
|
|
+#define TX_SLOT_HIZ 1
|
|
+#define TX_STATE 0
|
|
+
|
|
+/*I2S_FMT_CTRL2*/
|
|
+#define SLOT_WIDTH_SEL 4
|
|
+#define SAMPLE_RESOLUTION 0
|
|
+
|
|
+/*I2S_FMT_CTRL3*/
|
|
+#define TX_MLS 7
|
|
+#define SEXT 5
|
|
+#define OUT2_MUTE 4
|
|
+#define OUT1_MUTE 3
|
|
+#define LRCK_WIDTH 2
|
|
+#define TX_PDM 0
|
|
+
|
|
+/*I2S_TX1_CTRL1*/
|
|
+#define TX1_CHSEL 0
|
|
+
|
|
+/*I2S_TX1_CTRL2*/
|
|
+#define TX1_CH8_EN 7
|
|
+#define TX1_CH7_EN 6
|
|
+#define TX1_CH6_EN 5
|
|
+#define TX1_CH5_EN 4
|
|
+#define TX1_CH4_EN 3
|
|
+#define TX1_CH3_EN 2
|
|
+#define TX1_CH2_EN 1
|
|
+#define TX1_CH1_EN 0
|
|
+
|
|
+/*I2S_TX1_CTRL3*/
|
|
+#define TX1_CH16_EN 7
|
|
+#define TX1_CH15_EN 6
|
|
+#define TX1_CH14_EN 5
|
|
+#define TX1_CH13_EN 4
|
|
+#define TX1_CH12_EN 3
|
|
+#define TX1_CH11_EN 2
|
|
+#define TX1_CH10_EN 1
|
|
+#define TX1_CH9_EN 0
|
|
+
|
|
+/*I2S_TX1_CHMP_CTRL1*/
|
|
+#define TX1_CH4_MAP 6
|
|
+#define TX1_CH3_MAP 4
|
|
+#define TX1_CH2_MAP 2
|
|
+#define TX1_CH1_MAP 0
|
|
+
|
|
+/*I2S_TX1_CHMP_CTRL2*/
|
|
+#define TX1_CH8_MAP 6
|
|
+#define TX1_CH7_MAP 4
|
|
+#define TX1_CH6_MAP 2
|
|
+#define TX1_CH5_MAP 0
|
|
+
|
|
+/*I2S_TX1_CHMP_CTRL3*/
|
|
+#define TX1_CH12_MAP 6
|
|
+#define TX1_CH11_MAP 4
|
|
+#define TX1_CH10_MAP 2
|
|
+#define TX1_CH9_MAP 0
|
|
+
|
|
+/*I2S_TX1_CHMP_CTRL4*/
|
|
+#define TX1_CH16_MAP 6
|
|
+#define TX1_CH15_MAP 4
|
|
+#define TX1_CH14_MAP 2
|
|
+#define TX1_CH13_MAP 0
|
|
+
|
|
+/*I2S_TX2_CTRL1*/
|
|
+#define TX2_CHSEL 0
|
|
+
|
|
+/*I2S_TX2_CHMP_CTRL1*/
|
|
+#define TX2_CH4_MAP 6
|
|
+#define TX2_CH3_MAP 4
|
|
+#define TX2_CH2_MAP 2
|
|
+#define TX2_CH1_MAP 0
|
|
+
|
|
+/*I2S_TX2_CHMP_CTRL2*/
|
|
+#define TX2_CH8_MAP 6
|
|
+#define TX2_CH7_MAP 4
|
|
+#define TX2_CH6_MAP 2
|
|
+#define TX2_CH5_MAP 0
|
|
+
|
|
+/*I2S_TX2_CHMP_CTRL3*/
|
|
+#define TX2_CH12_MAP 6
|
|
+#define TX2_CH11_MAP 4
|
|
+#define TX2_CH10_MAP 2
|
|
+#define TX2_CH9_MAP 0
|
|
+
|
|
+/*I2S_TX2_CHMP_CTRL4*/
|
|
+#define TX2_CH16_MAP 6
|
|
+#define TX2_CH15_MAP 4
|
|
+#define TX2_CH14_MAP 2
|
|
+#define TX2_CH13_MAP 0
|
|
+
|
|
+/*I2S_RX1_CTRL1*/
|
|
+#define RX1_CHSEL 0
|
|
+
|
|
+/*I2S_RX1_CHMP_CTRL1*/
|
|
+#define RX1_CH4_MAP 6
|
|
+#define RX1_CH3_MAP 4
|
|
+#define RX1_CH2_MAP 2
|
|
+#define RX1_CH1_MAP 0
|
|
+
|
|
+/*I2S_RX1_CHMP_CTRL2*/
|
|
+#define RX1_CH8_MAP 6
|
|
+#define RX1_CH7_MAP 4
|
|
+#define RX1_CH6_MAP 2
|
|
+#define RX1_CH5_MAP 0
|
|
+
|
|
+/*I2S_RX1_CHMP_CTRL3*/
|
|
+#define RX1_CH12_MAP 6
|
|
+#define RX1_CH11_MAP 4
|
|
+#define RX1_CH10_MAP 2
|
|
+#define RX1_CH9_MAP 0
|
|
+
|
|
+/*I2S_RX1_CHMP_CTRL4*/
|
|
+#define RX1_CH16_MAP 6
|
|
+#define RX1_CH15_MAP 4
|
|
+#define RX1_CH14_MAP 2
|
|
+#define RX1_CH13_MAP 0
|
|
+
|
|
+/*I2S_LPB_DEBUG*/
|
|
+#define I2S_LPB_DEBUG_EN 0
|
|
+
|
|
+/*ADC_SPRC*/
|
|
+#define ADC_FS_I2S1 0
|
|
+
|
|
+/*ADC_DIG_EN*/
|
|
+#define DG_EN 4
|
|
+#define ENAD4 3
|
|
+#define ENAD3 2
|
|
+#define ENAD2 1
|
|
+#define ENAD1 0
|
|
+
|
|
+/*DMIC_EN*/
|
|
+#define DMIC2_EN 1
|
|
+#define DMIC1_EN 0
|
|
+
|
|
+/*ADC_DSR*/
|
|
+#define DIG_ADC4_SRS 6
|
|
+#define DIG_ADC3_SRS 4
|
|
+#define DIG_ADC2_SRS 2
|
|
+#define DIG_ADC1_SRS 0
|
|
+
|
|
+/*ADC_DDT_CTRL*/
|
|
+#define ADOUT_DLY_EN 2
|
|
+#define ADOUT_DTS 0
|
|
+
|
|
+/*HPF_EN*/
|
|
+#define DIG_ADC4_HPF_EN 3
|
|
+#define DIG_ADC3_HPF_EN 2
|
|
+#define DIG_ADC2_HPF_EN 1
|
|
+#define DIG_ADC1_HPF_EN 0
|
|
+
|
|
+/*ADC1_DMIX_SRC*/
|
|
+#define ADC1_ADC4_DMXL_GC 7
|
|
+#define ADC1_ADC3_DMXL_GC 6
|
|
+#define ADC1_ADC2_DMXL_GC 5
|
|
+#define ADC1_ADC1_DMXL_GC 4
|
|
+#define ADC1_ADC4_DMXL_SRC 3
|
|
+#define ADC1_ADC3_DMXL_SRC 2
|
|
+#define ADC1_ADC2_DMXL_SRC 1
|
|
+#define ADC1_ADC1_DMXL_SRC 0
|
|
+
|
|
+/*ADC2_DMIX_SRC*/
|
|
+#define ADC2_ADC4_DMXL_GC 7
|
|
+#define ADC2_ADC3_DMXL_GC 6
|
|
+#define ADC2_ADC2_DMXL_GC 5
|
|
+#define ADC2_ADC1_DMXL_GC 4
|
|
+#define ADC2_ADC4_DMXL_SRC 3
|
|
+#define ADC2_ADC3_DMXL_SRC 2
|
|
+#define ADC2_ADC2_DMXL_SRC 1
|
|
+#define ADC2_ADC1_DMXL_SRC 0
|
|
+
|
|
+/*ADC3_DMIX_SRC*/
|
|
+#define ADC3_ADC4_DMXL_GC 7
|
|
+#define ADC3_ADC3_DMXL_GC 6
|
|
+#define ADC3_ADC2_DMXL_GC 5
|
|
+#define ADC3_ADC1_DMXL_GC 4
|
|
+#define ADC3_ADC4_DMXL_SRC 3
|
|
+#define ADC3_ADC3_DMXL_SRC 2
|
|
+#define ADC3_ADC2_DMXL_SRC 1
|
|
+#define ADC3_ADC1_DMXL_SRC 0
|
|
+
|
|
+/*ADC4_DMIX_SRC*/
|
|
+#define ADC4_ADC4_DMXL_GC 7
|
|
+#define ADC4_ADC3_DMXL_GC 6
|
|
+#define ADC4_ADC2_DMXL_GC 5
|
|
+#define ADC4_ADC1_DMXL_GC 4
|
|
+#define ADC4_ADC4_DMXL_SRC 3
|
|
+#define ADC4_ADC3_DMXL_SRC 2
|
|
+#define ADC4_ADC2_DMXL_SRC 1
|
|
+#define ADC4_ADC1_DMXL_SRC 0
|
|
+
|
|
+/*ADC_DIG_DEBUG*/
|
|
+#define ADC_PTN_SEL 0
|
|
+
|
|
+/*I2S_DAT_PADDRV_CTRL*/
|
|
+#define TX2_DAT_DRV 4
|
|
+#define TX1_DAT_DRV 0
|
|
+
|
|
+/*I2S_CLK_PADDRV_CTRL*/
|
|
+#define LRCK_DRV 4
|
|
+#define BCLK_DRV 0
|
|
+
|
|
+/*ANA_PGA1_CTRL*/
|
|
+#define ADC1_ANALOG_PGA 1
|
|
+#define ADC1_ANALOG_PGA_STEP 0
|
|
+
|
|
+/*ANA_PGA2_CTRL*/
|
|
+#define ADC2_ANALOG_PGA 1
|
|
+#define ADC2_ANALOG_PGA_STEP 0
|
|
+
|
|
+/*ANA_PGA3_CTRL*/
|
|
+#define ADC3_ANALOG_PGA 1
|
|
+#define ADC3_ANALOG_PGA_STEP 0
|
|
+
|
|
+/*ANA_PGA4_CTRL*/
|
|
+#define ADC4_ANALOG_PGA 1
|
|
+#define ADC4_ANALOG_PGA_STEP 0
|
|
+
|
|
+
|
|
+/*MIC_OFFSET_CTRL1*/
|
|
+#define MIC_OFFSET_CAL_EN4 3
|
|
+#define MIC_OFFSET_CAL_EN3 2
|
|
+#define MIC_OFFSET_CAL_EN2 1
|
|
+#define MIC_OFFSET_CAL_EN1 0
|
|
+
|
|
+/*MIC_OFFSET_CTRL2*/
|
|
+#define MIC_OFFSET_CAL_GAIN 3
|
|
+#define MIC_OFFSET_CAL_CHANNEL 1
|
|
+#define MIC_OFFSET_CAL_EN_ONCE 0
|
|
+
|
|
+/*MIC1_OFFSET_STATU1*/
|
|
+#define MIC1_OFFSET_CAL_DONE 7
|
|
+#define MIC1_OFFSET_CAL_RUN_STA 6
|
|
+#define MIC1_OFFSET_MSB 0
|
|
+
|
|
+/*MIC1_OFFSET_STATU2*/
|
|
+#define MIC1_OFFSET_LSB 0
|
|
+
|
|
+/*MIC2_OFFSET_STATU1*/
|
|
+#define MIC2_OFFSET_CAL_DONE 7
|
|
+#define MIC2_OFFSET_CAL_RUN_STA 6
|
|
+#define MIC2_OFFSET_MSB 0
|
|
+
|
|
+/*MIC2_OFFSET_STATU2*/
|
|
+#define MIC2_OFFSET_LSB 0
|
|
+
|
|
+/*MIC3_OFFSET_STATU1*/
|
|
+#define MIC3_OFFSET_CAL_DONE 7
|
|
+#define MIC3_OFFSET_CAL_RUN_STA 6
|
|
+#define MIC3_OFFSET_MSB 0
|
|
+
|
|
+/*MIC3_OFFSET_STATU2*/
|
|
+#define MIC3_OFFSET_LSB 0
|
|
+
|
|
+/*MIC4_OFFSET_STATU1*/
|
|
+#define MIC4_OFFSET_CAL_DONE 7
|
|
+#define MIC4_OFFSET_CAL_RUN_STA 6
|
|
+#define MIC4_OFFSET_MSB 0
|
|
+
|
|
+/*MIC4_OFFSET_STATU2*/
|
|
+#define MIC4_OFFSET_LSB 0
|
|
+
|
|
+/*ANA_ADC1_CTRL1*/
|
|
+#define ADC1_PGA_BYPASS 7
|
|
+#define ADC1_PGA_BYP_RCM 6
|
|
+#define ADC1_PGA_CTRL_RCM 4
|
|
+#define ADC1_PGA_MUTE 3
|
|
+#define ADC1_DSM_ENABLE 2
|
|
+#define ADC1_PGA_ENABLE 1
|
|
+#define ADC1_MICBIAS_EN 0
|
|
+
|
|
+/*ANA_ADC1_CTRL3*/
|
|
+#define ADC1_ANA_CAL_EN 5
|
|
+#define ADC1_SEL_OUT_EDGE 3
|
|
+#define ADC1_DSM_DISABLE 2
|
|
+#define ADC1_VREFP_DISABLE 1
|
|
+#define ADC1_AAF_DISABLE 0
|
|
+
|
|
+/*ANA_ADC1_CTRL6*/
|
|
+#define PGA_CTRL_TC 6
|
|
+#define PGA_CTRL_RC 4
|
|
+#define PGA_CTRL_I_LIN 2
|
|
+#define PGA_CTRL_I_IN 0
|
|
+
|
|
+/*ANA_ADC1_CTRL7*/
|
|
+#define PGA_CTRL_HI_Z 7
|
|
+#define PGA_CTRL_SHORT_RF 6
|
|
+#define PGA_CTRL_VCM_VG 4
|
|
+#define PGA_CTRL_VCM_IN 0
|
|
+
|
|
+/*ANA_ADC2_CTRL1*/
|
|
+#define ADC2_PGA_BYPASS 7
|
|
+#define ADC2_PGA_BYP_RCM 6
|
|
+#define ADC2_PGA_CTRL_RCM 4
|
|
+#define ADC2_PGA_MUTE 3
|
|
+#define ADC2_DSM_ENABLE 2
|
|
+#define ADC2_PGA_ENABLE 1
|
|
+#define ADC2_MICBIAS_EN 0
|
|
+
|
|
+/*ANA_ADC2_CTRL3*/
|
|
+#define ADC2_ANA_CAL_EN 5
|
|
+#define ADC2_SEL_OUT_EDGE 3
|
|
+#define ADC2_DSM_DISABLE 2
|
|
+#define ADC2_VREFP_DISABLE 1
|
|
+#define ADC2_AAF_DISABLE 0
|
|
+
|
|
+/*ANA_ADC2_CTRL6*/
|
|
+#define PGA_CTRL_IBOOST 7
|
|
+#define PGA_CTRL_IQCTRL 6
|
|
+#define PGA_CTRL_OABIAS 4
|
|
+#define PGA_CTRL_CMLP_DIS 3
|
|
+#define PGA_CTRL_PDB_RIN 2
|
|
+#define PGA_CTRL_PEAKDET 0
|
|
+
|
|
+/*ANA_ADC2_CTRL7*/
|
|
+#define AAF_LPMODE_EN 7
|
|
+#define AAF_STG2_IB_SEL 4
|
|
+#define AAFDSM_IB_DIV2 3
|
|
+#define AAF_STG1_IB_SEL 0
|
|
+
|
|
+/*ANA_ADC3_CTRL1*/
|
|
+#define ADC3_PGA_BYPASS 7
|
|
+#define ADC3_PGA_BYP_RCM 6
|
|
+#define ADC3_PGA_CTRL_RCM 4
|
|
+#define ADC3_PGA_MUTE 3
|
|
+#define ADC3_DSM_ENABLE 2
|
|
+#define ADC3_PGA_ENABLE 1
|
|
+#define ADC3_MICBIAS_EN 0
|
|
+
|
|
+/*ANA_ADC3_CTRL3*/
|
|
+#define ADC3_ANA_CAL_EN 5
|
|
+#define ADC3_INVERT_CLK 4
|
|
+#define ADC3_SEL_OUT_EDGE 3
|
|
+#define ADC3_DSM_DISABLE 2
|
|
+#define ADC3_VREFP_DISABLE 1
|
|
+#define ADC3_AAF_DISABLE 0
|
|
+
|
|
+/*ANA_ADC3_CTRL7*/
|
|
+#define DSM_COMP_IB_SEL 6
|
|
+#define DSM_OTA_CTRL 4
|
|
+#define DSM_LPMODE 3
|
|
+#define DSM_OTA_IB_SEL 0
|
|
+
|
|
+/*ANA_ADC4_CTRL1*/
|
|
+#define ADC4_PGA_BYPASS 7
|
|
+#define ADC4_PGA_BYP_RCM 6
|
|
+#define ADC4_PGA_CTRL_RCM 4
|
|
+#define ADC4_PGA_MUTE 3
|
|
+#define ADC4_DSM_ENABLE 2
|
|
+#define ADC4_PGA_ENABLE 1
|
|
+#define ADC4_MICBIAS_EN 0
|
|
+
|
|
+/*ANA_ADC4_CTRL3*/
|
|
+#define ADC4_ANA_CAL_EN 5
|
|
+#define ADC4_SEL_OUT_EDGE 3
|
|
+#define ADC4_DSM_DISABLE 2
|
|
+#define ADC4_VREFP_DISABLE 1
|
|
+#define ADC4_AAF_DISABLE 0
|
|
+
|
|
+/*ANA_ADC4_CTRL6*/
|
|
+#define DSM_DEMOFF 5
|
|
+#define DSM_EN_DITHER 4
|
|
+#define DSM_VREFP_LPMODE 2
|
|
+#define DSM_VREFP_OUTCTRL 0
|
|
+
|
|
+/*ANA_ADC4_CTRL7*/
|
|
+#define CK8M_EN 5
|
|
+#define OSC_EN 4
|
|
+#define ADC4_CLK_GATING 3
|
|
+#define ADC3_CLK_GATING 2
|
|
+#define ADC2_CLK_GATING 1
|
|
+#define ADC1_CLK_GATING 0
|
|
+
|
|
+/*GPIO_CFG1*/
|
|
+#define GPIO2_SELECT 4
|
|
+#define GPIO1_SELECT 0
|
|
+
|
|
+/*GPIO_CFG2*/
|
|
+#define GPIO4_SELECT 4
|
|
+#define GPIO3_SELECT 0
|
|
+
|
|
+/*GPIO_DAT*///order???
|
|
+#define GPIO4_DAT 3
|
|
+#define GPIO3_DAT 2
|
|
+#define GPIO2_DAT 1
|
|
+#define GPIO1_DAT 0
|
|
+
|
|
+/*GPIO_DRV*/
|
|
+#define GPIO4_DRV 6
|
|
+#define GPIO3_DRV 4
|
|
+#define GPIO2_DRV 2
|
|
+#define GPIO1_DRV 0
|
|
+
|
|
+/*GPIO_PULL*/
|
|
+#define GPIO4_PULL 6
|
|
+#define GPIO3_PULL 4
|
|
+#define GPIO2_PULL 2
|
|
+#define GPIO1_PULL 0
|
|
+
|
|
+/*GPIO_INT_CFG*/
|
|
+#define GPIO4_EINT_CFG 6
|
|
+#define GPIO3_EINT_CFG 4
|
|
+#define GPIO2_EINT_CFG 2
|
|
+#define GPIO1_EINT_CFG 0
|
|
+
|
|
+/*GPIO_INT_EN*///order???
|
|
+#define GPIO4_EINT_EN 3
|
|
+#define GPIO3_EINT_EN 2
|
|
+#define GPIO2_EINT_EN 1
|
|
+#define GPIO1_EINT_EN 0
|
|
+
|
|
+/*GPIO_INT_STATUS*///order???
|
|
+#define GPIO4_EINT_STA 3
|
|
+#define GPIO3_EINT_STA 2
|
|
+#define GPIO2_EINT_STA 1
|
|
+#define GPIO1_EINT_STA 0
|
|
+
|
|
+/*PRNG_CLK_CTRL*/
|
|
+#define PRNG_CLK_EN 1
|
|
+#define PRNG_CLK_POS 0
|
|
+
|
|
+/*** Some Config Value ***/
|
|
+
|
|
+//[SYSCLK_CTRL]: PLLCLK_SRC
|
|
+#define PLLCLK_SRC_MCLK 0
|
|
+#define PLLCLK_SRC_BCLK 1
|
|
+#define PLLCLK_SRC_GPIO2 2
|
|
+#define PLLCLK_SRC_GPIO3 3
|
|
+
|
|
+//[SYSCLK_CTRL]: SYSCLK_SRC
|
|
+#define SYSCLK_SRC_MCLK 0
|
|
+#define SYSCLK_SRC_PLL 1
|
|
+
|
|
+//I2S BCLK POLARITY Control
|
|
+#define BCLK_NORMAL_DRIVE_N_SAMPLE_P 0
|
|
+#define BCLK_INVERT_DRIVE_P_SAMPLE_N 1
|
|
+
|
|
+//I2S LRCK POLARITY Control
|
|
+#define LRCK_LEFT_LOW_RIGHT_HIGH 0
|
|
+#define LRCK_LEFT_HIGH_RIGHT_LOW 1
|
|
+
|
|
+//I2S Format Selection
|
|
+#define PCM_FORMAT 0
|
|
+#define LEFT_JUSTIFIED_FORMAT 1
|
|
+#define RIGHT_JUSTIFIED_FORMAT 2
|
|
+
|
|
+//I2S data protocol types
|
|
+
|
|
+#define IS_ENCODING_MODE 0
|
|
+
|
|
+#endif
|
|
+
|
|
--- /dev/null
|
|
+++ b/sound/soc/codecs/ac10x.h
|
|
@@ -0,0 +1,152 @@
|
|
+/*
|
|
+ * ac10x.h
|
|
+ *
|
|
+ * (C) Copyright 2017-2018
|
|
+ * Seeed Technology Co., Ltd. <www.seeedstudio.com>
|
|
+ *
|
|
+ * PeterYang <linsheng.yang@seeed.cc>
|
|
+ *
|
|
+ * (C) Copyright 2010-2017
|
|
+ * Reuuimlla Technology Co., Ltd. <www.reuuimllatech.com>
|
|
+ * huangxin <huangxin@reuuimllatech.com>
|
|
+ *
|
|
+ * some simple description for this code
|
|
+ *
|
|
+ * 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.
|
|
+ *
|
|
+ */
|
|
+#ifndef __AC10X_H__
|
|
+#define __AC10X_H__
|
|
+
|
|
+#define AC101_I2C_ID 4
|
|
+#define _MASTER_AC108 0
|
|
+#define _MASTER_AC101 1
|
|
+#define _MASTER_MULTI_CODEC _MASTER_AC101
|
|
+
|
|
+/* enable headset detecting & headset button pressing */
|
|
+#define CONFIG_AC101_SWITCH_DETECT
|
|
+
|
|
+/* obsolete */
|
|
+#define CONFIG_AC10X_TRIG_LOCK 0
|
|
+
|
|
+#ifdef AC101_DEBG
|
|
+ #define AC101_DBG(format,args...) printk("[AC101] %s() L%d " format, __func__, __LINE__, ##args)
|
|
+#else
|
|
+ #define AC101_DBG(...)
|
|
+#endif
|
|
+
|
|
+#include <linux/version.h>
|
|
+
|
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,18,0)
|
|
+#define __NO_SND_SOC_CODEC_DRV 1
|
|
+#else
|
|
+#define __NO_SND_SOC_CODEC_DRV 0
|
|
+#endif
|
|
+
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5,4,0)
|
|
+#if __has_attribute(__fallthrough__)
|
|
+# define fallthrough __attribute__((__fallthrough__))
|
|
+#else
|
|
+# define fallthrough do {} while (0) /* fallthrough */
|
|
+#endif
|
|
+#endif
|
|
+
|
|
+#if __NO_SND_SOC_CODEC_DRV
|
|
+#define codec component
|
|
+#define snd_soc_codec snd_soc_component
|
|
+#define snd_soc_codec_driver snd_soc_component_driver
|
|
+#define snd_soc_codec_get_drvdata snd_soc_component_get_drvdata
|
|
+#define snd_soc_codec_get_dapm snd_soc_component_get_dapm
|
|
+#define snd_soc_codec_get_bias_level snd_soc_component_get_bias_level
|
|
+#define snd_soc_kcontrol_codec snd_soc_kcontrol_component
|
|
+#define snd_soc_read snd_soc_component_read32
|
|
+#define snd_soc_register_codec snd_soc_register_component
|
|
+#define snd_soc_unregister_codec snd_soc_unregister_component
|
|
+#define snd_soc_update_bits snd_soc_component_update_bits
|
|
+#define snd_soc_write snd_soc_component_write
|
|
+#define snd_soc_add_codec_controls snd_soc_add_component_controls
|
|
+#endif
|
|
+
|
|
+
|
|
+#ifdef CONFIG_AC101_SWITCH_DETECT
|
|
+enum headphone_mode_u {
|
|
+ HEADPHONE_IDLE,
|
|
+ FOUR_HEADPHONE_PLUGIN,
|
|
+ THREE_HEADPHONE_PLUGIN,
|
|
+};
|
|
+#endif
|
|
+
|
|
+struct ac10x_priv {
|
|
+ struct i2c_client *i2c[4];
|
|
+ struct regmap* i2cmap[4];
|
|
+ int codec_cnt;
|
|
+ unsigned sysclk;
|
|
+#define _FREQ_24_576K 24576000
|
|
+#define _FREQ_22_579K 22579200
|
|
+ unsigned mclk; /* master clock or aif_clock/aclk */
|
|
+ int clk_id;
|
|
+ unsigned char i2s_mode;
|
|
+ unsigned char data_protocol;
|
|
+ struct delayed_work dlywork;
|
|
+ int tdm_chips_cnt;
|
|
+ int sysclk_en;
|
|
+
|
|
+ /* member for ac101 .begin */
|
|
+ struct snd_soc_codec *codec;
|
|
+ struct i2c_client *i2c101;
|
|
+ struct regmap* regmap101;
|
|
+
|
|
+ struct mutex dac_mutex;
|
|
+ u8 dac_enable;
|
|
+ spinlock_t lock;
|
|
+ u8 aif1_clken;
|
|
+
|
|
+ struct work_struct codec_resume;
|
|
+ struct gpio_desc* gpiod_spk_amp_gate;
|
|
+
|
|
+ #ifdef CONFIG_AC101_SWITCH_DETECT
|
|
+ struct gpio_desc* gpiod_irq;
|
|
+ long irq;
|
|
+ volatile int irq_cntr;
|
|
+ volatile int pullout_cntr;
|
|
+ volatile int state;
|
|
+
|
|
+ enum headphone_mode_u mode;
|
|
+ struct work_struct work_switch;
|
|
+ struct work_struct work_clear_irq;
|
|
+
|
|
+ struct input_dev* inpdev;
|
|
+ #endif
|
|
+ /* member for ac101 .end */
|
|
+};
|
|
+
|
|
+
|
|
+/* AC101 DAI operations */
|
|
+int ac101_audio_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai);
|
|
+void ac101_aif_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai);
|
|
+int ac101_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt);
|
|
+int ac101_hw_params(struct snd_pcm_substream *substream,
|
|
+ struct snd_pcm_hw_params *params,
|
|
+ struct snd_soc_dai *codec_dai);
|
|
+int ac101_trigger(struct snd_pcm_substream *substream, int cmd,
|
|
+ struct snd_soc_dai *dai);
|
|
+int ac101_aif_mute(struct snd_soc_dai *codec_dai, int mute);
|
|
+
|
|
+/* codec driver specific */
|
|
+int ac101_codec_probe(struct snd_soc_codec *codec);
|
|
+int ac101_codec_remove(struct snd_soc_codec *codec);
|
|
+int ac101_codec_suspend(struct snd_soc_codec *codec);
|
|
+int ac101_codec_resume(struct snd_soc_codec *codec);
|
|
+int ac101_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level);
|
|
+
|
|
+/* i2c device specific */
|
|
+int ac101_probe(struct i2c_client *i2c, const struct i2c_device_id *id);
|
|
+void ac101_shutdown(struct i2c_client *i2c);
|
|
+int ac101_remove(struct i2c_client *i2c);
|
|
+
|
|
+int ac10x_fill_regcache(struct device* dev, struct regmap* map);
|
|
+
|
|
+#endif//__AC10X_H__
|