cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

sun50i-codec-analog.c (19118B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * This driver supports the analog controls for the internal codec
      4 * found in Allwinner's A64 SoC.
      5 *
      6 * Copyright (C) 2016 Chen-Yu Tsai <wens@csie.org>
      7 * Copyright (C) 2017 Marcus Cooper <codekipper@gmail.com>
      8 * Copyright (C) 2018 Vasily Khoruzhick <anarsoul@gmail.com>
      9 *
     10 * Based on sun8i-codec-analog.c
     11 *
     12 */
     13
     14#include <linux/io.h>
     15#include <linux/kernel.h>
     16#include <linux/module.h>
     17#include <linux/of.h>
     18#include <linux/of_device.h>
     19#include <linux/platform_device.h>
     20#include <linux/regmap.h>
     21
     22#include <sound/soc.h>
     23#include <sound/soc-dapm.h>
     24#include <sound/tlv.h>
     25
     26#include "sun8i-adda-pr-regmap.h"
     27
     28/* Codec analog control register offsets and bit fields */
     29#define SUN50I_ADDA_HP_CTRL		0x00
     30#define SUN50I_ADDA_HP_CTRL_PA_CLK_GATE		7
     31#define SUN50I_ADDA_HP_CTRL_HPPA_EN		6
     32#define SUN50I_ADDA_HP_CTRL_HPVOL		0
     33
     34#define SUN50I_ADDA_OL_MIX_CTRL		0x01
     35#define SUN50I_ADDA_OL_MIX_CTRL_MIC1		6
     36#define SUN50I_ADDA_OL_MIX_CTRL_MIC2		5
     37#define SUN50I_ADDA_OL_MIX_CTRL_PHONE		4
     38#define SUN50I_ADDA_OL_MIX_CTRL_PHONEN		3
     39#define SUN50I_ADDA_OL_MIX_CTRL_LINEINL		2
     40#define SUN50I_ADDA_OL_MIX_CTRL_DACL		1
     41#define SUN50I_ADDA_OL_MIX_CTRL_DACR		0
     42
     43#define SUN50I_ADDA_OR_MIX_CTRL		0x02
     44#define SUN50I_ADDA_OR_MIX_CTRL_MIC1		6
     45#define SUN50I_ADDA_OR_MIX_CTRL_MIC2		5
     46#define SUN50I_ADDA_OR_MIX_CTRL_PHONE		4
     47#define SUN50I_ADDA_OR_MIX_CTRL_PHONEP		3
     48#define SUN50I_ADDA_OR_MIX_CTRL_LINEINR		2
     49#define SUN50I_ADDA_OR_MIX_CTRL_DACR		1
     50#define SUN50I_ADDA_OR_MIX_CTRL_DACL		0
     51
     52#define SUN50I_ADDA_EARPIECE_CTRL0	0x03
     53#define SUN50I_ADDA_EARPIECE_CTRL0_EAR_RAMP_TIME	4
     54#define SUN50I_ADDA_EARPIECE_CTRL0_ESPSR		0
     55
     56#define SUN50I_ADDA_EARPIECE_CTRL1	0x04
     57#define SUN50I_ADDA_EARPIECE_CTRL1_ESPPA_EN	7
     58#define SUN50I_ADDA_EARPIECE_CTRL1_ESPPA_MUTE	6
     59#define SUN50I_ADDA_EARPIECE_CTRL1_ESP_VOL	0
     60
     61#define SUN50I_ADDA_LINEOUT_CTRL0	0x05
     62#define SUN50I_ADDA_LINEOUT_CTRL0_LEN		7
     63#define SUN50I_ADDA_LINEOUT_CTRL0_REN		6
     64#define SUN50I_ADDA_LINEOUT_CTRL0_LSRC_SEL	5
     65#define SUN50I_ADDA_LINEOUT_CTRL0_RSRC_SEL	4
     66
     67#define SUN50I_ADDA_LINEOUT_CTRL1	0x06
     68#define SUN50I_ADDA_LINEOUT_CTRL1_VOL		0
     69
     70#define SUN50I_ADDA_MIC1_CTRL		0x07
     71#define SUN50I_ADDA_MIC1_CTRL_MIC1G		4
     72#define SUN50I_ADDA_MIC1_CTRL_MIC1AMPEN		3
     73#define SUN50I_ADDA_MIC1_CTRL_MIC1BOOST		0
     74
     75#define SUN50I_ADDA_MIC2_CTRL		0x08
     76#define SUN50I_ADDA_MIC2_CTRL_MIC2G		4
     77#define SUN50I_ADDA_MIC2_CTRL_MIC2AMPEN		3
     78#define SUN50I_ADDA_MIC2_CTRL_MIC2BOOST		0
     79
     80#define SUN50I_ADDA_LINEIN_CTRL		0x09
     81#define SUN50I_ADDA_LINEIN_CTRL_LINEING		0
     82
     83#define SUN50I_ADDA_MIX_DAC_CTRL	0x0a
     84#define SUN50I_ADDA_MIX_DAC_CTRL_DACAREN	7
     85#define SUN50I_ADDA_MIX_DAC_CTRL_DACALEN	6
     86#define SUN50I_ADDA_MIX_DAC_CTRL_RMIXEN		5
     87#define SUN50I_ADDA_MIX_DAC_CTRL_LMIXEN		4
     88#define SUN50I_ADDA_MIX_DAC_CTRL_RHPPAMUTE	3
     89#define SUN50I_ADDA_MIX_DAC_CTRL_LHPPAMUTE	2
     90#define SUN50I_ADDA_MIX_DAC_CTRL_RHPIS		1
     91#define SUN50I_ADDA_MIX_DAC_CTRL_LHPIS		0
     92
     93#define SUN50I_ADDA_L_ADCMIX_SRC	0x0b
     94#define SUN50I_ADDA_L_ADCMIX_SRC_MIC1		6
     95#define SUN50I_ADDA_L_ADCMIX_SRC_MIC2		5
     96#define SUN50I_ADDA_L_ADCMIX_SRC_PHONE		4
     97#define SUN50I_ADDA_L_ADCMIX_SRC_PHONEN		3
     98#define SUN50I_ADDA_L_ADCMIX_SRC_LINEINL	2
     99#define SUN50I_ADDA_L_ADCMIX_SRC_OMIXRL		1
    100#define SUN50I_ADDA_L_ADCMIX_SRC_OMIXRR		0
    101
    102#define SUN50I_ADDA_R_ADCMIX_SRC	0x0c
    103#define SUN50I_ADDA_R_ADCMIX_SRC_MIC1		6
    104#define SUN50I_ADDA_R_ADCMIX_SRC_MIC2		5
    105#define SUN50I_ADDA_R_ADCMIX_SRC_PHONE		4
    106#define SUN50I_ADDA_R_ADCMIX_SRC_PHONEP		3
    107#define SUN50I_ADDA_R_ADCMIX_SRC_LINEINR	2
    108#define SUN50I_ADDA_R_ADCMIX_SRC_OMIXR		1
    109#define SUN50I_ADDA_R_ADCMIX_SRC_OMIXL		0
    110
    111#define SUN50I_ADDA_ADC_CTRL		0x0d
    112#define SUN50I_ADDA_ADC_CTRL_ADCREN		7
    113#define SUN50I_ADDA_ADC_CTRL_ADCLEN		6
    114#define SUN50I_ADDA_ADC_CTRL_ADCG		0
    115
    116#define SUN50I_ADDA_HS_MBIAS_CTRL	0x0e
    117#define SUN50I_ADDA_HS_MBIAS_CTRL_MMICBIASEN	7
    118
    119#define SUN50I_ADDA_JACK_MIC_CTRL	0x1d
    120#define SUN50I_ADDA_JACK_MIC_CTRL_HMICBIASEN	5
    121
    122/* mixer controls */
    123static const struct snd_kcontrol_new sun50i_a64_codec_mixer_controls[] = {
    124	SOC_DAPM_DOUBLE_R("Mic1 Playback Switch",
    125			  SUN50I_ADDA_OL_MIX_CTRL,
    126			  SUN50I_ADDA_OR_MIX_CTRL,
    127			  SUN50I_ADDA_OL_MIX_CTRL_MIC1, 1, 0),
    128	SOC_DAPM_DOUBLE_R("Mic2 Playback Switch",
    129			  SUN50I_ADDA_OL_MIX_CTRL,
    130			  SUN50I_ADDA_OR_MIX_CTRL,
    131			  SUN50I_ADDA_OL_MIX_CTRL_MIC2, 1, 0),
    132	SOC_DAPM_DOUBLE_R("Line In Playback Switch",
    133			  SUN50I_ADDA_OL_MIX_CTRL,
    134			  SUN50I_ADDA_OR_MIX_CTRL,
    135			  SUN50I_ADDA_OL_MIX_CTRL_LINEINL, 1, 0),
    136	SOC_DAPM_DOUBLE_R("DAC Playback Switch",
    137			  SUN50I_ADDA_OL_MIX_CTRL,
    138			  SUN50I_ADDA_OR_MIX_CTRL,
    139			  SUN50I_ADDA_OL_MIX_CTRL_DACL, 1, 0),
    140	SOC_DAPM_DOUBLE_R("DAC Reversed Playback Switch",
    141			  SUN50I_ADDA_OL_MIX_CTRL,
    142			  SUN50I_ADDA_OR_MIX_CTRL,
    143			  SUN50I_ADDA_OL_MIX_CTRL_DACR, 1, 0),
    144};
    145
    146/* ADC mixer controls */
    147static const struct snd_kcontrol_new sun50i_codec_adc_mixer_controls[] = {
    148	SOC_DAPM_DOUBLE_R("Mic1 Capture Switch",
    149			  SUN50I_ADDA_L_ADCMIX_SRC,
    150			  SUN50I_ADDA_R_ADCMIX_SRC,
    151			  SUN50I_ADDA_L_ADCMIX_SRC_MIC1, 1, 0),
    152	SOC_DAPM_DOUBLE_R("Mic2 Capture Switch",
    153			  SUN50I_ADDA_L_ADCMIX_SRC,
    154			  SUN50I_ADDA_R_ADCMIX_SRC,
    155			  SUN50I_ADDA_L_ADCMIX_SRC_MIC2, 1, 0),
    156	SOC_DAPM_DOUBLE_R("Line In Capture Switch",
    157			  SUN50I_ADDA_L_ADCMIX_SRC,
    158			  SUN50I_ADDA_R_ADCMIX_SRC,
    159			  SUN50I_ADDA_L_ADCMIX_SRC_LINEINL, 1, 0),
    160	SOC_DAPM_DOUBLE_R("Mixer Capture Switch",
    161			  SUN50I_ADDA_L_ADCMIX_SRC,
    162			  SUN50I_ADDA_R_ADCMIX_SRC,
    163			  SUN50I_ADDA_L_ADCMIX_SRC_OMIXRL, 1, 0),
    164	SOC_DAPM_DOUBLE_R("Mixer Reversed Capture Switch",
    165			  SUN50I_ADDA_L_ADCMIX_SRC,
    166			  SUN50I_ADDA_R_ADCMIX_SRC,
    167			  SUN50I_ADDA_L_ADCMIX_SRC_OMIXRR, 1, 0),
    168};
    169
    170static const DECLARE_TLV_DB_SCALE(sun50i_codec_out_mixer_pregain_scale,
    171				  -450, 150, 0);
    172static const DECLARE_TLV_DB_RANGE(sun50i_codec_mic_gain_scale,
    173	0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
    174	1, 7, TLV_DB_SCALE_ITEM(2400, 300, 0),
    175);
    176
    177static const DECLARE_TLV_DB_SCALE(sun50i_codec_hp_vol_scale, -6300, 100, 1);
    178
    179static const DECLARE_TLV_DB_RANGE(sun50i_codec_lineout_vol_scale,
    180	0, 1, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
    181	2, 31, TLV_DB_SCALE_ITEM(-4350, 150, 0),
    182);
    183
    184static const DECLARE_TLV_DB_RANGE(sun50i_codec_earpiece_vol_scale,
    185	0, 1, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
    186	2, 31, TLV_DB_SCALE_ITEM(-4350, 150, 0),
    187);
    188
    189/* volume / mute controls */
    190static const struct snd_kcontrol_new sun50i_a64_codec_controls[] = {
    191	SOC_SINGLE_TLV("Headphone Playback Volume",
    192		       SUN50I_ADDA_HP_CTRL,
    193		       SUN50I_ADDA_HP_CTRL_HPVOL, 0x3f, 0,
    194		       sun50i_codec_hp_vol_scale),
    195
    196	/* Mixer pre-gain */
    197	SOC_SINGLE_TLV("Mic1 Playback Volume", SUN50I_ADDA_MIC1_CTRL,
    198		       SUN50I_ADDA_MIC1_CTRL_MIC1G,
    199		       0x7, 0, sun50i_codec_out_mixer_pregain_scale),
    200
    201	/* Microphone Amp boost gain */
    202	SOC_SINGLE_TLV("Mic1 Boost Volume", SUN50I_ADDA_MIC1_CTRL,
    203		       SUN50I_ADDA_MIC1_CTRL_MIC1BOOST, 0x7, 0,
    204		       sun50i_codec_mic_gain_scale),
    205
    206	/* Mixer pre-gain */
    207	SOC_SINGLE_TLV("Mic2 Playback Volume",
    208		       SUN50I_ADDA_MIC2_CTRL, SUN50I_ADDA_MIC2_CTRL_MIC2G,
    209		       0x7, 0, sun50i_codec_out_mixer_pregain_scale),
    210
    211	/* Microphone Amp boost gain */
    212	SOC_SINGLE_TLV("Mic2 Boost Volume", SUN50I_ADDA_MIC2_CTRL,
    213		       SUN50I_ADDA_MIC2_CTRL_MIC2BOOST, 0x7, 0,
    214		       sun50i_codec_mic_gain_scale),
    215
    216	/* ADC */
    217	SOC_SINGLE_TLV("ADC Gain Capture Volume", SUN50I_ADDA_ADC_CTRL,
    218		       SUN50I_ADDA_ADC_CTRL_ADCG, 0x7, 0,
    219		       sun50i_codec_out_mixer_pregain_scale),
    220
    221	/* Mixer pre-gain */
    222	SOC_SINGLE_TLV("Line In Playback Volume", SUN50I_ADDA_LINEIN_CTRL,
    223		       SUN50I_ADDA_LINEIN_CTRL_LINEING,
    224		       0x7, 0, sun50i_codec_out_mixer_pregain_scale),
    225
    226	SOC_SINGLE_TLV("Line Out Playback Volume",
    227		       SUN50I_ADDA_LINEOUT_CTRL1,
    228		       SUN50I_ADDA_LINEOUT_CTRL1_VOL, 0x1f, 0,
    229		       sun50i_codec_lineout_vol_scale),
    230
    231	SOC_SINGLE_TLV("Earpiece Playback Volume",
    232		       SUN50I_ADDA_EARPIECE_CTRL1,
    233		       SUN50I_ADDA_EARPIECE_CTRL1_ESP_VOL, 0x1f, 0,
    234		       sun50i_codec_earpiece_vol_scale),
    235};
    236
    237static const char * const sun50i_codec_hp_src_enum_text[] = {
    238	"DAC", "Mixer",
    239};
    240
    241static SOC_ENUM_DOUBLE_DECL(sun50i_codec_hp_src_enum,
    242			    SUN50I_ADDA_MIX_DAC_CTRL,
    243			    SUN50I_ADDA_MIX_DAC_CTRL_LHPIS,
    244			    SUN50I_ADDA_MIX_DAC_CTRL_RHPIS,
    245			    sun50i_codec_hp_src_enum_text);
    246
    247static const struct snd_kcontrol_new sun50i_codec_hp_src[] = {
    248	SOC_DAPM_ENUM("Headphone Source Playback Route",
    249		      sun50i_codec_hp_src_enum),
    250};
    251
    252static const struct snd_kcontrol_new sun50i_codec_hp_switch =
    253	SOC_DAPM_DOUBLE("Headphone Playback Switch",
    254			SUN50I_ADDA_MIX_DAC_CTRL,
    255			SUN50I_ADDA_MIX_DAC_CTRL_LHPPAMUTE,
    256			SUN50I_ADDA_MIX_DAC_CTRL_RHPPAMUTE, 1, 0);
    257
    258static const char * const sun50i_codec_lineout_src_enum_text[] = {
    259	"Stereo", "Mono Differential",
    260};
    261
    262static SOC_ENUM_DOUBLE_DECL(sun50i_codec_lineout_src_enum,
    263			    SUN50I_ADDA_LINEOUT_CTRL0,
    264			    SUN50I_ADDA_LINEOUT_CTRL0_LSRC_SEL,
    265			    SUN50I_ADDA_LINEOUT_CTRL0_RSRC_SEL,
    266			    sun50i_codec_lineout_src_enum_text);
    267
    268static const struct snd_kcontrol_new sun50i_codec_lineout_src[] = {
    269	SOC_DAPM_ENUM("Line Out Source Playback Route",
    270		      sun50i_codec_lineout_src_enum),
    271};
    272
    273static const struct snd_kcontrol_new sun50i_codec_lineout_switch =
    274	SOC_DAPM_DOUBLE("Line Out Playback Switch",
    275			SUN50I_ADDA_LINEOUT_CTRL0,
    276			SUN50I_ADDA_LINEOUT_CTRL0_LEN,
    277			SUN50I_ADDA_LINEOUT_CTRL0_REN, 1, 0);
    278
    279static const char * const sun50i_codec_earpiece_src_enum_text[] = {
    280	"DACR", "DACL", "Right Mixer", "Left Mixer",
    281};
    282
    283static SOC_ENUM_SINGLE_DECL(sun50i_codec_earpiece_src_enum,
    284			    SUN50I_ADDA_EARPIECE_CTRL0,
    285			    SUN50I_ADDA_EARPIECE_CTRL0_ESPSR,
    286			    sun50i_codec_earpiece_src_enum_text);
    287
    288static const struct snd_kcontrol_new sun50i_codec_earpiece_src[] = {
    289	SOC_DAPM_ENUM("Earpiece Source Playback Route",
    290		      sun50i_codec_earpiece_src_enum),
    291};
    292
    293static const struct snd_kcontrol_new sun50i_codec_earpiece_switch[] = {
    294	SOC_DAPM_SINGLE("Earpiece Playback Switch",
    295			SUN50I_ADDA_EARPIECE_CTRL1,
    296			SUN50I_ADDA_EARPIECE_CTRL1_ESPPA_MUTE, 1, 0),
    297};
    298
    299static const struct snd_soc_dapm_widget sun50i_a64_codec_widgets[] = {
    300	/* DAC */
    301	SND_SOC_DAPM_DAC("Left DAC", NULL, SUN50I_ADDA_MIX_DAC_CTRL,
    302			 SUN50I_ADDA_MIX_DAC_CTRL_DACALEN, 0),
    303	SND_SOC_DAPM_DAC("Right DAC", NULL, SUN50I_ADDA_MIX_DAC_CTRL,
    304			 SUN50I_ADDA_MIX_DAC_CTRL_DACAREN, 0),
    305	/* ADC */
    306	SND_SOC_DAPM_ADC("Left ADC", NULL, SUN50I_ADDA_ADC_CTRL,
    307			 SUN50I_ADDA_ADC_CTRL_ADCLEN, 0),
    308	SND_SOC_DAPM_ADC("Right ADC", NULL, SUN50I_ADDA_ADC_CTRL,
    309			 SUN50I_ADDA_ADC_CTRL_ADCREN, 0),
    310	/*
    311	 * Due to this component and the codec belonging to separate DAPM
    312	 * contexts, we need to manually link the above widgets to their
    313	 * stream widgets at the card level.
    314	 */
    315
    316	SND_SOC_DAPM_REGULATOR_SUPPLY("cpvdd", 0, 0),
    317	SND_SOC_DAPM_MUX("Left Headphone Source",
    318			 SND_SOC_NOPM, 0, 0, sun50i_codec_hp_src),
    319	SND_SOC_DAPM_MUX("Right Headphone Source",
    320			 SND_SOC_NOPM, 0, 0, sun50i_codec_hp_src),
    321	SND_SOC_DAPM_SWITCH("Left Headphone Switch",
    322			    SND_SOC_NOPM, 0, 0, &sun50i_codec_hp_switch),
    323	SND_SOC_DAPM_SWITCH("Right Headphone Switch",
    324			    SND_SOC_NOPM, 0, 0, &sun50i_codec_hp_switch),
    325	SND_SOC_DAPM_OUT_DRV("Left Headphone Amp",
    326			     SND_SOC_NOPM, 0, 0, NULL, 0),
    327	SND_SOC_DAPM_OUT_DRV("Right Headphone Amp",
    328			     SND_SOC_NOPM, 0, 0, NULL, 0),
    329	SND_SOC_DAPM_SUPPLY("Headphone Amp", SUN50I_ADDA_HP_CTRL,
    330			     SUN50I_ADDA_HP_CTRL_HPPA_EN, 0, NULL, 0),
    331	SND_SOC_DAPM_OUTPUT("HP"),
    332
    333	SND_SOC_DAPM_MUX("Left Line Out Source",
    334			 SND_SOC_NOPM, 0, 0, sun50i_codec_lineout_src),
    335	SND_SOC_DAPM_MUX("Right Line Out Source",
    336			 SND_SOC_NOPM, 0, 0, sun50i_codec_lineout_src),
    337	SND_SOC_DAPM_SWITCH("Left Line Out Switch",
    338			    SND_SOC_NOPM, 0, 0, &sun50i_codec_lineout_switch),
    339	SND_SOC_DAPM_SWITCH("Right Line Out Switch",
    340			    SND_SOC_NOPM, 0, 0, &sun50i_codec_lineout_switch),
    341	SND_SOC_DAPM_OUTPUT("LINEOUT"),
    342
    343	SND_SOC_DAPM_MUX("Earpiece Source Playback Route",
    344			 SND_SOC_NOPM, 0, 0, sun50i_codec_earpiece_src),
    345	SOC_MIXER_NAMED_CTL_ARRAY("Earpiece Switch",
    346				  SND_SOC_NOPM, 0, 0,
    347				  sun50i_codec_earpiece_switch),
    348	SND_SOC_DAPM_OUT_DRV("Earpiece Amp", SUN50I_ADDA_EARPIECE_CTRL1,
    349			     SUN50I_ADDA_EARPIECE_CTRL1_ESPPA_EN, 0, NULL, 0),
    350	SND_SOC_DAPM_OUTPUT("EARPIECE"),
    351
    352	/* Microphone inputs */
    353	SND_SOC_DAPM_INPUT("MIC1"),
    354
    355	/* Microphone Bias */
    356	SND_SOC_DAPM_SUPPLY("MBIAS", SUN50I_ADDA_HS_MBIAS_CTRL,
    357			    SUN50I_ADDA_HS_MBIAS_CTRL_MMICBIASEN,
    358			    0, NULL, 0),
    359
    360	/* Mic input path */
    361	SND_SOC_DAPM_PGA("Mic1 Amplifier", SUN50I_ADDA_MIC1_CTRL,
    362			 SUN50I_ADDA_MIC1_CTRL_MIC1AMPEN, 0, NULL, 0),
    363
    364	/* Microphone input */
    365	SND_SOC_DAPM_INPUT("MIC2"),
    366
    367	/* Microphone Bias */
    368	SND_SOC_DAPM_SUPPLY("HBIAS", SUN50I_ADDA_JACK_MIC_CTRL,
    369			    SUN50I_ADDA_JACK_MIC_CTRL_HMICBIASEN,
    370			    0, NULL, 0),
    371
    372	/* Mic input path */
    373	SND_SOC_DAPM_PGA("Mic2 Amplifier", SUN50I_ADDA_MIC2_CTRL,
    374			 SUN50I_ADDA_MIC2_CTRL_MIC2AMPEN, 0, NULL, 0),
    375
    376	/* Line input */
    377	SND_SOC_DAPM_INPUT("LINEIN"),
    378
    379	/* Mixers */
    380	SND_SOC_DAPM_MIXER("Left Mixer", SUN50I_ADDA_MIX_DAC_CTRL,
    381			   SUN50I_ADDA_MIX_DAC_CTRL_LMIXEN, 0,
    382			   sun50i_a64_codec_mixer_controls,
    383			   ARRAY_SIZE(sun50i_a64_codec_mixer_controls)),
    384	SND_SOC_DAPM_MIXER("Right Mixer", SUN50I_ADDA_MIX_DAC_CTRL,
    385			   SUN50I_ADDA_MIX_DAC_CTRL_RMIXEN, 0,
    386			   sun50i_a64_codec_mixer_controls,
    387			   ARRAY_SIZE(sun50i_a64_codec_mixer_controls)),
    388	SND_SOC_DAPM_MIXER("Left ADC Mixer", SND_SOC_NOPM, 0, 0,
    389			   sun50i_codec_adc_mixer_controls,
    390			   ARRAY_SIZE(sun50i_codec_adc_mixer_controls)),
    391	SND_SOC_DAPM_MIXER("Right ADC Mixer", SND_SOC_NOPM, 0, 0,
    392			   sun50i_codec_adc_mixer_controls,
    393			   ARRAY_SIZE(sun50i_codec_adc_mixer_controls)),
    394};
    395
    396static const struct snd_soc_dapm_route sun50i_a64_codec_routes[] = {
    397	/* Left Mixer Routes */
    398	{ "Left Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
    399	{ "Left Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
    400	{ "Left Mixer", "Line In Playback Switch", "LINEIN" },
    401	{ "Left Mixer", "DAC Playback Switch", "Left DAC" },
    402	{ "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" },
    403
    404	/* Right Mixer Routes */
    405	{ "Right Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
    406	{ "Right Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
    407	{ "Right Mixer", "Line In Playback Switch", "LINEIN" },
    408	{ "Right Mixer", "DAC Playback Switch", "Right DAC" },
    409	{ "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" },
    410
    411	/* Left ADC Mixer Routes */
    412	{ "Left ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },
    413	{ "Left ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
    414	{ "Left ADC Mixer", "Line In Capture Switch", "LINEIN" },
    415	{ "Left ADC Mixer", "Mixer Capture Switch", "Left Mixer" },
    416	{ "Left ADC Mixer", "Mixer Reversed Capture Switch", "Right Mixer" },
    417
    418	/* Right ADC Mixer Routes */
    419	{ "Right ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },
    420	{ "Right ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
    421	{ "Right ADC Mixer", "Line In Capture Switch", "LINEIN" },
    422	{ "Right ADC Mixer", "Mixer Capture Switch", "Right Mixer" },
    423	{ "Right ADC Mixer", "Mixer Reversed Capture Switch", "Left Mixer" },
    424
    425	/* ADC Routes */
    426	{ "Left ADC", NULL, "Left ADC Mixer" },
    427	{ "Right ADC", NULL, "Right ADC Mixer" },
    428
    429	/* Headphone Routes */
    430	{ "Left Headphone Source", "DAC", "Left DAC" },
    431	{ "Left Headphone Source", "Mixer", "Left Mixer" },
    432	{ "Left Headphone Switch", "Headphone Playback Switch", "Left Headphone Source" },
    433	{ "Left Headphone Amp", NULL, "Left Headphone Switch" },
    434	{ "Left Headphone Amp", NULL, "Headphone Amp" },
    435	{ "HP", NULL, "Left Headphone Amp" },
    436
    437	{ "Right Headphone Source", "DAC", "Right DAC" },
    438	{ "Right Headphone Source", "Mixer", "Right Mixer" },
    439	{ "Right Headphone Switch", "Headphone Playback Switch", "Right Headphone Source" },
    440	{ "Right Headphone Amp", NULL, "Right Headphone Switch" },
    441	{ "Right Headphone Amp", NULL, "Headphone Amp" },
    442	{ "HP", NULL, "Right Headphone Amp" },
    443
    444	{ "Headphone Amp", NULL, "cpvdd" },
    445
    446	/* Microphone Routes */
    447	{ "Mic1 Amplifier", NULL, "MIC1"},
    448
    449	/* Microphone Routes */
    450	{ "Mic2 Amplifier", NULL, "MIC2"},
    451
    452	/* Line-out Routes */
    453	{ "Left Line Out Source", "Stereo", "Left Mixer" },
    454	{ "Left Line Out Source", "Mono Differential", "Left Mixer" },
    455	{ "Left Line Out Source", "Mono Differential", "Right Mixer" },
    456	{ "Left Line Out Switch", "Line Out Playback Switch", "Left Line Out Source" },
    457	{ "LINEOUT", NULL, "Left Line Out Switch" },
    458
    459	{ "Right Line Out Switch", "Line Out Playback Switch", "Right Mixer" },
    460	{ "Right Line Out Source", "Stereo", "Right Line Out Switch" },
    461	{ "Right Line Out Source", "Mono Differential", "Left Line Out Switch" },
    462	{ "LINEOUT", NULL, "Right Line Out Source" },
    463
    464	/* Earpiece Routes */
    465	{ "Earpiece Source Playback Route", "DACL", "Left DAC" },
    466	{ "Earpiece Source Playback Route", "DACR", "Right DAC" },
    467	{ "Earpiece Source Playback Route", "Left Mixer", "Left Mixer" },
    468	{ "Earpiece Source Playback Route", "Right Mixer", "Right Mixer" },
    469	{ "Earpiece Switch", "Earpiece Playback Switch", "Earpiece Source Playback Route" },
    470	{ "Earpiece Amp", NULL, "Earpiece Switch" },
    471	{ "EARPIECE", NULL, "Earpiece Amp" },
    472};
    473
    474static int sun50i_a64_codec_suspend(struct snd_soc_component *component)
    475{
    476	return regmap_update_bits(component->regmap, SUN50I_ADDA_HP_CTRL,
    477				  BIT(SUN50I_ADDA_HP_CTRL_PA_CLK_GATE),
    478				  BIT(SUN50I_ADDA_HP_CTRL_PA_CLK_GATE));
    479}
    480
    481static int sun50i_a64_codec_resume(struct snd_soc_component *component)
    482{
    483	return regmap_update_bits(component->regmap, SUN50I_ADDA_HP_CTRL,
    484				  BIT(SUN50I_ADDA_HP_CTRL_PA_CLK_GATE), 0);
    485}
    486
    487static const struct snd_soc_component_driver sun50i_codec_analog_cmpnt_drv = {
    488	.controls		= sun50i_a64_codec_controls,
    489	.num_controls		= ARRAY_SIZE(sun50i_a64_codec_controls),
    490	.dapm_widgets		= sun50i_a64_codec_widgets,
    491	.num_dapm_widgets	= ARRAY_SIZE(sun50i_a64_codec_widgets),
    492	.dapm_routes		= sun50i_a64_codec_routes,
    493	.num_dapm_routes	= ARRAY_SIZE(sun50i_a64_codec_routes),
    494	.suspend		= sun50i_a64_codec_suspend,
    495	.resume			= sun50i_a64_codec_resume,
    496};
    497
    498static const struct of_device_id sun50i_codec_analog_of_match[] = {
    499	{
    500		.compatible = "allwinner,sun50i-a64-codec-analog",
    501	},
    502	{}
    503};
    504MODULE_DEVICE_TABLE(of, sun50i_codec_analog_of_match);
    505
    506static int sun50i_codec_analog_probe(struct platform_device *pdev)
    507{
    508	struct regmap *regmap;
    509	void __iomem *base;
    510
    511	base = devm_platform_ioremap_resource(pdev, 0);
    512	if (IS_ERR(base)) {
    513		dev_err(&pdev->dev, "Failed to map the registers\n");
    514		return PTR_ERR(base);
    515	}
    516
    517	regmap = sun8i_adda_pr_regmap_init(&pdev->dev, base);
    518	if (IS_ERR(regmap)) {
    519		dev_err(&pdev->dev, "Failed to create regmap\n");
    520		return PTR_ERR(regmap);
    521	}
    522
    523	return devm_snd_soc_register_component(&pdev->dev,
    524					       &sun50i_codec_analog_cmpnt_drv,
    525					       NULL, 0);
    526}
    527
    528static struct platform_driver sun50i_codec_analog_driver = {
    529	.driver = {
    530		.name = "sun50i-codec-analog",
    531		.of_match_table = sun50i_codec_analog_of_match,
    532	},
    533	.probe = sun50i_codec_analog_probe,
    534};
    535module_platform_driver(sun50i_codec_analog_driver);
    536
    537MODULE_DESCRIPTION("Allwinner internal codec analog controls driver for A64");
    538MODULE_AUTHOR("Vasily Khoruzhick <anarsoul@gmail.com>");
    539MODULE_LICENSE("GPL");
    540MODULE_ALIAS("platform:sun50i-codec-analog");