aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSylwester Nawrocki <s.nawrocki@samsung.com>2016-11-02 12:05:45 -0400
committerMark Brown <broonie@kernel.org>2016-12-01 16:54:27 -0500
commit1bfbc260a5b474f1376bdfdfbc590f75645d62af (patch)
treec690a10f174c3ba32aec494e6d1c9c3f06103013
parent48a760279ba31c59b8cfb14c5be9ed1c8e191038 (diff)
ASoC: samsung: Add machine driver for Exynos5433 based TM2 board
This patch adds the sound machine driver for the TM2 and TM2E boards. Speaker and headphone playback, Main Mic capture, Bluetooth, Voice call and external accessory are supported. Signed-off-by: Inha Song <ideal.song@samsung.com> [k.kozlowski: rebased on 4.1] Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org> [s.nawrocki: rebased to 4.7, adjustment to the ASoC core changes, removed unused ops and direct calls to the max98504 function, added parsing of "audio-amplifier" and "audio-codec" properties, added TDM API calls, switched to gpiod API] Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Reviewed-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com> Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r--sound/soc/samsung/Kconfig9
-rw-r--r--sound/soc/samsung/Makefile2
-rw-r--r--sound/soc/samsung/tm2_wm5110.c552
3 files changed, 563 insertions, 0 deletions
diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig
index a6cc6ca93fa7..7c423151ef7d 100644
--- a/sound/soc/samsung/Kconfig
+++ b/sound/soc/samsung/Kconfig
@@ -190,4 +190,13 @@ config SND_SOC_ARNDALE_RT5631_ALC5631
190 select SND_SAMSUNG_I2S 190 select SND_SAMSUNG_I2S
191 select SND_SOC_RT5631 191 select SND_SOC_RT5631
192 192
193config SND_SOC_SAMSUNG_TM2_WM5110
194 tristate "SoC I2S Audio support for WM5110 on TM2 board"
195 depends on SND_SOC_SAMSUNG && MFD_ARIZONA && I2C && SPI_MASTER
196 select SND_SOC_MAX98504
197 select SND_SOC_WM5110
198 select SND_SAMSUNG_I2S
199 help
200 Say Y if you want to add support for SoC audio on the TM2 board.
201
193endif #SND_SOC_SAMSUNG 202endif #SND_SOC_SAMSUNG
diff --git a/sound/soc/samsung/Makefile b/sound/soc/samsung/Makefile
index c95b6835361f..b5df5e2e3d94 100644
--- a/sound/soc/samsung/Makefile
+++ b/sound/soc/samsung/Makefile
@@ -41,6 +41,7 @@ snd-soc-lowland-objs := lowland.o
41snd-soc-littlemill-objs := littlemill.o 41snd-soc-littlemill-objs := littlemill.o
42snd-soc-bells-objs := bells.o 42snd-soc-bells-objs := bells.o
43snd-soc-arndale-rt5631-objs := arndale_rt5631.o 43snd-soc-arndale-rt5631-objs := arndale_rt5631.o
44snd-soc-tm2-wm5110-objs := tm2_wm5110.o
44 45
45obj-$(CONFIG_SND_SOC_SAMSUNG_JIVE_WM8750) += snd-soc-jive-wm8750.o 46obj-$(CONFIG_SND_SOC_SAMSUNG_JIVE_WM8750) += snd-soc-jive-wm8750.o
46obj-$(CONFIG_SND_SOC_SAMSUNG_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o 47obj-$(CONFIG_SND_SOC_SAMSUNG_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
@@ -62,3 +63,4 @@ obj-$(CONFIG_SND_SOC_LOWLAND) += snd-soc-lowland.o
62obj-$(CONFIG_SND_SOC_LITTLEMILL) += snd-soc-littlemill.o 63obj-$(CONFIG_SND_SOC_LITTLEMILL) += snd-soc-littlemill.o
63obj-$(CONFIG_SND_SOC_BELLS) += snd-soc-bells.o 64obj-$(CONFIG_SND_SOC_BELLS) += snd-soc-bells.o
64obj-$(CONFIG_SND_SOC_ARNDALE_RT5631_ALC5631) += snd-soc-arndale-rt5631.o 65obj-$(CONFIG_SND_SOC_ARNDALE_RT5631_ALC5631) += snd-soc-arndale-rt5631.o
66obj-$(CONFIG_SND_SOC_SAMSUNG_TM2_WM5110) += snd-soc-tm2-wm5110.o
diff --git a/sound/soc/samsung/tm2_wm5110.c b/sound/soc/samsung/tm2_wm5110.c
new file mode 100644
index 000000000000..5cdf7d19b87f
--- /dev/null
+++ b/sound/soc/samsung/tm2_wm5110.c
@@ -0,0 +1,552 @@
1/*
2 * Copyright (C) 2015 - 2016 Samsung Electronics Co., Ltd.
3 *
4 * Authors: Inha Song <ideal.song@samsung.com>
5 * Sylwester Nawrocki <s.nawrocki@samsung.com>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version.
11 */
12
13#include <linux/clk.h>
14#include <linux/gpio.h>
15#include <linux/module.h>
16#include <linux/of.h>
17#include <sound/pcm_params.h>
18#include <sound/soc.h>
19
20#include "i2s.h"
21#include "../codecs/wm5110.h"
22
23/*
24 * The source clock is XCLKOUT with its mux set to the external fixed rate
25 * oscillator (XXTI).
26 */
27#define MCLK_RATE 24000000U
28
29#define TM2_DAI_AIF1 0
30#define TM2_DAI_AIF2 1
31
32struct tm2_machine_priv {
33 struct snd_soc_codec *codec;
34 unsigned int sysclk_rate;
35 struct gpio_desc *gpio_mic_bias;
36};
37
38static int tm2_start_sysclk(struct snd_soc_card *card)
39{
40 struct tm2_machine_priv *priv = snd_soc_card_get_drvdata(card);
41 struct snd_soc_codec *codec = priv->codec;
42 int ret;
43
44 ret = snd_soc_codec_set_pll(codec, WM5110_FLL1_REFCLK,
45 ARIZONA_FLL_SRC_MCLK1,
46 MCLK_RATE,
47 priv->sysclk_rate);
48 if (ret < 0) {
49 dev_err(codec->dev, "Failed to set FLL1 source: %d\n", ret);
50 return ret;
51 }
52
53 ret = snd_soc_codec_set_pll(codec, WM5110_FLL1,
54 ARIZONA_FLL_SRC_MCLK1,
55 MCLK_RATE,
56 priv->sysclk_rate);
57 if (ret < 0) {
58 dev_err(codec->dev, "Failed to start FLL1: %d\n", ret);
59 return ret;
60 }
61
62 ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_SYSCLK,
63 ARIZONA_CLK_SRC_FLL1,
64 priv->sysclk_rate,
65 SND_SOC_CLOCK_IN);
66 if (ret < 0) {
67 dev_err(codec->dev, "Failed to set SYSCLK source: %d\n", ret);
68 return ret;
69 }
70
71 return 0;
72}
73
74static int tm2_stop_sysclk(struct snd_soc_card *card)
75{
76 struct tm2_machine_priv *priv = snd_soc_card_get_drvdata(card);
77 struct snd_soc_codec *codec = priv->codec;
78 int ret;
79
80 ret = snd_soc_codec_set_pll(codec, WM5110_FLL1, 0, 0, 0);
81 if (ret < 0) {
82 dev_err(codec->dev, "Failed to stop FLL1: %d\n", ret);
83 return ret;
84 }
85
86 ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_SYSCLK,
87 ARIZONA_CLK_SRC_FLL1, 0, 0);
88 if (ret < 0) {
89 dev_err(codec->dev, "Failed to stop SYSCLK: %d\n", ret);
90 return ret;
91 }
92
93 return 0;
94}
95
96static int tm2_aif1_hw_params(struct snd_pcm_substream *substream,
97 struct snd_pcm_hw_params *params)
98{
99 struct snd_soc_pcm_runtime *rtd = substream->private_data;
100 struct snd_soc_codec *codec = rtd->codec;
101 struct tm2_machine_priv *priv = snd_soc_card_get_drvdata(rtd->card);
102
103 switch (params_rate(params)) {
104 case 4000:
105 case 8000:
106 case 12000:
107 case 16000:
108 case 24000:
109 case 32000:
110 case 48000:
111 case 96000:
112 case 192000:
113 /* Highest possible SYSCLK frequency: 147.456MHz */
114 priv->sysclk_rate = 147456000U;
115 break;
116 case 11025:
117 case 22050:
118 case 44100:
119 case 88200:
120 case 176400:
121 /* Highest possible SYSCLK frequency: 135.4752 MHz */
122 priv->sysclk_rate = 135475200U;
123 break;
124 default:
125 dev_err(codec->dev, "Not supported sample rate: %d\n",
126 params_rate(params));
127 return -EINVAL;
128 }
129
130 return tm2_start_sysclk(rtd->card);
131}
132
133static struct snd_soc_ops tm2_aif1_ops = {
134 .hw_params = tm2_aif1_hw_params,
135};
136
137static int tm2_aif2_hw_params(struct snd_pcm_substream *substream,
138 struct snd_pcm_hw_params *params)
139{
140 struct snd_soc_pcm_runtime *rtd = substream->private_data;
141 struct snd_soc_codec *codec = rtd->codec;
142 unsigned int asyncclk_rate;
143 int ret;
144
145 switch (params_rate(params)) {
146 case 8000:
147 case 12000:
148 case 16000:
149 /* Highest possible ASYNCCLK frequency: 49.152MHz */
150 asyncclk_rate = 49152000U;
151 break;
152 case 11025:
153 /* Highest possible ASYNCCLK frequency: 45.1584 MHz */
154 asyncclk_rate = 45158400U;
155 break;
156 default:
157 dev_err(codec->dev, "Not supported sample rate: %d\n",
158 params_rate(params));
159 return -EINVAL;
160 }
161
162 ret = snd_soc_codec_set_pll(codec, WM5110_FLL2_REFCLK,
163 ARIZONA_FLL_SRC_MCLK1,
164 MCLK_RATE,
165 asyncclk_rate);
166 if (ret < 0) {
167 dev_err(codec->dev, "Failed to set FLL2 source: %d\n", ret);
168 return ret;
169 }
170
171 ret = snd_soc_codec_set_pll(codec, WM5110_FLL2,
172 ARIZONA_FLL_SRC_MCLK1,
173 MCLK_RATE,
174 asyncclk_rate);
175 if (ret < 0) {
176 dev_err(codec->dev, "Failed to start FLL2: %d\n", ret);
177 return ret;
178 }
179
180 ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_ASYNCCLK,
181 ARIZONA_CLK_SRC_FLL2,
182 asyncclk_rate,
183 SND_SOC_CLOCK_IN);
184 if (ret < 0) {
185 dev_err(codec->dev, "Failed to set ASYNCCLK source: %d\n", ret);
186 return ret;
187 }
188
189 return 0;
190}
191
192static int tm2_aif2_hw_free(struct snd_pcm_substream *substream)
193{
194 struct snd_soc_pcm_runtime *rtd = substream->private_data;
195 struct snd_soc_codec *codec = rtd->codec;
196 int ret;
197
198 /* disable FLL2 */
199 ret = snd_soc_codec_set_pll(codec, WM5110_FLL2, ARIZONA_FLL_SRC_MCLK1,
200 0, 0);
201 if (ret < 0)
202 dev_err(codec->dev, "Failed to stop FLL2: %d\n", ret);
203
204 return ret;
205}
206
207static struct snd_soc_ops tm2_aif2_ops = {
208 .hw_params = tm2_aif2_hw_params,
209 .hw_free = tm2_aif2_hw_free,
210};
211
212static int tm2_mic_bias(struct snd_soc_dapm_widget *w,
213 struct snd_kcontrol *kcontrol, int event)
214{
215 struct snd_soc_card *card = w->dapm->card;
216 struct tm2_machine_priv *priv = snd_soc_card_get_drvdata(card);
217
218 switch (event) {
219 case SND_SOC_DAPM_PRE_PMU:
220 gpiod_set_value_cansleep(priv->gpio_mic_bias, 1);
221 break;
222 case SND_SOC_DAPM_POST_PMD:
223 gpiod_set_value_cansleep(priv->gpio_mic_bias, 0);
224 break;
225 }
226
227 return 0;
228}
229
230static int tm2_set_bias_level(struct snd_soc_card *card,
231 struct snd_soc_dapm_context *dapm,
232 enum snd_soc_bias_level level)
233{
234 struct snd_soc_pcm_runtime *rtd;
235
236 rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
237
238 if (dapm->dev != rtd->codec_dai->dev)
239 return 0;
240
241 switch (level) {
242 case SND_SOC_BIAS_STANDBY:
243 if (card->dapm.bias_level == SND_SOC_BIAS_OFF)
244 tm2_start_sysclk(card);
245 break;
246 case SND_SOC_BIAS_OFF:
247 tm2_stop_sysclk(card);
248 break;
249 default:
250 break;
251 }
252
253 return 0;
254}
255
256static struct snd_soc_aux_dev tm2_speaker_amp_dev;
257
258static int tm2_late_probe(struct snd_soc_card *card)
259{
260 struct tm2_machine_priv *priv = snd_soc_card_get_drvdata(card);
261 struct snd_soc_dai_link_component dlc = { 0 };
262 unsigned int ch_map[] = { 0, 1 };
263 struct snd_soc_dai *amp_pdm_dai;
264 struct snd_soc_pcm_runtime *rtd;
265 struct snd_soc_dai *aif1_dai;
266 struct snd_soc_dai *aif2_dai;
267 int ret;
268
269 rtd = snd_soc_get_pcm_runtime(card, card->dai_link[TM2_DAI_AIF1].name);
270 aif1_dai = rtd->codec_dai;
271 priv->codec = rtd->codec;
272
273 ret = snd_soc_dai_set_sysclk(aif1_dai, ARIZONA_CLK_SYSCLK, 0, 0);
274 if (ret < 0) {
275 dev_err(aif1_dai->dev, "Failed to set SYSCLK: %d\n", ret);
276 return ret;
277 }
278
279 rtd = snd_soc_get_pcm_runtime(card, card->dai_link[TM2_DAI_AIF2].name);
280 aif2_dai = rtd->codec_dai;
281
282 ret = snd_soc_dai_set_sysclk(aif2_dai, ARIZONA_CLK_ASYNCCLK, 0, 0);
283 if (ret < 0) {
284 dev_err(aif2_dai->dev, "Failed to set ASYNCCLK: %d\n", ret);
285 return ret;
286 }
287
288 dlc.of_node = tm2_speaker_amp_dev.codec_of_node;
289 amp_pdm_dai = snd_soc_find_dai(&dlc);
290 if (!amp_pdm_dai)
291 return -ENODEV;
292
293 /* Set the MAX98504 V/I sense PDM Tx DAI channel mapping */
294 ret = snd_soc_dai_set_channel_map(amp_pdm_dai, ARRAY_SIZE(ch_map),
295 ch_map, 0, NULL);
296 if (ret < 0)
297 return ret;
298
299 ret = snd_soc_dai_set_tdm_slot(amp_pdm_dai, 0x3, 0x0, 2, 16);
300 if (ret < 0)
301 return ret;
302
303 return 0;
304}
305
306static const struct snd_kcontrol_new tm2_controls[] = {
307 SOC_DAPM_PIN_SWITCH("HP"),
308 SOC_DAPM_PIN_SWITCH("SPK"),
309 SOC_DAPM_PIN_SWITCH("RCV"),
310 SOC_DAPM_PIN_SWITCH("VPS"),
311 SOC_DAPM_PIN_SWITCH("HDMI"),
312
313 SOC_DAPM_PIN_SWITCH("Main Mic"),
314 SOC_DAPM_PIN_SWITCH("Sub Mic"),
315 SOC_DAPM_PIN_SWITCH("Third Mic"),
316
317 SOC_DAPM_PIN_SWITCH("Headset Mic"),
318};
319
320const struct snd_soc_dapm_widget tm2_dapm_widgets[] = {
321 SND_SOC_DAPM_HP("HP", NULL),
322 SND_SOC_DAPM_SPK("SPK", NULL),
323 SND_SOC_DAPM_SPK("RCV", NULL),
324 SND_SOC_DAPM_LINE("VPS", NULL),
325 SND_SOC_DAPM_LINE("HDMI", NULL),
326
327 SND_SOC_DAPM_MIC("Main Mic", tm2_mic_bias),
328 SND_SOC_DAPM_MIC("Sub Mic", NULL),
329 SND_SOC_DAPM_MIC("Third Mic", NULL),
330
331 SND_SOC_DAPM_MIC("Headset Mic", NULL),
332};
333
334static const struct snd_soc_component_driver tm2_component = {
335 .name = "tm2-audio",
336};
337
338static struct snd_soc_dai_driver tm2_ext_dai[] = {
339 {
340 .name = "Voice call",
341 .playback = {
342 .channels_min = 1,
343 .channels_max = 4,
344 .rate_min = 8000,
345 .rate_max = 48000,
346 .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
347 SNDRV_PCM_RATE_48000),
348 .formats = SNDRV_PCM_FMTBIT_S16_LE,
349 },
350 .capture = {
351 .channels_min = 1,
352 .channels_max = 4,
353 .rate_min = 8000,
354 .rate_max = 48000,
355 .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
356 SNDRV_PCM_RATE_48000),
357 .formats = SNDRV_PCM_FMTBIT_S16_LE,
358 },
359 },
360 {
361 .name = "Bluetooth",
362 .playback = {
363 .channels_min = 1,
364 .channels_max = 4,
365 .rate_min = 8000,
366 .rate_max = 16000,
367 .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000),
368 .formats = SNDRV_PCM_FMTBIT_S16_LE,
369 },
370 .capture = {
371 .channels_min = 1,
372 .channels_max = 2,
373 .rate_min = 8000,
374 .rate_max = 16000,
375 .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000),
376 .formats = SNDRV_PCM_FMTBIT_S16_LE,
377 },
378 },
379};
380
381static struct snd_soc_dai_link tm2_dai_links[] = {
382 {
383 .name = "WM5110 AIF1",
384 .stream_name = "HiFi Primary",
385 .codec_dai_name = "wm5110-aif1",
386 .ops = &tm2_aif1_ops,
387 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
388 SND_SOC_DAIFMT_CBM_CFM,
389 }, {
390 .name = "WM5110 Voice",
391 .stream_name = "Voice call",
392 .codec_dai_name = "wm5110-aif2",
393 .ops = &tm2_aif2_ops,
394 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
395 SND_SOC_DAIFMT_CBM_CFM,
396 .ignore_suspend = 1,
397 }, {
398 .name = "WM5110 BT",
399 .stream_name = "Bluetooth",
400 .codec_dai_name = "wm5110-aif3",
401 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
402 SND_SOC_DAIFMT_CBM_CFM,
403 .ignore_suspend = 1,
404 }
405};
406
407static struct snd_soc_card tm2_card = {
408 .owner = THIS_MODULE,
409
410 .dai_link = tm2_dai_links,
411 .num_links = ARRAY_SIZE(tm2_dai_links),
412 .controls = tm2_controls,
413 .num_controls = ARRAY_SIZE(tm2_controls),
414 .dapm_widgets = tm2_dapm_widgets,
415 .num_dapm_widgets = ARRAY_SIZE(tm2_dapm_widgets),
416 .aux_dev = &tm2_speaker_amp_dev,
417 .num_aux_devs = 1,
418
419 .late_probe = tm2_late_probe,
420 .set_bias_level = tm2_set_bias_level,
421};
422
423static int tm2_probe(struct platform_device *pdev)
424{
425 struct device *dev = &pdev->dev;
426 struct snd_soc_card *card = &tm2_card;
427 struct tm2_machine_priv *priv;
428 struct device_node *cpu_dai_node, *codec_dai_node;
429 int ret, i;
430
431 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
432 if (!priv)
433 return -ENOMEM;
434
435 snd_soc_card_set_drvdata(card, priv);
436 card->dev = dev;
437
438 priv->gpio_mic_bias = devm_gpiod_get(dev, "mic-bias",
439 GPIOF_OUT_INIT_LOW);
440 if (IS_ERR(priv->gpio_mic_bias)) {
441 dev_err(dev, "Failed to get mic bias gpio\n");
442 return PTR_ERR(priv->gpio_mic_bias);
443 }
444
445 ret = snd_soc_of_parse_card_name(card, "model");
446 if (ret < 0) {
447 dev_err(dev, "Card name is not specified\n");
448 return ret;
449 }
450
451 ret = snd_soc_of_parse_audio_routing(card, "samsung,audio-routing");
452 if (ret < 0) {
453 dev_err(dev, "Audio routing is not specified or invalid\n");
454 return ret;
455 }
456
457 card->aux_dev[0].codec_of_node = of_parse_phandle(dev->of_node,
458 "audio-amplifier", 0);
459 if (!card->aux_dev[0].codec_of_node) {
460 dev_err(dev, "audio-amplifier property invalid or missing\n");
461 return -EINVAL;
462 }
463
464 cpu_dai_node = of_parse_phandle(dev->of_node, "i2s-controller", 0);
465 if (!cpu_dai_node) {
466 dev_err(dev, "i2s-controllers property invalid or missing\n");
467 ret = -EINVAL;
468 goto amp_node_put;
469 }
470
471 codec_dai_node = of_parse_phandle(dev->of_node, "audio-codec", 0);
472 if (!codec_dai_node) {
473 dev_err(dev, "audio-codec property invalid or missing\n");
474 ret = -EINVAL;
475 goto cpu_dai_node_put;
476 }
477
478 for (i = 0; i < card->num_links; i++) {
479 card->dai_link[i].cpu_dai_name = NULL;
480 card->dai_link[i].cpu_name = NULL;
481 card->dai_link[i].platform_name = NULL;
482 card->dai_link[i].codec_of_node = codec_dai_node;
483 card->dai_link[i].cpu_of_node = cpu_dai_node;
484 card->dai_link[i].platform_of_node = cpu_dai_node;
485 }
486
487 ret = devm_snd_soc_register_component(dev, &tm2_component,
488 tm2_ext_dai, ARRAY_SIZE(tm2_ext_dai));
489 if (ret < 0) {
490 dev_err(dev, "Failed to register component: %d\n", ret);
491 goto codec_dai_node_put;
492 }
493
494 ret = devm_snd_soc_register_card(dev, card);
495 if (ret < 0) {
496 dev_err(dev, "Failed to register card: %d\n", ret);
497 goto codec_dai_node_put;
498 }
499
500codec_dai_node_put:
501 of_node_put(codec_dai_node);
502cpu_dai_node_put:
503 of_node_put(cpu_dai_node);
504amp_node_put:
505 of_node_put(card->aux_dev[0].codec_of_node);
506 return ret;
507}
508
509static int tm2_pm_prepare(struct device *dev)
510{
511 struct snd_soc_card *card = dev_get_drvdata(dev);
512
513 return tm2_stop_sysclk(card);
514}
515
516static void tm2_pm_complete(struct device *dev)
517{
518 struct snd_soc_card *card = dev_get_drvdata(dev);
519
520 tm2_start_sysclk(card);
521}
522
523const struct dev_pm_ops tm2_pm_ops = {
524 .prepare = tm2_pm_prepare,
525 .suspend = snd_soc_suspend,
526 .resume = snd_soc_resume,
527 .complete = tm2_pm_complete,
528 .freeze = snd_soc_suspend,
529 .thaw = snd_soc_resume,
530 .poweroff = snd_soc_poweroff,
531 .restore = snd_soc_resume,
532};
533
534static const struct of_device_id tm2_of_match[] = {
535 { .compatible = "samsung,tm2-audio" },
536 { },
537};
538MODULE_DEVICE_TABLE(of, tm2_of_match);
539
540static struct platform_driver tm2_driver = {
541 .driver = {
542 .name = "tm2-audio",
543 .pm = &tm2_pm_ops,
544 .of_match_table = tm2_of_match,
545 },
546 .probe = tm2_probe,
547};
548module_platform_driver(tm2_driver);
549
550MODULE_AUTHOR("Inha Song <ideal.song@samsung.com>");
551MODULE_DESCRIPTION("ALSA SoC Exynos TM2 Audio Support");
552MODULE_LICENSE("GPL v2");