s3c24xx_simtec.c (8878B)
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 16static struct s3c24xx_audio_simtec_pdata *pdata; 17static struct clk *xtal_clk; 18 19static int spk_gain; 20static 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 */ 29static 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 */ 40static 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 */ 58static 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 71static 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 */ 80static 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 */ 99static 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 */ 114static 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 */ 125static 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 130void 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} 147EXPORT_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 */ 159static int simtec_hw_params(struct snd_pcm_substream *substream, 160 struct snd_pcm_hw_params *params) 161{ 162 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 163 struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); 164 struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); 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 if (ret) { 194 pr_err("%s: failed to set clock div\n", 195 __func__); 196 return ret; 197 } 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 const 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 device\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");