xref: /openbmc/linux/sound/soc/samsung/bells.c (revision 344c5edefbc8fcec7e2863b44ca43a07d9cf46b6)
1b545dd92SMark Brown /*
2b545dd92SMark Brown  * Bells audio support
3b545dd92SMark Brown  *
4b545dd92SMark Brown  * Copyright 2012 Wolfson Microelectronics
5b545dd92SMark Brown  *
6b545dd92SMark Brown  * This program is free software; you can redistribute  it and/or modify it
7b545dd92SMark Brown  * under  the terms of  the GNU General  Public License as published by the
8b545dd92SMark Brown  * Free Software Foundation;  either version 2 of the  License, or (at your
9b545dd92SMark Brown  * option) any later version.
10b545dd92SMark Brown  */
11b545dd92SMark Brown 
12b545dd92SMark Brown #include <sound/soc.h>
13b545dd92SMark Brown #include <sound/soc-dapm.h>
14b545dd92SMark Brown #include <sound/jack.h>
15b545dd92SMark Brown #include <linux/gpio.h>
16b545dd92SMark Brown #include <linux/module.h>
17b545dd92SMark Brown 
18b545dd92SMark Brown #include "../codecs/wm5102.h"
19b545dd92SMark Brown #include "../codecs/wm9081.h"
20b545dd92SMark Brown 
21b545dd92SMark Brown /*
22b545dd92SMark Brown  * 44.1kHz based clocks for the SYSCLK domain, use a very high clock
23b545dd92SMark Brown  * to allow all the DSP functionality to be enabled if desired.
24b545dd92SMark Brown  */
25b545dd92SMark Brown #define SYSCLK_RATE (44100 * 1024)
26b545dd92SMark Brown 
27b545dd92SMark Brown /* 48kHz based clocks for the ASYNC domain */
28b545dd92SMark Brown #define ASYNCCLK_RATE (48000 * 512)
29b545dd92SMark Brown 
30b545dd92SMark Brown /* BCLK2 is fixed at this currently */
31b545dd92SMark Brown #define BCLK2_RATE (64 * 8000)
32b545dd92SMark Brown 
33b545dd92SMark Brown /*
34b545dd92SMark Brown  * Expect a 24.576MHz crystal if one is fitted (the driver will function
35b545dd92SMark Brown  * if this is not fitted).
36b545dd92SMark Brown  */
37b545dd92SMark Brown #define MCLK_RATE 24576000
38b545dd92SMark Brown 
39*344c5edeSMark Brown #define SYS_AUDIO_RATE 44100
40*344c5edeSMark Brown #define SYS_MCLK_RATE  (SYS_AUDIO_RATE * 256)
41*344c5edeSMark Brown 
42*344c5edeSMark Brown #define DAI_AP_DSP    0
43*344c5edeSMark Brown #define DAI_DSP_CODEC 1
44*344c5edeSMark Brown #define DAI_CODEC_CP  2
45*344c5edeSMark Brown #define DAI_CODEC_SUB 3
46b545dd92SMark Brown 
47b545dd92SMark Brown static int bells_set_bias_level(struct snd_soc_card *card,
48b545dd92SMark Brown 				struct snd_soc_dapm_context *dapm,
49b545dd92SMark Brown 				enum snd_soc_bias_level level)
50b545dd92SMark Brown {
51*344c5edeSMark Brown 	struct snd_soc_dai *codec_dai = card->rtd[DAI_DSP_CODEC].codec_dai;
52b545dd92SMark Brown 	struct snd_soc_codec *codec = codec_dai->codec;
53b545dd92SMark Brown 	int ret;
54b545dd92SMark Brown 
55b545dd92SMark Brown 	if (dapm->dev != codec_dai->dev)
56b545dd92SMark Brown 		return 0;
57b545dd92SMark Brown 
58b545dd92SMark Brown 	switch (level) {
59b545dd92SMark Brown 	case SND_SOC_BIAS_PREPARE:
60b545dd92SMark Brown 		if (dapm->bias_level == SND_SOC_BIAS_STANDBY) {
61b545dd92SMark Brown 			ret = snd_soc_codec_set_pll(codec, WM5102_FLL1,
62b545dd92SMark Brown 						    ARIZONA_FLL_SRC_MCLK1,
63b545dd92SMark Brown 						    MCLK_RATE,
64b545dd92SMark Brown 						    SYSCLK_RATE);
65b545dd92SMark Brown 			if (ret < 0)
66b545dd92SMark Brown 				pr_err("Failed to start FLL: %d\n", ret);
67b545dd92SMark Brown 
68b545dd92SMark Brown 			ret = snd_soc_codec_set_pll(codec, WM5102_FLL2,
69b545dd92SMark Brown 						    ARIZONA_FLL_SRC_AIF2BCLK,
70b545dd92SMark Brown 						    BCLK2_RATE,
71b545dd92SMark Brown 						    ASYNCCLK_RATE);
72b545dd92SMark Brown 			if (ret < 0)
73b545dd92SMark Brown 				pr_err("Failed to start FLL: %d\n", ret);
74b545dd92SMark Brown 		}
75b545dd92SMark Brown 		break;
76b545dd92SMark Brown 
77b545dd92SMark Brown 	default:
78b545dd92SMark Brown 		break;
79b545dd92SMark Brown 	}
80b545dd92SMark Brown 
81b545dd92SMark Brown 	return 0;
82b545dd92SMark Brown }
83b545dd92SMark Brown 
84b545dd92SMark Brown static int bells_set_bias_level_post(struct snd_soc_card *card,
85b545dd92SMark Brown 				     struct snd_soc_dapm_context *dapm,
86b545dd92SMark Brown 				     enum snd_soc_bias_level level)
87b545dd92SMark Brown {
88*344c5edeSMark Brown 	struct snd_soc_dai *codec_dai = card->rtd[DAI_DSP_CODEC].codec_dai;
89b545dd92SMark Brown 	struct snd_soc_codec *codec = codec_dai->codec;
90b545dd92SMark Brown 	int ret;
91b545dd92SMark Brown 
92b545dd92SMark Brown 	if (dapm->dev != codec_dai->dev)
93b545dd92SMark Brown 		return 0;
94b545dd92SMark Brown 
95b545dd92SMark Brown 	switch (level) {
96b545dd92SMark Brown 	case SND_SOC_BIAS_STANDBY:
97b545dd92SMark Brown 		ret = snd_soc_codec_set_pll(codec, WM5102_FLL1, 0, 0, 0);
98b545dd92SMark Brown 		if (ret < 0) {
99b545dd92SMark Brown 			pr_err("Failed to stop FLL: %d\n", ret);
100b545dd92SMark Brown 			return ret;
101b545dd92SMark Brown 		}
102b545dd92SMark Brown 
103b545dd92SMark Brown 		ret = snd_soc_codec_set_pll(codec, WM5102_FLL2, 0, 0, 0);
104b545dd92SMark Brown 		if (ret < 0) {
105b545dd92SMark Brown 			pr_err("Failed to stop FLL: %d\n", ret);
106b545dd92SMark Brown 			return ret;
107b545dd92SMark Brown 		}
108b545dd92SMark Brown 		break;
109b545dd92SMark Brown 
110b545dd92SMark Brown 	default:
111b545dd92SMark Brown 		break;
112b545dd92SMark Brown 	}
113b545dd92SMark Brown 
114b545dd92SMark Brown 	dapm->bias_level = level;
115b545dd92SMark Brown 
116b545dd92SMark Brown 	return 0;
117b545dd92SMark Brown }
118b545dd92SMark Brown 
119b545dd92SMark Brown static int bells_late_probe(struct snd_soc_card *card)
120b545dd92SMark Brown {
121*344c5edeSMark Brown 	struct snd_soc_codec *wm0010 = card->rtd[DAI_AP_DSP].codec;
122*344c5edeSMark Brown 	struct snd_soc_codec *codec = card->rtd[DAI_DSP_CODEC].codec;
123*344c5edeSMark Brown 	struct snd_soc_dai *aif1_dai = card->rtd[DAI_DSP_CODEC].codec_dai;
124*344c5edeSMark Brown 	struct snd_soc_dai *aif2_dai = card->rtd[DAI_CODEC_CP].cpu_dai;
125*344c5edeSMark Brown 	struct snd_soc_dai *aif3_dai = card->rtd[DAI_CODEC_SUB].cpu_dai;
126*344c5edeSMark Brown 	struct snd_soc_dai *wm9081_dai = card->rtd[DAI_CODEC_SUB].codec_dai;
127b545dd92SMark Brown 	int ret;
128b545dd92SMark Brown 
129*344c5edeSMark Brown 	ret = snd_soc_codec_set_sysclk(wm0010, 0, 0, SYS_MCLK_RATE, 0);
130*344c5edeSMark Brown 	if (ret != 0) {
131*344c5edeSMark Brown 		dev_err(wm0010->dev, "Failed to set WM0010 clock: %d\n", ret);
132*344c5edeSMark Brown 		return ret;
133*344c5edeSMark Brown 	}
134*344c5edeSMark Brown 
135b545dd92SMark Brown 	ret = snd_soc_dai_set_sysclk(aif1_dai, ARIZONA_CLK_SYSCLK, 0, 0);
136b545dd92SMark Brown 	if (ret != 0) {
137b545dd92SMark Brown 		dev_err(aif1_dai->dev, "Failed to set AIF1 clock: %d\n", ret);
138b545dd92SMark Brown 		return ret;
139b545dd92SMark Brown 	}
140b545dd92SMark Brown 
141b545dd92SMark Brown 	ret = snd_soc_dai_set_sysclk(aif2_dai, ARIZONA_CLK_ASYNCCLK, 0, 0);
142b545dd92SMark Brown 	if (ret != 0) {
143b545dd92SMark Brown 		dev_err(aif2_dai->dev, "Failed to set AIF2 clock: %d\n", ret);
144b545dd92SMark Brown 		return ret;
145b545dd92SMark Brown 	}
146b545dd92SMark Brown 
147b545dd92SMark Brown 	ret = snd_soc_dai_set_sysclk(aif3_dai, ARIZONA_CLK_SYSCLK, 0, 0);
148b545dd92SMark Brown 	if (ret != 0) {
149b545dd92SMark Brown 		dev_err(aif1_dai->dev, "Failed to set AIF1 clock: %d\n", ret);
150b545dd92SMark Brown 		return ret;
151b545dd92SMark Brown 	}
152b545dd92SMark Brown 
153b545dd92SMark Brown 	ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_SYSCLK,
154b545dd92SMark Brown 				       ARIZONA_CLK_SRC_FLL1, SYSCLK_RATE,
155b545dd92SMark Brown 				       SND_SOC_CLOCK_IN);
156b545dd92SMark Brown 	if (ret != 0) {
157b545dd92SMark Brown 		dev_err(codec->dev, "Failed to set SYSCLK: %d\n", ret);
158b545dd92SMark Brown 		return ret;
159b545dd92SMark Brown 	}
160b545dd92SMark Brown 
161b545dd92SMark Brown 	ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_OPCLK, 0,
162*344c5edeSMark Brown 				       SYS_MCLK_RATE, SND_SOC_CLOCK_OUT);
163b545dd92SMark Brown 	if (ret != 0) {
164b545dd92SMark Brown 		dev_err(codec->dev, "Failed to set OPCLK: %d\n", ret);
165b545dd92SMark Brown 		return ret;
166b545dd92SMark Brown 	}
167b545dd92SMark Brown 
168b545dd92SMark Brown 	ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_ASYNCCLK,
169b545dd92SMark Brown 				       ARIZONA_CLK_SRC_FLL2, ASYNCCLK_RATE,
170b545dd92SMark Brown 				       SND_SOC_CLOCK_IN);
171b545dd92SMark Brown 	if (ret != 0) {
172b545dd92SMark Brown 		dev_err(codec->dev, "Failed to set SYSCLK: %d\n", ret);
173b545dd92SMark Brown 		return ret;
174b545dd92SMark Brown 	}
175b545dd92SMark Brown 
176b545dd92SMark Brown 	ret = snd_soc_codec_set_sysclk(wm9081_dai->codec, WM9081_SYSCLK_MCLK,
177*344c5edeSMark Brown 				       0, SYS_MCLK_RATE, 0);
178b545dd92SMark Brown 	if (ret != 0) {
179b545dd92SMark Brown 		dev_err(wm9081_dai->dev, "Failed to set MCLK: %d\n", ret);
180b545dd92SMark Brown 		return ret;
181b545dd92SMark Brown 	}
182b545dd92SMark Brown 
183b545dd92SMark Brown 	return 0;
184b545dd92SMark Brown }
185b545dd92SMark Brown 
186b545dd92SMark Brown static const struct snd_soc_pcm_stream baseband_params = {
187b545dd92SMark Brown 	.formats = SNDRV_PCM_FMTBIT_S32_LE,
188b545dd92SMark Brown 	.rate_min = 8000,
189b545dd92SMark Brown 	.rate_max = 8000,
190b545dd92SMark Brown 	.channels_min = 2,
191b545dd92SMark Brown 	.channels_max = 2,
192b545dd92SMark Brown };
193b545dd92SMark Brown 
194b545dd92SMark Brown static const struct snd_soc_pcm_stream sub_params = {
195b545dd92SMark Brown 	.formats = SNDRV_PCM_FMTBIT_S32_LE,
196*344c5edeSMark Brown 	.rate_min = SYS_AUDIO_RATE,
197*344c5edeSMark Brown 	.rate_max = SYS_AUDIO_RATE,
198b545dd92SMark Brown 	.channels_min = 2,
199b545dd92SMark Brown 	.channels_max = 2,
200b545dd92SMark Brown };
201b545dd92SMark Brown 
202b545dd92SMark Brown static struct snd_soc_dai_link bells_dai_wm5102[] = {
203b545dd92SMark Brown 	{
204*344c5edeSMark Brown 		.name = "CPU-DSP",
205*344c5edeSMark Brown 		.stream_name = "CPU-DSP",
206b545dd92SMark Brown 		.cpu_dai_name = "samsung-i2s.0",
207*344c5edeSMark Brown 		.codec_dai_name = "wm0010-sdi1",
208b545dd92SMark Brown 		.platform_name = "samsung-audio",
209*344c5edeSMark Brown 		.codec_name = "spi0.0",
210*344c5edeSMark Brown 		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
211*344c5edeSMark Brown 				| SND_SOC_DAIFMT_CBM_CFM,
212*344c5edeSMark Brown 	},
213*344c5edeSMark Brown 	{
214*344c5edeSMark Brown 		.name = "DSP-CODEC",
215*344c5edeSMark Brown 		.stream_name = "DSP-CODEC",
216*344c5edeSMark Brown 		.cpu_dai_name = "wm0010-sdi2",
217*344c5edeSMark Brown 		.codec_dai_name = "wm5102-aif1",
218b545dd92SMark Brown 		.codec_name = "wm5102-codec",
219b545dd92SMark Brown 		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
220b545dd92SMark Brown 				| SND_SOC_DAIFMT_CBM_CFM,
221*344c5edeSMark Brown 		.params = &sub_params,
222*344c5edeSMark Brown 		.ignore_suspend = 1,
223b545dd92SMark Brown 	},
224b545dd92SMark Brown 	{
225b545dd92SMark Brown 		.name = "Baseband",
226b545dd92SMark Brown 		.stream_name = "Baseband",
227b545dd92SMark Brown 		.cpu_dai_name = "wm5102-aif2",
228b545dd92SMark Brown 		.codec_dai_name = "wm1250-ev1",
229b545dd92SMark Brown 		.codec_name = "wm1250-ev1.1-0027",
230b545dd92SMark Brown 		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
231b545dd92SMark Brown 				| SND_SOC_DAIFMT_CBM_CFM,
232b545dd92SMark Brown 		.ignore_suspend = 1,
233b545dd92SMark Brown 		.params = &baseband_params,
234b545dd92SMark Brown 	},
235b545dd92SMark Brown 	{
236b545dd92SMark Brown 		.name = "Sub",
237b545dd92SMark Brown 		.stream_name = "Sub",
238b545dd92SMark Brown 		.cpu_dai_name = "wm5102-aif3",
239b545dd92SMark Brown 		.codec_dai_name = "wm9081-hifi",
240b545dd92SMark Brown 		.codec_name = "wm9081.1-006c",
241b545dd92SMark Brown 		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
242b545dd92SMark Brown 				| SND_SOC_DAIFMT_CBS_CFS,
243b545dd92SMark Brown 		.ignore_suspend = 1,
244b545dd92SMark Brown 		.params = &sub_params,
245b545dd92SMark Brown 	},
246b545dd92SMark Brown };
247b545dd92SMark Brown 
248b545dd92SMark Brown static struct snd_soc_dai_link bells_dai_wm5110[] = {
249b545dd92SMark Brown 	{
250*344c5edeSMark Brown 		.name = "CPU-DSP",
251*344c5edeSMark Brown 		.stream_name = "CPU-DSP",
252b545dd92SMark Brown 		.cpu_dai_name = "samsung-i2s.0",
253*344c5edeSMark Brown 		.codec_dai_name = "wm0010-sdi1",
254b545dd92SMark Brown 		.platform_name = "samsung-audio",
255*344c5edeSMark Brown 		.codec_name = "spi0.0",
256*344c5edeSMark Brown 		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
257*344c5edeSMark Brown 				| SND_SOC_DAIFMT_CBM_CFM,
258*344c5edeSMark Brown 	},
259*344c5edeSMark Brown 	{
260*344c5edeSMark Brown 		.name = "DSP-CODEC",
261*344c5edeSMark Brown 		.stream_name = "DSP-CODEC",
262*344c5edeSMark Brown 		.cpu_dai_name = "wm0010-sdi2",
263*344c5edeSMark Brown 		.codec_dai_name = "wm5110-aif1",
264b545dd92SMark Brown 		.codec_name = "wm5110-codec",
265b545dd92SMark Brown 		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
266b545dd92SMark Brown 				| SND_SOC_DAIFMT_CBM_CFM,
267*344c5edeSMark Brown 		.params = &sub_params,
268*344c5edeSMark Brown 		.ignore_suspend = 1,
269b545dd92SMark Brown 	},
270b545dd92SMark Brown 	{
271b545dd92SMark Brown 		.name = "Baseband",
272b545dd92SMark Brown 		.stream_name = "Baseband",
273b545dd92SMark Brown 		.cpu_dai_name = "wm5110-aif2",
274b545dd92SMark Brown 		.codec_dai_name = "wm1250-ev1",
275b545dd92SMark Brown 		.codec_name = "wm1250-ev1.1-0027",
276b545dd92SMark Brown 		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
277b545dd92SMark Brown 				| SND_SOC_DAIFMT_CBM_CFM,
278b545dd92SMark Brown 		.ignore_suspend = 1,
279b545dd92SMark Brown 		.params = &baseband_params,
280b545dd92SMark Brown 	},
281b545dd92SMark Brown 	{
282b545dd92SMark Brown 		.name = "Sub",
283b545dd92SMark Brown 		.stream_name = "Sub",
284b545dd92SMark Brown 		.cpu_dai_name = "wm5102-aif3",
285b545dd92SMark Brown 		.codec_dai_name = "wm9081-hifi",
286b545dd92SMark Brown 		.codec_name = "wm9081.1-006c",
287b545dd92SMark Brown 		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
288b545dd92SMark Brown 				| SND_SOC_DAIFMT_CBS_CFS,
289b545dd92SMark Brown 		.ignore_suspend = 1,
290b545dd92SMark Brown 		.params = &sub_params,
291b545dd92SMark Brown 	},
292b545dd92SMark Brown };
293b545dd92SMark Brown 
294b545dd92SMark Brown static struct snd_soc_codec_conf bells_codec_conf[] = {
295b545dd92SMark Brown 	{
296b545dd92SMark Brown 		.dev_name = "wm9081.1-006c",
297b545dd92SMark Brown 		.name_prefix = "Sub",
298b545dd92SMark Brown 	},
299b545dd92SMark Brown };
300b545dd92SMark Brown 
301b545dd92SMark Brown static struct snd_soc_dapm_route bells_routes[] = {
302b545dd92SMark Brown 	{ "Sub CLK_SYS", NULL, "OPCLK" },
303b545dd92SMark Brown };
304b545dd92SMark Brown 
305b545dd92SMark Brown static struct snd_soc_card bells_cards[] = {
306b545dd92SMark Brown 	{
307b545dd92SMark Brown 		.name = "Bells WM5102",
308b545dd92SMark Brown 		.owner = THIS_MODULE,
309b545dd92SMark Brown 		.dai_link = bells_dai_wm5102,
310b545dd92SMark Brown 		.num_links = ARRAY_SIZE(bells_dai_wm5102),
311b545dd92SMark Brown 		.codec_conf = bells_codec_conf,
312b545dd92SMark Brown 		.num_configs = ARRAY_SIZE(bells_codec_conf),
313b545dd92SMark Brown 
314b545dd92SMark Brown 		.late_probe = bells_late_probe,
315b545dd92SMark Brown 
316b545dd92SMark Brown 		.dapm_routes = bells_routes,
317b545dd92SMark Brown 		.num_dapm_routes = ARRAY_SIZE(bells_routes),
318b545dd92SMark Brown 
319b545dd92SMark Brown 		.set_bias_level = bells_set_bias_level,
320b545dd92SMark Brown 		.set_bias_level_post = bells_set_bias_level_post,
321b545dd92SMark Brown 	},
322b545dd92SMark Brown 	{
323b545dd92SMark Brown 		.name = "Bells WM5110",
324b545dd92SMark Brown 		.owner = THIS_MODULE,
325b545dd92SMark Brown 		.dai_link = bells_dai_wm5110,
326b545dd92SMark Brown 		.num_links = ARRAY_SIZE(bells_dai_wm5110),
327b545dd92SMark Brown 		.codec_conf = bells_codec_conf,
328b545dd92SMark Brown 		.num_configs = ARRAY_SIZE(bells_codec_conf),
329b545dd92SMark Brown 
330b545dd92SMark Brown 		.late_probe = bells_late_probe,
331b545dd92SMark Brown 
332b545dd92SMark Brown 		.dapm_routes = bells_routes,
333b545dd92SMark Brown 		.num_dapm_routes = ARRAY_SIZE(bells_routes),
334b545dd92SMark Brown 
335b545dd92SMark Brown 		.set_bias_level = bells_set_bias_level,
336b545dd92SMark Brown 		.set_bias_level_post = bells_set_bias_level_post,
337b545dd92SMark Brown 	},
338b545dd92SMark Brown };
339b545dd92SMark Brown 
340b545dd92SMark Brown 
341b545dd92SMark Brown static __devinit int bells_probe(struct platform_device *pdev)
342b545dd92SMark Brown {
343b545dd92SMark Brown 	int ret;
344b545dd92SMark Brown 
345b545dd92SMark Brown 	bells_cards[pdev->id].dev = &pdev->dev;
346b545dd92SMark Brown 
347b545dd92SMark Brown 	ret = snd_soc_register_card(&bells_cards[pdev->id]);
348b545dd92SMark Brown 	if (ret) {
349b545dd92SMark Brown 		dev_err(&pdev->dev,
350b545dd92SMark Brown 			"snd_soc_register_card(%s) failed: %d\n",
351b545dd92SMark Brown 			bells_cards[pdev->id].name, ret);
352b545dd92SMark Brown 		return ret;
353b545dd92SMark Brown 	}
354b545dd92SMark Brown 
355b545dd92SMark Brown 	return 0;
356b545dd92SMark Brown }
357b545dd92SMark Brown 
358b545dd92SMark Brown static int __devexit bells_remove(struct platform_device *pdev)
359b545dd92SMark Brown {
360b545dd92SMark Brown 	snd_soc_unregister_card(&bells_cards[pdev->id]);
361b545dd92SMark Brown 
362b545dd92SMark Brown 	return 0;
363b545dd92SMark Brown }
364b545dd92SMark Brown 
365b545dd92SMark Brown static struct platform_driver bells_driver = {
366b545dd92SMark Brown 	.driver = {
367b545dd92SMark Brown 		.name = "bells",
368b545dd92SMark Brown 		.owner = THIS_MODULE,
369b545dd92SMark Brown 		.pm = &snd_soc_pm_ops,
370b545dd92SMark Brown 	},
371b545dd92SMark Brown 	.probe = bells_probe,
372b545dd92SMark Brown 	.remove = __devexit_p(bells_remove),
373b545dd92SMark Brown };
374b545dd92SMark Brown 
375b545dd92SMark Brown module_platform_driver(bells_driver);
376b545dd92SMark Brown 
377b545dd92SMark Brown MODULE_DESCRIPTION("Bells audio support");
378b545dd92SMark Brown MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
379b545dd92SMark Brown MODULE_LICENSE("GPL");
380b545dd92SMark Brown MODULE_ALIAS("platform:bells");
381