1/* sound/soc/s3c24xx/smartq_wm8987.c 2 * 3 * Copyright 2010 Maurus Cuelenaere <mcuelenaere@gmail.com> 4 * 5 * Based on smdk6410_wm8987.c 6 * Copyright 2007 Wolfson Microelectronics PLC. - linux@wolfsonmicro.com 7 * Graeme Gregory - graeme.gregory@wolfsonmicro.com 8 * 9 * This program is free software; you can redistribute it and/or modify it 10 * under the terms of the GNU General Public License as published by the 11 * Free Software Foundation; either version 2 of the License, or (at your 12 * option) any later version. 13 * 14 */ 15 16#include <linux/module.h> 17#include <linux/platform_device.h> 18#include <linux/gpio.h> 19 20#include <sound/pcm.h> 21#include <sound/pcm_params.h> 22#include <sound/soc-dapm.h> 23#include <sound/jack.h> 24 25#include <asm/mach-types.h> 26 27#include "s3c-dma.h" 28#include "s3c64xx-i2s.h" 29 30#include "../codecs/wm8750.h" 31 32/* 33 * WM8987 is register compatible with WM8750, so using that as base driver. 34 */ 35 36static struct snd_soc_card snd_soc_smartq; 37 38static int smartq_hifi_hw_params(struct snd_pcm_substream *substream, 39 struct snd_pcm_hw_params *params) 40{ 41 struct snd_soc_pcm_runtime *rtd = substream->private_data; 42 struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; 43 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; 44 struct s3c_i2sv2_rate_calc div; 45 unsigned int clk = 0; 46 int ret; 47 48 s3c_i2sv2_iis_calc_rate(&div, NULL, params_rate(params), 49 s3c_i2sv2_get_clock(cpu_dai)); 50 51 switch (params_rate(params)) { 52 case 8000: 53 case 16000: 54 case 32000: 55 case 48000: 56 case 96000: 57 clk = 12288000; 58 break; 59 case 11025: 60 case 22050: 61 case 44100: 62 case 88200: 63 clk = 11289600; 64 break; 65 } 66 67 /* set codec DAI configuration */ 68 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | 69 SND_SOC_DAIFMT_NB_NF | 70 SND_SOC_DAIFMT_CBS_CFS); 71 if (ret < 0) 72 return ret; 73 74 /* set cpu DAI configuration */ 75 ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | 76 SND_SOC_DAIFMT_NB_NF | 77 SND_SOC_DAIFMT_CBS_CFS); 78 if (ret < 0) 79 return ret; 80 81 /* set the codec system clock for DAC and ADC */ 82 ret = snd_soc_dai_set_sysclk(codec_dai, WM8750_SYSCLK, clk, 83 SND_SOC_CLOCK_IN); 84 if (ret < 0) 85 return ret; 86 87 /* set MCLK division for sample rate */ 88 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C_I2SV2_DIV_RCLK, div.fs_div); 89 if (ret < 0) 90 return ret; 91 92 /* set prescaler division for sample rate */ 93 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C_I2SV2_DIV_PRESCALER, 94 div.clk_div - 1); 95 if (ret < 0) 96 return ret; 97 98 return 0; 99} 100 101/* 102 * SmartQ WM8987 HiFi DAI operations. 103 */ 104static struct snd_soc_ops smartq_hifi_ops = { 105 .hw_params = smartq_hifi_hw_params, 106}; 107 108static struct snd_soc_jack smartq_jack; 109 110static struct snd_soc_jack_pin smartq_jack_pins[] = { 111 /* Disable speaker when headphone is plugged in */ 112 { 113 .pin = "Internal Speaker", 114 .mask = SND_JACK_HEADPHONE, 115 }, 116}; 117 118static struct snd_soc_jack_gpio smartq_jack_gpios[] = { 119 { 120 .gpio = S3C64XX_GPL(12), 121 .name = "headphone detect", 122 .report = SND_JACK_HEADPHONE, 123 .debounce_time = 200, 124 }, 125}; 126 127static const struct snd_kcontrol_new wm8987_smartq_controls[] = { 128 SOC_DAPM_PIN_SWITCH("Internal Speaker"), 129 SOC_DAPM_PIN_SWITCH("Headphone Jack"), 130 SOC_DAPM_PIN_SWITCH("Internal Mic"), 131}; 132 133static int smartq_speaker_event(struct snd_soc_dapm_widget *w, 134 struct snd_kcontrol *k, 135 int event) 136{ 137 gpio_set_value(S3C64XX_GPK(12), SND_SOC_DAPM_EVENT_OFF(event)); 138 139 return 0; 140} 141 142static const struct snd_soc_dapm_widget wm8987_dapm_widgets[] = { 143 SND_SOC_DAPM_SPK("Internal Speaker", smartq_speaker_event), 144 SND_SOC_DAPM_HP("Headphone Jack", NULL), 145 SND_SOC_DAPM_MIC("Internal Mic", NULL), 146}; 147 148static const struct snd_soc_dapm_route audio_map[] = { 149 {"Headphone Jack", NULL, "LOUT2"}, 150 {"Headphone Jack", NULL, "ROUT2"}, 151 152 {"Internal Speaker", NULL, "LOUT2"}, 153 {"Internal Speaker", NULL, "ROUT2"}, 154 155 {"Mic Bias", NULL, "Internal Mic"}, 156 {"LINPUT2", NULL, "Mic Bias"}, 157}; 158 159static int smartq_wm8987_init(struct snd_soc_codec *codec) 160{ 161 int err = 0; 162 163 /* Add SmartQ specific widgets */ 164 snd_soc_dapm_new_controls(codec, wm8987_dapm_widgets, 165 ARRAY_SIZE(wm8987_dapm_widgets)); 166 167 /* add SmartQ specific controls */ 168 err = snd_soc_add_controls(codec, wm8987_smartq_controls, 169 ARRAY_SIZE(wm8987_smartq_controls)); 170 171 if (err < 0) 172 return err; 173 174 /* setup SmartQ specific audio path */ 175 snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); 176 177 /* set endpoints to not connected */ 178 snd_soc_dapm_nc_pin(codec, "LINPUT1"); 179 snd_soc_dapm_nc_pin(codec, "RINPUT1"); 180 snd_soc_dapm_nc_pin(codec, "OUT3"); 181 snd_soc_dapm_nc_pin(codec, "ROUT1"); 182 183 /* set endpoints to default off mode */ 184 snd_soc_dapm_enable_pin(codec, "Internal Speaker"); 185 snd_soc_dapm_enable_pin(codec, "Internal Mic"); 186 snd_soc_dapm_disable_pin(codec, "Headphone Jack"); 187 188 err = snd_soc_dapm_sync(codec); 189 if (err) 190 return err; 191 192 /* Headphone jack detection */ 193 err = snd_soc_jack_new(&snd_soc_smartq, "Headphone Jack", 194 SND_JACK_HEADPHONE, &smartq_jack); 195 if (err) 196 return err; 197 198 err = snd_soc_jack_add_pins(&smartq_jack, ARRAY_SIZE(smartq_jack_pins), 199 smartq_jack_pins); 200 if (err) 201 return err; 202 203 err = snd_soc_jack_add_gpios(&smartq_jack, 204 ARRAY_SIZE(smartq_jack_gpios), 205 smartq_jack_gpios); 206 207 return err; 208} 209 210static struct snd_soc_dai_link smartq_dai[] = { 211 { 212 .name = "wm8987", 213 .stream_name = "SmartQ Hi-Fi", 214 .cpu_dai = &s3c64xx_i2s_dai[0], 215 .codec_dai = &wm8750_dai, 216 .init = smartq_wm8987_init, 217 .ops = &smartq_hifi_ops, 218 }, 219}; 220 221static struct snd_soc_card snd_soc_smartq = { 222 .name = "SmartQ", 223 .platform = &s3c24xx_soc_platform, 224 .dai_link = smartq_dai, 225 .num_links = ARRAY_SIZE(smartq_dai), 226}; 227 228static struct snd_soc_device smartq_snd_devdata = { 229 .card = &snd_soc_smartq, 230 .codec_dev = &soc_codec_dev_wm8750, 231}; 232 233static struct platform_device *smartq_snd_device; 234 235static int __init smartq_init(void) 236{ 237 int ret; 238 239 if (!machine_is_smartq7() && !machine_is_smartq5()) { 240 pr_info("Only SmartQ is supported by this ASoC driver\n"); 241 return -ENODEV; 242 } 243 244 smartq_snd_device = platform_device_alloc("soc-audio", -1); 245 if (!smartq_snd_device) 246 return -ENOMEM; 247 248 platform_set_drvdata(smartq_snd_device, &smartq_snd_devdata); 249 smartq_snd_devdata.dev = &smartq_snd_device->dev; 250 251 ret = platform_device_add(smartq_snd_device); 252 if (ret) { 253 platform_device_put(smartq_snd_device); 254 return ret; 255 } 256 257 /* Initialise GPIOs used by amplifiers */ 258 ret = gpio_request(S3C64XX_GPK(12), "amplifiers shutdown"); 259 if (ret) { 260 dev_err(&smartq_snd_device->dev, "Failed to register GPK12\n"); 261 goto err_unregister_device; 262 } 263 264 /* Disable amplifiers */ 265 ret = gpio_direction_output(S3C64XX_GPK(12), 1); 266 if (ret) { 267 dev_err(&smartq_snd_device->dev, "Failed to configure GPK12\n"); 268 goto err_free_gpio_amp_shut; 269 } 270 271 return 0; 272 273err_free_gpio_amp_shut: 274 gpio_free(S3C64XX_GPK(12)); 275err_unregister_device: 276 platform_device_unregister(smartq_snd_device); 277 278 return ret; 279} 280 281static void __exit smartq_exit(void) 282{ 283 snd_soc_jack_free_gpios(&smartq_jack, ARRAY_SIZE(smartq_jack_gpios), 284 smartq_jack_gpios); 285 286 platform_device_unregister(smartq_snd_device); 287} 288 289module_init(smartq_init); 290module_exit(smartq_exit); 291 292/* Module information */ 293MODULE_AUTHOR("Maurus Cuelenaere <mcuelenaere@gmail.com>"); 294MODULE_DESCRIPTION("ALSA SoC SmartQ WM8987"); 295MODULE_LICENSE("GPL"); 296