0001
0002
0003
0004
0005 #include <linux/gpio.h>
0006 #include <linux/clk.h>
0007 #include <linux/module.h>
0008
0009 #include <sound/soc.h>
0010
0011 #include <linux/platform_data/asoc-s3c24xx_simtec.h>
0012
0013 #include "s3c24xx-i2s.h"
0014 #include "s3c24xx_simtec.h"
0015
0016 static struct s3c24xx_audio_simtec_pdata *pdata;
0017 static struct clk *xtal_clk;
0018
0019 static int spk_gain;
0020 static int spk_unmute;
0021
0022
0023
0024
0025
0026
0027
0028
0029 static int speaker_gain_get(struct snd_kcontrol *kcontrol,
0030 struct snd_ctl_elem_value *ucontrol)
0031 {
0032 ucontrol->value.integer.value[0] = spk_gain;
0033 return 0;
0034 }
0035
0036
0037
0038
0039
0040 static void speaker_gain_set(int value)
0041 {
0042 gpio_set_value_cansleep(pdata->amp_gain[0], value & 1);
0043 gpio_set_value_cansleep(pdata->amp_gain[1], value >> 1);
0044 }
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058 static int speaker_gain_put(struct snd_kcontrol *kcontrol,
0059 struct snd_ctl_elem_value *ucontrol)
0060 {
0061 int value = ucontrol->value.integer.value[0];
0062
0063 spk_gain = value;
0064
0065 if (!spk_unmute)
0066 speaker_gain_set(value);
0067
0068 return 0;
0069 }
0070
0071 static const struct snd_kcontrol_new amp_gain_controls[] = {
0072 SOC_SINGLE_EXT("Speaker Gain", 0, 0, 3, 0,
0073 speaker_gain_get, speaker_gain_put),
0074 };
0075
0076
0077
0078
0079
0080 static void spk_unmute_state(int to)
0081 {
0082 pr_debug("%s: to=%d\n", __func__, to);
0083
0084 spk_unmute = to;
0085 gpio_set_value(pdata->amp_gpio, to);
0086
0087
0088 if (to && pdata->amp_gain[0] > 0)
0089 speaker_gain_set(spk_gain);
0090 }
0091
0092
0093
0094
0095
0096
0097
0098
0099 static int speaker_unmute_get(struct snd_kcontrol *kcontrol,
0100 struct snd_ctl_elem_value *ucontrol)
0101 {
0102 ucontrol->value.integer.value[0] = spk_unmute;
0103 return 0;
0104 }
0105
0106
0107
0108
0109
0110
0111
0112
0113
0114 static int speaker_unmute_put(struct snd_kcontrol *kcontrol,
0115 struct snd_ctl_elem_value *ucontrol)
0116 {
0117 spk_unmute_state(ucontrol->value.integer.value[0]);
0118 return 0;
0119 }
0120
0121
0122
0123
0124
0125 static const struct snd_kcontrol_new amp_unmute_controls[] = {
0126 SOC_SINGLE_EXT("Speaker Switch", 0, 0, 1, 0,
0127 speaker_unmute_get, speaker_unmute_put),
0128 };
0129
0130 void simtec_audio_init(struct snd_soc_pcm_runtime *rtd)
0131 {
0132 struct snd_soc_card *card = rtd->card;
0133
0134 if (pdata->amp_gpio > 0) {
0135 pr_debug("%s: adding amp routes\n", __func__);
0136
0137 snd_soc_add_card_controls(card, amp_unmute_controls,
0138 ARRAY_SIZE(amp_unmute_controls));
0139 }
0140
0141 if (pdata->amp_gain[0] > 0) {
0142 pr_debug("%s: adding amp controls\n", __func__);
0143 snd_soc_add_card_controls(card, amp_gain_controls,
0144 ARRAY_SIZE(amp_gain_controls));
0145 }
0146 }
0147 EXPORT_SYMBOL_GPL(simtec_audio_init);
0148
0149 #define CODEC_CLOCK 12000000
0150
0151
0152
0153
0154
0155
0156
0157
0158
0159 static int simtec_hw_params(struct snd_pcm_substream *substream,
0160 struct snd_pcm_hw_params *params)
0161 {
0162 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
0163 struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
0164 struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
0165 int ret;
0166
0167 ret = snd_soc_dai_set_sysclk(codec_dai, 0,
0168 CODEC_CLOCK, SND_SOC_CLOCK_IN);
0169 if (ret) {
0170 pr_err( "%s: failed setting codec sysclk\n", __func__);
0171 return ret;
0172 }
0173
0174 if (pdata->use_mpllin) {
0175 ret = snd_soc_dai_set_sysclk(cpu_dai, S3C24XX_CLKSRC_MPLL,
0176 0, SND_SOC_CLOCK_OUT);
0177
0178 if (ret) {
0179 pr_err("%s: failed to set MPLLin as clksrc\n",
0180 __func__);
0181 return ret;
0182 }
0183 }
0184
0185 if (pdata->output_cdclk) {
0186 int cdclk_scale;
0187
0188 cdclk_scale = clk_get_rate(xtal_clk) / CODEC_CLOCK;
0189 cdclk_scale--;
0190
0191 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
0192 cdclk_scale);
0193 if (ret) {
0194 pr_err("%s: failed to set clock div\n",
0195 __func__);
0196 return ret;
0197 }
0198 }
0199
0200 return 0;
0201 }
0202
0203 static int simtec_call_startup(struct s3c24xx_audio_simtec_pdata *pd)
0204 {
0205
0206
0207
0208 if (pd->startup)
0209 pd->startup();
0210
0211 return 0;
0212 }
0213
0214 static const struct snd_soc_ops simtec_snd_ops = {
0215 .hw_params = simtec_hw_params,
0216 };
0217
0218
0219
0220
0221
0222
0223
0224
0225
0226 static int attach_gpio_amp(struct device *dev,
0227 struct s3c24xx_audio_simtec_pdata *pd)
0228 {
0229 int ret;
0230
0231
0232 if (pdata->amp_gain[0] > 0) {
0233 ret = gpio_request(pd->amp_gain[0], "gpio-amp-gain0");
0234 if (ret) {
0235 dev_err(dev, "cannot get amp gpio gain0\n");
0236 return ret;
0237 }
0238
0239 ret = gpio_request(pd->amp_gain[1], "gpio-amp-gain1");
0240 if (ret) {
0241 dev_err(dev, "cannot get amp gpio gain1\n");
0242 gpio_free(pdata->amp_gain[0]);
0243 return ret;
0244 }
0245
0246 gpio_direction_output(pd->amp_gain[0], 0);
0247 gpio_direction_output(pd->amp_gain[1], 0);
0248 }
0249
0250
0251 if (pdata->amp_gpio > 0) {
0252 ret = gpio_request(pd->amp_gpio, "gpio-amp");
0253 if (ret) {
0254 dev_err(dev, "cannot get amp gpio %d (%d)\n",
0255 pd->amp_gpio, ret);
0256 goto err_amp;
0257 }
0258
0259
0260 spk_unmute_state(0);
0261 }
0262
0263 return 0;
0264
0265 err_amp:
0266 if (pd->amp_gain[0] > 0) {
0267 gpio_free(pd->amp_gain[0]);
0268 gpio_free(pd->amp_gain[1]);
0269 }
0270
0271 return ret;
0272 }
0273
0274 static void detach_gpio_amp(struct s3c24xx_audio_simtec_pdata *pd)
0275 {
0276 if (pd->amp_gain[0] > 0) {
0277 gpio_free(pd->amp_gain[0]);
0278 gpio_free(pd->amp_gain[1]);
0279 }
0280
0281 if (pd->amp_gpio > 0)
0282 gpio_free(pd->amp_gpio);
0283 }
0284
0285 #ifdef CONFIG_PM
0286 static int simtec_audio_resume(struct device *dev)
0287 {
0288 simtec_call_startup(pdata);
0289 return 0;
0290 }
0291
0292 const struct dev_pm_ops simtec_audio_pmops = {
0293 .resume = simtec_audio_resume,
0294 };
0295 EXPORT_SYMBOL_GPL(simtec_audio_pmops);
0296 #endif
0297
0298 int simtec_audio_core_probe(struct platform_device *pdev,
0299 struct snd_soc_card *card)
0300 {
0301 struct platform_device *snd_dev;
0302 int ret;
0303
0304 card->dai_link->ops = &simtec_snd_ops;
0305 card->dai_link->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
0306 SND_SOC_DAIFMT_CBM_CFM;
0307
0308 pdata = pdev->dev.platform_data;
0309 if (!pdata) {
0310 dev_err(&pdev->dev, "no platform data supplied\n");
0311 return -EINVAL;
0312 }
0313
0314 simtec_call_startup(pdata);
0315
0316 xtal_clk = clk_get(&pdev->dev, "xtal");
0317 if (IS_ERR(xtal_clk)) {
0318 dev_err(&pdev->dev, "could not get clkout0\n");
0319 return -EINVAL;
0320 }
0321
0322 dev_info(&pdev->dev, "xtal rate is %ld\n", clk_get_rate(xtal_clk));
0323
0324 ret = attach_gpio_amp(&pdev->dev, pdata);
0325 if (ret)
0326 goto err_clk;
0327
0328 snd_dev = platform_device_alloc("soc-audio", -1);
0329 if (!snd_dev) {
0330 dev_err(&pdev->dev, "failed to alloc soc-audio device\n");
0331 ret = -ENOMEM;
0332 goto err_gpio;
0333 }
0334
0335 platform_set_drvdata(snd_dev, card);
0336
0337 ret = platform_device_add(snd_dev);
0338 if (ret) {
0339 dev_err(&pdev->dev, "failed to add soc-audio dev\n");
0340 goto err_pdev;
0341 }
0342
0343 platform_set_drvdata(pdev, snd_dev);
0344 return 0;
0345
0346 err_pdev:
0347 platform_device_put(snd_dev);
0348
0349 err_gpio:
0350 detach_gpio_amp(pdata);
0351
0352 err_clk:
0353 clk_put(xtal_clk);
0354 return ret;
0355 }
0356 EXPORT_SYMBOL_GPL(simtec_audio_core_probe);
0357
0358 int simtec_audio_remove(struct platform_device *pdev)
0359 {
0360 struct platform_device *snd_dev = platform_get_drvdata(pdev);
0361
0362 platform_device_unregister(snd_dev);
0363
0364 detach_gpio_amp(pdata);
0365 clk_put(xtal_clk);
0366 return 0;
0367 }
0368 EXPORT_SYMBOL_GPL(simtec_audio_remove);
0369
0370 MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
0371 MODULE_DESCRIPTION("ALSA SoC Simtec Audio common support");
0372 MODULE_LICENSE("GPL");