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