linux/sound/soc/sunxi/sun8i-codec.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * This driver supports the digital controls for the internal codec
   4 * found in Allwinner's A33 SoCs.
   5 *
   6 * (C) Copyright 2010-2016
   7 * Reuuimlla Technology Co., Ltd. <www.reuuimllatech.com>
   8 * huangxin <huangxin@Reuuimllatech.com>
   9 * Mylène Josserand <mylene.josserand@free-electrons.com>
  10 */
  11
  12#include <linux/module.h>
  13#include <linux/delay.h>
  14#include <linux/clk.h>
  15#include <linux/io.h>
  16#include <linux/of_device.h>
  17#include <linux/pm_runtime.h>
  18#include <linux/regmap.h>
  19#include <linux/log2.h>
  20
  21#include <sound/pcm_params.h>
  22#include <sound/soc.h>
  23#include <sound/soc-dapm.h>
  24
  25#define SUN8I_SYSCLK_CTL                                0x00c
  26#define SUN8I_SYSCLK_CTL_AIF1CLK_ENA                    11
  27#define SUN8I_SYSCLK_CTL_AIF1CLK_SRC_PLL                9
  28#define SUN8I_SYSCLK_CTL_AIF1CLK_SRC                    8
  29#define SUN8I_SYSCLK_CTL_SYSCLK_ENA                     3
  30#define SUN8I_SYSCLK_CTL_SYSCLK_SRC                     0
  31#define SUN8I_MOD_CLK_ENA                               0x010
  32#define SUN8I_MOD_CLK_ENA_AIF1                          15
  33#define SUN8I_MOD_CLK_ENA_ADC                           3
  34#define SUN8I_MOD_CLK_ENA_DAC                           2
  35#define SUN8I_MOD_RST_CTL                               0x014
  36#define SUN8I_MOD_RST_CTL_AIF1                          15
  37#define SUN8I_MOD_RST_CTL_ADC                           3
  38#define SUN8I_MOD_RST_CTL_DAC                           2
  39#define SUN8I_SYS_SR_CTRL                               0x018
  40#define SUN8I_SYS_SR_CTRL_AIF1_FS                       12
  41#define SUN8I_SYS_SR_CTRL_AIF2_FS                       8
  42#define SUN8I_AIF1CLK_CTRL                              0x040
  43#define SUN8I_AIF1CLK_CTRL_AIF1_MSTR_MOD                15
  44#define SUN8I_AIF1CLK_CTRL_AIF1_BCLK_INV                14
  45#define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV                13
  46#define SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV                9
  47#define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV                6
  48#define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ                4
  49#define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_16             (1 << 4)
  50#define SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT                2
  51#define SUN8I_AIF1_ADCDAT_CTRL                          0x044
  52#define SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0L_ENA            15
  53#define SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_ENA            14
  54#define SUN8I_AIF1_DACDAT_CTRL                          0x048
  55#define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_ENA            15
  56#define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA            14
  57#define SUN8I_AIF1_MXR_SRC                              0x04c
  58#define SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF1DA0L        15
  59#define SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF2DACL        14
  60#define SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_ADCL            13
  61#define SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF2DACR        12
  62#define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF1DA0R        11
  63#define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACR        10
  64#define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_ADCR            9
  65#define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACL        8
  66#define SUN8I_ADC_DIG_CTRL                              0x100
  67#define SUN8I_ADC_DIG_CTRL_ENAD                         15
  68#define SUN8I_ADC_DIG_CTRL_ADOUT_DTS                    2
  69#define SUN8I_ADC_DIG_CTRL_ADOUT_DLY                    1
  70#define SUN8I_DAC_DIG_CTRL                              0x120
  71#define SUN8I_DAC_DIG_CTRL_ENDA                         15
  72#define SUN8I_DAC_MXR_SRC                               0x130
  73#define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA0L         15
  74#define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA1L         14
  75#define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF2DACL         13
  76#define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_ADCL             12
  77#define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA0R         11
  78#define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA1R         10
  79#define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF2DACR         9
  80#define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_ADCR             8
  81
  82#define SUN8I_SYS_SR_CTRL_AIF1_FS_MASK          GENMASK(15, 12)
  83#define SUN8I_SYS_SR_CTRL_AIF2_FS_MASK          GENMASK(11, 8)
  84#define SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV_MASK   GENMASK(12, 9)
  85#define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_MASK   GENMASK(8, 6)
  86#define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_MASK   GENMASK(5, 4)
  87#define SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT_MASK   GENMASK(3, 2)
  88
  89struct sun8i_codec_quirks {
  90        bool legacy_widgets     : 1;
  91        bool lrck_inversion     : 1;
  92};
  93
  94struct sun8i_codec {
  95        struct regmap                   *regmap;
  96        struct clk                      *clk_module;
  97        const struct sun8i_codec_quirks *quirks;
  98};
  99
 100static int sun8i_codec_runtime_resume(struct device *dev)
 101{
 102        struct sun8i_codec *scodec = dev_get_drvdata(dev);
 103        int ret;
 104
 105        regcache_cache_only(scodec->regmap, false);
 106
 107        ret = regcache_sync(scodec->regmap);
 108        if (ret) {
 109                dev_err(dev, "Failed to sync regmap cache\n");
 110                return ret;
 111        }
 112
 113        return 0;
 114}
 115
 116static int sun8i_codec_runtime_suspend(struct device *dev)
 117{
 118        struct sun8i_codec *scodec = dev_get_drvdata(dev);
 119
 120        regcache_cache_only(scodec->regmap, true);
 121        regcache_mark_dirty(scodec->regmap);
 122
 123        return 0;
 124}
 125
 126static int sun8i_codec_get_hw_rate(struct snd_pcm_hw_params *params)
 127{
 128        unsigned int rate = params_rate(params);
 129
 130        switch (rate) {
 131        case 8000:
 132        case 7350:
 133                return 0x0;
 134        case 11025:
 135                return 0x1;
 136        case 12000:
 137                return 0x2;
 138        case 16000:
 139                return 0x3;
 140        case 22050:
 141                return 0x4;
 142        case 24000:
 143                return 0x5;
 144        case 32000:
 145                return 0x6;
 146        case 44100:
 147                return 0x7;
 148        case 48000:
 149                return 0x8;
 150        case 96000:
 151                return 0x9;
 152        case 192000:
 153                return 0xa;
 154        default:
 155                return -EINVAL;
 156        }
 157}
 158
 159static int sun8i_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 160{
 161        struct sun8i_codec *scodec = snd_soc_component_get_drvdata(dai->component);
 162        u32 value;
 163
 164        /* clock masters */
 165        switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
 166        case SND_SOC_DAIFMT_CBS_CFS: /* Codec slave, DAI master */
 167                value = 0x1;
 168                break;
 169        case SND_SOC_DAIFMT_CBM_CFM: /* Codec Master, DAI slave */
 170                value = 0x0;
 171                break;
 172        default:
 173                return -EINVAL;
 174        }
 175        regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
 176                           BIT(SUN8I_AIF1CLK_CTRL_AIF1_MSTR_MOD),
 177                           value << SUN8I_AIF1CLK_CTRL_AIF1_MSTR_MOD);
 178
 179        /* clock inversion */
 180        switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
 181        case SND_SOC_DAIFMT_NB_NF: /* Normal */
 182                value = 0x0;
 183                break;
 184        case SND_SOC_DAIFMT_IB_IF: /* Inversion */
 185                value = 0x1;
 186                break;
 187        default:
 188                return -EINVAL;
 189        }
 190        regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
 191                           BIT(SUN8I_AIF1CLK_CTRL_AIF1_BCLK_INV),
 192                           value << SUN8I_AIF1CLK_CTRL_AIF1_BCLK_INV);
 193
 194        /*
 195         * It appears that the DAI and the codec in the A33 SoC don't
 196         * share the same polarity for the LRCK signal when they mean
 197         * 'normal' and 'inverted' in the datasheet.
 198         *
 199         * Since the DAI here is our regular i2s driver that have been
 200         * tested with way more codecs than just this one, it means
 201         * that the codec probably gets it backward, and we have to
 202         * invert the value here.
 203         */
 204        value ^= scodec->quirks->lrck_inversion;
 205        regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
 206                           BIT(SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV),
 207                           value << SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV);
 208
 209        /* DAI format */
 210        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
 211        case SND_SOC_DAIFMT_I2S:
 212                value = 0x0;
 213                break;
 214        case SND_SOC_DAIFMT_LEFT_J:
 215                value = 0x1;
 216                break;
 217        case SND_SOC_DAIFMT_RIGHT_J:
 218                value = 0x2;
 219                break;
 220        case SND_SOC_DAIFMT_DSP_A:
 221        case SND_SOC_DAIFMT_DSP_B:
 222                value = 0x3;
 223                break;
 224        default:
 225                return -EINVAL;
 226        }
 227        regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
 228                           SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT_MASK,
 229                           value << SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT);
 230
 231        return 0;
 232}
 233
 234struct sun8i_codec_clk_div {
 235        u8      div;
 236        u8      val;
 237};
 238
 239static const struct sun8i_codec_clk_div sun8i_codec_bclk_div[] = {
 240        { .div = 1,     .val = 0 },
 241        { .div = 2,     .val = 1 },
 242        { .div = 4,     .val = 2 },
 243        { .div = 6,     .val = 3 },
 244        { .div = 8,     .val = 4 },
 245        { .div = 12,    .val = 5 },
 246        { .div = 16,    .val = 6 },
 247        { .div = 24,    .val = 7 },
 248        { .div = 32,    .val = 8 },
 249        { .div = 48,    .val = 9 },
 250        { .div = 64,    .val = 10 },
 251        { .div = 96,    .val = 11 },
 252        { .div = 128,   .val = 12 },
 253        { .div = 192,   .val = 13 },
 254};
 255
 256static u8 sun8i_codec_get_bclk_div(struct sun8i_codec *scodec,
 257                                   unsigned int rate,
 258                                   unsigned int word_size)
 259{
 260        unsigned long clk_rate = clk_get_rate(scodec->clk_module);
 261        unsigned int div = clk_rate / rate / word_size / 2;
 262        unsigned int best_val = 0, best_diff = ~0;
 263        int i;
 264
 265        for (i = 0; i < ARRAY_SIZE(sun8i_codec_bclk_div); i++) {
 266                const struct sun8i_codec_clk_div *bdiv = &sun8i_codec_bclk_div[i];
 267                unsigned int diff = abs(bdiv->div - div);
 268
 269                if (diff < best_diff) {
 270                        best_diff = diff;
 271                        best_val = bdiv->val;
 272                }
 273        }
 274
 275        return best_val;
 276}
 277
 278static int sun8i_codec_get_lrck_div(unsigned int channels,
 279                                    unsigned int word_size)
 280{
 281        unsigned int div = word_size * channels;
 282
 283        if (div < 16 || div > 256)
 284                return -EINVAL;
 285
 286        return ilog2(div) - 4;
 287}
 288
 289static int sun8i_codec_hw_params(struct snd_pcm_substream *substream,
 290                                 struct snd_pcm_hw_params *params,
 291                                 struct snd_soc_dai *dai)
 292{
 293        struct sun8i_codec *scodec = snd_soc_component_get_drvdata(dai->component);
 294        int sample_rate, lrck_div;
 295        u8 bclk_div;
 296
 297        /*
 298         * The CPU DAI handles only a sample of 16 bits. Configure the
 299         * codec to handle this type of sample resolution.
 300         */
 301        regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
 302                           SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_MASK,
 303                           SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_16);
 304
 305        bclk_div = sun8i_codec_get_bclk_div(scodec, params_rate(params), 16);
 306        regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
 307                           SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV_MASK,
 308                           bclk_div << SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV);
 309
 310        lrck_div = sun8i_codec_get_lrck_div(params_channels(params),
 311                                            params_physical_width(params));
 312        if (lrck_div < 0)
 313                return lrck_div;
 314
 315        regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
 316                           SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_MASK,
 317                           lrck_div << SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV);
 318
 319        sample_rate = sun8i_codec_get_hw_rate(params);
 320        if (sample_rate < 0)
 321                return sample_rate;
 322
 323        regmap_update_bits(scodec->regmap, SUN8I_SYS_SR_CTRL,
 324                           SUN8I_SYS_SR_CTRL_AIF1_FS_MASK,
 325                           sample_rate << SUN8I_SYS_SR_CTRL_AIF1_FS);
 326        regmap_update_bits(scodec->regmap, SUN8I_SYS_SR_CTRL,
 327                           SUN8I_SYS_SR_CTRL_AIF2_FS_MASK,
 328                           sample_rate << SUN8I_SYS_SR_CTRL_AIF2_FS);
 329
 330        return 0;
 331}
 332
 333static const struct snd_kcontrol_new sun8i_dac_mixer_controls[] = {
 334        SOC_DAPM_DOUBLE("AIF1 Slot 0 Digital DAC Playback Switch",
 335                        SUN8I_DAC_MXR_SRC,
 336                        SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA0L,
 337                        SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA0R, 1, 0),
 338        SOC_DAPM_DOUBLE("AIF1 Slot 1 Digital DAC Playback Switch",
 339                        SUN8I_DAC_MXR_SRC,
 340                        SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA1L,
 341                        SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA1R, 1, 0),
 342        SOC_DAPM_DOUBLE("AIF2 Digital DAC Playback Switch", SUN8I_DAC_MXR_SRC,
 343                        SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF2DACL,
 344                        SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF2DACR, 1, 0),
 345        SOC_DAPM_DOUBLE("ADC Digital DAC Playback Switch", SUN8I_DAC_MXR_SRC,
 346                        SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_ADCL,
 347                        SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_ADCR, 1, 0),
 348};
 349
 350static const struct snd_kcontrol_new sun8i_input_mixer_controls[] = {
 351        SOC_DAPM_DOUBLE("AIF1 Slot 0 Digital ADC Capture Switch",
 352                        SUN8I_AIF1_MXR_SRC,
 353                        SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF1DA0L,
 354                        SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF1DA0R, 1, 0),
 355        SOC_DAPM_DOUBLE("AIF2 Digital ADC Capture Switch", SUN8I_AIF1_MXR_SRC,
 356                        SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF2DACL,
 357                        SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACR, 1, 0),
 358        SOC_DAPM_DOUBLE("AIF1 Data Digital ADC Capture Switch",
 359                        SUN8I_AIF1_MXR_SRC,
 360                        SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_ADCL,
 361                        SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_ADCR, 1, 0),
 362        SOC_DAPM_DOUBLE("AIF2 Inv Digital ADC Capture Switch",
 363                        SUN8I_AIF1_MXR_SRC,
 364                        SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF2DACR,
 365                        SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACL, 1, 0),
 366};
 367
 368static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = {
 369        SND_SOC_DAPM_CLOCK_SUPPLY("mod"),
 370
 371        /* Digital parts of the DACs and ADC */
 372        SND_SOC_DAPM_SUPPLY("DAC", SUN8I_DAC_DIG_CTRL, SUN8I_DAC_DIG_CTRL_ENDA,
 373                            0, NULL, 0),
 374        SND_SOC_DAPM_SUPPLY("ADC", SUN8I_ADC_DIG_CTRL, SUN8I_ADC_DIG_CTRL_ENAD,
 375                            0, NULL, 0),
 376
 377        /* AIF "DAC" Inputs */
 378        SND_SOC_DAPM_AIF_IN("AIF1 DA0L", "Playback", 0,
 379                            SUN8I_AIF1_DACDAT_CTRL,
 380                            SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_ENA, 0),
 381        SND_SOC_DAPM_AIF_IN("AIF1 DA0R", "Playback", 0,
 382                            SUN8I_AIF1_DACDAT_CTRL,
 383                            SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA, 0),
 384
 385        /* AIF "ADC" Outputs */
 386        SND_SOC_DAPM_AIF_IN("AIF1 AD0L", "Capture", 0,
 387                            SUN8I_AIF1_ADCDAT_CTRL,
 388                            SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0L_ENA, 0),
 389        SND_SOC_DAPM_AIF_IN("AIF1 AD0R", "Capture", 0,
 390                            SUN8I_AIF1_ADCDAT_CTRL,
 391                            SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_ENA, 0),
 392
 393        /* ADC Inputs (connected to analog codec DAPM context) */
 394        SND_SOC_DAPM_ADC("ADCL", NULL, SND_SOC_NOPM, 0, 0),
 395        SND_SOC_DAPM_ADC("ADCR", NULL, SND_SOC_NOPM, 0, 0),
 396
 397        /* DAC Outputs (connected to analog codec DAPM context) */
 398        SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0),
 399        SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0),
 400
 401        /* DAC and ADC Mixers */
 402        SOC_MIXER_ARRAY("Left Digital DAC Mixer", SND_SOC_NOPM, 0, 0,
 403                        sun8i_dac_mixer_controls),
 404        SOC_MIXER_ARRAY("Right Digital DAC Mixer", SND_SOC_NOPM, 0, 0,
 405                        sun8i_dac_mixer_controls),
 406        SOC_MIXER_ARRAY("Left Digital ADC Mixer", SND_SOC_NOPM, 0, 0,
 407                        sun8i_input_mixer_controls),
 408        SOC_MIXER_ARRAY("Right Digital ADC Mixer", SND_SOC_NOPM, 0, 0,
 409                        sun8i_input_mixer_controls),
 410
 411        /* Clocks */
 412        SND_SOC_DAPM_SUPPLY("MODCLK AIF1", SUN8I_MOD_CLK_ENA,
 413                            SUN8I_MOD_CLK_ENA_AIF1, 0, NULL, 0),
 414        SND_SOC_DAPM_SUPPLY("MODCLK DAC", SUN8I_MOD_CLK_ENA,
 415                            SUN8I_MOD_CLK_ENA_DAC, 0, NULL, 0),
 416        SND_SOC_DAPM_SUPPLY("MODCLK ADC", SUN8I_MOD_CLK_ENA,
 417                            SUN8I_MOD_CLK_ENA_ADC, 0, NULL, 0),
 418        SND_SOC_DAPM_SUPPLY("AIF1", SUN8I_SYSCLK_CTL,
 419                            SUN8I_SYSCLK_CTL_AIF1CLK_ENA, 0, NULL, 0),
 420        SND_SOC_DAPM_SUPPLY("SYSCLK", SUN8I_SYSCLK_CTL,
 421                            SUN8I_SYSCLK_CTL_SYSCLK_ENA, 0, NULL, 0),
 422
 423        SND_SOC_DAPM_SUPPLY("AIF1 PLL", SUN8I_SYSCLK_CTL,
 424                            SUN8I_SYSCLK_CTL_AIF1CLK_SRC_PLL, 0, NULL, 0),
 425        /* Inversion as 0=AIF1, 1=AIF2 */
 426        SND_SOC_DAPM_SUPPLY("SYSCLK AIF1", SUN8I_SYSCLK_CTL,
 427                            SUN8I_SYSCLK_CTL_SYSCLK_SRC, 1, NULL, 0),
 428
 429        /* Module reset */
 430        SND_SOC_DAPM_SUPPLY("RST AIF1", SUN8I_MOD_RST_CTL,
 431                            SUN8I_MOD_RST_CTL_AIF1, 0, NULL, 0),
 432        SND_SOC_DAPM_SUPPLY("RST DAC", SUN8I_MOD_RST_CTL,
 433                            SUN8I_MOD_RST_CTL_DAC, 0, NULL, 0),
 434        SND_SOC_DAPM_SUPPLY("RST ADC", SUN8I_MOD_RST_CTL,
 435                            SUN8I_MOD_RST_CTL_ADC, 0, NULL, 0),
 436};
 437
 438static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = {
 439        /* Clock Routes */
 440        { "AIF1", NULL, "mod" },
 441
 442        { "AIF1", NULL, "SYSCLK AIF1" },
 443        { "AIF1 PLL", NULL, "AIF1" },
 444        { "SYSCLK", NULL, "AIF1 PLL" },
 445
 446        { "RST AIF1", NULL, "SYSCLK" },
 447        { "MODCLK AIF1", NULL, "RST AIF1" },
 448        { "AIF1 AD0L", NULL, "MODCLK AIF1" },
 449        { "AIF1 AD0R", NULL, "MODCLK AIF1" },
 450        { "AIF1 DA0L", NULL, "MODCLK AIF1" },
 451        { "AIF1 DA0R", NULL, "MODCLK AIF1" },
 452
 453        { "RST DAC", NULL, "SYSCLK" },
 454        { "MODCLK DAC", NULL, "RST DAC" },
 455        { "DAC", NULL, "MODCLK DAC" },
 456        { "DACL", NULL, "DAC" },
 457        { "DACR", NULL, "DAC" },
 458
 459        { "RST ADC", NULL, "SYSCLK" },
 460        { "MODCLK ADC", NULL, "RST ADC" },
 461        { "ADC", NULL, "MODCLK ADC" },
 462        { "ADCL", NULL, "ADC" },
 463        { "ADCR", NULL, "ADC" },
 464
 465        /* DAC Routes */
 466        { "DACL", NULL, "Left Digital DAC Mixer" },
 467        { "DACR", NULL, "Right Digital DAC Mixer" },
 468
 469        /* DAC Mixer Routes */
 470        { "Left Digital DAC Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", "AIF1 DA0L" },
 471        { "Left Digital DAC Mixer", "ADC Digital DAC Playback Switch", "ADCL" },
 472
 473        { "Right Digital DAC Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", "AIF1 DA0R" },
 474        { "Right Digital DAC Mixer", "ADC Digital DAC Playback Switch", "ADCR" },
 475
 476        /* ADC Routes */
 477        { "AIF1 AD0L", NULL, "Left Digital ADC Mixer" },
 478        { "AIF1 AD0R", NULL, "Right Digital ADC Mixer" },
 479
 480        /* ADC Mixer Routes */
 481        { "Left Digital ADC Mixer", "AIF1 Slot 0 Digital ADC Capture Switch", "AIF1 DA0L" },
 482        { "Left Digital ADC Mixer", "AIF1 Data Digital ADC Capture Switch", "ADCL" },
 483
 484        { "Right Digital ADC Mixer", "AIF1 Slot 0 Digital ADC Capture Switch", "AIF1 DA0R" },
 485        { "Right Digital ADC Mixer", "AIF1 Data Digital ADC Capture Switch", "ADCR" },
 486};
 487
 488static const struct snd_soc_dapm_widget sun8i_codec_legacy_widgets[] = {
 489        /* Legacy ADC Inputs (connected to analog codec DAPM context) */
 490        SND_SOC_DAPM_ADC("AIF1 Slot 0 Left ADC", NULL, SND_SOC_NOPM, 0, 0),
 491        SND_SOC_DAPM_ADC("AIF1 Slot 0 Right ADC", NULL, SND_SOC_NOPM, 0, 0),
 492
 493        /* Legacy DAC Outputs (connected to analog codec DAPM context) */
 494        SND_SOC_DAPM_DAC("AIF1 Slot 0 Left", NULL, SND_SOC_NOPM, 0, 0),
 495        SND_SOC_DAPM_DAC("AIF1 Slot 0 Right", NULL, SND_SOC_NOPM, 0, 0),
 496};
 497
 498static const struct snd_soc_dapm_route sun8i_codec_legacy_routes[] = {
 499        /* Legacy ADC Routes */
 500        { "ADCL", NULL, "AIF1 Slot 0 Left ADC" },
 501        { "ADCR", NULL, "AIF1 Slot 0 Right ADC" },
 502
 503        /* Legacy DAC Routes */
 504        { "AIF1 Slot 0 Left", NULL, "DACL" },
 505        { "AIF1 Slot 0 Right", NULL, "DACR" },
 506};
 507
 508static int sun8i_codec_component_probe(struct snd_soc_component *component)
 509{
 510        struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
 511        struct sun8i_codec *scodec = snd_soc_component_get_drvdata(component);
 512        int ret;
 513
 514        /* Add widgets for backward compatibility with old device trees. */
 515        if (scodec->quirks->legacy_widgets) {
 516                ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_legacy_widgets,
 517                                                ARRAY_SIZE(sun8i_codec_legacy_widgets));
 518                if (ret)
 519                        return ret;
 520
 521                ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_legacy_routes,
 522                                              ARRAY_SIZE(sun8i_codec_legacy_routes));
 523                if (ret)
 524                        return ret;
 525        }
 526
 527        return 0;
 528}
 529
 530static const struct snd_soc_dai_ops sun8i_codec_dai_ops = {
 531        .hw_params = sun8i_codec_hw_params,
 532        .set_fmt = sun8i_set_fmt,
 533};
 534
 535static struct snd_soc_dai_driver sun8i_codec_dai = {
 536        .name = "sun8i",
 537        /* playback capabilities */
 538        .playback = {
 539                .stream_name = "Playback",
 540                .channels_min = 1,
 541                .channels_max = 2,
 542                .rates = SNDRV_PCM_RATE_8000_192000,
 543                .formats = SNDRV_PCM_FMTBIT_S16_LE,
 544        },
 545        /* capture capabilities */
 546        .capture = {
 547                .stream_name = "Capture",
 548                .channels_min = 1,
 549                .channels_max = 2,
 550                .rates = SNDRV_PCM_RATE_8000_192000,
 551                .formats = SNDRV_PCM_FMTBIT_S16_LE,
 552                .sig_bits = 24,
 553        },
 554        /* pcm operations */
 555        .ops = &sun8i_codec_dai_ops,
 556};
 557
 558static const struct snd_soc_component_driver sun8i_soc_component = {
 559        .dapm_widgets           = sun8i_codec_dapm_widgets,
 560        .num_dapm_widgets       = ARRAY_SIZE(sun8i_codec_dapm_widgets),
 561        .dapm_routes            = sun8i_codec_dapm_routes,
 562        .num_dapm_routes        = ARRAY_SIZE(sun8i_codec_dapm_routes),
 563        .probe                  = sun8i_codec_component_probe,
 564        .idle_bias_on           = 1,
 565        .use_pmdown_time        = 1,
 566        .endianness             = 1,
 567        .non_legacy_dai_naming  = 1,
 568};
 569
 570static const struct regmap_config sun8i_codec_regmap_config = {
 571        .reg_bits       = 32,
 572        .reg_stride     = 4,
 573        .val_bits       = 32,
 574        .max_register   = SUN8I_DAC_MXR_SRC,
 575
 576        .cache_type     = REGCACHE_FLAT,
 577};
 578
 579static int sun8i_codec_probe(struct platform_device *pdev)
 580{
 581        struct sun8i_codec *scodec;
 582        void __iomem *base;
 583        int ret;
 584
 585        scodec = devm_kzalloc(&pdev->dev, sizeof(*scodec), GFP_KERNEL);
 586        if (!scodec)
 587                return -ENOMEM;
 588
 589        scodec->clk_module = devm_clk_get(&pdev->dev, "mod");
 590        if (IS_ERR(scodec->clk_module)) {
 591                dev_err(&pdev->dev, "Failed to get the module clock\n");
 592                return PTR_ERR(scodec->clk_module);
 593        }
 594
 595        base = devm_platform_ioremap_resource(pdev, 0);
 596        if (IS_ERR(base)) {
 597                dev_err(&pdev->dev, "Failed to map the registers\n");
 598                return PTR_ERR(base);
 599        }
 600
 601        scodec->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "bus", base,
 602                                                   &sun8i_codec_regmap_config);
 603        if (IS_ERR(scodec->regmap)) {
 604                dev_err(&pdev->dev, "Failed to create our regmap\n");
 605                return PTR_ERR(scodec->regmap);
 606        }
 607
 608        scodec->quirks = of_device_get_match_data(&pdev->dev);
 609
 610        platform_set_drvdata(pdev, scodec);
 611
 612        pm_runtime_enable(&pdev->dev);
 613        if (!pm_runtime_enabled(&pdev->dev)) {
 614                ret = sun8i_codec_runtime_resume(&pdev->dev);
 615                if (ret)
 616                        goto err_pm_disable;
 617        }
 618
 619        ret = devm_snd_soc_register_component(&pdev->dev, &sun8i_soc_component,
 620                                     &sun8i_codec_dai, 1);
 621        if (ret) {
 622                dev_err(&pdev->dev, "Failed to register codec\n");
 623                goto err_suspend;
 624        }
 625
 626        return ret;
 627
 628err_suspend:
 629        if (!pm_runtime_status_suspended(&pdev->dev))
 630                sun8i_codec_runtime_suspend(&pdev->dev);
 631
 632err_pm_disable:
 633        pm_runtime_disable(&pdev->dev);
 634
 635        return ret;
 636}
 637
 638static int sun8i_codec_remove(struct platform_device *pdev)
 639{
 640        pm_runtime_disable(&pdev->dev);
 641        if (!pm_runtime_status_suspended(&pdev->dev))
 642                sun8i_codec_runtime_suspend(&pdev->dev);
 643
 644        return 0;
 645}
 646
 647static const struct sun8i_codec_quirks sun8i_a33_quirks = {
 648        .legacy_widgets = true,
 649        .lrck_inversion = true,
 650};
 651
 652static const struct sun8i_codec_quirks sun50i_a64_quirks = {
 653};
 654
 655static const struct of_device_id sun8i_codec_of_match[] = {
 656        { .compatible = "allwinner,sun8i-a33-codec", .data = &sun8i_a33_quirks },
 657        { .compatible = "allwinner,sun50i-a64-codec", .data = &sun50i_a64_quirks },
 658        {}
 659};
 660MODULE_DEVICE_TABLE(of, sun8i_codec_of_match);
 661
 662static const struct dev_pm_ops sun8i_codec_pm_ops = {
 663        SET_RUNTIME_PM_OPS(sun8i_codec_runtime_suspend,
 664                           sun8i_codec_runtime_resume, NULL)
 665};
 666
 667static struct platform_driver sun8i_codec_driver = {
 668        .driver = {
 669                .name = "sun8i-codec",
 670                .of_match_table = sun8i_codec_of_match,
 671                .pm = &sun8i_codec_pm_ops,
 672        },
 673        .probe = sun8i_codec_probe,
 674        .remove = sun8i_codec_remove,
 675};
 676module_platform_driver(sun8i_codec_driver);
 677
 678MODULE_DESCRIPTION("Allwinner A33 (sun8i) codec driver");
 679MODULE_AUTHOR("Mylène Josserand <mylene.josserand@free-electrons.com>");
 680MODULE_LICENSE("GPL");
 681MODULE_ALIAS("platform:sun8i-codec");
 682