cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

tas2764.c (17690B)


      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 const 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_rate = 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{
    626	struct tas2764_priv *tas2764;
    627	int result;
    628
    629	tas2764 = devm_kzalloc(&client->dev, sizeof(struct tas2764_priv),
    630			       GFP_KERNEL);
    631	if (!tas2764)
    632		return -ENOMEM;
    633
    634	tas2764->dev = &client->dev;
    635	i2c_set_clientdata(client, tas2764);
    636	dev_set_drvdata(&client->dev, tas2764);
    637
    638	tas2764->regmap = devm_regmap_init_i2c(client, &tas2764_i2c_regmap);
    639	if (IS_ERR(tas2764->regmap)) {
    640		result = PTR_ERR(tas2764->regmap);
    641		dev_err(&client->dev, "Failed to allocate register map: %d\n",
    642					result);
    643		return result;
    644	}
    645
    646	if (client->dev.of_node) {
    647		result = tas2764_parse_dt(&client->dev, tas2764);
    648		if (result) {
    649			dev_err(tas2764->dev, "%s: Failed to parse devicetree\n",
    650				__func__);
    651			return result;
    652		}
    653	}
    654
    655	return devm_snd_soc_register_component(tas2764->dev,
    656					       &soc_component_driver_tas2764,
    657					       tas2764_dai_driver,
    658					       ARRAY_SIZE(tas2764_dai_driver));
    659}
    660
    661static const struct i2c_device_id tas2764_i2c_id[] = {
    662	{ "tas2764", 0},
    663	{ }
    664};
    665MODULE_DEVICE_TABLE(i2c, tas2764_i2c_id);
    666
    667#if defined(CONFIG_OF)
    668static const struct of_device_id tas2764_of_match[] = {
    669	{ .compatible = "ti,tas2764" },
    670	{},
    671};
    672MODULE_DEVICE_TABLE(of, tas2764_of_match);
    673#endif
    674
    675static struct i2c_driver tas2764_i2c_driver = {
    676	.driver = {
    677		.name   = "tas2764",
    678		.of_match_table = of_match_ptr(tas2764_of_match),
    679	},
    680	.probe_new  = tas2764_i2c_probe,
    681	.id_table   = tas2764_i2c_id,
    682};
    683module_i2c_driver(tas2764_i2c_driver);
    684
    685MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
    686MODULE_DESCRIPTION("TAS2764 I2C Smart Amplifier driver");
    687MODULE_LICENSE("GPL v2");