linux/sound/soc/codecs/tas2764.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2//
   3// Driver for the Texas Instruments TAS2764 CODEC
   4// Copyright (C) 2020 Texas Instruments Inc.
   5
   6#include <linux/module.h>
   7#include <linux/moduleparam.h>
   8#include <linux/err.h>
   9#include <linux/init.h>
  10#include <linux/delay.h>
  11#include <linux/pm.h>
  12#include <linux/i2c.h>
  13#include <linux/gpio.h>
  14#include <linux/gpio/consumer.h>
  15#include <linux/regulator/consumer.h>
  16#include <linux/regmap.h>
  17#include <linux/of.h>
  18#include <linux/of_gpio.h>
  19#include <linux/slab.h>
  20#include <sound/soc.h>
  21#include <sound/pcm.h>
  22#include <sound/pcm_params.h>
  23#include <sound/initval.h>
  24#include <sound/tlv.h>
  25
  26#include "tas2764.h"
  27
  28struct tas2764_priv {
  29        struct snd_soc_component *component;
  30        struct gpio_desc *reset_gpio;
  31        struct gpio_desc *sdz_gpio;
  32        struct regmap *regmap;
  33        struct device *dev;
  34        
  35        int v_sense_slot;
  36        int i_sense_slot;
  37};
  38
  39static void tas2764_reset(struct tas2764_priv *tas2764)
  40{
  41        if (tas2764->reset_gpio) {
  42                gpiod_set_value_cansleep(tas2764->reset_gpio, 0);
  43                msleep(20);
  44                gpiod_set_value_cansleep(tas2764->reset_gpio, 1);
  45        }
  46
  47        snd_soc_component_write(tas2764->component, TAS2764_SW_RST,
  48                                TAS2764_RST);
  49}
  50
  51static int tas2764_set_bias_level(struct snd_soc_component *component,
  52                                 enum snd_soc_bias_level level)
  53{
  54        struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component);
  55
  56        switch (level) {
  57        case SND_SOC_BIAS_ON:
  58                snd_soc_component_update_bits(component, TAS2764_PWR_CTRL,
  59                                              TAS2764_PWR_CTRL_MASK,
  60                                              TAS2764_PWR_CTRL_ACTIVE);
  61                break;
  62        case SND_SOC_BIAS_STANDBY:
  63        case SND_SOC_BIAS_PREPARE:
  64                snd_soc_component_update_bits(component, TAS2764_PWR_CTRL,
  65                                              TAS2764_PWR_CTRL_MASK,
  66                                              TAS2764_PWR_CTRL_MUTE);
  67                break;
  68        case SND_SOC_BIAS_OFF:
  69                snd_soc_component_update_bits(component, TAS2764_PWR_CTRL,
  70                                              TAS2764_PWR_CTRL_MASK,
  71                                              TAS2764_PWR_CTRL_SHUTDOWN);
  72                break;
  73
  74        default:
  75                dev_err(tas2764->dev,
  76                                "wrong power level setting %d\n", level);
  77                return -EINVAL;
  78        }
  79
  80        return 0;
  81}
  82
  83#ifdef CONFIG_PM
  84static int tas2764_codec_suspend(struct snd_soc_component *component)
  85{
  86        struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component);
  87        int ret;
  88
  89        ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL,
  90                                            TAS2764_PWR_CTRL_MASK,
  91                                            TAS2764_PWR_CTRL_SHUTDOWN);
  92
  93        if (ret < 0)
  94                return ret;
  95
  96        if (tas2764->sdz_gpio)
  97                gpiod_set_value_cansleep(tas2764->sdz_gpio, 0);
  98
  99        regcache_cache_only(tas2764->regmap, true);
 100        regcache_mark_dirty(tas2764->regmap);
 101
 102        return 0;
 103}
 104
 105static int tas2764_codec_resume(struct snd_soc_component *component)
 106{
 107        struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component);
 108        int ret;
 109
 110        if (tas2764->sdz_gpio)
 111                gpiod_set_value_cansleep(tas2764->sdz_gpio, 1);
 112
 113        ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL,
 114                                            TAS2764_PWR_CTRL_MASK,
 115                                            TAS2764_PWR_CTRL_ACTIVE);
 116
 117        if (ret < 0)
 118                return ret;
 119
 120        regcache_cache_only(tas2764->regmap, false);
 121
 122        return regcache_sync(tas2764->regmap);
 123}
 124#else
 125#define tas2764_codec_suspend NULL
 126#define tas2764_codec_resume NULL
 127#endif
 128
 129static const char * const tas2764_ASI1_src[] = {
 130        "I2C offset", "Left", "Right", "LeftRightDiv2",
 131};
 132
 133static SOC_ENUM_SINGLE_DECL(
 134        tas2764_ASI1_src_enum, TAS2764_TDM_CFG2, 4, tas2764_ASI1_src);
 135
 136static const struct snd_kcontrol_new tas2764_asi1_mux =
 137        SOC_DAPM_ENUM("ASI1 Source", tas2764_ASI1_src_enum);
 138
 139static int tas2764_dac_event(struct snd_soc_dapm_widget *w,
 140                             struct snd_kcontrol *kcontrol, int event)
 141{
 142        struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
 143        struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component);
 144        int ret;
 145
 146        switch (event) {
 147        case SND_SOC_DAPM_POST_PMU:
 148                ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL,
 149                                                    TAS2764_PWR_CTRL_MASK,
 150                                                    TAS2764_PWR_CTRL_MUTE);
 151                break;
 152        case SND_SOC_DAPM_PRE_PMD:
 153                ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL,
 154                                                    TAS2764_PWR_CTRL_MASK,
 155                                                    TAS2764_PWR_CTRL_SHUTDOWN);
 156                break;
 157        default:
 158                dev_err(tas2764->dev, "Unsupported event\n");
 159                return -EINVAL;
 160        }
 161
 162        if (ret < 0)
 163                return ret;
 164
 165        return 0;
 166}
 167
 168static const struct snd_kcontrol_new isense_switch =
 169        SOC_DAPM_SINGLE("Switch", TAS2764_PWR_CTRL, TAS2764_ISENSE_POWER_EN, 1, 1);
 170static const struct snd_kcontrol_new vsense_switch =
 171        SOC_DAPM_SINGLE("Switch", TAS2764_PWR_CTRL, TAS2764_VSENSE_POWER_EN, 1, 1);
 172
 173static const struct snd_soc_dapm_widget tas2764_dapm_widgets[] = {
 174        SND_SOC_DAPM_AIF_IN("ASI1", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0),
 175        SND_SOC_DAPM_MUX("ASI1 Sel", SND_SOC_NOPM, 0, 0, &tas2764_asi1_mux),
 176        SND_SOC_DAPM_SWITCH("ISENSE", TAS2764_PWR_CTRL, TAS2764_ISENSE_POWER_EN,
 177                            1, &isense_switch),
 178        SND_SOC_DAPM_SWITCH("VSENSE", TAS2764_PWR_CTRL, TAS2764_VSENSE_POWER_EN,
 179                            1, &vsense_switch),
 180        SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas2764_dac_event,
 181                           SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
 182        SND_SOC_DAPM_OUTPUT("OUT"),
 183        SND_SOC_DAPM_SIGGEN("VMON"),
 184        SND_SOC_DAPM_SIGGEN("IMON")
 185};
 186
 187static const struct snd_soc_dapm_route tas2764_audio_map[] = {
 188        {"ASI1 Sel", "I2C offset", "ASI1"},
 189        {"ASI1 Sel", "Left", "ASI1"},
 190        {"ASI1 Sel", "Right", "ASI1"},
 191        {"ASI1 Sel", "LeftRightDiv2", "ASI1"},
 192        {"DAC", NULL, "ASI1 Sel"},
 193        {"OUT", NULL, "DAC"},
 194        {"ISENSE", "Switch", "IMON"},
 195        {"VSENSE", "Switch", "VMON"},
 196};
 197
 198static int tas2764_mute(struct snd_soc_dai *dai, int mute, int direction)
 199{
 200        struct snd_soc_component *component = dai->component;
 201        int ret;
 202
 203        ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL,
 204                                            TAS2764_PWR_CTRL_MASK,
 205                                            mute ? TAS2764_PWR_CTRL_MUTE : 0);
 206
 207        if (ret < 0)
 208                return ret;
 209
 210        return 0;
 211}
 212
 213static int tas2764_set_bitwidth(struct tas2764_priv *tas2764, int bitwidth)
 214{
 215        struct snd_soc_component *component = tas2764->component;
 216        int sense_en;
 217        int val;
 218        int ret;
 219
 220        switch (bitwidth) {
 221        case SNDRV_PCM_FORMAT_S16_LE:
 222                ret = snd_soc_component_update_bits(component,
 223                                                    TAS2764_TDM_CFG2,
 224                                                    TAS2764_TDM_CFG2_RXW_MASK,
 225                                                    TAS2764_TDM_CFG2_RXW_16BITS);
 226                break;
 227        case SNDRV_PCM_FORMAT_S24_LE:
 228                ret = snd_soc_component_update_bits(component,
 229                                                    TAS2764_TDM_CFG2,
 230                                                    TAS2764_TDM_CFG2_RXW_MASK,
 231                                                    TAS2764_TDM_CFG2_RXW_24BITS);
 232                break;
 233        case SNDRV_PCM_FORMAT_S32_LE:
 234                ret = snd_soc_component_update_bits(component,
 235                                                    TAS2764_TDM_CFG2,
 236                                                    TAS2764_TDM_CFG2_RXW_MASK,
 237                                                    TAS2764_TDM_CFG2_RXW_32BITS);
 238                break;
 239
 240        default:
 241                return -EINVAL;
 242        }
 243
 244        if (ret < 0)
 245                return ret;
 246
 247        val = snd_soc_component_read(tas2764->component, TAS2764_PWR_CTRL);
 248        if (val < 0)
 249                return val;
 250
 251        if (val & (1 << TAS2764_VSENSE_POWER_EN))
 252                sense_en = 0;
 253        else
 254                sense_en = TAS2764_TDM_CFG5_VSNS_ENABLE;
 255
 256        ret = snd_soc_component_update_bits(tas2764->component, TAS2764_TDM_CFG5,
 257                                            TAS2764_TDM_CFG5_VSNS_ENABLE,
 258                                            sense_en);
 259        if (ret < 0)
 260                return ret;
 261
 262        if (val & (1 << TAS2764_ISENSE_POWER_EN))
 263                sense_en = 0;
 264        else
 265                sense_en = TAS2764_TDM_CFG6_ISNS_ENABLE;
 266
 267        ret = snd_soc_component_update_bits(tas2764->component, TAS2764_TDM_CFG6,
 268                                            TAS2764_TDM_CFG6_ISNS_ENABLE,
 269                                            sense_en);
 270        if (ret < 0)
 271                return ret;
 272
 273        return 0;
 274}
 275
 276static int tas2764_set_samplerate(struct tas2764_priv *tas2764, int samplerate)
 277{
 278        struct snd_soc_component *component = tas2764->component;
 279        int ramp_rate_val;
 280        int ret;
 281
 282        switch (samplerate) {
 283        case 48000:
 284                ramp_rate_val = TAS2764_TDM_CFG0_SMP_48KHZ |
 285                                TAS2764_TDM_CFG0_44_1_48KHZ;
 286                break;
 287        case 44100:
 288                ramp_rate_val = TAS2764_TDM_CFG0_SMP_44_1KHZ |
 289                                TAS2764_TDM_CFG0_44_1_48KHZ;
 290                break;
 291        case 96000:
 292                ramp_rate_val = TAS2764_TDM_CFG0_SMP_48KHZ |
 293                                TAS2764_TDM_CFG0_88_2_96KHZ;
 294                break;
 295        case 88200:
 296                ramp_rate_val = TAS2764_TDM_CFG0_SMP_44_1KHZ |
 297                                TAS2764_TDM_CFG0_88_2_96KHZ;
 298                break;
 299        default:
 300                return -EINVAL;
 301        }
 302
 303        ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG0,
 304                                            TAS2764_TDM_CFG0_SMP_MASK |
 305                                            TAS2764_TDM_CFG0_MASK,
 306                                            ramp_rate_val);
 307        if (ret < 0)
 308                return ret;
 309
 310        return 0;
 311}
 312
 313static int tas2764_hw_params(struct snd_pcm_substream *substream,
 314                             struct snd_pcm_hw_params *params,
 315                             struct snd_soc_dai *dai)
 316{
 317        struct snd_soc_component *component = dai->component;
 318        struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component);
 319        int ret;
 320
 321        ret = tas2764_set_bitwidth(tas2764, params_format(params));
 322        if (ret < 0)
 323                return ret;
 324
 325        return tas2764_set_samplerate(tas2764, params_rate(params));
 326}
 327
 328static int tas2764_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 329{
 330        struct snd_soc_component *component = dai->component;
 331        struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component);
 332        u8 tdm_rx_start_slot = 0, asi_cfg_1 = 0;
 333        int iface;
 334        int ret;
 335
 336        switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
 337        case SND_SOC_DAIFMT_NB_NF:
 338                asi_cfg_1 = TAS2764_TDM_CFG1_RX_RISING;
 339                break;
 340        case SND_SOC_DAIFMT_IB_NF:
 341                asi_cfg_1 = TAS2764_TDM_CFG1_RX_FALLING;
 342                break;
 343        default:
 344                dev_err(tas2764->dev, "ASI format Inverse is not found\n");
 345                return -EINVAL;
 346        }
 347
 348        ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG1,
 349                                            TAS2764_TDM_CFG1_RX_MASK,
 350                                            asi_cfg_1);
 351        if (ret < 0)
 352                return ret;
 353
 354        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
 355        case SND_SOC_DAIFMT_I2S:
 356        case SND_SOC_DAIFMT_DSP_A:
 357                iface = TAS2764_TDM_CFG2_SCFG_I2S;
 358                tdm_rx_start_slot = 1;
 359                break;
 360        case SND_SOC_DAIFMT_DSP_B:
 361        case SND_SOC_DAIFMT_LEFT_J:
 362                iface = TAS2764_TDM_CFG2_SCFG_LEFT_J;
 363                tdm_rx_start_slot = 0;
 364                break;
 365        default:
 366                dev_err(tas2764->dev,
 367                        "DAI Format is not found, fmt=0x%x\n", fmt);
 368                return -EINVAL;
 369        }
 370
 371        ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG1,
 372                                            TAS2764_TDM_CFG1_MASK,
 373                                            (tdm_rx_start_slot << TAS2764_TDM_CFG1_51_SHIFT));
 374        if (ret < 0)
 375                return ret;
 376
 377        ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG2,
 378                                            TAS2764_TDM_CFG2_SCFG_MASK, iface);
 379        if (ret < 0)
 380                return ret;
 381
 382        return 0;
 383}
 384
 385static int tas2764_set_dai_tdm_slot(struct snd_soc_dai *dai,
 386                                unsigned int tx_mask,
 387                                unsigned int rx_mask,
 388                                int slots, int slot_width)
 389{
 390        struct snd_soc_component *component = dai->component;
 391        struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component);
 392        int left_slot, right_slot;
 393        int slots_cfg;
 394        int slot_size;
 395        int ret;
 396
 397        if (tx_mask == 0 || rx_mask != 0)
 398                return -EINVAL;
 399
 400        if (slots == 1) {
 401                if (tx_mask != 1)
 402                        return -EINVAL;
 403                left_slot = 0;
 404                right_slot = 0;
 405        } else {
 406                left_slot = __ffs(tx_mask);
 407                tx_mask &= ~(1 << left_slot);
 408                if (tx_mask == 0) {
 409                        right_slot = left_slot;
 410                } else {
 411                        right_slot = __ffs(tx_mask);
 412                        tx_mask &= ~(1 << right_slot);
 413                }
 414        }
 415
 416        if (tx_mask != 0 || left_slot >= slots || right_slot >= slots)
 417                return -EINVAL;
 418
 419        slots_cfg = (right_slot << TAS2764_TDM_CFG3_RXS_SHIFT) | left_slot;
 420
 421        ret = snd_soc_component_write(component, TAS2764_TDM_CFG3, slots_cfg);
 422        if (ret)
 423                return ret;
 424
 425        switch (slot_width) {
 426        case 16:
 427                slot_size = TAS2764_TDM_CFG2_RXS_16BITS;
 428                break;
 429        case 24:
 430                slot_size = TAS2764_TDM_CFG2_RXS_24BITS;
 431                break;
 432        case 32:
 433                slot_size = TAS2764_TDM_CFG2_RXS_32BITS;
 434                break;
 435        default:
 436                return -EINVAL;
 437        }
 438
 439        ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG2,
 440                                            TAS2764_TDM_CFG2_RXS_MASK,
 441                                            slot_size);
 442        if (ret < 0)
 443                return ret;
 444
 445        ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG5,
 446                                            TAS2764_TDM_CFG5_50_MASK,
 447                                            tas2764->v_sense_slot);
 448        if (ret < 0)
 449                return ret;
 450
 451        ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG6,
 452                                            TAS2764_TDM_CFG6_50_MASK,
 453                                            tas2764->i_sense_slot);
 454        if (ret < 0)
 455                return ret;
 456
 457        return 0;
 458}
 459
 460static struct snd_soc_dai_ops tas2764_dai_ops = {
 461        .mute_stream = tas2764_mute,
 462        .hw_params  = tas2764_hw_params,
 463        .set_fmt    = tas2764_set_fmt,
 464        .set_tdm_slot = tas2764_set_dai_tdm_slot,
 465        .no_capture_mute = 1,
 466};
 467
 468#define TAS2764_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
 469                         SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
 470
 471#define TAS2764_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
 472                       SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_88200)
 473
 474static struct snd_soc_dai_driver tas2764_dai_driver[] = {
 475        {
 476                .name = "tas2764 ASI1",
 477                .id = 0,
 478                .playback = {
 479                        .stream_name    = "ASI1 Playback",
 480                        .channels_min   = 2,
 481                        .channels_max   = 2,
 482                        .rates      = TAS2764_RATES,
 483                        .formats    = TAS2764_FORMATS,
 484                },
 485                .capture = {
 486                        .stream_name    = "ASI1 Capture",
 487                        .channels_min   = 0,
 488                        .channels_max   = 2,
 489                        .rates = TAS2764_RATES,
 490                        .formats = TAS2764_FORMATS,
 491                },
 492                .ops = &tas2764_dai_ops,
 493                .symmetric_rates = 1,
 494        },
 495};
 496
 497static int tas2764_codec_probe(struct snd_soc_component *component)
 498{
 499        struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component);
 500        int ret;
 501
 502        tas2764->component = component;
 503
 504        if (tas2764->sdz_gpio)
 505                gpiod_set_value_cansleep(tas2764->sdz_gpio, 1);
 506
 507        tas2764_reset(tas2764);
 508
 509        ret = snd_soc_component_update_bits(tas2764->component, TAS2764_TDM_CFG5,
 510                                            TAS2764_TDM_CFG5_VSNS_ENABLE, 0);
 511        if (ret < 0)
 512                return ret;
 513
 514        ret = snd_soc_component_update_bits(tas2764->component, TAS2764_TDM_CFG6,
 515                                            TAS2764_TDM_CFG6_ISNS_ENABLE, 0);
 516        if (ret < 0)
 517                return ret;
 518
 519        ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL,
 520                                            TAS2764_PWR_CTRL_MASK,
 521                                            TAS2764_PWR_CTRL_MUTE);
 522        if (ret < 0)
 523                return ret;
 524
 525        return 0;
 526}
 527
 528static DECLARE_TLV_DB_SCALE(tas2764_digital_tlv, 1100, 50, 0);
 529static DECLARE_TLV_DB_SCALE(tas2764_playback_volume, -10000, 50, 0);
 530
 531static const struct snd_kcontrol_new tas2764_snd_controls[] = {
 532        SOC_SINGLE_TLV("Speaker Volume", TAS2764_DVC, 0,
 533                       TAS2764_DVC_MAX, 1, tas2764_playback_volume),
 534        SOC_SINGLE_TLV("Amp Gain Volume", TAS2764_CHNL_0, 0, 0x14, 0,
 535                       tas2764_digital_tlv),
 536};
 537
 538static const struct snd_soc_component_driver soc_component_driver_tas2764 = {
 539        .probe                  = tas2764_codec_probe,
 540        .suspend                = tas2764_codec_suspend,
 541        .resume                 = tas2764_codec_resume,
 542        .set_bias_level         = tas2764_set_bias_level,
 543        .controls               = tas2764_snd_controls,
 544        .num_controls           = ARRAY_SIZE(tas2764_snd_controls),
 545        .dapm_widgets           = tas2764_dapm_widgets,
 546        .num_dapm_widgets       = ARRAY_SIZE(tas2764_dapm_widgets),
 547        .dapm_routes            = tas2764_audio_map,
 548        .num_dapm_routes        = ARRAY_SIZE(tas2764_audio_map),
 549        .idle_bias_on           = 1,
 550        .endianness             = 1,
 551        .non_legacy_dai_naming  = 1,
 552};
 553
 554static const struct reg_default tas2764_reg_defaults[] = {
 555        { TAS2764_PAGE, 0x00 },
 556        { TAS2764_SW_RST, 0x00 },
 557        { TAS2764_PWR_CTRL, 0x1a },
 558        { TAS2764_DVC, 0x00 },
 559        { TAS2764_CHNL_0, 0x00 },
 560        { TAS2764_TDM_CFG0, 0x09 },
 561        { TAS2764_TDM_CFG1, 0x02 },
 562        { TAS2764_TDM_CFG2, 0x0a },
 563        { TAS2764_TDM_CFG3, 0x10 },
 564        { TAS2764_TDM_CFG5, 0x42 },
 565};
 566
 567static const struct regmap_range_cfg tas2764_regmap_ranges[] = {
 568        {
 569                .range_min = 0,
 570                .range_max = 1 * 128,
 571                .selector_reg = TAS2764_PAGE,
 572                .selector_mask = 0xff,
 573                .selector_shift = 0,
 574                .window_start = 0,
 575                .window_len = 128,
 576        },
 577};
 578
 579static const struct regmap_config tas2764_i2c_regmap = {
 580        .reg_bits = 8,
 581        .val_bits = 8,
 582        .reg_defaults = tas2764_reg_defaults,
 583        .num_reg_defaults = ARRAY_SIZE(tas2764_reg_defaults),
 584        .cache_type = REGCACHE_RBTREE,
 585        .ranges = tas2764_regmap_ranges,
 586        .num_ranges = ARRAY_SIZE(tas2764_regmap_ranges),
 587        .max_register = 1 * 128,
 588};
 589
 590static int tas2764_parse_dt(struct device *dev, struct tas2764_priv *tas2764)
 591{
 592        int ret = 0;
 593
 594        tas2764->reset_gpio = devm_gpiod_get_optional(tas2764->dev, "reset",
 595                                                      GPIOD_OUT_HIGH);
 596        if (IS_ERR(tas2764->reset_gpio)) {
 597                if (PTR_ERR(tas2764->reset_gpio) == -EPROBE_DEFER) {
 598                        tas2764->reset_gpio = NULL;
 599                        return -EPROBE_DEFER;
 600                }
 601        }
 602
 603        tas2764->sdz_gpio = devm_gpiod_get_optional(dev, "shutdown", GPIOD_OUT_HIGH);
 604        if (IS_ERR(tas2764->sdz_gpio)) {
 605                if (PTR_ERR(tas2764->sdz_gpio) == -EPROBE_DEFER)
 606                        return -EPROBE_DEFER;
 607
 608                tas2764->sdz_gpio = NULL;
 609        }
 610
 611        ret = fwnode_property_read_u32(dev->fwnode, "ti,imon-slot-no",
 612                                       &tas2764->i_sense_slot);
 613        if (ret)
 614                tas2764->i_sense_slot = 0;
 615
 616        ret = fwnode_property_read_u32(dev->fwnode, "ti,vmon-slot-no",
 617                                       &tas2764->v_sense_slot);
 618        if (ret)
 619                tas2764->v_sense_slot = 2;
 620
 621        return 0;
 622}
 623
 624static int tas2764_i2c_probe(struct i2c_client *client,
 625                        const struct i2c_device_id *id)
 626{
 627        struct tas2764_priv *tas2764;
 628        int result;
 629
 630        tas2764 = devm_kzalloc(&client->dev, sizeof(struct tas2764_priv),
 631                               GFP_KERNEL);
 632        if (!tas2764)
 633                return -ENOMEM;
 634
 635        tas2764->dev = &client->dev;
 636        i2c_set_clientdata(client, tas2764);
 637        dev_set_drvdata(&client->dev, tas2764);
 638
 639        tas2764->regmap = devm_regmap_init_i2c(client, &tas2764_i2c_regmap);
 640        if (IS_ERR(tas2764->regmap)) {
 641                result = PTR_ERR(tas2764->regmap);
 642                dev_err(&client->dev, "Failed to allocate register map: %d\n",
 643                                        result);
 644                return result;
 645        }
 646
 647        if (client->dev.of_node) {
 648                result = tas2764_parse_dt(&client->dev, tas2764);
 649                if (result) {
 650                        dev_err(tas2764->dev, "%s: Failed to parse devicetree\n",
 651                                __func__);
 652                        return result;
 653                }
 654        }
 655
 656        return devm_snd_soc_register_component(tas2764->dev,
 657                                               &soc_component_driver_tas2764,
 658                                               tas2764_dai_driver,
 659                                               ARRAY_SIZE(tas2764_dai_driver));
 660}
 661
 662static const struct i2c_device_id tas2764_i2c_id[] = {
 663        { "tas2764", 0},
 664        { }
 665};
 666MODULE_DEVICE_TABLE(i2c, tas2764_i2c_id);
 667
 668#if defined(CONFIG_OF)
 669static const struct of_device_id tas2764_of_match[] = {
 670        { .compatible = "ti,tas2764" },
 671        {},
 672};
 673MODULE_DEVICE_TABLE(of, tas2764_of_match);
 674#endif
 675
 676static struct i2c_driver tas2764_i2c_driver = {
 677        .driver = {
 678                .name   = "tas2764",
 679                .of_match_table = of_match_ptr(tas2764_of_match),
 680        },
 681        .probe      = tas2764_i2c_probe,
 682        .id_table   = tas2764_i2c_id,
 683};
 684module_i2c_driver(tas2764_i2c_driver);
 685
 686MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
 687MODULE_DESCRIPTION("TAS2764 I2C Smart Amplifier driver");
 688MODULE_LICENSE("GPL v2");
 689