1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * ALSA SoC driver for 4 * Asahi Kasei AK5386 Single-ended 24-Bit 192kHz delta-sigma ADC 5 * 6 * (c) 2013 Daniel Mack <zonque@gmail.com> 7 */ 8 9 #include <linux/module.h> 10 #include <linux/slab.h> 11 #include <linux/of.h> 12 #include <linux/of_gpio.h> 13 #include <linux/regulator/consumer.h> 14 #include <sound/soc.h> 15 #include <sound/pcm.h> 16 #include <sound/initval.h> 17 18 static const char * const supply_names[] = { 19 "va", "vd" 20 }; 21 22 struct ak5386_priv { 23 int reset_gpio; 24 struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)]; 25 }; 26 27 static const struct snd_soc_dapm_widget ak5386_dapm_widgets[] = { 28 SND_SOC_DAPM_INPUT("AINL"), 29 SND_SOC_DAPM_INPUT("AINR"), 30 }; 31 32 static const struct snd_soc_dapm_route ak5386_dapm_routes[] = { 33 { "Capture", NULL, "AINL" }, 34 { "Capture", NULL, "AINR" }, 35 }; 36 37 static int ak5386_soc_probe(struct snd_soc_component *component) 38 { 39 struct ak5386_priv *priv = snd_soc_component_get_drvdata(component); 40 return regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies); 41 } 42 43 static void ak5386_soc_remove(struct snd_soc_component *component) 44 { 45 struct ak5386_priv *priv = snd_soc_component_get_drvdata(component); 46 regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies); 47 } 48 49 #ifdef CONFIG_PM 50 static int ak5386_soc_suspend(struct snd_soc_component *component) 51 { 52 struct ak5386_priv *priv = snd_soc_component_get_drvdata(component); 53 regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies); 54 return 0; 55 } 56 57 static int ak5386_soc_resume(struct snd_soc_component *component) 58 { 59 struct ak5386_priv *priv = snd_soc_component_get_drvdata(component); 60 return regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies); 61 } 62 #else 63 #define ak5386_soc_suspend NULL 64 #define ak5386_soc_resume NULL 65 #endif /* CONFIG_PM */ 66 67 static const struct snd_soc_component_driver soc_component_ak5386 = { 68 .probe = ak5386_soc_probe, 69 .remove = ak5386_soc_remove, 70 .suspend = ak5386_soc_suspend, 71 .resume = ak5386_soc_resume, 72 .dapm_widgets = ak5386_dapm_widgets, 73 .num_dapm_widgets = ARRAY_SIZE(ak5386_dapm_widgets), 74 .dapm_routes = ak5386_dapm_routes, 75 .num_dapm_routes = ARRAY_SIZE(ak5386_dapm_routes), 76 .idle_bias_on = 1, 77 .use_pmdown_time = 1, 78 .endianness = 1, 79 }; 80 81 static int ak5386_set_dai_fmt(struct snd_soc_dai *codec_dai, 82 unsigned int format) 83 { 84 struct snd_soc_component *component = codec_dai->component; 85 86 format &= SND_SOC_DAIFMT_FORMAT_MASK; 87 if (format != SND_SOC_DAIFMT_LEFT_J && 88 format != SND_SOC_DAIFMT_I2S) { 89 dev_err(component->dev, "Invalid DAI format\n"); 90 return -EINVAL; 91 } 92 93 return 0; 94 } 95 96 static int ak5386_hw_params(struct snd_pcm_substream *substream, 97 struct snd_pcm_hw_params *params, 98 struct snd_soc_dai *dai) 99 { 100 struct snd_soc_component *component = dai->component; 101 struct ak5386_priv *priv = snd_soc_component_get_drvdata(component); 102 103 /* 104 * From the datasheet: 105 * 106 * All external clocks (MCLK, SCLK and LRCK) must be present unless 107 * PDN pin = āLā. If these clocks are not provided, the AK5386 may 108 * draw excess current due to its use of internal dynamically 109 * refreshed logic. If the external clocks are not present, place 110 * the AK5386 in power-down mode (PDN pin = āLā). 111 */ 112 113 if (gpio_is_valid(priv->reset_gpio)) 114 gpio_set_value(priv->reset_gpio, 1); 115 116 return 0; 117 } 118 119 static int ak5386_hw_free(struct snd_pcm_substream *substream, 120 struct snd_soc_dai *dai) 121 { 122 struct snd_soc_component *component = dai->component; 123 struct ak5386_priv *priv = snd_soc_component_get_drvdata(component); 124 125 if (gpio_is_valid(priv->reset_gpio)) 126 gpio_set_value(priv->reset_gpio, 0); 127 128 return 0; 129 } 130 131 static const struct snd_soc_dai_ops ak5386_dai_ops = { 132 .set_fmt = ak5386_set_dai_fmt, 133 .hw_params = ak5386_hw_params, 134 .hw_free = ak5386_hw_free, 135 }; 136 137 static struct snd_soc_dai_driver ak5386_dai = { 138 .name = "ak5386-hifi", 139 .capture = { 140 .stream_name = "Capture", 141 .channels_min = 1, 142 .channels_max = 2, 143 .rates = SNDRV_PCM_RATE_8000_192000, 144 .formats = SNDRV_PCM_FMTBIT_S8 | 145 SNDRV_PCM_FMTBIT_S16_LE | 146 SNDRV_PCM_FMTBIT_S24_LE | 147 SNDRV_PCM_FMTBIT_S24_3LE, 148 }, 149 .ops = &ak5386_dai_ops, 150 }; 151 152 #ifdef CONFIG_OF 153 static const struct of_device_id ak5386_dt_ids[] = { 154 { .compatible = "asahi-kasei,ak5386", }, 155 { } 156 }; 157 MODULE_DEVICE_TABLE(of, ak5386_dt_ids); 158 #endif 159 160 static int ak5386_probe(struct platform_device *pdev) 161 { 162 struct device *dev = &pdev->dev; 163 struct ak5386_priv *priv; 164 int ret, i; 165 166 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 167 if (!priv) 168 return -ENOMEM; 169 170 dev_set_drvdata(dev, priv); 171 172 for (i = 0; i < ARRAY_SIZE(supply_names); i++) 173 priv->supplies[i].supply = supply_names[i]; 174 175 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(priv->supplies), 176 priv->supplies); 177 if (ret < 0) 178 return ret; 179 180 priv->reset_gpio = of_get_named_gpio(dev->of_node, 181 "reset-gpio", 0); 182 183 if (gpio_is_valid(priv->reset_gpio)) 184 if (devm_gpio_request_one(dev, priv->reset_gpio, 185 GPIOF_OUT_INIT_LOW, 186 "AK5386 Reset")) 187 priv->reset_gpio = -EINVAL; 188 189 return devm_snd_soc_register_component(dev, &soc_component_ak5386, 190 &ak5386_dai, 1); 191 } 192 193 static struct platform_driver ak5386_driver = { 194 .probe = ak5386_probe, 195 .driver = { 196 .name = "ak5386", 197 .of_match_table = of_match_ptr(ak5386_dt_ids), 198 }, 199 }; 200 201 module_platform_driver(ak5386_driver); 202 203 MODULE_DESCRIPTION("ASoC driver for AK5386 ADC"); 204 MODULE_AUTHOR("Daniel Mack <zonque@gmail.com>"); 205 MODULE_LICENSE("GPL"); 206
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.