Commit | Line | Data |
---|---|---|
5033f43c | 1 | /* sound/soc/samsung/smartq_wm8987.c |
ce93a370 MC |
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 | ||
df0cc2d1 | 16 | #include <linux/gpio/consumer.h> |
da155d5b | 17 | #include <linux/module.h> |
ce93a370 | 18 | |
1c8f2c42 | 19 | #include <sound/soc.h> |
ce93a370 MC |
20 | #include <sound/jack.h> |
21 | ||
b9493d6c | 22 | #include "i2s.h" |
ce93a370 MC |
23 | #include "../codecs/wm8750.h" |
24 | ||
25 | /* | |
26 | * WM8987 is register compatible with WM8750, so using that as base driver. | |
27 | */ | |
28 | ||
29 | static struct snd_soc_card snd_soc_smartq; | |
30 | ||
31 | static int smartq_hifi_hw_params(struct snd_pcm_substream *substream, | |
32 | struct snd_pcm_hw_params *params) | |
33 | { | |
34 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
74bd21e9 AL |
35 | struct snd_soc_dai *codec_dai = rtd->codec_dai; |
36 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; | |
ce93a370 MC |
37 | unsigned int clk = 0; |
38 | int ret; | |
39 | ||
ce93a370 MC |
40 | switch (params_rate(params)) { |
41 | case 8000: | |
42 | case 16000: | |
43 | case 32000: | |
44 | case 48000: | |
45 | case 96000: | |
46 | clk = 12288000; | |
47 | break; | |
48 | case 11025: | |
49 | case 22050: | |
50 | case 44100: | |
51 | case 88200: | |
52 | clk = 11289600; | |
53 | break; | |
54 | } | |
55 | ||
b9493d6c JB |
56 | /* Use PCLK for I2S signal generation */ |
57 | ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_RCLKSRC_0, | |
58 | 0, SND_SOC_CLOCK_IN); | |
ce93a370 MC |
59 | if (ret < 0) |
60 | return ret; | |
61 | ||
b9493d6c JB |
62 | /* Gate the RCLK output on PAD */ |
63 | ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_CDCLK, | |
64 | 0, SND_SOC_CLOCK_IN); | |
ce93a370 MC |
65 | if (ret < 0) |
66 | return ret; | |
67 | ||
b9493d6c JB |
68 | /* set the codec system clock for DAC and ADC */ |
69 | ret = snd_soc_dai_set_sysclk(codec_dai, WM8750_SYSCLK, clk, | |
70 | SND_SOC_CLOCK_IN); | |
ce93a370 MC |
71 | if (ret < 0) |
72 | return ret; | |
73 | ||
74 | return 0; | |
75 | } | |
76 | ||
77 | /* | |
78 | * SmartQ WM8987 HiFi DAI operations. | |
79 | */ | |
80 | static struct snd_soc_ops smartq_hifi_ops = { | |
81 | .hw_params = smartq_hifi_hw_params, | |
82 | }; | |
83 | ||
84 | static struct snd_soc_jack smartq_jack; | |
85 | ||
86 | static struct snd_soc_jack_pin smartq_jack_pins[] = { | |
87 | /* Disable speaker when headphone is plugged in */ | |
88 | { | |
89 | .pin = "Internal Speaker", | |
90 | .mask = SND_JACK_HEADPHONE, | |
ce93a370 MC |
91 | }, |
92 | }; | |
93 | ||
94 | static struct snd_soc_jack_gpio smartq_jack_gpios[] = { | |
95 | { | |
df0cc2d1 | 96 | .gpio = -1, |
ce93a370 MC |
97 | .name = "headphone detect", |
98 | .report = SND_JACK_HEADPHONE, | |
99 | .debounce_time = 200, | |
100 | }, | |
101 | }; | |
102 | ||
103 | static const struct snd_kcontrol_new wm8987_smartq_controls[] = { | |
104 | SOC_DAPM_PIN_SWITCH("Internal Speaker"), | |
105 | SOC_DAPM_PIN_SWITCH("Headphone Jack"), | |
106 | SOC_DAPM_PIN_SWITCH("Internal Mic"), | |
107 | }; | |
108 | ||
109 | static int smartq_speaker_event(struct snd_soc_dapm_widget *w, | |
110 | struct snd_kcontrol *k, | |
111 | int event) | |
112 | { | |
df0cc2d1 AB |
113 | struct gpio_desc *gpio = snd_soc_card_get_drvdata(&snd_soc_smartq); |
114 | ||
115 | gpiod_set_value(gpio, SND_SOC_DAPM_EVENT_OFF(event)); | |
ce93a370 MC |
116 | |
117 | return 0; | |
118 | } | |
119 | ||
120 | static const struct snd_soc_dapm_widget wm8987_dapm_widgets[] = { | |
121 | SND_SOC_DAPM_SPK("Internal Speaker", smartq_speaker_event), | |
122 | SND_SOC_DAPM_HP("Headphone Jack", NULL), | |
123 | SND_SOC_DAPM_MIC("Internal Mic", NULL), | |
124 | }; | |
125 | ||
126 | static const struct snd_soc_dapm_route audio_map[] = { | |
127 | {"Headphone Jack", NULL, "LOUT2"}, | |
128 | {"Headphone Jack", NULL, "ROUT2"}, | |
129 | ||
130 | {"Internal Speaker", NULL, "LOUT2"}, | |
131 | {"Internal Speaker", NULL, "ROUT2"}, | |
132 | ||
133 | {"Mic Bias", NULL, "Internal Mic"}, | |
134 | {"LINPUT2", NULL, "Mic Bias"}, | |
135 | }; | |
136 | ||
74bd21e9 | 137 | static int smartq_wm8987_init(struct snd_soc_pcm_runtime *rtd) |
ce93a370 | 138 | { |
d01d7d3d | 139 | struct snd_soc_dapm_context *dapm = &rtd->card->dapm; |
ce93a370 MC |
140 | int err = 0; |
141 | ||
ce93a370 | 142 | /* set endpoints to not connected */ |
ce6120cc LG |
143 | snd_soc_dapm_nc_pin(dapm, "LINPUT1"); |
144 | snd_soc_dapm_nc_pin(dapm, "RINPUT1"); | |
145 | snd_soc_dapm_nc_pin(dapm, "OUT3"); | |
146 | snd_soc_dapm_nc_pin(dapm, "ROUT1"); | |
ce93a370 | 147 | |
ce93a370 | 148 | /* Headphone jack detection */ |
55b2ed2d LPC |
149 | err = snd_soc_card_jack_new(rtd->card, "Headphone Jack", |
150 | SND_JACK_HEADPHONE, &smartq_jack, | |
151 | smartq_jack_pins, | |
152 | ARRAY_SIZE(smartq_jack_pins)); | |
ce93a370 MC |
153 | if (err) |
154 | return err; | |
155 | ||
156 | err = snd_soc_jack_add_gpios(&smartq_jack, | |
157 | ARRAY_SIZE(smartq_jack_gpios), | |
158 | smartq_jack_gpios); | |
159 | ||
160 | return err; | |
161 | } | |
162 | ||
16088cb6 | 163 | static int smartq_wm8987_card_remove(struct snd_soc_card *card) |
e1d4d3c8 SW |
164 | { |
165 | snd_soc_jack_free_gpios(&smartq_jack, ARRAY_SIZE(smartq_jack_gpios), | |
166 | smartq_jack_gpios); | |
167 | ||
168 | return 0; | |
169 | } | |
170 | ||
ce93a370 MC |
171 | static struct snd_soc_dai_link smartq_dai[] = { |
172 | { | |
173 | .name = "wm8987", | |
174 | .stream_name = "SmartQ Hi-Fi", | |
b9493d6c | 175 | .cpu_dai_name = "samsung-i2s.0", |
f0fba2ad | 176 | .codec_dai_name = "wm8750-hifi", |
a08485d8 | 177 | .platform_name = "samsung-i2s.0", |
dc5de62b | 178 | .codec_name = "wm8750.0-0x1a", |
ce93a370 | 179 | .init = smartq_wm8987_init, |
10756c27 LPC |
180 | .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | |
181 | SND_SOC_DAIFMT_CBS_CFS, | |
ce93a370 MC |
182 | .ops = &smartq_hifi_ops, |
183 | }, | |
184 | }; | |
185 | ||
186 | static struct snd_soc_card snd_soc_smartq = { | |
187 | .name = "SmartQ", | |
095d79dc | 188 | .owner = THIS_MODULE, |
e1d4d3c8 | 189 | .remove = smartq_wm8987_card_remove, |
ce93a370 MC |
190 | .dai_link = smartq_dai, |
191 | .num_links = ARRAY_SIZE(smartq_dai), | |
257fe593 MB |
192 | |
193 | .dapm_widgets = wm8987_dapm_widgets, | |
194 | .num_dapm_widgets = ARRAY_SIZE(wm8987_dapm_widgets), | |
195 | .dapm_routes = audio_map, | |
196 | .num_dapm_routes = ARRAY_SIZE(audio_map), | |
197 | .controls = wm8987_smartq_controls, | |
198 | .num_controls = ARRAY_SIZE(wm8987_smartq_controls), | |
ce93a370 MC |
199 | }; |
200 | ||
df0cc2d1 | 201 | static int smartq_probe(struct platform_device *pdev) |
ce93a370 | 202 | { |
df0cc2d1 | 203 | struct gpio_desc *gpio; |
ce93a370 MC |
204 | int ret; |
205 | ||
df0cc2d1 | 206 | platform_set_drvdata(pdev, &snd_soc_smartq); |
ce93a370 MC |
207 | |
208 | /* Initialise GPIOs used by amplifiers */ | |
df0cc2d1 AB |
209 | gpio = devm_gpiod_get(&pdev->dev, "amplifiers shutdown", |
210 | GPIOD_OUT_HIGH); | |
211 | if (IS_ERR(gpio)) { | |
212 | dev_err(&pdev->dev, "Failed to register GPK12\n"); | |
213 | ret = PTR_ERR(gpio); | |
214 | goto out; | |
ce93a370 | 215 | } |
df0cc2d1 | 216 | snd_soc_card_set_drvdata(&snd_soc_smartq, gpio); |
ce93a370 | 217 | |
df0cc2d1 AB |
218 | ret = devm_snd_soc_register_card(&pdev->dev, &snd_soc_smartq); |
219 | if (ret) | |
220 | dev_err(&pdev->dev, "Failed to register card\n"); | |
ce93a370 | 221 | |
df0cc2d1 | 222 | out: |
ce93a370 MC |
223 | return ret; |
224 | } | |
225 | ||
df0cc2d1 AB |
226 | static struct platform_driver smartq_driver = { |
227 | .driver = { | |
228 | .name = "smartq-audio", | |
229 | }, | |
230 | .probe = smartq_probe, | |
231 | }; | |
ce93a370 | 232 | |
df0cc2d1 | 233 | module_platform_driver(smartq_driver); |
ce93a370 MC |
234 | |
235 | /* Module information */ | |
236 | MODULE_AUTHOR("Maurus Cuelenaere <mcuelenaere@gmail.com>"); | |
237 | MODULE_DESCRIPTION("ALSA SoC SmartQ WM8987"); | |
238 | MODULE_LICENSE("GPL"); |