linux/sound/soc/codecs/ssm4567.c
<<
>>
Prefs
   1/*
   2 * SSM4567 amplifier audio driver
   3 *
   4 * Copyright 2014 Google Chromium project.
   5 *  Author: Anatol Pomozov <anatol@chromium.org>
   6 *
   7 * Based on code copyright/by:
   8 *   Copyright 2013 Analog Devices Inc.
   9 *
  10 * Licensed under the GPL-2.
  11 */
  12
  13#include <linux/acpi.h>
  14#include <linux/module.h>
  15#include <linux/init.h>
  16#include <linux/i2c.h>
  17#include <linux/regmap.h>
  18#include <linux/slab.h>
  19#include <sound/core.h>
  20#include <sound/pcm.h>
  21#include <sound/pcm_params.h>
  22#include <sound/soc.h>
  23#include <sound/initval.h>
  24#include <sound/tlv.h>
  25
  26#define SSM4567_REG_POWER_CTRL          0x00
  27#define SSM4567_REG_AMP_SNS_CTRL                0x01
  28#define SSM4567_REG_DAC_CTRL            0x02
  29#define SSM4567_REG_DAC_VOLUME          0x03
  30#define SSM4567_REG_SAI_CTRL_1          0x04
  31#define SSM4567_REG_SAI_CTRL_2          0x05
  32#define SSM4567_REG_SAI_PLACEMENT_1             0x06
  33#define SSM4567_REG_SAI_PLACEMENT_2             0x07
  34#define SSM4567_REG_SAI_PLACEMENT_3             0x08
  35#define SSM4567_REG_SAI_PLACEMENT_4             0x09
  36#define SSM4567_REG_SAI_PLACEMENT_5             0x0a
  37#define SSM4567_REG_SAI_PLACEMENT_6             0x0b
  38#define SSM4567_REG_BATTERY_V_OUT               0x0c
  39#define SSM4567_REG_LIMITER_CTRL_1              0x0d
  40#define SSM4567_REG_LIMITER_CTRL_2              0x0e
  41#define SSM4567_REG_LIMITER_CTRL_3              0x0f
  42#define SSM4567_REG_STATUS_1            0x10
  43#define SSM4567_REG_STATUS_2            0x11
  44#define SSM4567_REG_FAULT_CTRL          0x12
  45#define SSM4567_REG_PDM_CTRL            0x13
  46#define SSM4567_REG_MCLK_RATIO          0x14
  47#define SSM4567_REG_BOOST_CTRL_1                0x15
  48#define SSM4567_REG_BOOST_CTRL_2                0x16
  49#define SSM4567_REG_SOFT_RESET          0xff
  50
  51/* POWER_CTRL */
  52#define SSM4567_POWER_APWDN_EN          BIT(7)
  53#define SSM4567_POWER_BSNS_PWDN         BIT(6)
  54#define SSM4567_POWER_VSNS_PWDN         BIT(5)
  55#define SSM4567_POWER_ISNS_PWDN         BIT(4)
  56#define SSM4567_POWER_BOOST_PWDN                BIT(3)
  57#define SSM4567_POWER_AMP_PWDN          BIT(2)
  58#define SSM4567_POWER_VBAT_ONLY         BIT(1)
  59#define SSM4567_POWER_SPWDN                     BIT(0)
  60
  61/* DAC_CTRL */
  62#define SSM4567_DAC_HV                  BIT(7)
  63#define SSM4567_DAC_MUTE                BIT(6)
  64#define SSM4567_DAC_HPF                 BIT(5)
  65#define SSM4567_DAC_LPM                 BIT(4)
  66#define SSM4567_DAC_FS_MASK     0x7
  67#define SSM4567_DAC_FS_8000_12000       0x0
  68#define SSM4567_DAC_FS_16000_24000      0x1
  69#define SSM4567_DAC_FS_32000_48000      0x2
  70#define SSM4567_DAC_FS_64000_96000      0x3
  71#define SSM4567_DAC_FS_128000_192000    0x4
  72
  73/* SAI_CTRL_1 */
  74#define SSM4567_SAI_CTRL_1_BCLK                 BIT(6)
  75#define SSM4567_SAI_CTRL_1_TDM_BLCKS_MASK       (0x3 << 4)
  76#define SSM4567_SAI_CTRL_1_TDM_BLCKS_32         (0x0 << 4)
  77#define SSM4567_SAI_CTRL_1_TDM_BLCKS_48         (0x1 << 4)
  78#define SSM4567_SAI_CTRL_1_TDM_BLCKS_64         (0x2 << 4)
  79#define SSM4567_SAI_CTRL_1_FSYNC                BIT(3)
  80#define SSM4567_SAI_CTRL_1_LJ                   BIT(2)
  81#define SSM4567_SAI_CTRL_1_TDM                  BIT(1)
  82#define SSM4567_SAI_CTRL_1_PDM                  BIT(0)
  83
  84/* SAI_CTRL_2 */
  85#define SSM4567_SAI_CTRL_2_AUTO_SLOT            BIT(3)
  86#define SSM4567_SAI_CTRL_2_TDM_SLOT_MASK        0x7
  87#define SSM4567_SAI_CTRL_2_TDM_SLOT(x)          (x)
  88
  89struct ssm4567 {
  90        struct regmap *regmap;
  91};
  92
  93static const struct reg_default ssm4567_reg_defaults[] = {
  94        { SSM4567_REG_POWER_CTRL,       0x81 },
  95        { SSM4567_REG_AMP_SNS_CTRL, 0x09 },
  96        { SSM4567_REG_DAC_CTRL, 0x32 },
  97        { SSM4567_REG_DAC_VOLUME, 0x40 },
  98        { SSM4567_REG_SAI_CTRL_1, 0x00 },
  99        { SSM4567_REG_SAI_CTRL_2, 0x08 },
 100        { SSM4567_REG_SAI_PLACEMENT_1, 0x01 },
 101        { SSM4567_REG_SAI_PLACEMENT_2, 0x20 },
 102        { SSM4567_REG_SAI_PLACEMENT_3, 0x32 },
 103        { SSM4567_REG_SAI_PLACEMENT_4, 0x07 },
 104        { SSM4567_REG_SAI_PLACEMENT_5, 0x07 },
 105        { SSM4567_REG_SAI_PLACEMENT_6, 0x07 },
 106        { SSM4567_REG_BATTERY_V_OUT, 0x00 },
 107        { SSM4567_REG_LIMITER_CTRL_1, 0xa4 },
 108        { SSM4567_REG_LIMITER_CTRL_2, 0x73 },
 109        { SSM4567_REG_LIMITER_CTRL_3, 0x00 },
 110        { SSM4567_REG_STATUS_1, 0x00 },
 111        { SSM4567_REG_STATUS_2, 0x00 },
 112        { SSM4567_REG_FAULT_CTRL, 0x30 },
 113        { SSM4567_REG_PDM_CTRL, 0x40 },
 114        { SSM4567_REG_MCLK_RATIO, 0x11 },
 115        { SSM4567_REG_BOOST_CTRL_1, 0x03 },
 116        { SSM4567_REG_BOOST_CTRL_2, 0x00 },
 117        { SSM4567_REG_SOFT_RESET, 0x00 },
 118};
 119
 120
 121static bool ssm4567_readable_reg(struct device *dev, unsigned int reg)
 122{
 123        switch (reg) {
 124        case SSM4567_REG_POWER_CTRL ... SSM4567_REG_BOOST_CTRL_2:
 125                return true;
 126        default:
 127                return false;
 128        }
 129
 130}
 131
 132static bool ssm4567_writeable_reg(struct device *dev, unsigned int reg)
 133{
 134        switch (reg) {
 135        case SSM4567_REG_POWER_CTRL ... SSM4567_REG_SAI_PLACEMENT_6:
 136        case SSM4567_REG_LIMITER_CTRL_1 ... SSM4567_REG_LIMITER_CTRL_3:
 137        case SSM4567_REG_FAULT_CTRL ... SSM4567_REG_BOOST_CTRL_2:
 138        /* The datasheet states that soft reset register is read-only,
 139         * but logically it is write-only. */
 140        case SSM4567_REG_SOFT_RESET:
 141                return true;
 142        default:
 143                return false;
 144        }
 145}
 146
 147static bool ssm4567_volatile_reg(struct device *dev, unsigned int reg)
 148{
 149        switch (reg) {
 150        case SSM4567_REG_BATTERY_V_OUT:
 151        case SSM4567_REG_STATUS_1 ... SSM4567_REG_STATUS_2:
 152        case SSM4567_REG_SOFT_RESET:
 153                return true;
 154        default:
 155                return false;
 156        }
 157}
 158
 159static const DECLARE_TLV_DB_MINMAX_MUTE(ssm4567_vol_tlv, -7125, 2400);
 160
 161static const struct snd_kcontrol_new ssm4567_snd_controls[] = {
 162        SOC_SINGLE_TLV("Master Playback Volume", SSM4567_REG_DAC_VOLUME, 0,
 163                0xff, 1, ssm4567_vol_tlv),
 164        SOC_SINGLE("DAC Low Power Mode Switch", SSM4567_REG_DAC_CTRL, 4, 1, 0),
 165        SOC_SINGLE("DAC High Pass Filter Switch", SSM4567_REG_DAC_CTRL,
 166                5, 1, 0),
 167};
 168
 169static const struct snd_kcontrol_new ssm4567_amplifier_boost_control =
 170        SOC_DAPM_SINGLE("Switch", SSM4567_REG_POWER_CTRL, 1, 1, 1);
 171
 172static const struct snd_soc_dapm_widget ssm4567_dapm_widgets[] = {
 173        SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SSM4567_REG_POWER_CTRL, 2, 1),
 174        SND_SOC_DAPM_SWITCH("Amplifier Boost", SSM4567_REG_POWER_CTRL, 3, 1,
 175                &ssm4567_amplifier_boost_control),
 176
 177        SND_SOC_DAPM_SIGGEN("Sense"),
 178
 179        SND_SOC_DAPM_PGA("Current Sense", SSM4567_REG_POWER_CTRL, 4, 1, NULL, 0),
 180        SND_SOC_DAPM_PGA("Voltage Sense", SSM4567_REG_POWER_CTRL, 5, 1, NULL, 0),
 181        SND_SOC_DAPM_PGA("VBAT Sense", SSM4567_REG_POWER_CTRL, 6, 1, NULL, 0),
 182
 183        SND_SOC_DAPM_OUTPUT("OUT"),
 184};
 185
 186static const struct snd_soc_dapm_route ssm4567_routes[] = {
 187        { "OUT", NULL, "Amplifier Boost" },
 188        { "Amplifier Boost", "Switch", "DAC" },
 189        { "OUT", NULL, "DAC" },
 190
 191        { "Current Sense", NULL, "Sense" },
 192        { "Voltage Sense", NULL, "Sense" },
 193        { "VBAT Sense", NULL, "Sense" },
 194        { "Capture Sense", NULL, "Current Sense" },
 195        { "Capture Sense", NULL, "Voltage Sense" },
 196        { "Capture Sense", NULL, "VBAT Sense" },
 197};
 198
 199static int ssm4567_hw_params(struct snd_pcm_substream *substream,
 200        struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
 201{
 202        struct snd_soc_codec *codec = dai->codec;
 203        struct ssm4567 *ssm4567 = snd_soc_codec_get_drvdata(codec);
 204        unsigned int rate = params_rate(params);
 205        unsigned int dacfs;
 206
 207        if (rate >= 8000 && rate <= 12000)
 208                dacfs = SSM4567_DAC_FS_8000_12000;
 209        else if (rate >= 16000 && rate <= 24000)
 210                dacfs = SSM4567_DAC_FS_16000_24000;
 211        else if (rate >= 32000 && rate <= 48000)
 212                dacfs = SSM4567_DAC_FS_32000_48000;
 213        else if (rate >= 64000 && rate <= 96000)
 214                dacfs = SSM4567_DAC_FS_64000_96000;
 215        else if (rate >= 128000 && rate <= 192000)
 216                dacfs = SSM4567_DAC_FS_128000_192000;
 217        else
 218                return -EINVAL;
 219
 220        return regmap_update_bits(ssm4567->regmap, SSM4567_REG_DAC_CTRL,
 221                                SSM4567_DAC_FS_MASK, dacfs);
 222}
 223
 224static int ssm4567_mute(struct snd_soc_dai *dai, int mute)
 225{
 226        struct ssm4567 *ssm4567 = snd_soc_codec_get_drvdata(dai->codec);
 227        unsigned int val;
 228
 229        val = mute ? SSM4567_DAC_MUTE : 0;
 230        return regmap_update_bits(ssm4567->regmap, SSM4567_REG_DAC_CTRL,
 231                        SSM4567_DAC_MUTE, val);
 232}
 233
 234static int ssm4567_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
 235        unsigned int rx_mask, int slots, int width)
 236{
 237        struct ssm4567 *ssm4567 = snd_soc_dai_get_drvdata(dai);
 238        unsigned int blcks;
 239        int slot;
 240        int ret;
 241
 242        if (tx_mask == 0)
 243                return -EINVAL;
 244
 245        if (rx_mask && rx_mask != tx_mask)
 246                return -EINVAL;
 247
 248        slot = __ffs(tx_mask);
 249        if (tx_mask != BIT(slot))
 250                return -EINVAL;
 251
 252        switch (width) {
 253        case 32:
 254                blcks = SSM4567_SAI_CTRL_1_TDM_BLCKS_32;
 255                break;
 256        case 48:
 257                blcks = SSM4567_SAI_CTRL_1_TDM_BLCKS_48;
 258                break;
 259        case 64:
 260                blcks = SSM4567_SAI_CTRL_1_TDM_BLCKS_64;
 261                break;
 262        default:
 263                return -EINVAL;
 264        }
 265
 266        ret = regmap_update_bits(ssm4567->regmap, SSM4567_REG_SAI_CTRL_2,
 267                SSM4567_SAI_CTRL_2_AUTO_SLOT | SSM4567_SAI_CTRL_2_TDM_SLOT_MASK,
 268                SSM4567_SAI_CTRL_2_TDM_SLOT(slot));
 269        if (ret)
 270                return ret;
 271
 272        return regmap_update_bits(ssm4567->regmap, SSM4567_REG_SAI_CTRL_1,
 273                SSM4567_SAI_CTRL_1_TDM_BLCKS_MASK, blcks);
 274}
 275
 276static int ssm4567_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 277{
 278        struct ssm4567 *ssm4567 = snd_soc_dai_get_drvdata(dai);
 279        unsigned int ctrl1 = 0;
 280        bool invert_fclk;
 281
 282        switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
 283        case SND_SOC_DAIFMT_CBS_CFS:
 284                break;
 285        default:
 286                return -EINVAL;
 287        }
 288
 289        switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
 290        case SND_SOC_DAIFMT_NB_NF:
 291                invert_fclk = false;
 292                break;
 293        case SND_SOC_DAIFMT_IB_NF:
 294                ctrl1 |= SSM4567_SAI_CTRL_1_BCLK;
 295                invert_fclk = false;
 296                break;
 297        case SND_SOC_DAIFMT_NB_IF:
 298                ctrl1 |= SSM4567_SAI_CTRL_1_FSYNC;
 299                invert_fclk = true;
 300                break;
 301        case SND_SOC_DAIFMT_IB_IF:
 302                ctrl1 |= SSM4567_SAI_CTRL_1_BCLK;
 303                invert_fclk = true;
 304                break;
 305        default:
 306                return -EINVAL;
 307        }
 308
 309        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
 310        case SND_SOC_DAIFMT_I2S:
 311                break;
 312        case SND_SOC_DAIFMT_LEFT_J:
 313                ctrl1 |= SSM4567_SAI_CTRL_1_LJ;
 314                invert_fclk = !invert_fclk;
 315                break;
 316        case SND_SOC_DAIFMT_DSP_A:
 317                ctrl1 |= SSM4567_SAI_CTRL_1_TDM;
 318                break;
 319        case SND_SOC_DAIFMT_DSP_B:
 320                ctrl1 |= SSM4567_SAI_CTRL_1_TDM | SSM4567_SAI_CTRL_1_LJ;
 321                break;
 322        case SND_SOC_DAIFMT_PDM:
 323                ctrl1 |= SSM4567_SAI_CTRL_1_PDM;
 324                break;
 325        default:
 326                return -EINVAL;
 327        }
 328
 329        if (invert_fclk)
 330                ctrl1 |= SSM4567_SAI_CTRL_1_FSYNC;
 331
 332        return regmap_update_bits(ssm4567->regmap, SSM4567_REG_SAI_CTRL_1,
 333                        SSM4567_SAI_CTRL_1_BCLK |
 334                        SSM4567_SAI_CTRL_1_FSYNC |
 335                        SSM4567_SAI_CTRL_1_LJ |
 336                        SSM4567_SAI_CTRL_1_TDM |
 337                        SSM4567_SAI_CTRL_1_PDM,
 338                        ctrl1);
 339}
 340
 341static int ssm4567_set_power(struct ssm4567 *ssm4567, bool enable)
 342{
 343        int ret = 0;
 344
 345        if (!enable) {
 346                ret = regmap_update_bits(ssm4567->regmap,
 347                        SSM4567_REG_POWER_CTRL,
 348                        SSM4567_POWER_SPWDN, SSM4567_POWER_SPWDN);
 349                regcache_mark_dirty(ssm4567->regmap);
 350        }
 351
 352        regcache_cache_only(ssm4567->regmap, !enable);
 353
 354        if (enable) {
 355                ret = regmap_write(ssm4567->regmap, SSM4567_REG_SOFT_RESET,
 356                        0x00);
 357                if (ret)
 358                        return ret;
 359
 360                ret = regmap_update_bits(ssm4567->regmap,
 361                        SSM4567_REG_POWER_CTRL,
 362                        SSM4567_POWER_SPWDN, 0x00);
 363                regcache_sync(ssm4567->regmap);
 364        }
 365
 366        return ret;
 367}
 368
 369static int ssm4567_set_bias_level(struct snd_soc_codec *codec,
 370        enum snd_soc_bias_level level)
 371{
 372        struct ssm4567 *ssm4567 = snd_soc_codec_get_drvdata(codec);
 373        int ret = 0;
 374
 375        switch (level) {
 376        case SND_SOC_BIAS_ON:
 377                break;
 378        case SND_SOC_BIAS_PREPARE:
 379                break;
 380        case SND_SOC_BIAS_STANDBY:
 381                if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
 382                        ret = ssm4567_set_power(ssm4567, true);
 383                break;
 384        case SND_SOC_BIAS_OFF:
 385                ret = ssm4567_set_power(ssm4567, false);
 386                break;
 387        }
 388
 389        return ret;
 390}
 391
 392static const struct snd_soc_dai_ops ssm4567_dai_ops = {
 393        .hw_params      = ssm4567_hw_params,
 394        .digital_mute   = ssm4567_mute,
 395        .set_fmt        = ssm4567_set_dai_fmt,
 396        .set_tdm_slot   = ssm4567_set_tdm_slot,
 397};
 398
 399static struct snd_soc_dai_driver ssm4567_dai = {
 400        .name = "ssm4567-hifi",
 401        .playback = {
 402                .stream_name = "Playback",
 403                .channels_min = 1,
 404                .channels_max = 1,
 405                .rates = SNDRV_PCM_RATE_8000_192000,
 406                .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
 407                        SNDRV_PCM_FMTBIT_S32,
 408        },
 409        .capture = {
 410                .stream_name = "Capture Sense",
 411                .channels_min = 1,
 412                .channels_max = 1,
 413                .rates = SNDRV_PCM_RATE_8000_192000,
 414                .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
 415                        SNDRV_PCM_FMTBIT_S32,
 416        },
 417        .ops = &ssm4567_dai_ops,
 418};
 419
 420static const struct snd_soc_codec_driver ssm4567_codec_driver = {
 421        .set_bias_level = ssm4567_set_bias_level,
 422        .idle_bias_off = true,
 423
 424        .component_driver = {
 425                .controls               = ssm4567_snd_controls,
 426                .num_controls           = ARRAY_SIZE(ssm4567_snd_controls),
 427                .dapm_widgets           = ssm4567_dapm_widgets,
 428                .num_dapm_widgets       = ARRAY_SIZE(ssm4567_dapm_widgets),
 429                .dapm_routes            = ssm4567_routes,
 430                .num_dapm_routes        = ARRAY_SIZE(ssm4567_routes),
 431        },
 432};
 433
 434static const struct regmap_config ssm4567_regmap_config = {
 435        .val_bits = 8,
 436        .reg_bits = 8,
 437
 438        .max_register = SSM4567_REG_SOFT_RESET,
 439        .readable_reg = ssm4567_readable_reg,
 440        .writeable_reg = ssm4567_writeable_reg,
 441        .volatile_reg = ssm4567_volatile_reg,
 442
 443        .cache_type = REGCACHE_RBTREE,
 444        .reg_defaults = ssm4567_reg_defaults,
 445        .num_reg_defaults = ARRAY_SIZE(ssm4567_reg_defaults),
 446};
 447
 448static int ssm4567_i2c_probe(struct i2c_client *i2c,
 449        const struct i2c_device_id *id)
 450{
 451        struct ssm4567 *ssm4567;
 452        int ret;
 453
 454        ssm4567 = devm_kzalloc(&i2c->dev, sizeof(*ssm4567), GFP_KERNEL);
 455        if (ssm4567 == NULL)
 456                return -ENOMEM;
 457
 458        i2c_set_clientdata(i2c, ssm4567);
 459
 460        ssm4567->regmap = devm_regmap_init_i2c(i2c, &ssm4567_regmap_config);
 461        if (IS_ERR(ssm4567->regmap))
 462                return PTR_ERR(ssm4567->regmap);
 463
 464        ret = regmap_write(ssm4567->regmap, SSM4567_REG_SOFT_RESET, 0x00);
 465        if (ret)
 466                return ret;
 467
 468        ret = ssm4567_set_power(ssm4567, false);
 469        if (ret)
 470                return ret;
 471
 472        return snd_soc_register_codec(&i2c->dev, &ssm4567_codec_driver,
 473                        &ssm4567_dai, 1);
 474}
 475
 476static int ssm4567_i2c_remove(struct i2c_client *client)
 477{
 478        snd_soc_unregister_codec(&client->dev);
 479        return 0;
 480}
 481
 482static const struct i2c_device_id ssm4567_i2c_ids[] = {
 483        { "ssm4567", 0 },
 484        { }
 485};
 486MODULE_DEVICE_TABLE(i2c, ssm4567_i2c_ids);
 487
 488#ifdef CONFIG_OF
 489static const struct of_device_id ssm4567_of_match[] = {
 490        { .compatible = "adi,ssm4567", },
 491        { }
 492};
 493MODULE_DEVICE_TABLE(of, ssm4567_of_match);
 494#endif
 495
 496#ifdef CONFIG_ACPI
 497
 498static const struct acpi_device_id ssm4567_acpi_match[] = {
 499        { "INT343B", 0 },
 500        {},
 501};
 502MODULE_DEVICE_TABLE(acpi, ssm4567_acpi_match);
 503
 504#endif
 505
 506static struct i2c_driver ssm4567_driver = {
 507        .driver = {
 508                .name = "ssm4567",
 509                .of_match_table = of_match_ptr(ssm4567_of_match),
 510                .acpi_match_table = ACPI_PTR(ssm4567_acpi_match),
 511        },
 512        .probe = ssm4567_i2c_probe,
 513        .remove = ssm4567_i2c_remove,
 514        .id_table = ssm4567_i2c_ids,
 515};
 516module_i2c_driver(ssm4567_driver);
 517
 518MODULE_DESCRIPTION("ASoC SSM4567 driver");
 519MODULE_AUTHOR("Anatol Pomozov <anatol@chromium.org>");
 520MODULE_LICENSE("GPL");
 521