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