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