Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.19 388 lines 9.3 kB view raw
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 /* Set the CODEC as the bus clock master, I2S */ 173 ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | 174 SND_SOC_DAIFMT_NB_NF | 175 SND_SOC_DAIFMT_CBM_CFM); 176 if (ret) { 177 pr_err("%s: failed set cpu dai format\n", __func__); 178 return ret; 179 } 180 181 /* Set the CODEC as the bus clock master */ 182 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | 183 SND_SOC_DAIFMT_NB_NF | 184 SND_SOC_DAIFMT_CBM_CFM); 185 if (ret) { 186 pr_err("%s: failed set codec dai format\n", __func__); 187 return ret; 188 } 189 190 ret = snd_soc_dai_set_sysclk(codec_dai, 0, 191 CODEC_CLOCK, SND_SOC_CLOCK_IN); 192 if (ret) { 193 pr_err( "%s: failed setting codec sysclk\n", __func__); 194 return ret; 195 } 196 197 if (pdata->use_mpllin) { 198 ret = snd_soc_dai_set_sysclk(cpu_dai, S3C24XX_CLKSRC_MPLL, 199 0, SND_SOC_CLOCK_OUT); 200 201 if (ret) { 202 pr_err("%s: failed to set MPLLin as clksrc\n", 203 __func__); 204 return ret; 205 } 206 } 207 208 if (pdata->output_cdclk) { 209 int cdclk_scale; 210 211 cdclk_scale = clk_get_rate(xtal_clk) / CODEC_CLOCK; 212 cdclk_scale--; 213 214 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER, 215 cdclk_scale); 216 } 217 218 return 0; 219} 220 221static int simtec_call_startup(struct s3c24xx_audio_simtec_pdata *pd) 222{ 223 /* call any board supplied startup code, this currently only 224 * covers the bast/vr1000 which have a CPLD in the way of the 225 * LRCLK */ 226 if (pd->startup) 227 pd->startup(); 228 229 return 0; 230} 231 232static struct snd_soc_ops simtec_snd_ops = { 233 .hw_params = simtec_hw_params, 234}; 235 236/** 237 * attach_gpio_amp - get and configure the necessary gpios 238 * @dev: The device we're probing. 239 * @pd: The platform data supplied by the board. 240 * 241 * If there is a GPIO based amplifier attached to the board, claim 242 * the necessary GPIO lines for it, and set default values. 243 */ 244static int attach_gpio_amp(struct device *dev, 245 struct s3c24xx_audio_simtec_pdata *pd) 246{ 247 int ret; 248 249 /* attach gpio amp gain (if any) */ 250 if (pdata->amp_gain[0] > 0) { 251 ret = gpio_request(pd->amp_gain[0], "gpio-amp-gain0"); 252 if (ret) { 253 dev_err(dev, "cannot get amp gpio gain0\n"); 254 return ret; 255 } 256 257 ret = gpio_request(pd->amp_gain[1], "gpio-amp-gain1"); 258 if (ret) { 259 dev_err(dev, "cannot get amp gpio gain1\n"); 260 gpio_free(pdata->amp_gain[0]); 261 return ret; 262 } 263 264 gpio_direction_output(pd->amp_gain[0], 0); 265 gpio_direction_output(pd->amp_gain[1], 0); 266 } 267 268 /* note, currently we assume GPA0 isn't valid amp */ 269 if (pdata->amp_gpio > 0) { 270 ret = gpio_request(pd->amp_gpio, "gpio-amp"); 271 if (ret) { 272 dev_err(dev, "cannot get amp gpio %d (%d)\n", 273 pd->amp_gpio, ret); 274 goto err_amp; 275 } 276 277 /* set the amp off at startup */ 278 spk_unmute_state(0); 279 } 280 281 return 0; 282 283err_amp: 284 if (pd->amp_gain[0] > 0) { 285 gpio_free(pd->amp_gain[0]); 286 gpio_free(pd->amp_gain[1]); 287 } 288 289 return ret; 290} 291 292static void detach_gpio_amp(struct s3c24xx_audio_simtec_pdata *pd) 293{ 294 if (pd->amp_gain[0] > 0) { 295 gpio_free(pd->amp_gain[0]); 296 gpio_free(pd->amp_gain[1]); 297 } 298 299 if (pd->amp_gpio > 0) 300 gpio_free(pd->amp_gpio); 301} 302 303#ifdef CONFIG_PM 304static int simtec_audio_resume(struct device *dev) 305{ 306 simtec_call_startup(pdata); 307 return 0; 308} 309 310const struct dev_pm_ops simtec_audio_pmops = { 311 .resume = simtec_audio_resume, 312}; 313EXPORT_SYMBOL_GPL(simtec_audio_pmops); 314#endif 315 316int simtec_audio_core_probe(struct platform_device *pdev, 317 struct snd_soc_card *card) 318{ 319 struct platform_device *snd_dev; 320 int ret; 321 322 card->dai_link->ops = &simtec_snd_ops; 323 324 pdata = pdev->dev.platform_data; 325 if (!pdata) { 326 dev_err(&pdev->dev, "no platform data supplied\n"); 327 return -EINVAL; 328 } 329 330 simtec_call_startup(pdata); 331 332 xtal_clk = clk_get(&pdev->dev, "xtal"); 333 if (IS_ERR(xtal_clk)) { 334 dev_err(&pdev->dev, "could not get clkout0\n"); 335 return -EINVAL; 336 } 337 338 dev_info(&pdev->dev, "xtal rate is %ld\n", clk_get_rate(xtal_clk)); 339 340 ret = attach_gpio_amp(&pdev->dev, pdata); 341 if (ret) 342 goto err_clk; 343 344 snd_dev = platform_device_alloc("soc-audio", -1); 345 if (!snd_dev) { 346 dev_err(&pdev->dev, "failed to alloc soc-audio devicec\n"); 347 ret = -ENOMEM; 348 goto err_gpio; 349 } 350 351 platform_set_drvdata(snd_dev, card); 352 353 ret = platform_device_add(snd_dev); 354 if (ret) { 355 dev_err(&pdev->dev, "failed to add soc-audio dev\n"); 356 goto err_pdev; 357 } 358 359 platform_set_drvdata(pdev, snd_dev); 360 return 0; 361 362err_pdev: 363 platform_device_put(snd_dev); 364 365err_gpio: 366 detach_gpio_amp(pdata); 367 368err_clk: 369 clk_put(xtal_clk); 370 return ret; 371} 372EXPORT_SYMBOL_GPL(simtec_audio_core_probe); 373 374int simtec_audio_remove(struct platform_device *pdev) 375{ 376 struct platform_device *snd_dev = platform_get_drvdata(pdev); 377 378 platform_device_unregister(snd_dev); 379 380 detach_gpio_amp(pdata); 381 clk_put(xtal_clk); 382 return 0; 383} 384EXPORT_SYMBOL_GPL(simtec_audio_remove); 385 386MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); 387MODULE_DESCRIPTION("ALSA SoC Simtec Audio common support"); 388MODULE_LICENSE("GPL");