From 6eb4890e1b83490864f18d3926182069e54a4dfb Mon Sep 17 00:00:00 2001 From: Ondrej Jirman Date: Sun, 12 Nov 2017 23:09:14 +0100 Subject: [PATCH] sound: soc: ac100-codec: Initial implementation This driver provides AC100 codec controls. Note: This does not yet provide anything, it's just a skeleton for a future driver. Signed-off-by: Ondrej Jirman --- sound/soc/sunxi/Kconfig | 11 ++ sound/soc/sunxi/Makefile | 1 + sound/soc/sunxi/ac100-codec.c | 291 ++++++++++++++++++++++++++++++++++ 3 files changed, 303 insertions(+) create mode 100644 sound/soc/sunxi/ac100-codec.c diff --git a/sound/soc/sunxi/Kconfig b/sound/soc/sunxi/Kconfig index 22408bc2d6ec6..e276cc94a8c5e 100644 --- a/sound/soc/sunxi/Kconfig +++ b/sound/soc/sunxi/Kconfig @@ -20,6 +20,17 @@ config SND_SUN8I_CODEC Say Y or M if you want to add sun8i digital audio codec support. +config SND_AC100_CODEC + tristate "Allwinner (X-Powers) AC100 audio codec" + depends on OF + depends on MACH_SUN8I || COMPILE_TEST + select REGMAP_MMIO + help + This option enables the audio codec support for Allwinner (X-Powers) + AC100 chip. + + Say Y or M if you want to add AC100 audio codec support. + config SND_SUN8I_CODEC_ANALOG tristate "Allwinner sun8i Codec Analog Controls Support" depends on MACH_SUN8I || (ARM64 && ARCH_SUNXI) || COMPILE_TEST diff --git a/sound/soc/sunxi/Makefile b/sound/soc/sunxi/Makefile index 4a9ef67386caf..83461fdbfaa2a 100644 --- a/sound/soc/sunxi/Makefile +++ b/sound/soc/sunxi/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_SND_SUN4I_I2S) += sun4i-i2s.o obj-$(CONFIG_SND_SUN4I_SPDIF) += sun4i-spdif.o obj-$(CONFIG_SND_SUN8I_CODEC_ANALOG) += sun8i-codec-analog.o obj-$(CONFIG_SND_SUN8I_CODEC) += sun8i-codec.o +obj-$(CONFIG_SND_AC100_CODEC) += ac100-codec.o diff --git a/sound/soc/sunxi/ac100-codec.c b/sound/soc/sunxi/ac100-codec.c new file mode 100644 index 0000000000000..d5438815be754 --- /dev/null +++ b/sound/soc/sunxi/ac100-codec.c @@ -0,0 +1,291 @@ +/* + * This driver supports the controls for X-Powers (Allwinner) + * AC100 audio codec. This codec is co-packaged with AXP81x PMICs. + * + * (C) Copyright 2017 Ondrej Jirman + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* + * Reasearch + * --------- + * + * Features: + * 2 A/D + * 2 D/A + * 2 I2S/PCM #1 and #2 + * 1 PCM mono #3 muxable with I2S #2 + * 3 mic inputs (mic #2 and #3 exclusively muxable at input) + * 1 line in (directly or through amp) + * 1 aux input + * all input signals mixable directly into output (bypass AD/DA) + * + * outputs: + * HPOUTL - headphone left + * HPOUTR - headphone right + * LINEOUT - line out differential + * EAROUT - earpeiece differential + * SPKOUT1 - SPOL left speaker differnetial + * SPKOUT2 - SPOR right speaker differnetial + * + * Power up: + * LDOIN - 1.5-3.3V external power + * AVCC - analog power 3V + * CPVDD - 1.8V + * VDD-IO1 - power for I2S #1 and #2 1.8/3V + * VDD-IO2 - power for I2S #3 1.8/3V + * VCC-RTC - power for RTC 1.8/3V + * + * Clocks: page 28 + * SYSCLK must be 24576000 Hz (48kHz) or 22579200 Hz (44.1kHz) + * - Source I2S1CLK/MCLK1 + * - If SRC# module is used SYSCLK must be generated by PLL + * + * A/D: + * ADC_APC_CTRL B15 B11 enable/disable A/D to save power + * ADC_APC_CTRL B14-12 B10-8 volume control for A/D + * ADC_DIG_CTRL B15 enable/disable digital A/D to save power + * ADDA_FS_I2S1 ADDA_FS_I2S2 - select sample rate + * + * D/A: + * OMIXER_DACA_CTRL B15-14 enable/disable D/A channels + * DAC_DIG_CTRL B15 enable/disable digital D/A + * + * Mixer: + * - 2 channels DAC Output mixers + * - inputs: + * - LINEINL/R + * - AXIL/R + * - MIC1P/N,MIC2P/N + * - Stereo DAC output + * - 2 channels ADC Record mixers + * - inputs: + * - LINEINL/R + * - AXIL/R + * - MIC1P/N,MIC2P/N + * - Stereo DAC output + * - Digital mixers + * - avaliable on: + * - before stereo DAC - DAC_MXR_SRC + * - I2S1 output - I2S1_MXR_SRC + * - I2S2 output - I2S2_MXR_SRC + * - Analogue inputs + * - LINEINL/R - 1 ch. mono + * - can be mixed into ADC record mixer or DAC output mixer + * - -9dB to 12dB in 1.5dB step by LINEIN_DIFF_PREG + * - AXIL/R - 2 ch. stereo + * - can be mixed into ADC record mixer or DAC output mixer + * - programmable volume level adj. and mute + * - -9dB to 12dB in 1.5dB steps by AXI_PREG + * - MIC1P/N - has preamp + * - MIC2P/N - has preamp + * - MIC3P/N - has preamp muxed with MIC2 (sel by ADC_SRCBST_CTRL B7) + * - can be mixed into ADC record mixer or DAC output mixer + * - preamps enable at ADC_SRCBST_CTRL B15 and B11 + * - preamp gain at MIC1BOOST MIC2BOOST + * - Analogue outputs: + * - HPOUTL/R, HPOUTFB - 2ch. headphones + * - sources output mixer or directly from DAC + * - sel HPOUT_CTRL B15 B14 + * - mute HPOUT_CTRL B13 B12 + * - power amp + * - powerdown/up HPOUT_CTRL B11 + * - volume HP_VOL[5:0] - 64dB range in 1dB step from 0dB to -62dB + * - mute by using 0 for HP_VOL[5:0] + * - DC offset cancellation (POP noise) HP_DCRM_EN + * - This bit must be set 0xf before headphone PA enabled, and this bit + * must be set 0x0 before headphone PA disabled. + * - zero cross to prevent noise/clicsk on volume change ZCROSS_EN + * - SPOLP/N, SPORP/N 2 ch. speakers (mono/stereo) + * - source for SPOLP + * - left output mixer or (left+right) output mixer + * - source for SPORP + * - right output mixer or (left+right) output mixer + * - volume 43.5dB rang in 1.5dB step from -43.5dB to 0dB + * - amp enable SPKOUT_CTRL B11 B7 + * - EAROUTP/N - 1 ch earpeice + * - source left DAC, right DAC, left output mixer or right output mixer + * - volume ERPOUT_CTRL[4:0] 43.5dB range in 1.5dB step from -43.5dB to 0dB + * - power amp enable ERPOUT_CTRL B5 + * - LINEOUTP/N - 1ch line out + * - source MIC1 preamp, MIC2 preamp, left output mix or right output mix + * - volume 10.5dB range in 1.5dB step from -4.5dB to 6dB + * - out buffer power up/down LOUT_CTRL B4 + * + * Jack insert detection: + * - HBIAS current detection + * - 5bit ADC sample rate 16/32/64/128Hz + * - HMIC_STATUS[12:8] - ADC value + * - 2 thresholds TH1 for plug connection, TH2 for key press + * - can periodically trigger interrupts during key press (HBIAS above TH2) + * + * Interrupts: + * - FALLING_EDGE + * - for: + * - KEYDOWN + * - KEYUP + * - PLUG_IN + * - PLUG_OUT + * - HMIC_DATA + * + * High Pass Filter: + * - remove DC offset, can be disabled + * + * AGC: + * - automatic gain control before ADC input channels + * - params: + * - attack, decay time - 32/fs to 2^15*32/fs + * - target gain - –1dB to –30dB relative to a full-scale signal + * - noise threshold - –30dB to –90dB of full-scale (mute if input below this level) + * - max gain 0dB to 40dB in steps of 0.5dB + * - hysteresis - for noise detection in terms of signal level + * - debounce time - hysteresis for noise det in terms of time + * - also provides some output flags: + * - noise threshold reached + * - current gain + * - agc saturated (gain could get higher for the given input, but limited by + * params) + * - adc saturated - clipping at ADC input stage + * + * DRC: + * - dynamic range control for digital playback path + * - energy filter + * - compressor + * - smooth filter + * - can be disabled + */ + +#define AC100_HMIC_DATA_MASK GENMASK(12, 8) +#define AC100_HMIC_DATA_OFF 8 +#define AC100_HMIC_PULLOUT_PENDING BIT(4) +#define AC100_HMIC_PLUGIN_PENDING BIT(3) +#define AC100_HMIC_KEYUP_PENDING BIT(2) +#define AC100_HMIC_KEYDOWN_PENDING BIT(1) +#define AC100_HMIC_DATA_PENDING BIT(0) + +struct ac100_codec { + struct device *dev; + struct regmap *regmap; + int irq; +}; + +static irqreturn_t ac100_codec_irq(int irq, void *data) +{ + struct ac100_codec *codec = data; + unsigned int val = 0; + int ret; + + /* read status */ + ret = regmap_read(codec->regmap, AC100_HMIC_STATUS, &val); + if (ret) + return IRQ_HANDLED; + + if (val & AC100_HMIC_PULLOUT_PENDING) { + dev_info(codec->dev, "IRQ: Pull out"); + } + + if (val & AC100_HMIC_PLUGIN_PENDING) { + dev_info(codec->dev, "IRQ: Plug in"); + } + + if (val & AC100_HMIC_KEYUP_PENDING) { + dev_info(codec->dev, "IRQ: Key up"); + } + + if (val & AC100_HMIC_KEYDOWN_PENDING) { + dev_info(codec->dev, "IRQ: Key down"); + } + + if (val & AC100_HMIC_DATA_PENDING) { + dev_info(codec->dev, "IRQ: Data"); + } + + /* clear status */ + ret = regmap_write(codec->regmap, AC100_HMIC_STATUS, 0); + if (ret) + return IRQ_HANDLED; + + return IRQ_HANDLED; +} + +static int ac100_codec_probe(struct platform_device *pdev) +{ + struct ac100_dev *ac100 = dev_get_drvdata(pdev->dev.parent); + struct ac100_codec *codec; + int ret; + + codec = devm_kzalloc(&pdev->dev, sizeof(*codec), GFP_KERNEL); + if (!codec) + return -ENOMEM; + + platform_set_drvdata(pdev, codec); + codec->dev = &pdev->dev; + codec->regmap = ac100->regmap; + + codec->irq = platform_get_irq(pdev, 0); + if (codec->irq < 0) { + dev_err(&pdev->dev, "No IRQ resource\n"); + return codec->irq; + } + + ret = devm_request_threaded_irq(&pdev->dev, codec->irq, NULL, + ac100_codec_irq, + IRQF_SHARED | IRQF_ONESHOT, + dev_name(&pdev->dev), codec); + if (ret) { + dev_err(&pdev->dev, "Could not request IRQ\n"); + return ret; + } + + return ret; +} + +static int ac100_codec_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct ac100_codec *codec = snd_soc_card_get_drvdata(card); + + return 0; +} + +static const struct of_device_id ac100_codec_of_match[] = { + { .compatible = "x-powers,ac100-codec" }, + {} +}; +MODULE_DEVICE_TABLE(of, ac100_codec_of_match); + +static struct platform_driver ac100_codec_driver = { + .driver = { + .name = "ac100-codec", + .of_match_table = ac100_codec_of_match, + }, + .probe = ac100_codec_probe, + .remove = ac100_codec_remove, +}; +module_platform_driver(ac100_codec_driver); + +MODULE_DESCRIPTION("X-Powers AC100 codec driver"); +MODULE_AUTHOR("Ondrej Jirman "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ac100-codec");