295 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2//3// uda1334.c -- UDA1334 ALSA SoC Audio driver4//5// Based on WM8523 ALSA SoC Audio driver written by Mark Brown6 7#include <linux/mod_devicetable.h>8#include <linux/module.h>9#include <linux/moduleparam.h>10#include <linux/init.h>11#include <linux/delay.h>12#include <linux/slab.h>13#include <linux/gpio/consumer.h>14#include <sound/core.h>15#include <sound/pcm.h>16#include <sound/pcm_params.h>17#include <sound/soc.h>18#include <sound/initval.h>19 20#define UDA1334_NUM_RATES 621 22/* codec private data */23struct uda1334_priv {24 struct gpio_desc *mute;25 struct gpio_desc *deemph;26 unsigned int sysclk;27 unsigned int rate_constraint_list[UDA1334_NUM_RATES];28 struct snd_pcm_hw_constraint_list rate_constraint;29};30 31static const struct snd_soc_dapm_widget uda1334_dapm_widgets[] = {32SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),33SND_SOC_DAPM_OUTPUT("LINEVOUTL"),34SND_SOC_DAPM_OUTPUT("LINEVOUTR"),35};36 37static const struct snd_soc_dapm_route uda1334_dapm_routes[] = {38 { "LINEVOUTL", NULL, "DAC" },39 { "LINEVOUTR", NULL, "DAC" },40};41 42static int uda1334_put_deemph(struct snd_kcontrol *kcontrol,43 struct snd_ctl_elem_value *ucontrol)44{45 struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);46 struct uda1334_priv *uda1334 = snd_soc_component_get_drvdata(component);47 int deemph = ucontrol->value.integer.value[0];48 49 if (deemph > 1)50 return -EINVAL;51 52 gpiod_set_value_cansleep(uda1334->deemph, deemph);53 54 return 0;55};56 57static int uda1334_get_deemph(struct snd_kcontrol *kcontrol,58 struct snd_ctl_elem_value *ucontrol)59{60 struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);61 struct uda1334_priv *uda1334 = snd_soc_component_get_drvdata(component);62 int ret;63 64 ret = gpiod_get_value_cansleep(uda1334->deemph);65 if (ret < 0)66 return -EINVAL;67 68 ucontrol->value.integer.value[0] = ret;69 70 return 0;71};72 73static const struct snd_kcontrol_new uda1334_snd_controls[] = {74 SOC_SINGLE_BOOL_EXT("Playback Deemphasis Switch", 0,75 uda1334_get_deemph, uda1334_put_deemph),76};77 78static const struct {79 int value;80 int ratio;81} lrclk_ratios[UDA1334_NUM_RATES] = {82 { 1, 128 },83 { 2, 192 },84 { 3, 256 },85 { 4, 384 },86 { 5, 512 },87 { 6, 768 },88};89 90static int uda1334_startup(struct snd_pcm_substream *substream,91 struct snd_soc_dai *dai)92{93 struct snd_soc_component *component = dai->component;94 struct uda1334_priv *uda1334 = snd_soc_component_get_drvdata(component);95 96 /*97 * The set of sample rates that can be supported depends on the98 * MCLK supplied to the CODEC - enforce this.99 */100 if (!uda1334->sysclk) {101 dev_err(component->dev,102 "No MCLK configured, call set_sysclk() on init\n");103 return -EINVAL;104 }105 106 snd_pcm_hw_constraint_list(substream->runtime, 0,107 SNDRV_PCM_HW_PARAM_RATE,108 &uda1334->rate_constraint);109 110 gpiod_set_value_cansleep(uda1334->mute, 1);111 112 return 0;113}114 115static void uda1334_shutdown(struct snd_pcm_substream *substream,116 struct snd_soc_dai *dai)117{118 struct snd_soc_component *component = dai->component;119 struct uda1334_priv *uda1334 = snd_soc_component_get_drvdata(component);120 121 gpiod_set_value_cansleep(uda1334->mute, 0);122}123 124static int uda1334_set_dai_sysclk(struct snd_soc_dai *codec_dai,125 int clk_id, unsigned int freq, int dir)126{127 struct snd_soc_component *component = codec_dai->component;128 struct uda1334_priv *uda1334 = snd_soc_component_get_drvdata(component);129 unsigned int val;130 int i, j = 0;131 132 uda1334->sysclk = freq;133 134 uda1334->rate_constraint.count = 0;135 for (i = 0; i < ARRAY_SIZE(lrclk_ratios); i++) {136 val = freq / lrclk_ratios[i].ratio;137 /*138 * Check that it's a standard rate since core can't139 * cope with others and having the odd rates confuses140 * constraint matching.141 */142 143 switch (val) {144 case 8000:145 case 32000:146 case 44100:147 case 48000:148 case 64000:149 case 88200:150 case 96000:151 dev_dbg(component->dev, "Supported sample rate: %dHz\n",152 val);153 uda1334->rate_constraint_list[j++] = val;154 uda1334->rate_constraint.count++;155 break;156 default:157 dev_dbg(component->dev, "Skipping sample rate: %dHz\n",158 val);159 }160 }161 162 /* Need at least one supported rate... */163 if (uda1334->rate_constraint.count == 0)164 return -EINVAL;165 166 return 0;167}168 169static int uda1334_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)170{171 fmt &= (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_INV_MASK |172 SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK);173 174 if (fmt != (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |175 SND_SOC_DAIFMT_CBC_CFC)) {176 dev_err(codec_dai->dev, "Invalid DAI format\n");177 return -EINVAL;178 }179 180 return 0;181}182 183static int uda1334_mute_stream(struct snd_soc_dai *dai, int mute, int stream)184{185 struct uda1334_priv *uda1334 = snd_soc_component_get_drvdata(dai->component);186 187 if (uda1334->mute)188 gpiod_set_value_cansleep(uda1334->mute, mute);189 190 return 0;191}192 193#define UDA1334_RATES SNDRV_PCM_RATE_8000_96000194 195#define UDA1334_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)196 197static const struct snd_soc_dai_ops uda1334_dai_ops = {198 .startup = uda1334_startup,199 .shutdown = uda1334_shutdown,200 .set_sysclk = uda1334_set_dai_sysclk,201 .set_fmt = uda1334_set_fmt,202 .mute_stream = uda1334_mute_stream,203};204 205static struct snd_soc_dai_driver uda1334_dai = {206 .name = "uda1334-hifi",207 .playback = {208 .stream_name = "Playback",209 .channels_min = 2,210 .channels_max = 2,211 .rates = UDA1334_RATES,212 .formats = UDA1334_FORMATS,213 },214 .ops = &uda1334_dai_ops,215};216 217static int uda1334_probe(struct snd_soc_component *component)218{219 struct uda1334_priv *uda1334 = snd_soc_component_get_drvdata(component);220 221 uda1334->rate_constraint.list = &uda1334->rate_constraint_list[0];222 uda1334->rate_constraint.count =223 ARRAY_SIZE(uda1334->rate_constraint_list);224 225 return 0;226}227 228static const struct snd_soc_component_driver soc_component_dev_uda1334 = {229 .probe = uda1334_probe,230 .controls = uda1334_snd_controls,231 .num_controls = ARRAY_SIZE(uda1334_snd_controls),232 .dapm_widgets = uda1334_dapm_widgets,233 .num_dapm_widgets = ARRAY_SIZE(uda1334_dapm_widgets),234 .dapm_routes = uda1334_dapm_routes,235 .num_dapm_routes = ARRAY_SIZE(uda1334_dapm_routes),236 .idle_bias_on = 1,237 .use_pmdown_time = 1,238 .endianness = 1,239};240 241static const struct of_device_id uda1334_of_match[] = {242 { .compatible = "nxp,uda1334" },243 { /* sentinel*/ }244};245MODULE_DEVICE_TABLE(of, uda1334_of_match);246 247static int uda1334_codec_probe(struct platform_device *pdev)248{249 struct uda1334_priv *uda1334;250 int ret;251 252 uda1334 = devm_kzalloc(&pdev->dev, sizeof(struct uda1334_priv),253 GFP_KERNEL);254 if (!uda1334)255 return -ENOMEM;256 257 platform_set_drvdata(pdev, uda1334);258 259 uda1334->mute = devm_gpiod_get(&pdev->dev, "nxp,mute", GPIOD_OUT_LOW);260 if (IS_ERR(uda1334->mute)) {261 ret = PTR_ERR(uda1334->mute);262 dev_err(&pdev->dev, "Failed to get mute line: %d\n", ret);263 return ret;264 }265 266 uda1334->deemph = devm_gpiod_get(&pdev->dev, "nxp,deemph", GPIOD_OUT_LOW);267 if (IS_ERR(uda1334->deemph)) {268 ret = PTR_ERR(uda1334->deemph);269 dev_err(&pdev->dev, "Failed to get deemph line: %d\n", ret);270 return ret;271 }272 273 ret = devm_snd_soc_register_component(&pdev->dev,274 &soc_component_dev_uda1334,275 &uda1334_dai, 1);276 if (ret < 0)277 dev_err(&pdev->dev, "Failed to register component: %d\n", ret);278 279 return ret;280}281 282static struct platform_driver uda1334_codec_driver = {283 .probe = uda1334_codec_probe,284 .driver = {285 .name = "uda1334-codec",286 .of_match_table = uda1334_of_match,287 },288};289module_platform_driver(uda1334_codec_driver);290 291MODULE_DESCRIPTION("ASoC UDA1334 driver");292MODULE_AUTHOR("Andra Danciu <andradanciu1997@gmail.com>");293MODULE_ALIAS("platform:uda1334-codec");294MODULE_LICENSE("GPL v2");295