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