1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * PCM1754 DAC ASoC codec driver 4 * 5 * Copyright (c) 2022 Alvin Šipraga <alsi@bang-olufsen.dk> 6 * Copyright (c) 2025 Stefan Kerkmann <s.kerkmann@pengutronix.de> 7 */ 8 9 #include <linux/gpio/consumer.h> 10 #include <linux/module.h> 11 #include <linux/regulator/consumer.h> 12 13 #include <sound/pcm_params.h> 14 #include <sound/soc.h> 15 16 struct pcm1754_priv { 17 unsigned int format; 18 struct gpio_desc *gpiod_mute; 19 struct gpio_desc *gpiod_format; 20 }; 21 22 static int pcm1754_set_dai_fmt(struct snd_soc_dai *codec_dai, 23 unsigned int format) 24 { 25 struct snd_soc_component *component = codec_dai->component; 26 struct pcm1754_priv *priv = snd_soc_component_get_drvdata(component); 27 28 priv->format = format; 29 30 return 0; 31 } 32 33 static int pcm1754_hw_params(struct snd_pcm_substream *substream, 34 struct snd_pcm_hw_params *params, 35 struct snd_soc_dai *codec_dai) 36 { 37 struct snd_soc_component *component = codec_dai->component; 38 struct pcm1754_priv *priv = snd_soc_component_get_drvdata(component); 39 int format; 40 41 switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) { 42 case SND_SOC_DAIFMT_RIGHT_J: 43 switch (params_width(params)) { 44 case 16: 45 format = 1; 46 break; 47 default: 48 return -EINVAL; 49 } 50 break; 51 case SND_SOC_DAIFMT_I2S: 52 switch (params_width(params)) { 53 case 16: 54 fallthrough; 55 case 24: 56 format = 0; 57 break; 58 default: 59 return -EINVAL; 60 } 61 break; 62 default: 63 dev_err(component->dev, "Invalid DAI format\n"); 64 return -EINVAL; 65 } 66 67 gpiod_set_value_cansleep(priv->gpiod_format, format); 68 69 return 0; 70 } 71 72 static int pcm1754_mute_stream(struct snd_soc_dai *dai, int mute, int stream) 73 { 74 struct pcm1754_priv *priv = snd_soc_component_get_drvdata(dai->component); 75 76 gpiod_set_value_cansleep(priv->gpiod_mute, mute); 77 78 return 0; 79 } 80 81 static const struct snd_soc_dai_ops pcm1754_dai_ops = { 82 .set_fmt = pcm1754_set_dai_fmt, 83 .hw_params = pcm1754_hw_params, 84 .mute_stream = pcm1754_mute_stream, 85 }; 86 87 static const struct snd_soc_dai_driver pcm1754_dai = { 88 .name = "pcm1754", 89 .playback = { 90 .stream_name = "Playback", 91 .channels_min = 2, 92 .channels_max = 2, 93 .rates = SNDRV_PCM_RATE_CONTINUOUS, 94 .rate_min = 5000, 95 .rate_max = 200000, 96 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE 97 }, 98 .ops = &pcm1754_dai_ops, 99 }; 100 101 static const struct snd_soc_dapm_widget pcm1754_dapm_widgets[] = { 102 SND_SOC_DAPM_REGULATOR_SUPPLY("VCC", 0, 0), 103 104 SND_SOC_DAPM_DAC("DAC1", "Channel 1 Playback", SND_SOC_NOPM, 0, 0), 105 SND_SOC_DAPM_DAC("DAC2", "Channel 2 Playback", SND_SOC_NOPM, 0, 0), 106 107 SND_SOC_DAPM_OUTPUT("VOUTL"), 108 SND_SOC_DAPM_OUTPUT("VOUTR"), 109 }; 110 111 static const struct snd_soc_dapm_route pcm1754_dapm_routes[] = { 112 { "DAC1", NULL, "Playback" }, 113 { "DAC2", NULL, "Playback" }, 114 115 { "DAC1", NULL, "VCC" }, 116 { "DAC2", NULL, "VCC" }, 117 118 { "VOUTL", NULL, "DAC1" }, 119 { "VOUTR", NULL, "DAC2" }, 120 }; 121 122 static const struct snd_soc_component_driver soc_component_dev_pcm1754 = { 123 .dapm_widgets = pcm1754_dapm_widgets, 124 .num_dapm_widgets = ARRAY_SIZE(pcm1754_dapm_widgets), 125 .dapm_routes = pcm1754_dapm_routes, 126 .num_dapm_routes = ARRAY_SIZE(pcm1754_dapm_routes), 127 }; 128 129 static int pcm1754_probe(struct platform_device *pdev) 130 { 131 struct pcm1754_priv *priv; 132 struct device *dev = &pdev->dev; 133 struct snd_soc_dai_driver *dai_drv; 134 int ret; 135 136 dai_drv = devm_kmemdup(dev, &pcm1754_dai, sizeof(*dai_drv), GFP_KERNEL); 137 if (!dai_drv) 138 return -ENOMEM; 139 140 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 141 if (!priv) 142 return -ENOMEM; 143 144 priv->gpiod_mute = devm_gpiod_get_optional(dev, "mute", GPIOD_OUT_HIGH); 145 if (IS_ERR(priv->gpiod_mute)) 146 return dev_err_probe(dev, PTR_ERR(priv->gpiod_mute), 147 "failed to get mute gpio"); 148 149 priv->gpiod_format = devm_gpiod_get_optional(dev, "format", GPIOD_OUT_LOW); 150 if (IS_ERR(priv->gpiod_format)) 151 return dev_err_probe(dev, PTR_ERR(priv->gpiod_format), 152 "failed to get format gpio"); 153 154 dev_set_drvdata(dev, priv); 155 156 ret = devm_snd_soc_register_component( 157 &pdev->dev, &soc_component_dev_pcm1754, dai_drv, 1); 158 if (ret) 159 return dev_err_probe(dev, ret, "failed to register"); 160 161 return 0; 162 } 163 164 #ifdef CONFIG_OF 165 static const struct of_device_id pcm1754_of_match[] = { 166 { .compatible = "ti,pcm1754" }, 167 { } 168 }; 169 MODULE_DEVICE_TABLE(of, pcm1754_of_match); 170 #endif 171 172 static struct platform_driver pcm1754_codec_driver = { 173 .driver = { 174 .name = "pcm1754-codec", 175 .of_match_table = of_match_ptr(pcm1754_of_match), 176 }, 177 .probe = pcm1754_probe, 178 }; 179 180 module_platform_driver(pcm1754_codec_driver); 181 182 MODULE_DESCRIPTION("ASoC PCM1754 driver"); 183 MODULE_AUTHOR("Alvin Šipraga <alsi@bang-olufsen.dk>"); 184 MODULE_AUTHOR("Stefan Kerkmann <s.kerkmann@pengutronix.de>"); 185 MODULE_LICENSE("GPL"); 186