GameShell/Code/Kernel/v0.6/515_sound.patch
2023-08-22 16:13:21 +12:00

384 lines
13 KiB
Diff

diff --git a/sound/soc/sunxi/sun8i-codec-analog.c b/sound/soc/sunxi/sun8i-codec-analog.c
index be872eefa61e..ca5f4e43476b 100644
--- a/sound/soc/sunxi/sun8i-codec-analog.c
+++ b/sound/soc/sunxi/sun8i-codec-analog.c
@@ -19,6 +19,8 @@
#include <sound/tlv.h>
#include "sun8i-adda-pr-regmap.h"
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
/* Codec analog control register offsets and bit fields */
#define SUN8I_ADDA_HP_VOLC 0x00
@@ -113,6 +115,81 @@
#define SUN8I_ADDA_ADC_AP_EN_ADCLEN 6
#define SUN8I_ADDA_ADC_AP_EN_ADCG 0
+/* Analog control register access bits */
+#define ADDA_PR 0x0 /* PRCM base + 0x1c0 */
+#define ADDA_PR_RESET BIT(28)
+#define ADDA_PR_WRITE BIT(24)
+#define ADDA_PR_ADDR_SHIFT 16
+#define ADDA_PR_ADDR_MASK GENMASK(4, 0)
+#define ADDA_PR_DATA_IN_SHIFT 8
+#define ADDA_PR_DATA_IN_MASK GENMASK(7, 0)
+#define ADDA_PR_DATA_OUT_SHIFT 0
+#define ADDA_PR_DATA_OUT_MASK GENMASK(7, 0)
+
+/* regmap access bits */
+static int adda_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+ void __iomem *base = (void __iomem *)context;
+ u32 tmp;
+
+ /* De-assert reset */
+ writel(readl(base) | ADDA_PR_RESET, base);
+
+ /* Clear write bit */
+ writel(readl(base) & ~ADDA_PR_WRITE, base);
+
+ /* Set register address */
+ tmp = readl(base);
+ tmp &= ~(ADDA_PR_ADDR_MASK << ADDA_PR_ADDR_SHIFT);
+ tmp |= (reg & ADDA_PR_ADDR_MASK) << ADDA_PR_ADDR_SHIFT;
+ writel(tmp, base);
+
+ /* Read back value */
+ *val = readl(base) & ADDA_PR_DATA_OUT_MASK;
+
+ return 0;
+}
+
+static int adda_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+ void __iomem *base = (void __iomem *)context;
+ u32 tmp;
+
+ /* De-assert reset */
+ writel(readl(base) | ADDA_PR_RESET, base);
+
+ /* Set register address */
+ tmp = readl(base);
+ tmp &= ~(ADDA_PR_ADDR_MASK << ADDA_PR_ADDR_SHIFT);
+ tmp |= (reg & ADDA_PR_ADDR_MASK) << ADDA_PR_ADDR_SHIFT;
+ writel(tmp, base);
+
+ /* Set data to write */
+ tmp = readl(base);
+ tmp &= ~(ADDA_PR_DATA_IN_MASK << ADDA_PR_DATA_IN_SHIFT);
+ tmp |= (val & ADDA_PR_DATA_IN_MASK) << ADDA_PR_DATA_IN_SHIFT;
+ writel(tmp, base);
+
+ /* Set write bit to signal a write */
+ writel(readl(base) | ADDA_PR_WRITE, base);
+
+ /* Clear write bit */
+ writel(readl(base) & ~ADDA_PR_WRITE, base);
+
+ return 0;
+}
+
+static const struct regmap_config adda_pr_regmap_cfg = {
+ .name = "adda-pr",
+ .reg_bits = 5,
+ .reg_stride = 1,
+ .val_bits = 8,
+ .reg_read = adda_reg_read,
+ .reg_write = adda_reg_write,
+ .fast_io = true,
+ .max_register = 24,
+};
+
/* mixer controls */
static const struct snd_kcontrol_new sun8i_codec_mixer_controls[] = {
SOC_DAPM_DOUBLE_R("DAC Playback Switch",
@@ -316,7 +393,7 @@ static const struct snd_soc_dapm_route sun8i_codec_mixer_routes[] = {
/* headphone specific controls, widgets, and routes */
static const DECLARE_TLV_DB_SCALE(sun8i_codec_hp_vol_scale, -6300, 100, 1);
static const struct snd_kcontrol_new sun8i_codec_headphone_controls[] = {
- SOC_SINGLE_TLV("Headphone Playback Volume",
+ SOC_SINGLE_TLV("Master Playback Volume",
SUN8I_ADDA_HP_VOLC,
SUN8I_ADDA_HP_VOLC_HP_VOL, 0x3f, 0,
sun8i_codec_hp_vol_scale),
@@ -673,7 +750,7 @@ struct sun8i_codec_analog_quirks {
static const struct sun8i_codec_analog_quirks sun8i_a23_quirks = {
.has_headphone = true,
- .has_hmic = true,
+// .has_hmic = true,
.has_linein = true,
.has_mbias = true,
.has_mic2 = true,
@@ -817,10 +894,148 @@ static const struct of_device_id sun8i_codec_analog_of_match[] = {
};
MODULE_DEVICE_TABLE(of, sun8i_codec_analog_of_match);
+#define SUNXI_HMIC_ENABLE (0x4)
+#define SUNXI_HMIC_CTL (0x8)
+#define SUNXI_HMIC_DATA (0xc)
+
+/*
+* SUNXI_HMIC_CTL
+*HMIC Control Register
+*CONFIG_ARCH_SUN8IW5:0x1c8
+*/
+#define HMIC_M (28)
+#define HMIC_N (24)
+#define HMIC_DIRQ (23)
+#define HMIC_TH1_HYS (21)
+#define HMIC_EARPHONE_OUT_IRQ_EN (20)
+#define HMIC_EARPHONE_IN_IRQ_EN (19)
+#define HMIC_KEY_UP_IRQ_EN (18)
+#define HMIC_KEY_DOWN_IRQ_EN (17)
+#define HMIC_DATA_IRQ_EN (16)
+#define HMIC_DS_SAMP (14)
+#define HMIC_TH2_HYS (13)
+#define HMIC_TH2_KEY (8)
+#define HMIC_SF_SMOOTH_FIL (6)
+#define KEY_UP_IRQ_PEND (5)
+#define HMIC_TH1_EARPHONE (0)
+
+/*
+* SUNXI_HMIC_DATA
+*HMIC Data Register
+*
+*CONFIG_ARCH_SUN8IW5:0x1cc
+*/
+#define HMIC_EARPHONE_OUT_IRQ_PEND (20)
+#define HMIC_EARPHONE_IN_IRQ_PEND (19)
+#define HMIC_KEY_UP_IRQ_PEND (18)
+#define HMIC_KEY_DOWN_IRQ_PEND (17)
+#define HMIC_DATA_IRQ_PEND (16)
+#define HMIC_ADC_DATA (0)
+
+#define HP_VOLC (0x00)
+#define LOMIXSC (0x01)
+#define ROMIXSC (0x02)
+#define DAC_PA_SRC (0x03)
+#define PAEN_HP_CTRL (0x07)
+#define ADDA_APT2 (0x12)
+#define MIC1G_MICBIAS_CTRL (0x0B)
+#define PA_ANTI_POP_REG_CTRL (0x0E)
+#define PA_SLOPE_SELECT (3)
+#define PA_ANTI_POP_EN (0)
+
+static void __iomem *sun8i_codec_analog_base;
+static struct gpio_desc * speaker_amplifier_gpio;
+
+static int hmic_wrreg_prcm_bits(unsigned short reg, unsigned int mask, unsigned int value)
+{
+ unsigned int old, new;
+
+ adda_reg_read(sun8i_codec_analog_base, reg, &old);
+ new = (old & ~mask) | value;
+ adda_reg_write(sun8i_codec_analog_base, reg,new);
+
+ return 0;
+}
+
+static int hmic_wr_prcm_control(u32 reg, u32 mask, u32 shift, u32 val)
+{
+ u32 reg_val;
+ reg_val = val << shift;
+ mask = mask << shift;
+ hmic_wrreg_prcm_bits(reg, mask, reg_val);
+ return 0;
+}
+
+static int hmic_wrreg_bits(unsigned short reg, unsigned int mask, unsigned int value)
+{
+ unsigned int old, new;
+
+ old = readl(sun8i_codec_analog_base + reg);
+ new = (old & ~mask) | value;
+
+ writel(new, sun8i_codec_analog_base + reg);
+
+ return 0;
+}
+
+static int hmic_wr_control(u32 reg, u32 mask, u32 shift, u32 val)
+{
+ u32 reg_val;
+ reg_val = val << shift;
+ mask = mask << shift;
+ hmic_wrreg_bits(reg, mask, reg_val);
+ return 0;
+}
+
+static irqreturn_t sunxi_codec_analog_irq(int irq, void *dev_id)
+{
+ u32 tmp;
+
+ hmic_wr_control(SUNXI_HMIC_DATA, 0x1, HMIC_KEY_DOWN_IRQ_PEND, 0x1);
+ hmic_wr_control(SUNXI_HMIC_DATA, 0x1, HMIC_EARPHONE_IN_IRQ_PEND, 0x1);
+ hmic_wr_control(SUNXI_HMIC_DATA, 0x1, HMIC_KEY_UP_IRQ_PEND, 0x1);
+ hmic_wr_control(SUNXI_HMIC_DATA, 0x1, HMIC_EARPHONE_OUT_IRQ_PEND, 0x1);
+ hmic_wr_control(SUNXI_HMIC_DATA, 0x1, HMIC_DATA_IRQ_PEND, 0x1);
+
+ tmp = readl(sun8i_codec_analog_base + SUNXI_HMIC_DATA);
+ if(tmp & 0x1f)
+ gpiod_set_value(speaker_amplifier_gpio, 0);
+ else
+ gpiod_set_value(speaker_amplifier_gpio, 1);
+
+ return IRQ_HANDLED;
+}
+
+static void sunxi_hppa_enable(void) {
+ /*fix the resume blaze blaze noise*/
+ hmic_wr_prcm_control(ADDA_APT2, 0x1, PA_SLOPE_SELECT, 0x0);
+ hmic_wr_prcm_control(SUN8I_ADDA_PAEN_HP_CTRL, 0x3, SUN8I_ADDA_PAEN_HP_CTRL_PA_ANTI_POP_CTRL, 0x1);
+ hmic_wr_prcm_control(PA_ANTI_POP_REG_CTRL, 0x7, PA_ANTI_POP_EN, 0x2);
+ usleep_range(100,200);
+ /*enable pa*/
+ hmic_wr_prcm_control(SUN8I_ADDA_PAEN_HP_CTRL, 0x1, SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN, 0x1);
+}
+
+static void sunxi_hbias_enable(void) {
+ /*audio codec hardware bug. the HBIASADCEN bit must be enable in init*/
+ hmic_wr_prcm_control(SUN8I_ADDA_MIC1G_MICBIAS_CTRL, 0x1, SUN8I_ADDA_MIC1G_MICBIAS_CTRL_HMICBIAS_MODE, 0x1);
+ hmic_wr_prcm_control(SUN8I_ADDA_MIC1G_MICBIAS_CTRL, 0x1, SUN8I_ADDA_MIC1G_MICBIAS_CTRL_HMICBIASEN, 0x1);
+}
+
+static void codec_init_events(void)
+{
+ /*fix the resume blaze blaze noise*/
+ sunxi_hppa_enable();
+ msleep(450);
+ /*audio codec hardware bug. the HBIASADCEN bit must be enable in init*/
+ sunxi_hbias_enable();
+}
+
static int sun8i_codec_analog_probe(struct platform_device *pdev)
{
struct regmap *regmap;
void __iomem *base;
+ int irq, ret;
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base)) {
@@ -828,6 +1043,38 @@ static int sun8i_codec_analog_probe(struct platform_device *pdev)
return PTR_ERR(base);
}
+ sun8i_codec_analog_base = base;
+ speaker_amplifier_gpio = devm_gpiod_get_optional(&pdev->dev, "speaker-amplifier", GPIOD_OUT_HIGH);
+ if (!IS_ERR_OR_NULL(speaker_amplifier_gpio)) {
+
+ hmic_wr_control(SUNXI_HMIC_CTL, 0xf, HMIC_M, 0x0); /*0xf should be get from hw_debug 28*/
+ hmic_wr_control(SUNXI_HMIC_CTL, 0xf, HMIC_N, 0x0); /*0xf should be get from hw_debug 24 0xf*/
+// hmic_wr_control(SUNXI_HMIC_CTL, 0x1, HMIC_DIRQ, 0x1); /*23*/
+ hmic_wr_control(SUNXI_HMIC_CTL, 0x1, HMIC_EARPHONE_OUT_IRQ_EN, 0x1); /*20*/
+ hmic_wr_control(SUNXI_HMIC_CTL, 0x1, HMIC_EARPHONE_IN_IRQ_EN, 0x1); /*19*/
+ hmic_wr_control(SUNXI_HMIC_CTL, 0x1, HMIC_KEY_UP_IRQ_EN, 0x1); /*18*/
+ hmic_wr_control(SUNXI_HMIC_CTL, 0x1, HMIC_KEY_DOWN_IRQ_EN, 0x1); /*17*/
+ hmic_wr_control(SUNXI_HMIC_CTL, 0x1, HMIC_DATA_IRQ_EN, 0x1); /*16*/
+ hmic_wr_control(SUNXI_HMIC_CTL, 0x3, HMIC_DS_SAMP, 0x0); /*14 */
+ hmic_wr_control(SUNXI_HMIC_CTL, 0x1f, HMIC_TH2_KEY, 0x0); /*0xf should be get from hw_debug 8*/
+ hmic_wr_control(SUNXI_HMIC_CTL, 0x1f, HMIC_TH1_EARPHONE, 0x1); /*0x1 should be get from hw_debug 0*/
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "Can't retrieve our interrupt\n");
+ return irq;
+ }
+
+ ret = devm_request_irq(&pdev->dev, irq, sunxi_codec_analog_irq, 0, "audio_hmic_irq", NULL);
+ if (ret) {
+ dev_err(&pdev->dev, "can't register interrupt handler irq %d: %d\n",
+ irq, ret);
+ return ret;
+ }
+
+ codec_init_events();
+ }
+
regmap = sun8i_adda_pr_regmap_init(&pdev->dev, base);
if (IS_ERR(regmap)) {
dev_err(&pdev->dev, "Failed to create regmap\n");
diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c
index 518bfb724a5b..b6a90e33f20a 100644
--- a/sound/soc/sunxi/sun8i-codec.c
+++ b/sound/soc/sunxi/sun8i-codec.c
@@ -257,6 +257,9 @@ static int sun8i_codec_update_sample_rate(struct sun8i_codec *scodec)
regmap_update_bits(scodec->regmap, SUN8I_SYS_SR_CTRL,
SUN8I_SYS_SR_CTRL_AIF1_FS_MASK,
hw_rate << SUN8I_SYS_SR_CTRL_AIF1_FS);
+ regmap_update_bits(scodec->regmap, SUN8I_SYS_SR_CTRL,
+ SUN8I_SYS_SR_CTRL_AIF2_FS_MASK,
+ hw_rate << SUN8I_SYS_SR_CTRL_AIF2_FS);
return 0;
}
@@ -288,6 +291,9 @@ static int sun8i_codec_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
SUN8I_AIF3_CLK_CTRL_AIF3_CLK_SRC_MASK,
SUN8I_AIF3_CLK_CTRL_AIF3_CLK_SRC_AIF2);
} else {
+ regmap_update_bits(scodec->regmap, SUN8I_AIF_CLK_CTRL(SUN8I_CODEC_AIF1),
+ BIT(SUN8I_AIF_CLK_CTRL_MSTR_MOD),
+ value << SUN8I_AIF_CLK_CTRL_MSTR_MOD);
regmap_update_bits(scodec->regmap, SUN8I_AIF_CLK_CTRL(dai->id),
BIT(SUN8I_AIF_CLK_CTRL_MSTR_MOD),
value << SUN8I_AIF_CLK_CTRL_MSTR_MOD);
@@ -321,6 +327,9 @@ static int sun8i_codec_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
if (format != 3)
return -EINVAL;
} else {
+ regmap_update_bits(scodec->regmap, SUN8I_AIF_CLK_CTRL(SUN8I_CODEC_AIF1),
+ SUN8I_AIF_CLK_CTRL_DATA_FMT_MASK,
+ format << SUN8I_AIF_CLK_CTRL_DATA_FMT);
regmap_update_bits(scodec->regmap, SUN8I_AIF_CLK_CTRL(dai->id),
SUN8I_AIF_CLK_CTRL_DATA_FMT_MASK,
format << SUN8I_AIF_CLK_CTRL_DATA_FMT);
@@ -365,6 +374,9 @@ static int sun8i_codec_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
invert ^= scodec->quirks->lrck_inversion;
}
+ regmap_update_bits(scodec->regmap, SUN8I_AIF_CLK_CTRL(SUN8I_CODEC_AIF1),
+ SUN8I_AIF_CLK_CTRL_CLK_INV_MASK,
+ invert << SUN8I_AIF_CLK_CTRL_CLK_INV);
regmap_update_bits(scodec->regmap, SUN8I_AIF_CLK_CTRL(dai->id),
SUN8I_AIF_CLK_CTRL_CLK_INV_MASK,
invert << SUN8I_AIF_CLK_CTRL_CLK_INV);
@@ -519,6 +531,9 @@ static int sun8i_codec_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
+ regmap_update_bits(scodec->regmap, SUN8I_AIF_CLK_CTRL(SUN8I_CODEC_AIF1),
+ SUN8I_AIF_CLK_CTRL_WORD_SIZ_MASK,
+ word_size << SUN8I_AIF_CLK_CTRL_WORD_SIZ);
regmap_update_bits(scodec->regmap, SUN8I_AIF_CLK_CTRL(dai->id),
SUN8I_AIF_CLK_CTRL_WORD_SIZ_MASK,
word_size << SUN8I_AIF_CLK_CTRL_WORD_SIZ);
@@ -548,6 +563,9 @@ static int sun8i_codec_hw_params(struct snd_pcm_substream *substream,
clk_reg = SUN8I_AIF_CLK_CTRL(dai->id);
}
+ regmap_update_bits(scodec->regmap, SUN8I_AIF_CLK_CTRL(SUN8I_CODEC_AIF1),
+ SUN8I_AIF_CLK_CTRL_LRCK_DIV_MASK,
+ (lrck_div_order - 4) << SUN8I_AIF_CLK_CTRL_LRCK_DIV);
regmap_update_bits(scodec->regmap, clk_reg,
SUN8I_AIF_CLK_CTRL_LRCK_DIV_MASK,
(lrck_div_order - 4) << SUN8I_AIF_CLK_CTRL_LRCK_DIV);
@@ -557,6 +575,9 @@ static int sun8i_codec_hw_params(struct snd_pcm_substream *substream,
if (bclk_div < 0)
return bclk_div;
+ regmap_update_bits(scodec->regmap, SUN8I_AIF_CLK_CTRL(SUN8I_CODEC_AIF1),
+ SUN8I_AIF_CLK_CTRL_BCLK_DIV_MASK,
+ bclk_div << SUN8I_AIF_CLK_CTRL_BCLK_DIV);
regmap_update_bits(scodec->regmap, clk_reg,
SUN8I_AIF_CLK_CTRL_BCLK_DIV_MASK,
bclk_div << SUN8I_AIF_CLK_CTRL_BCLK_DIV);
@@ -1226,6 +1247,7 @@ static const struct snd_soc_component_driver sun8i_soc_component = {
};
static const struct regmap_config sun8i_codec_regmap_config = {
+ .name = "sun8i_codec",
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,