14ae340d1SMylène Josserand // SPDX-License-Identifier: GPL-2.0 24ae340d1SMylène Josserand // Audio driver for PCM1789 34ae340d1SMylène Josserand // Copyright (C) 2018 Bootlin 44ae340d1SMylène Josserand // Mylène Josserand <mylene.josserand@bootlin.com> 54ae340d1SMylène Josserand 63217004aSArnd Bergmann #include <linux/gpio/consumer.h> 74ae340d1SMylène Josserand #include <linux/module.h> 84ae340d1SMylène Josserand #include <linux/workqueue.h> 94ae340d1SMylène Josserand 104ae340d1SMylène Josserand #include <sound/pcm_params.h> 114ae340d1SMylène Josserand #include <sound/soc.h> 124ae340d1SMylène Josserand #include <sound/tlv.h> 134ae340d1SMylène Josserand 144ae340d1SMylène Josserand #include "pcm1789.h" 154ae340d1SMylène Josserand 164ae340d1SMylène Josserand #define PCM1789_MUTE_CONTROL 0x10 174ae340d1SMylène Josserand #define PCM1789_FMT_CONTROL 0x11 184ae340d1SMylène Josserand #define PCM1789_SOFT_MUTE 0x14 194ae340d1SMylène Josserand #define PCM1789_DAC_VOL_LEFT 0x18 204ae340d1SMylène Josserand #define PCM1789_DAC_VOL_RIGHT 0x19 214ae340d1SMylène Josserand 224ae340d1SMylène Josserand #define PCM1789_FMT_MASK 0x07 234ae340d1SMylène Josserand #define PCM1789_MUTE_MASK 0x03 244ae340d1SMylène Josserand #define PCM1789_MUTE_SRET 0x06 254ae340d1SMylène Josserand 264ae340d1SMylène Josserand struct pcm1789_private { 274ae340d1SMylène Josserand struct regmap *regmap; 284ae340d1SMylène Josserand unsigned int format; 294ae340d1SMylène Josserand unsigned int rate; 304ae340d1SMylène Josserand struct gpio_desc *reset; 314ae340d1SMylène Josserand struct work_struct work; 324ae340d1SMylène Josserand struct device *dev; 334ae340d1SMylène Josserand }; 344ae340d1SMylène Josserand 354ae340d1SMylène Josserand static const struct reg_default pcm1789_reg_defaults[] = { 364ae340d1SMylène Josserand { PCM1789_FMT_CONTROL, 0x00 }, 374ae340d1SMylène Josserand { PCM1789_SOFT_MUTE, 0x00 }, 384ae340d1SMylène Josserand { PCM1789_DAC_VOL_LEFT, 0xff }, 394ae340d1SMylène Josserand { PCM1789_DAC_VOL_RIGHT, 0xff }, 404ae340d1SMylène Josserand }; 414ae340d1SMylène Josserand 424ae340d1SMylène Josserand static bool pcm1789_accessible_reg(struct device *dev, unsigned int reg) 434ae340d1SMylène Josserand { 444ae340d1SMylène Josserand return reg >= PCM1789_MUTE_CONTROL && reg <= PCM1789_DAC_VOL_RIGHT; 454ae340d1SMylène Josserand } 464ae340d1SMylène Josserand 474ae340d1SMylène Josserand static bool pcm1789_writeable_reg(struct device *dev, unsigned int reg) 484ae340d1SMylène Josserand { 494ae340d1SMylène Josserand return pcm1789_accessible_reg(dev, reg); 504ae340d1SMylène Josserand } 514ae340d1SMylène Josserand 524ae340d1SMylène Josserand static int pcm1789_set_dai_fmt(struct snd_soc_dai *codec_dai, 534ae340d1SMylène Josserand unsigned int format) 544ae340d1SMylène Josserand { 554ae340d1SMylène Josserand struct snd_soc_component *component = codec_dai->component; 564ae340d1SMylène Josserand struct pcm1789_private *priv = snd_soc_component_get_drvdata(component); 574ae340d1SMylène Josserand 584ae340d1SMylène Josserand priv->format = format; 594ae340d1SMylène Josserand 604ae340d1SMylène Josserand return 0; 614ae340d1SMylène Josserand } 624ae340d1SMylène Josserand 63*1eb2c43dSKuninori Morimoto static int pcm1789_mute(struct snd_soc_dai *codec_dai, int mute, int direction) 644ae340d1SMylène Josserand { 654ae340d1SMylène Josserand struct snd_soc_component *component = codec_dai->component; 664ae340d1SMylène Josserand struct pcm1789_private *priv = snd_soc_component_get_drvdata(component); 674ae340d1SMylène Josserand 684ae340d1SMylène Josserand return regmap_update_bits(priv->regmap, PCM1789_SOFT_MUTE, 694ae340d1SMylène Josserand PCM1789_MUTE_MASK, 704ae340d1SMylène Josserand mute ? 0 : PCM1789_MUTE_MASK); 714ae340d1SMylène Josserand } 724ae340d1SMylène Josserand 734ae340d1SMylène Josserand static int pcm1789_hw_params(struct snd_pcm_substream *substream, 744ae340d1SMylène Josserand struct snd_pcm_hw_params *params, 754ae340d1SMylène Josserand struct snd_soc_dai *codec_dai) 764ae340d1SMylène Josserand { 774ae340d1SMylène Josserand struct snd_soc_component *component = codec_dai->component; 784ae340d1SMylène Josserand struct pcm1789_private *priv = snd_soc_component_get_drvdata(component); 794ae340d1SMylène Josserand int val = 0, ret; 804ae340d1SMylène Josserand 814ae340d1SMylène Josserand priv->rate = params_rate(params); 824ae340d1SMylène Josserand 834ae340d1SMylène Josserand switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) { 844ae340d1SMylène Josserand case SND_SOC_DAIFMT_RIGHT_J: 854ae340d1SMylène Josserand switch (params_width(params)) { 864ae340d1SMylène Josserand case 24: 874ae340d1SMylène Josserand val = 2; 884ae340d1SMylène Josserand break; 894ae340d1SMylène Josserand case 16: 904ae340d1SMylène Josserand val = 3; 914ae340d1SMylène Josserand break; 924ae340d1SMylène Josserand default: 934ae340d1SMylène Josserand return -EINVAL; 944ae340d1SMylène Josserand } 954ae340d1SMylène Josserand break; 964ae340d1SMylène Josserand case SND_SOC_DAIFMT_I2S: 974ae340d1SMylène Josserand switch (params_width(params)) { 984ae340d1SMylène Josserand case 16: 994ae340d1SMylène Josserand case 24: 1004ae340d1SMylène Josserand case 32: 1014ae340d1SMylène Josserand val = 0; 1024ae340d1SMylène Josserand break; 1034ae340d1SMylène Josserand default: 1044ae340d1SMylène Josserand return -EINVAL; 1054ae340d1SMylène Josserand } 1064ae340d1SMylène Josserand break; 1074ae340d1SMylène Josserand case SND_SOC_DAIFMT_LEFT_J: 1084ae340d1SMylène Josserand switch (params_width(params)) { 1094ae340d1SMylène Josserand case 16: 1104ae340d1SMylène Josserand case 24: 1114ae340d1SMylène Josserand case 32: 1124ae340d1SMylène Josserand val = 1; 1134ae340d1SMylène Josserand break; 1144ae340d1SMylène Josserand default: 1154ae340d1SMylène Josserand return -EINVAL; 1164ae340d1SMylène Josserand } 1174ae340d1SMylène Josserand break; 1184ae340d1SMylène Josserand default: 1194ae340d1SMylène Josserand dev_err(component->dev, "Invalid DAI format\n"); 1204ae340d1SMylène Josserand return -EINVAL; 1214ae340d1SMylène Josserand } 1224ae340d1SMylène Josserand 1234ae340d1SMylène Josserand ret = regmap_update_bits(priv->regmap, PCM1789_FMT_CONTROL, 1244ae340d1SMylène Josserand PCM1789_FMT_MASK, val); 1254ae340d1SMylène Josserand if (ret < 0) 1264ae340d1SMylène Josserand return ret; 1274ae340d1SMylène Josserand 1284ae340d1SMylène Josserand return 0; 1294ae340d1SMylène Josserand } 1304ae340d1SMylène Josserand 1314ae340d1SMylène Josserand static void pcm1789_work_queue(struct work_struct *work) 1324ae340d1SMylène Josserand { 1334ae340d1SMylène Josserand struct pcm1789_private *priv = container_of(work, 1344ae340d1SMylène Josserand struct pcm1789_private, 1354ae340d1SMylène Josserand work); 1364ae340d1SMylène Josserand 1374ae340d1SMylène Josserand /* Perform a software reset to remove codec from desynchronized state */ 1384ae340d1SMylène Josserand if (regmap_update_bits(priv->regmap, PCM1789_MUTE_CONTROL, 1394ae340d1SMylène Josserand 0x3 << PCM1789_MUTE_SRET, 0) < 0) 1404ae340d1SMylène Josserand dev_err(priv->dev, "Error while setting SRET"); 1414ae340d1SMylène Josserand } 1424ae340d1SMylène Josserand 1434ae340d1SMylène Josserand static int pcm1789_trigger(struct snd_pcm_substream *substream, int cmd, 1444ae340d1SMylène Josserand struct snd_soc_dai *dai) 1454ae340d1SMylène Josserand { 1464ae340d1SMylène Josserand struct snd_soc_component *component = dai->component; 1474ae340d1SMylène Josserand struct pcm1789_private *priv = snd_soc_component_get_drvdata(component); 1484ae340d1SMylène Josserand int ret = 0; 1494ae340d1SMylène Josserand 1504ae340d1SMylène Josserand switch (cmd) { 1514ae340d1SMylène Josserand case SNDRV_PCM_TRIGGER_START: 1524ae340d1SMylène Josserand case SNDRV_PCM_TRIGGER_RESUME: 1534ae340d1SMylène Josserand case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 1544ae340d1SMylène Josserand schedule_work(&priv->work); 1554ae340d1SMylène Josserand break; 1564ae340d1SMylène Josserand case SNDRV_PCM_TRIGGER_STOP: 1574ae340d1SMylène Josserand case SNDRV_PCM_TRIGGER_SUSPEND: 1584ae340d1SMylène Josserand case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 1594ae340d1SMylène Josserand break; 1604ae340d1SMylène Josserand default: 1614ae340d1SMylène Josserand ret = -EINVAL; 1624ae340d1SMylène Josserand } 1634ae340d1SMylène Josserand 1644ae340d1SMylène Josserand return ret; 1654ae340d1SMylène Josserand } 1664ae340d1SMylène Josserand 1674ae340d1SMylène Josserand static const struct snd_soc_dai_ops pcm1789_dai_ops = { 1684ae340d1SMylène Josserand .set_fmt = pcm1789_set_dai_fmt, 1694ae340d1SMylène Josserand .hw_params = pcm1789_hw_params, 170*1eb2c43dSKuninori Morimoto .mute_stream = pcm1789_mute, 1714ae340d1SMylène Josserand .trigger = pcm1789_trigger, 172*1eb2c43dSKuninori Morimoto .no_capture_mute = 1, 1734ae340d1SMylène Josserand }; 1744ae340d1SMylène Josserand 1754ae340d1SMylène Josserand static const DECLARE_TLV_DB_SCALE(pcm1789_dac_tlv, -12000, 50, 1); 1764ae340d1SMylène Josserand 1774ae340d1SMylène Josserand static const struct snd_kcontrol_new pcm1789_controls[] = { 1784ae340d1SMylène Josserand SOC_DOUBLE_R_RANGE_TLV("DAC Playback Volume", PCM1789_DAC_VOL_LEFT, 1794ae340d1SMylène Josserand PCM1789_DAC_VOL_RIGHT, 0, 0xf, 0xff, 0, 1804ae340d1SMylène Josserand pcm1789_dac_tlv), 1814ae340d1SMylène Josserand }; 1824ae340d1SMylène Josserand 1834ae340d1SMylène Josserand static const struct snd_soc_dapm_widget pcm1789_dapm_widgets[] = { 1844ae340d1SMylène Josserand SND_SOC_DAPM_OUTPUT("IOUTL+"), 1854ae340d1SMylène Josserand SND_SOC_DAPM_OUTPUT("IOUTL-"), 1864ae340d1SMylène Josserand SND_SOC_DAPM_OUTPUT("IOUTR+"), 1874ae340d1SMylène Josserand SND_SOC_DAPM_OUTPUT("IOUTR-"), 1884ae340d1SMylène Josserand }; 1894ae340d1SMylène Josserand 1904ae340d1SMylène Josserand static const struct snd_soc_dapm_route pcm1789_dapm_routes[] = { 1914ae340d1SMylène Josserand { "IOUTL+", NULL, "Playback" }, 1924ae340d1SMylène Josserand { "IOUTL-", NULL, "Playback" }, 1934ae340d1SMylène Josserand { "IOUTR+", NULL, "Playback" }, 1944ae340d1SMylène Josserand { "IOUTR-", NULL, "Playback" }, 1954ae340d1SMylène Josserand }; 1964ae340d1SMylène Josserand 1974ae340d1SMylène Josserand static struct snd_soc_dai_driver pcm1789_dai = { 1984ae340d1SMylène Josserand .name = "pcm1789-hifi", 1994ae340d1SMylène Josserand .playback = { 2004ae340d1SMylène Josserand .stream_name = "Playback", 2014ae340d1SMylène Josserand .channels_min = 2, 2024ae340d1SMylène Josserand .channels_max = 2, 2034ae340d1SMylène Josserand .rates = SNDRV_PCM_RATE_CONTINUOUS, 2044ae340d1SMylène Josserand .rate_min = 10000, 2054ae340d1SMylène Josserand .rate_max = 200000, 2064ae340d1SMylène Josserand .formats = PCM1789_FORMATS, 2074ae340d1SMylène Josserand }, 2084ae340d1SMylène Josserand .ops = &pcm1789_dai_ops, 2094ae340d1SMylène Josserand }; 2104ae340d1SMylène Josserand 2114ae340d1SMylène Josserand const struct regmap_config pcm1789_regmap_config = { 2124ae340d1SMylène Josserand .reg_bits = 8, 2134ae340d1SMylène Josserand .val_bits = 8, 2144ae340d1SMylène Josserand .max_register = PCM1789_DAC_VOL_RIGHT, 2154ae340d1SMylène Josserand .reg_defaults = pcm1789_reg_defaults, 2164ae340d1SMylène Josserand .num_reg_defaults = ARRAY_SIZE(pcm1789_reg_defaults), 2174ae340d1SMylène Josserand .writeable_reg = pcm1789_writeable_reg, 2184ae340d1SMylène Josserand .readable_reg = pcm1789_accessible_reg, 2194ae340d1SMylène Josserand }; 2204ae340d1SMylène Josserand EXPORT_SYMBOL_GPL(pcm1789_regmap_config); 2214ae340d1SMylène Josserand 2224ae340d1SMylène Josserand static const struct snd_soc_component_driver soc_component_dev_pcm1789 = { 2234ae340d1SMylène Josserand .controls = pcm1789_controls, 2244ae340d1SMylène Josserand .num_controls = ARRAY_SIZE(pcm1789_controls), 2254ae340d1SMylène Josserand .dapm_widgets = pcm1789_dapm_widgets, 2264ae340d1SMylène Josserand .num_dapm_widgets = ARRAY_SIZE(pcm1789_dapm_widgets), 2274ae340d1SMylène Josserand .dapm_routes = pcm1789_dapm_routes, 2284ae340d1SMylène Josserand .num_dapm_routes = ARRAY_SIZE(pcm1789_dapm_routes), 2294ae340d1SMylène Josserand .idle_bias_on = 1, 2304ae340d1SMylène Josserand .use_pmdown_time = 1, 2314ae340d1SMylène Josserand .endianness = 1, 2324ae340d1SMylène Josserand .non_legacy_dai_naming = 1, 2334ae340d1SMylène Josserand }; 2344ae340d1SMylène Josserand 2354ae340d1SMylène Josserand int pcm1789_common_init(struct device *dev, struct regmap *regmap) 2364ae340d1SMylène Josserand { 2374ae340d1SMylène Josserand struct pcm1789_private *pcm1789; 2384ae340d1SMylène Josserand 2394ae340d1SMylène Josserand pcm1789 = devm_kzalloc(dev, sizeof(struct pcm1789_private), 2404ae340d1SMylène Josserand GFP_KERNEL); 2414ae340d1SMylène Josserand if (!pcm1789) 2424ae340d1SMylène Josserand return -ENOMEM; 2434ae340d1SMylène Josserand 2444ae340d1SMylène Josserand pcm1789->regmap = regmap; 2454ae340d1SMylène Josserand pcm1789->dev = dev; 2464ae340d1SMylène Josserand dev_set_drvdata(dev, pcm1789); 2474ae340d1SMylène Josserand 2484ae340d1SMylène Josserand pcm1789->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); 2494ae340d1SMylène Josserand if (IS_ERR(pcm1789->reset)) 2504ae340d1SMylène Josserand return PTR_ERR(pcm1789->reset); 2514ae340d1SMylène Josserand 2524ae340d1SMylène Josserand gpiod_set_value_cansleep(pcm1789->reset, 0); 2534ae340d1SMylène Josserand msleep(300); 2544ae340d1SMylène Josserand 2554ae340d1SMylène Josserand INIT_WORK(&pcm1789->work, pcm1789_work_queue); 2564ae340d1SMylène Josserand 2574ae340d1SMylène Josserand return devm_snd_soc_register_component(dev, &soc_component_dev_pcm1789, 2584ae340d1SMylène Josserand &pcm1789_dai, 1); 2594ae340d1SMylène Josserand } 2604ae340d1SMylène Josserand EXPORT_SYMBOL_GPL(pcm1789_common_init); 2614ae340d1SMylène Josserand 2624ae340d1SMylène Josserand int pcm1789_common_exit(struct device *dev) 2634ae340d1SMylène Josserand { 2644ae340d1SMylène Josserand struct pcm1789_private *priv = dev_get_drvdata(dev); 2654ae340d1SMylène Josserand 2664ae340d1SMylène Josserand flush_work(&priv->work); 2674ae340d1SMylène Josserand 2684ae340d1SMylène Josserand return 0; 2694ae340d1SMylène Josserand } 2704ae340d1SMylène Josserand EXPORT_SYMBOL_GPL(pcm1789_common_exit); 2714ae340d1SMylène Josserand 2724ae340d1SMylène Josserand MODULE_DESCRIPTION("ASoC PCM1789 driver"); 2734ae340d1SMylène Josserand MODULE_AUTHOR("Mylène Josserand <mylene.josserand@free-electrons.com>"); 2744ae340d1SMylène Josserand MODULE_LICENSE("GPL"); 275