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