linux/sound/soc/meson/t9015.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2//
   3// Copyright (c) 2020 BayLibre, SAS.
   4// Author: Jerome Brunet <jbrunet@baylibre.com>
   5
   6#include <linux/clk.h>
   7#include <linux/delay.h>
   8#include <linux/module.h>
   9#include <linux/regmap.h>
  10#include <linux/regulator/consumer.h>
  11#include <linux/reset.h>
  12#include <sound/soc.h>
  13#include <sound/tlv.h>
  14
  15#define BLOCK_EN        0x00
  16#define  LORN_EN        0
  17#define  LORP_EN        1
  18#define  LOLN_EN        2
  19#define  LOLP_EN        3
  20#define  DACR_EN        4
  21#define  DACL_EN        5
  22#define  DACR_INV       20
  23#define  DACL_INV       21
  24#define  DACR_SRC       22
  25#define  DACL_SRC       23
  26#define  REFP_BUF_EN    BIT(12)
  27#define  BIAS_CURRENT_EN BIT(13)
  28#define  VMID_GEN_FAST  BIT(14)
  29#define  VMID_GEN_EN    BIT(15)
  30#define  I2S_MODE       BIT(30)
  31#define VOL_CTRL0       0x04
  32#define  GAIN_H         31
  33#define  GAIN_L         23
  34#define VOL_CTRL1       0x08
  35#define  DAC_MONO       8
  36#define  RAMP_RATE      10
  37#define  VC_RAMP_MODE   12
  38#define  MUTE_MODE      13
  39#define  UNMUTE_MODE    14
  40#define  DAC_SOFT_MUTE  15
  41#define  DACR_VC        16
  42#define  DACL_VC        24
  43#define LINEOUT_CFG     0x0c
  44#define  LORN_POL       0
  45#define  LORP_POL       4
  46#define  LOLN_POL       8
  47#define  LOLP_POL       12
  48#define POWER_CFG       0x10
  49
  50struct t9015 {
  51        struct clk *pclk;
  52        struct regulator *avdd;
  53};
  54
  55static int t9015_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
  56{
  57        struct snd_soc_component *component = dai->component;
  58        unsigned int val;
  59
  60        switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
  61        case SND_SOC_DAIFMT_CBM_CFM:
  62                val = I2S_MODE;
  63                break;
  64
  65        case SND_SOC_DAIFMT_CBS_CFS:
  66                val = 0;
  67                break;
  68
  69        default:
  70                return -EINVAL;
  71        }
  72
  73        snd_soc_component_update_bits(component, BLOCK_EN, I2S_MODE, val);
  74
  75        if (((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_I2S) &&
  76            ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_LEFT_J))
  77                return -EINVAL;
  78
  79        return 0;
  80}
  81
  82static const struct snd_soc_dai_ops t9015_dai_ops = {
  83        .set_fmt = t9015_dai_set_fmt,
  84};
  85
  86static struct snd_soc_dai_driver t9015_dai = {
  87        .name = "t9015-hifi",
  88        .playback = {
  89                .stream_name = "Playback",
  90                .channels_min = 1,
  91                .channels_max = 2,
  92                .rates = SNDRV_PCM_RATE_8000_96000,
  93                .formats = (SNDRV_PCM_FMTBIT_S8 |
  94                            SNDRV_PCM_FMTBIT_S16_LE |
  95                            SNDRV_PCM_FMTBIT_S20_LE |
  96                            SNDRV_PCM_FMTBIT_S24_LE),
  97        },
  98        .ops = &t9015_dai_ops,
  99};
 100
 101static const DECLARE_TLV_DB_MINMAX_MUTE(dac_vol_tlv, -9525, 0);
 102
 103static const char * const ramp_rate_txt[] = { "Fast", "Slow" };
 104static SOC_ENUM_SINGLE_DECL(ramp_rate_enum, VOL_CTRL1, RAMP_RATE,
 105                            ramp_rate_txt);
 106
 107static const char * const dacr_in_txt[] = { "Right", "Left" };
 108static SOC_ENUM_SINGLE_DECL(dacr_in_enum, BLOCK_EN, DACR_SRC, dacr_in_txt);
 109
 110static const char * const dacl_in_txt[] = { "Left", "Right" };
 111static SOC_ENUM_SINGLE_DECL(dacl_in_enum, BLOCK_EN, DACL_SRC, dacl_in_txt);
 112
 113static const char * const mono_txt[] = { "Stereo", "Mono"};
 114static SOC_ENUM_SINGLE_DECL(mono_enum, VOL_CTRL1, DAC_MONO, mono_txt);
 115
 116static const struct snd_kcontrol_new t9015_snd_controls[] = {
 117        /* Volume Controls */
 118        SOC_ENUM("Playback Channel Mode", mono_enum),
 119        SOC_SINGLE("Playback Switch", VOL_CTRL1, DAC_SOFT_MUTE, 1, 1),
 120        SOC_DOUBLE_TLV("Playback Volume", VOL_CTRL1, DACL_VC, DACR_VC,
 121                       0xff, 0, dac_vol_tlv),
 122
 123        /* Ramp Controls */
 124        SOC_ENUM("Ramp Rate", ramp_rate_enum),
 125        SOC_SINGLE("Volume Ramp Switch", VOL_CTRL1, VC_RAMP_MODE, 1, 0),
 126        SOC_SINGLE("Mute Ramp Switch", VOL_CTRL1, MUTE_MODE, 1, 0),
 127        SOC_SINGLE("Unmute Ramp Switch", VOL_CTRL1, UNMUTE_MODE, 1, 0),
 128};
 129
 130static const struct snd_kcontrol_new t9015_right_dac_mux =
 131        SOC_DAPM_ENUM("Right DAC Source", dacr_in_enum);
 132static const struct snd_kcontrol_new t9015_left_dac_mux =
 133        SOC_DAPM_ENUM("Left DAC Source", dacl_in_enum);
 134
 135static const struct snd_soc_dapm_widget t9015_dapm_widgets[] = {
 136        SND_SOC_DAPM_AIF_IN("Right IN", NULL, 0, SND_SOC_NOPM, 0, 0),
 137        SND_SOC_DAPM_AIF_IN("Left IN", NULL, 0, SND_SOC_NOPM, 0, 0),
 138        SND_SOC_DAPM_MUX("Right DAC Sel", SND_SOC_NOPM, 0, 0,
 139                         &t9015_right_dac_mux),
 140        SND_SOC_DAPM_MUX("Left DAC Sel", SND_SOC_NOPM, 0, 0,
 141                         &t9015_left_dac_mux),
 142        SND_SOC_DAPM_DAC("Right DAC", NULL, BLOCK_EN, DACR_EN, 0),
 143        SND_SOC_DAPM_DAC("Left DAC",  NULL, BLOCK_EN, DACL_EN, 0),
 144        SND_SOC_DAPM_OUT_DRV("Right- Driver", BLOCK_EN, LORN_EN, 0,
 145                         NULL, 0),
 146        SND_SOC_DAPM_OUT_DRV("Right+ Driver", BLOCK_EN, LORP_EN, 0,
 147                         NULL, 0),
 148        SND_SOC_DAPM_OUT_DRV("Left- Driver",  BLOCK_EN, LOLN_EN, 0,
 149                         NULL, 0),
 150        SND_SOC_DAPM_OUT_DRV("Left+ Driver",  BLOCK_EN, LOLP_EN, 0,
 151                         NULL, 0),
 152        SND_SOC_DAPM_OUTPUT("LORN"),
 153        SND_SOC_DAPM_OUTPUT("LORP"),
 154        SND_SOC_DAPM_OUTPUT("LOLN"),
 155        SND_SOC_DAPM_OUTPUT("LOLP"),
 156};
 157
 158static const struct snd_soc_dapm_route t9015_dapm_routes[] = {
 159        { "Right IN", NULL, "Playback" },
 160        { "Left IN",  NULL, "Playback" },
 161        { "Right DAC Sel", "Right", "Right IN" },
 162        { "Right DAC Sel", "Left",  "Left IN" },
 163        { "Left DAC Sel",  "Right", "Right IN" },
 164        { "Left DAC Sel",  "Left",  "Left IN" },
 165        { "Right DAC", NULL, "Right DAC Sel" },
 166        { "Left DAC",  NULL, "Left DAC Sel" },
 167        { "Right- Driver", NULL, "Right DAC" },
 168        { "Right+ Driver", NULL, "Right DAC" },
 169        { "Left- Driver",  NULL, "Left DAC"  },
 170        { "Left+ Driver",  NULL, "Left DAC"  },
 171        { "LORN", NULL, "Right- Driver", },
 172        { "LORP", NULL, "Right+ Driver", },
 173        { "LOLN", NULL, "Left- Driver",  },
 174        { "LOLP", NULL, "Left+ Driver",  },
 175};
 176
 177static int t9015_set_bias_level(struct snd_soc_component *component,
 178                                enum snd_soc_bias_level level)
 179{
 180        struct t9015 *priv = snd_soc_component_get_drvdata(component);
 181        enum snd_soc_bias_level now =
 182                snd_soc_component_get_bias_level(component);
 183        int ret;
 184
 185        switch (level) {
 186        case SND_SOC_BIAS_ON:
 187                snd_soc_component_update_bits(component, BLOCK_EN,
 188                                              BIAS_CURRENT_EN,
 189                                              BIAS_CURRENT_EN);
 190                break;
 191        case SND_SOC_BIAS_PREPARE:
 192                snd_soc_component_update_bits(component, BLOCK_EN,
 193                                              BIAS_CURRENT_EN,
 194                                              0);
 195                break;
 196        case SND_SOC_BIAS_STANDBY:
 197                ret = regulator_enable(priv->avdd);
 198                if (ret) {
 199                        dev_err(component->dev, "AVDD enable failed\n");
 200                        return ret;
 201                }
 202
 203                if (now == SND_SOC_BIAS_OFF) {
 204                        snd_soc_component_update_bits(component, BLOCK_EN,
 205                                VMID_GEN_EN | VMID_GEN_FAST | REFP_BUF_EN,
 206                                VMID_GEN_EN | VMID_GEN_FAST | REFP_BUF_EN);
 207
 208                        mdelay(200);
 209                        snd_soc_component_update_bits(component, BLOCK_EN,
 210                                                      VMID_GEN_FAST,
 211                                                      0);
 212                }
 213
 214                break;
 215        case SND_SOC_BIAS_OFF:
 216                snd_soc_component_update_bits(component, BLOCK_EN,
 217                        VMID_GEN_EN | VMID_GEN_FAST | REFP_BUF_EN,
 218                        0);
 219
 220                regulator_disable(priv->avdd);
 221                break;
 222        }
 223
 224        return 0;
 225}
 226
 227static const struct snd_soc_component_driver t9015_codec_driver = {
 228        .set_bias_level         = t9015_set_bias_level,
 229        .controls               = t9015_snd_controls,
 230        .num_controls           = ARRAY_SIZE(t9015_snd_controls),
 231        .dapm_widgets           = t9015_dapm_widgets,
 232        .num_dapm_widgets       = ARRAY_SIZE(t9015_dapm_widgets),
 233        .dapm_routes            = t9015_dapm_routes,
 234        .num_dapm_routes        = ARRAY_SIZE(t9015_dapm_routes),
 235        .suspend_bias_off       = 1,
 236        .endianness             = 1,
 237        .non_legacy_dai_naming  = 1,
 238};
 239
 240static const struct regmap_config t9015_regmap_config = {
 241        .reg_bits               = 32,
 242        .reg_stride             = 4,
 243        .val_bits               = 32,
 244        .max_register           = POWER_CFG,
 245};
 246
 247static int t9015_probe(struct platform_device *pdev)
 248{
 249        struct device *dev = &pdev->dev;
 250        struct t9015 *priv;
 251        void __iomem *regs;
 252        struct regmap *regmap;
 253        int ret;
 254
 255        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 256        if (!priv)
 257                return -ENOMEM;
 258        platform_set_drvdata(pdev, priv);
 259
 260        priv->pclk = devm_clk_get(dev, "pclk");
 261        if (IS_ERR(priv->pclk)) {
 262                if (PTR_ERR(priv->pclk) != -EPROBE_DEFER)
 263                        dev_err(dev, "failed to get core clock\n");
 264                return PTR_ERR(priv->pclk);
 265        }
 266
 267        priv->avdd = devm_regulator_get(dev, "AVDD");
 268        if (IS_ERR(priv->avdd)) {
 269                if (PTR_ERR(priv->avdd) != -EPROBE_DEFER)
 270                        dev_err(dev, "failed to AVDD\n");
 271                return PTR_ERR(priv->avdd);
 272        }
 273
 274        ret = clk_prepare_enable(priv->pclk);
 275        if (ret) {
 276                dev_err(dev, "core clock enable failed\n");
 277                return ret;
 278        }
 279
 280        ret = devm_add_action_or_reset(dev,
 281                        (void(*)(void *))clk_disable_unprepare,
 282                        priv->pclk);
 283        if (ret)
 284                return ret;
 285
 286        ret = device_reset(dev);
 287        if (ret) {
 288                dev_err(dev, "reset failed\n");
 289                return ret;
 290        }
 291
 292        regs = devm_platform_ioremap_resource(pdev, 0);
 293        if (IS_ERR(regs)) {
 294                dev_err(dev, "register map failed\n");
 295                return PTR_ERR(regs);
 296        }
 297
 298        regmap = devm_regmap_init_mmio(dev, regs, &t9015_regmap_config);
 299        if (IS_ERR(regmap)) {
 300                dev_err(dev, "regmap init failed\n");
 301                return PTR_ERR(regmap);
 302        }
 303
 304        /*
 305         * Initialize output polarity:
 306         * ATM the output polarity is fixed but in the future it might useful
 307         * to add DT property to set this depending on the platform needs
 308         */
 309        regmap_write(regmap, LINEOUT_CFG, 0x1111);
 310
 311        return devm_snd_soc_register_component(dev, &t9015_codec_driver,
 312                                               &t9015_dai, 1);
 313}
 314
 315static const struct of_device_id t9015_ids[] __maybe_unused = {
 316        { .compatible = "amlogic,t9015", },
 317        { }
 318};
 319MODULE_DEVICE_TABLE(of, t9015_ids);
 320
 321static struct platform_driver t9015_driver = {
 322        .driver = {
 323                .name = "t9015-codec",
 324                .of_match_table = of_match_ptr(t9015_ids),
 325        },
 326        .probe = t9015_probe,
 327};
 328
 329module_platform_driver(t9015_driver);
 330
 331MODULE_DESCRIPTION("ASoC Amlogic T9015 codec driver");
 332MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
 333MODULE_LICENSE("GPL");
 334