root/sound/soc/samsung/s3c24xx_simtec.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. speaker_gain_get
  2. speaker_gain_set
  3. speaker_gain_put
  4. spk_unmute_state
  5. speaker_unmute_get
  6. speaker_unmute_put
  7. simtec_audio_init
  8. simtec_hw_params
  9. simtec_call_startup
  10. attach_gpio_amp
  11. detach_gpio_amp
  12. simtec_audio_resume
  13. simtec_audio_core_probe
  14. simtec_audio_remove

   1 // SPDX-License-Identifier: GPL-2.0
   2 //
   3 // Copyright 2009 Simtec Electronics
   4 
   5 #include <linux/gpio.h>
   6 #include <linux/clk.h>
   7 #include <linux/module.h>
   8 
   9 #include <sound/soc.h>
  10 
  11 #include <linux/platform_data/asoc-s3c24xx_simtec.h>
  12 
  13 #include "s3c24xx-i2s.h"
  14 #include "s3c24xx_simtec.h"
  15 
  16 static struct s3c24xx_audio_simtec_pdata *pdata;
  17 static struct clk *xtal_clk;
  18 
  19 static int spk_gain;
  20 static int spk_unmute;
  21 
  22 /**
  23  * speaker_gain_get - read the speaker gain setting.
  24  * @kcontrol: The control for the speaker gain.
  25  * @ucontrol: The value that needs to be updated.
  26  *
  27  * Read the value for the AMP gain control.
  28  */
  29 static int speaker_gain_get(struct snd_kcontrol *kcontrol,
  30                             struct snd_ctl_elem_value *ucontrol)
  31 {
  32         ucontrol->value.integer.value[0] = spk_gain;
  33         return 0;
  34 }
  35 
  36 /**
  37  * speaker_gain_set - set the value of the speaker amp gain
  38  * @value: The value to write.
  39  */
  40 static void speaker_gain_set(int value)
  41 {
  42         gpio_set_value_cansleep(pdata->amp_gain[0], value & 1);
  43         gpio_set_value_cansleep(pdata->amp_gain[1], value >> 1);
  44 }
  45 
  46 /**
  47  * speaker_gain_put - set the speaker gain setting.
  48  * @kcontrol: The control for the speaker gain.
  49  * @ucontrol: The value that needs to be set.
  50  *
  51  * Set the value of the speaker gain from the specified
  52  * @ucontrol setting.
  53  *
  54  * Note, if the speaker amp is muted, then we do not set a gain value
  55  * as at-least one of the ICs that is fitted will try and power up even
  56  * if the main control is set to off.
  57  */
  58 static int speaker_gain_put(struct snd_kcontrol *kcontrol,
  59                             struct snd_ctl_elem_value *ucontrol)
  60 {
  61         int value = ucontrol->value.integer.value[0];
  62 
  63         spk_gain = value;
  64 
  65         if (!spk_unmute)
  66                 speaker_gain_set(value);
  67 
  68         return 0;
  69 }
  70 
  71 static const struct snd_kcontrol_new amp_gain_controls[] = {
  72         SOC_SINGLE_EXT("Speaker Gain", 0, 0, 3, 0,
  73                        speaker_gain_get, speaker_gain_put),
  74 };
  75 
  76 /**
  77  * spk_unmute_state - set the unmute state of the speaker
  78  * @to: zero to unmute, non-zero to ununmute.
  79  */
  80 static void spk_unmute_state(int to)
  81 {
  82         pr_debug("%s: to=%d\n", __func__, to);
  83 
  84         spk_unmute = to;
  85         gpio_set_value(pdata->amp_gpio, to);
  86 
  87         /* if we're umuting, also re-set the gain */
  88         if (to && pdata->amp_gain[0] > 0)
  89                 speaker_gain_set(spk_gain);
  90 }
  91 
  92 /**
  93  * speaker_unmute_get - read the speaker unmute setting.
  94  * @kcontrol: The control for the speaker gain.
  95  * @ucontrol: The value that needs to be updated.
  96  *
  97  * Read the value for the AMP gain control.
  98  */
  99 static int speaker_unmute_get(struct snd_kcontrol *kcontrol,
 100                             struct snd_ctl_elem_value *ucontrol)
 101 {
 102         ucontrol->value.integer.value[0] = spk_unmute;
 103         return 0;
 104 }
 105 
 106 /**
 107  * speaker_unmute_put - set the speaker unmute setting.
 108  * @kcontrol: The control for the speaker gain.
 109  * @ucontrol: The value that needs to be set.
 110  *
 111  * Set the value of the speaker gain from the specified
 112  * @ucontrol setting.
 113  */
 114 static int speaker_unmute_put(struct snd_kcontrol *kcontrol,
 115                             struct snd_ctl_elem_value *ucontrol)
 116 {
 117         spk_unmute_state(ucontrol->value.integer.value[0]);
 118         return 0;
 119 }
 120 
 121 /* This is added as a manual control as the speaker amps create clicks
 122  * when their power state is changed, which are far more noticeable than
 123  * anything produced by the CODEC itself.
 124  */
 125 static const struct snd_kcontrol_new amp_unmute_controls[] = {
 126         SOC_SINGLE_EXT("Speaker Switch", 0, 0, 1, 0,
 127                        speaker_unmute_get, speaker_unmute_put),
 128 };
 129 
 130 void simtec_audio_init(struct snd_soc_pcm_runtime *rtd)
 131 {
 132         struct snd_soc_card *card = rtd->card;
 133 
 134         if (pdata->amp_gpio > 0) {
 135                 pr_debug("%s: adding amp routes\n", __func__);
 136 
 137                 snd_soc_add_card_controls(card, amp_unmute_controls,
 138                                      ARRAY_SIZE(amp_unmute_controls));
 139         }
 140 
 141         if (pdata->amp_gain[0] > 0) {
 142                 pr_debug("%s: adding amp controls\n", __func__);
 143                 snd_soc_add_card_controls(card, amp_gain_controls,
 144                                      ARRAY_SIZE(amp_gain_controls));
 145         }
 146 }
 147 EXPORT_SYMBOL_GPL(simtec_audio_init);
 148 
 149 #define CODEC_CLOCK 12000000
 150 
 151 /**
 152  * simtec_hw_params - update hardware parameters
 153  * @substream: The audio substream instance.
 154  * @params: The parameters requested.
 155  *
 156  * Update the codec data routing and configuration  settings
 157  * from the supplied data.
 158  */
 159 static int simtec_hw_params(struct snd_pcm_substream *substream,
 160                             struct snd_pcm_hw_params *params)
 161 {
 162         struct snd_soc_pcm_runtime *rtd = substream->private_data;
 163         struct snd_soc_dai *codec_dai = rtd->codec_dai;
 164         struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
 165         int ret;
 166 
 167         ret = snd_soc_dai_set_sysclk(codec_dai, 0,
 168                                      CODEC_CLOCK, SND_SOC_CLOCK_IN);
 169         if (ret) {
 170                 pr_err( "%s: failed setting codec sysclk\n", __func__);
 171                 return ret;
 172         }
 173 
 174         if (pdata->use_mpllin) {
 175                 ret = snd_soc_dai_set_sysclk(cpu_dai, S3C24XX_CLKSRC_MPLL,
 176                                              0, SND_SOC_CLOCK_OUT);
 177 
 178                 if (ret) {
 179                         pr_err("%s: failed to set MPLLin as clksrc\n",
 180                                __func__);
 181                         return ret;
 182                 }
 183         }
 184 
 185         if (pdata->output_cdclk) {
 186                 int cdclk_scale;
 187 
 188                 cdclk_scale = clk_get_rate(xtal_clk) / CODEC_CLOCK;
 189                 cdclk_scale--;
 190 
 191                 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
 192                                              cdclk_scale);
 193         }
 194 
 195         return 0;
 196 }
 197 
 198 static int simtec_call_startup(struct s3c24xx_audio_simtec_pdata *pd)
 199 {
 200         /* call any board supplied startup code, this currently only
 201          * covers the bast/vr1000 which have a CPLD in the way of the
 202          * LRCLK */
 203         if (pd->startup)
 204                 pd->startup();
 205 
 206         return 0;
 207 }
 208 
 209 static const struct snd_soc_ops simtec_snd_ops = {
 210         .hw_params      = simtec_hw_params,
 211 };
 212 
 213 /**
 214  * attach_gpio_amp - get and configure the necessary gpios
 215  * @dev: The device we're probing.
 216  * @pd: The platform data supplied by the board.
 217  *
 218  * If there is a GPIO based amplifier attached to the board, claim
 219  * the necessary GPIO lines for it, and set default values.
 220  */
 221 static int attach_gpio_amp(struct device *dev,
 222                            struct s3c24xx_audio_simtec_pdata *pd)
 223 {
 224         int ret;
 225 
 226         /* attach gpio amp gain (if any) */
 227         if (pdata->amp_gain[0] > 0) {
 228                 ret = gpio_request(pd->amp_gain[0], "gpio-amp-gain0");
 229                 if (ret) {
 230                         dev_err(dev, "cannot get amp gpio gain0\n");
 231                         return ret;
 232                 }
 233 
 234                 ret = gpio_request(pd->amp_gain[1], "gpio-amp-gain1");
 235                 if (ret) {
 236                         dev_err(dev, "cannot get amp gpio gain1\n");
 237                         gpio_free(pdata->amp_gain[0]);
 238                         return ret;
 239                 }
 240 
 241                 gpio_direction_output(pd->amp_gain[0], 0);
 242                 gpio_direction_output(pd->amp_gain[1], 0);
 243         }
 244 
 245         /* note, currently we assume GPA0 isn't valid amp */
 246         if (pdata->amp_gpio > 0) {
 247                 ret = gpio_request(pd->amp_gpio, "gpio-amp");
 248                 if (ret) {
 249                         dev_err(dev, "cannot get amp gpio %d (%d)\n",
 250                                 pd->amp_gpio, ret);
 251                         goto err_amp;
 252                 }
 253 
 254                 /* set the amp off at startup */
 255                 spk_unmute_state(0);
 256         }
 257 
 258         return 0;
 259 
 260 err_amp:
 261         if (pd->amp_gain[0] > 0) {
 262                 gpio_free(pd->amp_gain[0]);
 263                 gpio_free(pd->amp_gain[1]);
 264         }
 265 
 266         return ret;
 267 }
 268 
 269 static void detach_gpio_amp(struct s3c24xx_audio_simtec_pdata *pd)
 270 {
 271         if (pd->amp_gain[0] > 0) {
 272                 gpio_free(pd->amp_gain[0]);
 273                 gpio_free(pd->amp_gain[1]);
 274         }
 275 
 276         if (pd->amp_gpio > 0)
 277                 gpio_free(pd->amp_gpio);
 278 }
 279 
 280 #ifdef CONFIG_PM
 281 static int simtec_audio_resume(struct device *dev)
 282 {
 283         simtec_call_startup(pdata);
 284         return 0;
 285 }
 286 
 287 const struct dev_pm_ops simtec_audio_pmops = {
 288         .resume = simtec_audio_resume,
 289 };
 290 EXPORT_SYMBOL_GPL(simtec_audio_pmops);
 291 #endif
 292 
 293 int simtec_audio_core_probe(struct platform_device *pdev,
 294                             struct snd_soc_card *card)
 295 {
 296         struct platform_device *snd_dev;
 297         int ret;
 298 
 299         card->dai_link->ops = &simtec_snd_ops;
 300         card->dai_link->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
 301                                   SND_SOC_DAIFMT_CBM_CFM;
 302 
 303         pdata = pdev->dev.platform_data;
 304         if (!pdata) {
 305                 dev_err(&pdev->dev, "no platform data supplied\n");
 306                 return -EINVAL;
 307         }
 308 
 309         simtec_call_startup(pdata);
 310 
 311         xtal_clk = clk_get(&pdev->dev, "xtal");
 312         if (IS_ERR(xtal_clk)) {
 313                 dev_err(&pdev->dev, "could not get clkout0\n");
 314                 return -EINVAL;
 315         }
 316 
 317         dev_info(&pdev->dev, "xtal rate is %ld\n", clk_get_rate(xtal_clk));
 318 
 319         ret = attach_gpio_amp(&pdev->dev, pdata);
 320         if (ret)
 321                 goto err_clk;
 322 
 323         snd_dev = platform_device_alloc("soc-audio", -1);
 324         if (!snd_dev) {
 325                 dev_err(&pdev->dev, "failed to alloc soc-audio devicec\n");
 326                 ret = -ENOMEM;
 327                 goto err_gpio;
 328         }
 329 
 330         platform_set_drvdata(snd_dev, card);
 331 
 332         ret = platform_device_add(snd_dev);
 333         if (ret) {
 334                 dev_err(&pdev->dev, "failed to add soc-audio dev\n");
 335                 goto err_pdev;
 336         }
 337 
 338         platform_set_drvdata(pdev, snd_dev);
 339         return 0;
 340 
 341 err_pdev:
 342         platform_device_put(snd_dev);
 343 
 344 err_gpio:
 345         detach_gpio_amp(pdata);
 346 
 347 err_clk:
 348         clk_put(xtal_clk);
 349         return ret;
 350 }
 351 EXPORT_SYMBOL_GPL(simtec_audio_core_probe);
 352 
 353 int simtec_audio_remove(struct platform_device *pdev)
 354 {
 355         struct platform_device *snd_dev = platform_get_drvdata(pdev);
 356 
 357         platform_device_unregister(snd_dev);
 358 
 359         detach_gpio_amp(pdata);
 360         clk_put(xtal_clk);
 361         return 0;
 362 }
 363 EXPORT_SYMBOL_GPL(simtec_audio_remove);
 364 
 365 MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
 366 MODULE_DESCRIPTION("ALSA SoC Simtec Audio common support");
 367 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */