xref: /openbmc/linux/sound/soc/codecs/max98504.c (revision 2612e3bbc0386368a850140a6c9b990cd496a5ec)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
24c5d1469SSylwester Nawrocki /*
34c5d1469SSylwester Nawrocki  * MAX98504 ALSA SoC Audio driver
44c5d1469SSylwester Nawrocki  *
54c5d1469SSylwester Nawrocki  * Copyright 2013 - 2014 Maxim Integrated Products
64c5d1469SSylwester Nawrocki  * Copyright 2016 Samsung Electronics Co., Ltd.
74c5d1469SSylwester Nawrocki  */
84c5d1469SSylwester Nawrocki 
94c5d1469SSylwester Nawrocki #include <linux/delay.h>
104c5d1469SSylwester Nawrocki #include <linux/i2c.h>
114c5d1469SSylwester Nawrocki #include <linux/module.h>
124c5d1469SSylwester Nawrocki #include <linux/regulator/consumer.h>
134c5d1469SSylwester Nawrocki #include <linux/slab.h>
144c5d1469SSylwester Nawrocki #include <linux/types.h>
154c5d1469SSylwester Nawrocki #include <sound/soc.h>
164c5d1469SSylwester Nawrocki 
174c5d1469SSylwester Nawrocki #include "max98504.h"
184c5d1469SSylwester Nawrocki 
194c5d1469SSylwester Nawrocki static const char * const max98504_supply_names[] = {
204c5d1469SSylwester Nawrocki 	"DVDD",
214c5d1469SSylwester Nawrocki 	"DIOVDD",
224c5d1469SSylwester Nawrocki 	"PVDD",
234c5d1469SSylwester Nawrocki };
244c5d1469SSylwester Nawrocki #define MAX98504_NUM_SUPPLIES ARRAY_SIZE(max98504_supply_names)
254c5d1469SSylwester Nawrocki 
264c5d1469SSylwester Nawrocki struct max98504_priv {
274c5d1469SSylwester Nawrocki 	struct regmap *regmap;
284c5d1469SSylwester Nawrocki 	struct regulator_bulk_data supplies[MAX98504_NUM_SUPPLIES];
294c5d1469SSylwester Nawrocki 	unsigned int pcm_rx_channels;
304c5d1469SSylwester Nawrocki 	bool brownout_enable;
314c5d1469SSylwester Nawrocki 	unsigned int brownout_threshold;
324c5d1469SSylwester Nawrocki 	unsigned int brownout_attenuation;
334c5d1469SSylwester Nawrocki 	unsigned int brownout_attack_hold;
344c5d1469SSylwester Nawrocki 	unsigned int brownout_timed_hold;
354c5d1469SSylwester Nawrocki 	unsigned int brownout_release_rate;
364c5d1469SSylwester Nawrocki };
374c5d1469SSylwester Nawrocki 
384c5d1469SSylwester Nawrocki static struct reg_default max98504_reg_defaults[] = {
394c5d1469SSylwester Nawrocki 	{ 0x01,	0},
404c5d1469SSylwester Nawrocki 	{ 0x02,	0},
414c5d1469SSylwester Nawrocki 	{ 0x03,	0},
424c5d1469SSylwester Nawrocki 	{ 0x04,	0},
434c5d1469SSylwester Nawrocki 	{ 0x10, 0},
444c5d1469SSylwester Nawrocki 	{ 0x11, 0},
454c5d1469SSylwester Nawrocki 	{ 0x12, 0},
464c5d1469SSylwester Nawrocki 	{ 0x13, 0},
474c5d1469SSylwester Nawrocki 	{ 0x14, 0},
484c5d1469SSylwester Nawrocki 	{ 0x15, 0},
494c5d1469SSylwester Nawrocki 	{ 0x16, 0},
504c5d1469SSylwester Nawrocki 	{ 0x17, 0},
514c5d1469SSylwester Nawrocki 	{ 0x18, 0},
524c5d1469SSylwester Nawrocki 	{ 0x19, 0},
534c5d1469SSylwester Nawrocki 	{ 0x1A, 0},
544c5d1469SSylwester Nawrocki 	{ 0x20, 0},
554c5d1469SSylwester Nawrocki 	{ 0x21, 0},
564c5d1469SSylwester Nawrocki 	{ 0x22, 0},
574c5d1469SSylwester Nawrocki 	{ 0x23, 0},
584c5d1469SSylwester Nawrocki 	{ 0x24, 0},
594c5d1469SSylwester Nawrocki 	{ 0x25, 0},
604c5d1469SSylwester Nawrocki 	{ 0x26, 0},
614c5d1469SSylwester Nawrocki 	{ 0x27, 0},
624c5d1469SSylwester Nawrocki 	{ 0x28, 0},
634c5d1469SSylwester Nawrocki 	{ 0x30, 0},
644c5d1469SSylwester Nawrocki 	{ 0x31, 0},
654c5d1469SSylwester Nawrocki 	{ 0x32, 0},
664c5d1469SSylwester Nawrocki 	{ 0x33, 0},
674c5d1469SSylwester Nawrocki 	{ 0x34, 0},
684c5d1469SSylwester Nawrocki 	{ 0x35, 0},
694c5d1469SSylwester Nawrocki 	{ 0x36, 0},
704c5d1469SSylwester Nawrocki 	{ 0x37, 0},
714c5d1469SSylwester Nawrocki 	{ 0x38, 0},
724c5d1469SSylwester Nawrocki 	{ 0x39, 0},
734c5d1469SSylwester Nawrocki 	{ 0x40, 0},
744c5d1469SSylwester Nawrocki 	{ 0x41, 0},
754c5d1469SSylwester Nawrocki };
764c5d1469SSylwester Nawrocki 
max98504_volatile_register(struct device * dev,unsigned int reg)774c5d1469SSylwester Nawrocki static bool max98504_volatile_register(struct device *dev, unsigned int reg)
784c5d1469SSylwester Nawrocki {
794c5d1469SSylwester Nawrocki 	switch (reg) {
804c5d1469SSylwester Nawrocki 	case MAX98504_INTERRUPT_STATUS:
814c5d1469SSylwester Nawrocki 	case MAX98504_INTERRUPT_FLAGS:
824c5d1469SSylwester Nawrocki 	case MAX98504_INTERRUPT_FLAG_CLEARS:
834c5d1469SSylwester Nawrocki 	case MAX98504_WATCHDOG_CLEAR:
844c5d1469SSylwester Nawrocki 	case MAX98504_GLOBAL_ENABLE:
854c5d1469SSylwester Nawrocki 	case MAX98504_SOFTWARE_RESET:
864c5d1469SSylwester Nawrocki 		return true;
874c5d1469SSylwester Nawrocki 	default:
884c5d1469SSylwester Nawrocki 		return false;
894c5d1469SSylwester Nawrocki 	}
904c5d1469SSylwester Nawrocki }
914c5d1469SSylwester Nawrocki 
max98504_readable_register(struct device * dev,unsigned int reg)924c5d1469SSylwester Nawrocki static bool max98504_readable_register(struct device *dev, unsigned int reg)
934c5d1469SSylwester Nawrocki {
944c5d1469SSylwester Nawrocki 	switch (reg) {
954c5d1469SSylwester Nawrocki 	case MAX98504_SOFTWARE_RESET:
964c5d1469SSylwester Nawrocki 	case MAX98504_WATCHDOG_CLEAR:
974c5d1469SSylwester Nawrocki 	case MAX98504_INTERRUPT_FLAG_CLEARS:
984c5d1469SSylwester Nawrocki 		return false;
994c5d1469SSylwester Nawrocki 	default:
1004c5d1469SSylwester Nawrocki 		return true;
1014c5d1469SSylwester Nawrocki 	}
1024c5d1469SSylwester Nawrocki }
1034c5d1469SSylwester Nawrocki 
max98504_pcm_rx_ev(struct snd_soc_dapm_widget * w,struct snd_kcontrol * kcontrol,int event)1044c5d1469SSylwester Nawrocki static int max98504_pcm_rx_ev(struct snd_soc_dapm_widget *w,
1054c5d1469SSylwester Nawrocki 			      struct snd_kcontrol *kcontrol, int event)
1064c5d1469SSylwester Nawrocki {
1074c5d1469SSylwester Nawrocki 	struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
1084c5d1469SSylwester Nawrocki 	struct max98504_priv *max98504 = snd_soc_component_get_drvdata(c);
1094c5d1469SSylwester Nawrocki 
1104c5d1469SSylwester Nawrocki 	switch (event) {
1114c5d1469SSylwester Nawrocki 	case SND_SOC_DAPM_PRE_PMU:
1124c5d1469SSylwester Nawrocki 		regmap_write(max98504->regmap, MAX98504_PCM_RX_ENABLE,
1134c5d1469SSylwester Nawrocki 			     max98504->pcm_rx_channels);
1144c5d1469SSylwester Nawrocki 		break;
1154c5d1469SSylwester Nawrocki 	case SND_SOC_DAPM_POST_PMD:
1164c5d1469SSylwester Nawrocki 		regmap_write(max98504->regmap, MAX98504_PCM_RX_ENABLE, 0);
1174c5d1469SSylwester Nawrocki 		break;
1184c5d1469SSylwester Nawrocki 	}
1194c5d1469SSylwester Nawrocki 
1204c5d1469SSylwester Nawrocki 	return 0;
1214c5d1469SSylwester Nawrocki }
1224c5d1469SSylwester Nawrocki 
max98504_component_probe(struct snd_soc_component * c)1234c5d1469SSylwester Nawrocki static int max98504_component_probe(struct snd_soc_component *c)
1244c5d1469SSylwester Nawrocki {
1254c5d1469SSylwester Nawrocki 	struct max98504_priv *max98504 = snd_soc_component_get_drvdata(c);
1264c5d1469SSylwester Nawrocki 	struct regmap *map = max98504->regmap;
1274c5d1469SSylwester Nawrocki 	int ret;
1284c5d1469SSylwester Nawrocki 
1294c5d1469SSylwester Nawrocki 	ret = regulator_bulk_enable(MAX98504_NUM_SUPPLIES, max98504->supplies);
1304c5d1469SSylwester Nawrocki 	if (ret < 0)
1314c5d1469SSylwester Nawrocki 		return ret;
1324c5d1469SSylwester Nawrocki 
1334c5d1469SSylwester Nawrocki 	regmap_write(map, MAX98504_SOFTWARE_RESET, 0x1);
1344c5d1469SSylwester Nawrocki 	msleep(20);
1354c5d1469SSylwester Nawrocki 
1364c5d1469SSylwester Nawrocki 	if (!max98504->brownout_enable)
1374c5d1469SSylwester Nawrocki 		return 0;
1384c5d1469SSylwester Nawrocki 
1394c5d1469SSylwester Nawrocki 	regmap_write(map, MAX98504_PVDD_BROWNOUT_ENABLE, 0x1);
1404c5d1469SSylwester Nawrocki 
1414c5d1469SSylwester Nawrocki 	regmap_write(map, MAX98504_PVDD_BROWNOUT_CONFIG_1,
1424c5d1469SSylwester Nawrocki 		     (max98504->brownout_threshold & 0x1f) << 3 |
1434c5d1469SSylwester Nawrocki 		     (max98504->brownout_attenuation & 0x3));
1444c5d1469SSylwester Nawrocki 
1454c5d1469SSylwester Nawrocki 	regmap_write(map, MAX98504_PVDD_BROWNOUT_CONFIG_2,
1464c5d1469SSylwester Nawrocki 		     max98504->brownout_attack_hold & 0xff);
1474c5d1469SSylwester Nawrocki 
1484c5d1469SSylwester Nawrocki 	regmap_write(map, MAX98504_PVDD_BROWNOUT_CONFIG_3,
1494c5d1469SSylwester Nawrocki 		     max98504->brownout_timed_hold & 0xff);
1504c5d1469SSylwester Nawrocki 
1514c5d1469SSylwester Nawrocki 	regmap_write(map, MAX98504_PVDD_BROWNOUT_CONFIG_4,
1524c5d1469SSylwester Nawrocki 		     max98504->brownout_release_rate & 0xff);
1534c5d1469SSylwester Nawrocki 
1544c5d1469SSylwester Nawrocki 	return 0;
1554c5d1469SSylwester Nawrocki }
1564c5d1469SSylwester Nawrocki 
max98504_component_remove(struct snd_soc_component * c)1574c5d1469SSylwester Nawrocki static void max98504_component_remove(struct snd_soc_component *c)
1584c5d1469SSylwester Nawrocki {
1594c5d1469SSylwester Nawrocki 	struct max98504_priv *max98504 = snd_soc_component_get_drvdata(c);
1604c5d1469SSylwester Nawrocki 
1614c5d1469SSylwester Nawrocki 	regulator_bulk_disable(MAX98504_NUM_SUPPLIES, max98504->supplies);
1624c5d1469SSylwester Nawrocki }
1634c5d1469SSylwester Nawrocki 
1644c5d1469SSylwester Nawrocki static const char *spk_source_mux_text[] = {
1654c5d1469SSylwester Nawrocki 	"PCM Monomix", "Analog In", "PDM Left", "PDM Right"
1664c5d1469SSylwester Nawrocki };
1674c5d1469SSylwester Nawrocki 
1684c5d1469SSylwester Nawrocki static const struct soc_enum spk_source_mux_enum =
1694c5d1469SSylwester Nawrocki 	SOC_ENUM_SINGLE(MAX98504_SPEAKER_SOURCE_SELECT,
1704c5d1469SSylwester Nawrocki 			0, ARRAY_SIZE(spk_source_mux_text),
1714c5d1469SSylwester Nawrocki 			spk_source_mux_text);
1724c5d1469SSylwester Nawrocki 
1734c5d1469SSylwester Nawrocki static const struct snd_kcontrol_new spk_source_mux =
1744c5d1469SSylwester Nawrocki 	SOC_DAPM_ENUM("SPK Source", spk_source_mux_enum);
1754c5d1469SSylwester Nawrocki 
1764c5d1469SSylwester Nawrocki static const struct snd_soc_dapm_route max98504_dapm_routes[] = {
1774c5d1469SSylwester Nawrocki 	{ "SPKOUT", NULL, "Global Enable" },
1784c5d1469SSylwester Nawrocki 	{ "SPK Source", "PCM Monomix", "DAC PCM" },
1794c5d1469SSylwester Nawrocki 	{ "SPK Source", "Analog In", "AIN" },
1804c5d1469SSylwester Nawrocki 	{ "SPK Source", "PDM Left", "DAC PDM" },
1814c5d1469SSylwester Nawrocki 	{ "SPK Source", "PDM Right", "DAC PDM" },
1824c5d1469SSylwester Nawrocki };
1834c5d1469SSylwester Nawrocki 
1844c5d1469SSylwester Nawrocki static const struct snd_soc_dapm_widget max98504_dapm_widgets[] = {
1854c5d1469SSylwester Nawrocki 	SND_SOC_DAPM_SUPPLY("Global Enable", MAX98504_GLOBAL_ENABLE,
1864c5d1469SSylwester Nawrocki 		0, 0, NULL, 0),
1874c5d1469SSylwester Nawrocki 	SND_SOC_DAPM_INPUT("AIN"),
1884c5d1469SSylwester Nawrocki 	SND_SOC_DAPM_AIF_OUT("AIF2OUTL", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0),
1894c5d1469SSylwester Nawrocki 	SND_SOC_DAPM_AIF_OUT("AIF2OUTR", "AIF2 Capture", 1, SND_SOC_NOPM, 0, 0),
1904c5d1469SSylwester Nawrocki 	SND_SOC_DAPM_DAC_E("DAC PCM", NULL, SND_SOC_NOPM, 0, 0,
1914c5d1469SSylwester Nawrocki 		max98504_pcm_rx_ev,
1924c5d1469SSylwester Nawrocki 		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
1934c5d1469SSylwester Nawrocki 	SND_SOC_DAPM_DAC("DAC PDM", NULL, MAX98504_PDM_RX_ENABLE, 0, 0),
1944c5d1469SSylwester Nawrocki 	SND_SOC_DAPM_MUX("SPK Source", SND_SOC_NOPM, 0, 0, &spk_source_mux),
1954c5d1469SSylwester Nawrocki 	SND_SOC_DAPM_REG(snd_soc_dapm_spk, "SPKOUT",
1964c5d1469SSylwester Nawrocki 		MAX98504_SPEAKER_ENABLE, 0, 1, 1, 0),
1974c5d1469SSylwester Nawrocki };
1984c5d1469SSylwester Nawrocki 
max98504_set_tdm_slot(struct snd_soc_dai * dai,unsigned int tx_mask,unsigned int rx_mask,int slots,int slot_width)1994c5d1469SSylwester Nawrocki static int max98504_set_tdm_slot(struct snd_soc_dai *dai,
2004c5d1469SSylwester Nawrocki 		unsigned int tx_mask, unsigned int rx_mask,
2014c5d1469SSylwester Nawrocki 		int slots, int slot_width)
2024c5d1469SSylwester Nawrocki {
2034c5d1469SSylwester Nawrocki 	struct max98504_priv *max98504 = snd_soc_dai_get_drvdata(dai);
2044c5d1469SSylwester Nawrocki 	struct regmap *map = max98504->regmap;
2054c5d1469SSylwester Nawrocki 
2064c5d1469SSylwester Nawrocki 
2074c5d1469SSylwester Nawrocki 	switch (dai->id) {
2084c5d1469SSylwester Nawrocki 	case MAX98504_DAI_ID_PCM:
2094c5d1469SSylwester Nawrocki 		regmap_write(map, MAX98504_PCM_TX_ENABLE, tx_mask);
2104c5d1469SSylwester Nawrocki 		max98504->pcm_rx_channels = rx_mask;
2114c5d1469SSylwester Nawrocki 		break;
2124c5d1469SSylwester Nawrocki 
2134c5d1469SSylwester Nawrocki 	case MAX98504_DAI_ID_PDM:
2144c5d1469SSylwester Nawrocki 		regmap_write(map, MAX98504_PDM_TX_ENABLE, tx_mask);
2154c5d1469SSylwester Nawrocki 		break;
2164c5d1469SSylwester Nawrocki 	default:
2174c5d1469SSylwester Nawrocki 		WARN_ON(1);
2184c5d1469SSylwester Nawrocki 	}
2194c5d1469SSylwester Nawrocki 
2204c5d1469SSylwester Nawrocki 	return 0;
2214c5d1469SSylwester Nawrocki }
max98504_set_channel_map(struct snd_soc_dai * dai,unsigned int tx_num,unsigned int * tx_slot,unsigned int rx_num,unsigned int * rx_slot)2224c5d1469SSylwester Nawrocki static int max98504_set_channel_map(struct snd_soc_dai *dai,
2234c5d1469SSylwester Nawrocki 		unsigned int tx_num, unsigned int *tx_slot,
2244c5d1469SSylwester Nawrocki 		unsigned int rx_num, unsigned int *rx_slot)
2254c5d1469SSylwester Nawrocki {
2264c5d1469SSylwester Nawrocki 	struct max98504_priv *max98504 = snd_soc_dai_get_drvdata(dai);
2274c5d1469SSylwester Nawrocki 	struct regmap *map = max98504->regmap;
2284c5d1469SSylwester Nawrocki 	unsigned int i, sources = 0;
2294c5d1469SSylwester Nawrocki 
2304c5d1469SSylwester Nawrocki 	for (i = 0; i < tx_num; i++)
2314c5d1469SSylwester Nawrocki 		if (tx_slot[i])
2324c5d1469SSylwester Nawrocki 			sources |= (1 << i);
2334c5d1469SSylwester Nawrocki 
2344c5d1469SSylwester Nawrocki 	switch (dai->id) {
2354c5d1469SSylwester Nawrocki 	case MAX98504_DAI_ID_PCM:
2364c5d1469SSylwester Nawrocki 		regmap_write(map, MAX98504_PCM_TX_CHANNEL_SOURCES,
2374c5d1469SSylwester Nawrocki 			     sources);
2384c5d1469SSylwester Nawrocki 		break;
2394c5d1469SSylwester Nawrocki 
2404c5d1469SSylwester Nawrocki 	case MAX98504_DAI_ID_PDM:
2414c5d1469SSylwester Nawrocki 		regmap_write(map, MAX98504_PDM_TX_CONTROL, sources);
2424c5d1469SSylwester Nawrocki 		break;
2434c5d1469SSylwester Nawrocki 	default:
2444c5d1469SSylwester Nawrocki 		WARN_ON(1);
2454c5d1469SSylwester Nawrocki 	}
2464c5d1469SSylwester Nawrocki 
2474c5d1469SSylwester Nawrocki 	regmap_write(map, MAX98504_MEASUREMENT_ENABLE, sources ? 0x3 : 0x01);
2484c5d1469SSylwester Nawrocki 
2494c5d1469SSylwester Nawrocki 	return 0;
2504c5d1469SSylwester Nawrocki }
2514c5d1469SSylwester Nawrocki 
2524c5d1469SSylwester Nawrocki static const struct snd_soc_dai_ops max98504_dai_ops = {
2534c5d1469SSylwester Nawrocki 	.set_tdm_slot		= max98504_set_tdm_slot,
2544c5d1469SSylwester Nawrocki 	.set_channel_map	= max98504_set_channel_map,
2554c5d1469SSylwester Nawrocki };
2564c5d1469SSylwester Nawrocki 
2574c5d1469SSylwester Nawrocki #define MAX98504_FORMATS	(SNDRV_PCM_FMTBIT_S8|SNDRV_PCM_FMTBIT_S16_LE|\
2584c5d1469SSylwester Nawrocki 				SNDRV_PCM_FMTBIT_S24_LE|SNDRV_PCM_FMTBIT_S32_LE)
2594c5d1469SSylwester Nawrocki #define MAX98504_PDM_RATES	(SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|\
2604c5d1469SSylwester Nawrocki 				SNDRV_PCM_RATE_32000|SNDRV_PCM_RATE_44100|\
2614c5d1469SSylwester Nawrocki 				SNDRV_PCM_RATE_48000|SNDRV_PCM_RATE_88200|\
2624c5d1469SSylwester Nawrocki 				SNDRV_PCM_RATE_96000)
2634c5d1469SSylwester Nawrocki 
2644c5d1469SSylwester Nawrocki static struct snd_soc_dai_driver max98504_dai[] = {
2654c5d1469SSylwester Nawrocki 	/* TODO: Add the PCM interface definitions */
2664c5d1469SSylwester Nawrocki 	{
2674c5d1469SSylwester Nawrocki 		.name = "max98504-aif2",
2684c5d1469SSylwester Nawrocki 		.id = MAX98504_DAI_ID_PDM,
2694c5d1469SSylwester Nawrocki 		.playback = {
2704c5d1469SSylwester Nawrocki 			.stream_name	= "AIF2 Playback",
2714c5d1469SSylwester Nawrocki 			.channels_min	= 1,
2724c5d1469SSylwester Nawrocki 			.channels_max	= 2,
2734c5d1469SSylwester Nawrocki 			.rates		= MAX98504_PDM_RATES,
2744c5d1469SSylwester Nawrocki 			.formats	= MAX98504_FORMATS,
2754c5d1469SSylwester Nawrocki 		},
2764c5d1469SSylwester Nawrocki 		.capture = {
2774c5d1469SSylwester Nawrocki 			.stream_name	= "AIF2 Capture",
2784c5d1469SSylwester Nawrocki 			.channels_min	= 1,
2794c5d1469SSylwester Nawrocki 			.channels_max	= 2,
2804c5d1469SSylwester Nawrocki 			.rates		= MAX98504_PDM_RATES,
2814c5d1469SSylwester Nawrocki 			.formats	= MAX98504_FORMATS,
2824c5d1469SSylwester Nawrocki 		},
2834c5d1469SSylwester Nawrocki 		.ops = &max98504_dai_ops,
2844c5d1469SSylwester Nawrocki 	},
2854c5d1469SSylwester Nawrocki };
2864c5d1469SSylwester Nawrocki 
2874c5d1469SSylwester Nawrocki static const struct snd_soc_component_driver max98504_component_driver = {
2884c5d1469SSylwester Nawrocki 	.probe			= max98504_component_probe,
2894c5d1469SSylwester Nawrocki 	.remove			= max98504_component_remove,
2904c5d1469SSylwester Nawrocki 	.dapm_widgets		= max98504_dapm_widgets,
2914c5d1469SSylwester Nawrocki 	.num_dapm_widgets	= ARRAY_SIZE(max98504_dapm_widgets),
2924c5d1469SSylwester Nawrocki 	.dapm_routes		= max98504_dapm_routes,
2934c5d1469SSylwester Nawrocki 	.num_dapm_routes	= ARRAY_SIZE(max98504_dapm_routes),
294a0556e3aSCharles Keepax 	.endianness		= 1,
2954c5d1469SSylwester Nawrocki };
2964c5d1469SSylwester Nawrocki 
2974c5d1469SSylwester Nawrocki static const struct regmap_config max98504_regmap = {
2984c5d1469SSylwester Nawrocki 	.reg_bits		= 16,
2994c5d1469SSylwester Nawrocki 	.val_bits		= 8,
3004c5d1469SSylwester Nawrocki 	.max_register		= MAX98504_MAX_REGISTER,
3014c5d1469SSylwester Nawrocki 	.reg_defaults		= max98504_reg_defaults,
3024c5d1469SSylwester Nawrocki 	.num_reg_defaults	= ARRAY_SIZE(max98504_reg_defaults),
3034c5d1469SSylwester Nawrocki 	.volatile_reg		= max98504_volatile_register,
3044c5d1469SSylwester Nawrocki 	.readable_reg		= max98504_readable_register,
3054c5d1469SSylwester Nawrocki 	.cache_type		= REGCACHE_RBTREE,
3064c5d1469SSylwester Nawrocki };
3074c5d1469SSylwester Nawrocki 
max98504_i2c_probe(struct i2c_client * client)308fead49e3SStephen Kitt static int max98504_i2c_probe(struct i2c_client *client)
3094c5d1469SSylwester Nawrocki {
3104c5d1469SSylwester Nawrocki 	struct device *dev = &client->dev;
3114c5d1469SSylwester Nawrocki 	struct device_node *node = dev->of_node;
3124c5d1469SSylwester Nawrocki 	struct max98504_priv *max98504;
3134c5d1469SSylwester Nawrocki 	int i, ret;
3144c5d1469SSylwester Nawrocki 
3154c5d1469SSylwester Nawrocki 	max98504 = devm_kzalloc(dev, sizeof(*max98504), GFP_KERNEL);
3164c5d1469SSylwester Nawrocki 	if (!max98504)
3174c5d1469SSylwester Nawrocki 		return -ENOMEM;
3184c5d1469SSylwester Nawrocki 
3194c5d1469SSylwester Nawrocki 	if (node) {
3204c5d1469SSylwester Nawrocki 		if (!of_property_read_u32(node, "maxim,brownout-threshold",
3214c5d1469SSylwester Nawrocki 					&max98504->brownout_threshold))
3224c5d1469SSylwester Nawrocki 			max98504->brownout_enable = true;
3234c5d1469SSylwester Nawrocki 
3244c5d1469SSylwester Nawrocki 		of_property_read_u32(node, "maxim,brownout-attenuation",
3254c5d1469SSylwester Nawrocki 					&max98504->brownout_attenuation);
3264c5d1469SSylwester Nawrocki 		of_property_read_u32(node, "maxim,brownout-attack-hold-ms",
3274c5d1469SSylwester Nawrocki 					&max98504->brownout_attack_hold);
3284c5d1469SSylwester Nawrocki 		of_property_read_u32(node, "maxim,brownout-timed-hold-ms",
3294c5d1469SSylwester Nawrocki 					&max98504->brownout_timed_hold);
3304c5d1469SSylwester Nawrocki 		of_property_read_u32(node, "maxim,brownout-release-rate-ms",
3314c5d1469SSylwester Nawrocki 					&max98504->brownout_release_rate);
3324c5d1469SSylwester Nawrocki 	}
3334c5d1469SSylwester Nawrocki 
3344c5d1469SSylwester Nawrocki 	max98504->regmap = devm_regmap_init_i2c(client, &max98504_regmap);
3354c5d1469SSylwester Nawrocki 	if (IS_ERR(max98504->regmap)) {
3364c5d1469SSylwester Nawrocki 		ret = PTR_ERR(max98504->regmap);
3374c5d1469SSylwester Nawrocki 		dev_err(&client->dev, "regmap initialization failed: %d\n", ret);
3384c5d1469SSylwester Nawrocki 		return ret;
3394c5d1469SSylwester Nawrocki 	}
3404c5d1469SSylwester Nawrocki 
3414c5d1469SSylwester Nawrocki 	for (i = 0; i < MAX98504_NUM_SUPPLIES; i++)
3424c5d1469SSylwester Nawrocki 		max98504->supplies[i].supply = max98504_supply_names[i];
3434c5d1469SSylwester Nawrocki 
3444c5d1469SSylwester Nawrocki 	ret = devm_regulator_bulk_get(dev, MAX98504_NUM_SUPPLIES,
3454c5d1469SSylwester Nawrocki 				      max98504->supplies);
3464c5d1469SSylwester Nawrocki 	if (ret < 0)
3474c5d1469SSylwester Nawrocki 		return ret;
3484c5d1469SSylwester Nawrocki 
3494c5d1469SSylwester Nawrocki 	i2c_set_clientdata(client, max98504);
3504c5d1469SSylwester Nawrocki 
3514c5d1469SSylwester Nawrocki 	return devm_snd_soc_register_component(dev, &max98504_component_driver,
3524c5d1469SSylwester Nawrocki 				max98504_dai, ARRAY_SIZE(max98504_dai));
3534c5d1469SSylwester Nawrocki }
3544c5d1469SSylwester Nawrocki 
3554c5d1469SSylwester Nawrocki #ifdef CONFIG_OF
3564c5d1469SSylwester Nawrocki static const struct of_device_id max98504_of_match[] = {
3574c5d1469SSylwester Nawrocki 	{ .compatible = "maxim,max98504" },
3584c5d1469SSylwester Nawrocki 	{ },
3594c5d1469SSylwester Nawrocki };
3604c5d1469SSylwester Nawrocki MODULE_DEVICE_TABLE(of, max98504_of_match);
3614c5d1469SSylwester Nawrocki #endif
3624c5d1469SSylwester Nawrocki 
3634c5d1469SSylwester Nawrocki static const struct i2c_device_id max98504_i2c_id[] = {
3644c5d1469SSylwester Nawrocki 	{ "max98504" },
3654c5d1469SSylwester Nawrocki 	{ }
3664c5d1469SSylwester Nawrocki };
3674c5d1469SSylwester Nawrocki MODULE_DEVICE_TABLE(i2c, max98504_i2c_id);
3684c5d1469SSylwester Nawrocki 
3694c5d1469SSylwester Nawrocki static struct i2c_driver max98504_i2c_driver = {
3704c5d1469SSylwester Nawrocki 	.driver = {
3714c5d1469SSylwester Nawrocki 		.name = "max98504",
3724c5d1469SSylwester Nawrocki 		.of_match_table = of_match_ptr(max98504_of_match),
3734c5d1469SSylwester Nawrocki 	},
374*9abcd240SUwe Kleine-König 	.probe = max98504_i2c_probe,
3754c5d1469SSylwester Nawrocki 	.id_table = max98504_i2c_id,
3764c5d1469SSylwester Nawrocki };
3774c5d1469SSylwester Nawrocki module_i2c_driver(max98504_i2c_driver);
3784c5d1469SSylwester Nawrocki 
3794c5d1469SSylwester Nawrocki MODULE_DESCRIPTION("ASoC MAX98504 driver");
3804c5d1469SSylwester Nawrocki MODULE_LICENSE("GPL");
381