1/* sound/soc/samsung/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/gpio.h> 17#include <linux/module.h> 18 19#include <sound/soc.h> 20#include <sound/jack.h> 21 22#include <mach/gpio-samsung.h> 23#include <asm/mach-types.h> 24 25#include "i2s.h" 26#include "../codecs/wm8750.h" 27 28/* 29 * WM8987 is register compatible with WM8750, so using that as base driver. 30 */ 31 32static struct snd_soc_card snd_soc_smartq; 33 34static int smartq_hifi_hw_params(struct snd_pcm_substream *substream, 35 struct snd_pcm_hw_params *params) 36{ 37 struct snd_soc_pcm_runtime *rtd = substream->private_data; 38 struct snd_soc_dai *codec_dai = rtd->codec_dai; 39 struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 40 unsigned int clk = 0; 41 int ret; 42 43 switch (params_rate(params)) { 44 case 8000: 45 case 16000: 46 case 32000: 47 case 48000: 48 case 96000: 49 clk = 12288000; 50 break; 51 case 11025: 52 case 22050: 53 case 44100: 54 case 88200: 55 clk = 11289600; 56 break; 57 } 58 59 /* Use PCLK for I2S signal generation */ 60 ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_RCLKSRC_0, 61 0, SND_SOC_CLOCK_IN); 62 if (ret < 0) 63 return ret; 64 65 /* Gate the RCLK output on PAD */ 66 ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_CDCLK, 67 0, SND_SOC_CLOCK_IN); 68 if (ret < 0) 69 return ret; 70 71 /* set the codec system clock for DAC and ADC */ 72 ret = snd_soc_dai_set_sysclk(codec_dai, WM8750_SYSCLK, clk, 73 SND_SOC_CLOCK_IN); 74 if (ret < 0) 75 return ret; 76 77 return 0; 78} 79 80/* 81 * SmartQ WM8987 HiFi DAI operations. 82 */ 83static struct snd_soc_ops smartq_hifi_ops = { 84 .hw_params = smartq_hifi_hw_params, 85}; 86 87static struct snd_soc_jack smartq_jack; 88 89static struct snd_soc_jack_pin smartq_jack_pins[] = { 90 /* Disable speaker when headphone is plugged in */ 91 { 92 .pin = "Internal Speaker", 93 .mask = SND_JACK_HEADPHONE, 94 }, 95}; 96 97static struct snd_soc_jack_gpio smartq_jack_gpios[] = { 98 { 99 .gpio = S3C64XX_GPL(12), 100 .name = "headphone detect", 101 .report = SND_JACK_HEADPHONE, 102 .debounce_time = 200, 103 }, 104}; 105 106static const struct snd_kcontrol_new wm8987_smartq_controls[] = { 107 SOC_DAPM_PIN_SWITCH("Internal Speaker"), 108 SOC_DAPM_PIN_SWITCH("Headphone Jack"), 109 SOC_DAPM_PIN_SWITCH("Internal Mic"), 110}; 111 112static int smartq_speaker_event(struct snd_soc_dapm_widget *w, 113 struct snd_kcontrol *k, 114 int event) 115{ 116 gpio_set_value(S3C64XX_GPK(12), SND_SOC_DAPM_EVENT_OFF(event)); 117 118 return 0; 119} 120 121static const struct snd_soc_dapm_widget wm8987_dapm_widgets[] = { 122 SND_SOC_DAPM_SPK("Internal Speaker", smartq_speaker_event), 123 SND_SOC_DAPM_HP("Headphone Jack", NULL), 124 SND_SOC_DAPM_MIC("Internal Mic", NULL), 125}; 126 127static const struct snd_soc_dapm_route audio_map[] = { 128 {"Headphone Jack", NULL, "LOUT2"}, 129 {"Headphone Jack", NULL, "ROUT2"}, 130 131 {"Internal Speaker", NULL, "LOUT2"}, 132 {"Internal Speaker", NULL, "ROUT2"}, 133 134 {"Mic Bias", NULL, "Internal Mic"}, 135 {"LINPUT2", NULL, "Mic Bias"}, 136}; 137 138static int smartq_wm8987_init(struct snd_soc_pcm_runtime *rtd) 139{ 140 struct snd_soc_codec *codec = rtd->codec; 141 struct snd_soc_dapm_context *dapm = &codec->dapm; 142 int err = 0; 143 144 /* set endpoints to not connected */ 145 snd_soc_dapm_nc_pin(dapm, "LINPUT1"); 146 snd_soc_dapm_nc_pin(dapm, "RINPUT1"); 147 snd_soc_dapm_nc_pin(dapm, "OUT3"); 148 snd_soc_dapm_nc_pin(dapm, "ROUT1"); 149 150 /* set endpoints to default off mode */ 151 snd_soc_dapm_disable_pin(dapm, "Headphone Jack"); 152 153 /* Headphone jack detection */ 154 err = snd_soc_card_jack_new(rtd->card, "Headphone Jack", 155 SND_JACK_HEADPHONE, &smartq_jack, 156 smartq_jack_pins, 157 ARRAY_SIZE(smartq_jack_pins)); 158 if (err) 159 return err; 160 161 err = snd_soc_jack_add_gpios(&smartq_jack, 162 ARRAY_SIZE(smartq_jack_gpios), 163 smartq_jack_gpios); 164 165 return err; 166} 167 168static int smartq_wm8987_card_remove(struct snd_soc_card *card) 169{ 170 snd_soc_jack_free_gpios(&smartq_jack, ARRAY_SIZE(smartq_jack_gpios), 171 smartq_jack_gpios); 172 173 return 0; 174} 175 176static struct snd_soc_dai_link smartq_dai[] = { 177 { 178 .name = "wm8987", 179 .stream_name = "SmartQ Hi-Fi", 180 .cpu_dai_name = "samsung-i2s.0", 181 .codec_dai_name = "wm8750-hifi", 182 .platform_name = "samsung-i2s.0", 183 .codec_name = "wm8750.0-0x1a", 184 .init = smartq_wm8987_init, 185 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 186 SND_SOC_DAIFMT_CBS_CFS, 187 .ops = &smartq_hifi_ops, 188 }, 189}; 190 191static struct snd_soc_card snd_soc_smartq = { 192 .name = "SmartQ", 193 .owner = THIS_MODULE, 194 .remove = smartq_wm8987_card_remove, 195 .dai_link = smartq_dai, 196 .num_links = ARRAY_SIZE(smartq_dai), 197 198 .dapm_widgets = wm8987_dapm_widgets, 199 .num_dapm_widgets = ARRAY_SIZE(wm8987_dapm_widgets), 200 .dapm_routes = audio_map, 201 .num_dapm_routes = ARRAY_SIZE(audio_map), 202 .controls = wm8987_smartq_controls, 203 .num_controls = ARRAY_SIZE(wm8987_smartq_controls), 204}; 205 206static struct platform_device *smartq_snd_device; 207 208static int __init smartq_init(void) 209{ 210 int ret; 211 212 if (!machine_is_smartq7() && !machine_is_smartq5()) { 213 pr_info("Only SmartQ is supported by this ASoC driver\n"); 214 return -ENODEV; 215 } 216 217 smartq_snd_device = platform_device_alloc("soc-audio", -1); 218 if (!smartq_snd_device) 219 return -ENOMEM; 220 221 platform_set_drvdata(smartq_snd_device, &snd_soc_smartq); 222 223 ret = platform_device_add(smartq_snd_device); 224 if (ret) { 225 platform_device_put(smartq_snd_device); 226 return ret; 227 } 228 229 /* Initialise GPIOs used by amplifiers */ 230 ret = gpio_request(S3C64XX_GPK(12), "amplifiers shutdown"); 231 if (ret) { 232 dev_err(&smartq_snd_device->dev, "Failed to register GPK12\n"); 233 goto err_unregister_device; 234 } 235 236 /* Disable amplifiers */ 237 ret = gpio_direction_output(S3C64XX_GPK(12), 1); 238 if (ret) { 239 dev_err(&smartq_snd_device->dev, "Failed to configure GPK12\n"); 240 goto err_free_gpio_amp_shut; 241 } 242 243 return 0; 244 245err_free_gpio_amp_shut: 246 gpio_free(S3C64XX_GPK(12)); 247err_unregister_device: 248 platform_device_unregister(smartq_snd_device); 249 250 return ret; 251} 252 253static void __exit smartq_exit(void) 254{ 255 gpio_free(S3C64XX_GPK(12)); 256 257 platform_device_unregister(smartq_snd_device); 258} 259 260module_init(smartq_init); 261module_exit(smartq_exit); 262 263/* Module information */ 264MODULE_AUTHOR("Maurus Cuelenaere <mcuelenaere@gmail.com>"); 265MODULE_DESCRIPTION("ALSA SoC SmartQ WM8987"); 266MODULE_LICENSE("GPL"); 267