xref: /openbmc/linux/sound/soc/samsung/bells.c (revision b138707874729473bed70561c88566821156d6a6)
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 /* BCLK2 is fixed at this currently */
22b545dd92SMark Brown #define BCLK2_RATE (64 * 8000)
23b545dd92SMark Brown 
24b545dd92SMark Brown /*
25b545dd92SMark Brown  * Expect a 24.576MHz crystal if one is fitted (the driver will function
26b545dd92SMark Brown  * if this is not fitted).
27b545dd92SMark Brown  */
28b545dd92SMark Brown #define MCLK_RATE 24576000
29b545dd92SMark Brown 
30344c5edeSMark Brown #define SYS_AUDIO_RATE 44100
31344c5edeSMark Brown #define SYS_MCLK_RATE  (SYS_AUDIO_RATE * 256)
32344c5edeSMark Brown 
33344c5edeSMark Brown #define DAI_AP_DSP    0
34344c5edeSMark Brown #define DAI_DSP_CODEC 1
35344c5edeSMark Brown #define DAI_CODEC_CP  2
36344c5edeSMark Brown #define DAI_CODEC_SUB 3
37b545dd92SMark Brown 
38*b1387078SMark Brown struct bells_drvdata {
39*b1387078SMark Brown 	int sysclk_rate;
40*b1387078SMark Brown 	int asyncclk_rate;
41*b1387078SMark Brown };
42*b1387078SMark Brown 
43*b1387078SMark Brown static struct bells_drvdata wm5102_drvdata = {
44*b1387078SMark Brown 	.sysclk_rate = 45158400,
45*b1387078SMark Brown 	.asyncclk_rate = 49152000,
46*b1387078SMark Brown };
47*b1387078SMark Brown 
48*b1387078SMark Brown static struct bells_drvdata wm5110_drvdata = {
49*b1387078SMark Brown 	.sysclk_rate = 135475200,
50*b1387078SMark Brown 	.asyncclk_rate = 147456000,
51*b1387078SMark Brown };
52*b1387078SMark Brown 
53b545dd92SMark Brown static int bells_set_bias_level(struct snd_soc_card *card,
54b545dd92SMark Brown 				struct snd_soc_dapm_context *dapm,
55b545dd92SMark Brown 				enum snd_soc_bias_level level)
56b545dd92SMark Brown {
57344c5edeSMark Brown 	struct snd_soc_dai *codec_dai = card->rtd[DAI_DSP_CODEC].codec_dai;
58b545dd92SMark Brown 	struct snd_soc_codec *codec = codec_dai->codec;
59*b1387078SMark Brown 	struct bells_drvdata *bells = card->drvdata;
60b545dd92SMark Brown 	int ret;
61b545dd92SMark Brown 
62b545dd92SMark Brown 	if (dapm->dev != codec_dai->dev)
63b545dd92SMark Brown 		return 0;
64b545dd92SMark Brown 
65b545dd92SMark Brown 	switch (level) {
66b545dd92SMark Brown 	case SND_SOC_BIAS_PREPARE:
67*b1387078SMark Brown 		if (dapm->bias_level != SND_SOC_BIAS_STANDBY)
68*b1387078SMark Brown 			break;
69*b1387078SMark Brown 
70b545dd92SMark Brown 		ret = snd_soc_codec_set_pll(codec, WM5102_FLL1,
71b545dd92SMark Brown 					    ARIZONA_FLL_SRC_MCLK1,
72b545dd92SMark Brown 					    MCLK_RATE,
73*b1387078SMark Brown 					    bells->sysclk_rate);
74b545dd92SMark Brown 		if (ret < 0)
75b545dd92SMark Brown 			pr_err("Failed to start FLL: %d\n", ret);
76b545dd92SMark Brown 
77*b1387078SMark Brown 		if (bells->asyncclk_rate) {
78b545dd92SMark Brown 			ret = snd_soc_codec_set_pll(codec, WM5102_FLL2,
79b545dd92SMark Brown 						    ARIZONA_FLL_SRC_AIF2BCLK,
80b545dd92SMark Brown 						    BCLK2_RATE,
81*b1387078SMark Brown 						    bells->asyncclk_rate);
82b545dd92SMark Brown 			if (ret < 0)
83b545dd92SMark Brown 				pr_err("Failed to start FLL: %d\n", ret);
84b545dd92SMark Brown 		}
85b545dd92SMark Brown 		break;
86b545dd92SMark Brown 
87b545dd92SMark Brown 	default:
88b545dd92SMark Brown 		break;
89b545dd92SMark Brown 	}
90b545dd92SMark Brown 
91b545dd92SMark Brown 	return 0;
92b545dd92SMark Brown }
93b545dd92SMark Brown 
94b545dd92SMark Brown static int bells_set_bias_level_post(struct snd_soc_card *card,
95b545dd92SMark Brown 				     struct snd_soc_dapm_context *dapm,
96b545dd92SMark Brown 				     enum snd_soc_bias_level level)
97b545dd92SMark Brown {
98344c5edeSMark Brown 	struct snd_soc_dai *codec_dai = card->rtd[DAI_DSP_CODEC].codec_dai;
99b545dd92SMark Brown 	struct snd_soc_codec *codec = codec_dai->codec;
100*b1387078SMark Brown 	struct bells_drvdata *bells = card->drvdata;
101b545dd92SMark Brown 	int ret;
102b545dd92SMark Brown 
103b545dd92SMark Brown 	if (dapm->dev != codec_dai->dev)
104b545dd92SMark Brown 		return 0;
105b545dd92SMark Brown 
106b545dd92SMark Brown 	switch (level) {
107b545dd92SMark Brown 	case SND_SOC_BIAS_STANDBY:
108b545dd92SMark Brown 		ret = snd_soc_codec_set_pll(codec, WM5102_FLL1, 0, 0, 0);
109b545dd92SMark Brown 		if (ret < 0) {
110b545dd92SMark Brown 			pr_err("Failed to stop FLL: %d\n", ret);
111b545dd92SMark Brown 			return ret;
112b545dd92SMark Brown 		}
113b545dd92SMark Brown 
114*b1387078SMark Brown 		if (bells->asyncclk_rate) {
115*b1387078SMark Brown 			ret = snd_soc_codec_set_pll(codec, WM5102_FLL2,
116*b1387078SMark Brown 						    0, 0, 0);
117b545dd92SMark Brown 			if (ret < 0) {
118b545dd92SMark Brown 				pr_err("Failed to stop FLL: %d\n", ret);
119b545dd92SMark Brown 				return ret;
120b545dd92SMark Brown 			}
121*b1387078SMark Brown 		}
122b545dd92SMark Brown 		break;
123b545dd92SMark Brown 
124b545dd92SMark Brown 	default:
125b545dd92SMark Brown 		break;
126b545dd92SMark Brown 	}
127b545dd92SMark Brown 
128b545dd92SMark Brown 	dapm->bias_level = level;
129b545dd92SMark Brown 
130b545dd92SMark Brown 	return 0;
131b545dd92SMark Brown }
132b545dd92SMark Brown 
133b545dd92SMark Brown static int bells_late_probe(struct snd_soc_card *card)
134b545dd92SMark Brown {
135*b1387078SMark Brown 	struct bells_drvdata *bells = card->drvdata;
136344c5edeSMark Brown 	struct snd_soc_codec *wm0010 = card->rtd[DAI_AP_DSP].codec;
137344c5edeSMark Brown 	struct snd_soc_codec *codec = card->rtd[DAI_DSP_CODEC].codec;
138344c5edeSMark Brown 	struct snd_soc_dai *aif1_dai = card->rtd[DAI_DSP_CODEC].codec_dai;
139*b1387078SMark Brown 	struct snd_soc_dai *aif2_dai;
140*b1387078SMark Brown 	struct snd_soc_dai *aif3_dai;
141*b1387078SMark Brown 	struct snd_soc_dai *wm9081_dai;
142b545dd92SMark Brown 	int ret;
143b545dd92SMark Brown 
144*b1387078SMark Brown 	ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_SYSCLK,
145*b1387078SMark Brown 				       ARIZONA_CLK_SRC_FLL1,
146*b1387078SMark Brown 				       bells->sysclk_rate,
147*b1387078SMark Brown 				       SND_SOC_CLOCK_IN);
148*b1387078SMark Brown 	if (ret != 0) {
149*b1387078SMark Brown 		dev_err(codec->dev, "Failed to set SYSCLK: %d\n", ret);
150*b1387078SMark Brown 		return ret;
151*b1387078SMark Brown 	}
152*b1387078SMark Brown 
153344c5edeSMark Brown 	ret = snd_soc_codec_set_sysclk(wm0010, 0, 0, SYS_MCLK_RATE, 0);
154344c5edeSMark Brown 	if (ret != 0) {
155344c5edeSMark Brown 		dev_err(wm0010->dev, "Failed to set WM0010 clock: %d\n", ret);
156344c5edeSMark Brown 		return ret;
157344c5edeSMark Brown 	}
158344c5edeSMark Brown 
159b545dd92SMark Brown 	ret = snd_soc_dai_set_sysclk(aif1_dai, ARIZONA_CLK_SYSCLK, 0, 0);
160*b1387078SMark Brown 	if (ret != 0)
161b545dd92SMark Brown 		dev_err(aif1_dai->dev, "Failed to set AIF1 clock: %d\n", ret);
162*b1387078SMark Brown 
163*b1387078SMark Brown 	ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_OPCLK, 0,
164*b1387078SMark Brown 				       SYS_MCLK_RATE, SND_SOC_CLOCK_OUT);
165*b1387078SMark Brown 	if (ret != 0)
166*b1387078SMark Brown 		dev_err(codec->dev, "Failed to set OPCLK: %d\n", ret);
167*b1387078SMark Brown 
168*b1387078SMark Brown 	if (card->num_rtd == DAI_CODEC_CP)
169*b1387078SMark Brown 		return 0;
170*b1387078SMark Brown 
171*b1387078SMark Brown 	ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_ASYNCCLK,
172*b1387078SMark Brown 				       ARIZONA_CLK_SRC_FLL2,
173*b1387078SMark Brown 				       bells->asyncclk_rate,
174*b1387078SMark Brown 				       SND_SOC_CLOCK_IN);
175*b1387078SMark Brown 	if (ret != 0) {
176*b1387078SMark Brown 		dev_err(codec->dev, "Failed to set ASYNCCLK: %d\n", ret);
177b545dd92SMark Brown 		return ret;
178b545dd92SMark Brown 	}
179b545dd92SMark Brown 
180*b1387078SMark Brown 	aif2_dai = card->rtd[DAI_CODEC_CP].cpu_dai;
181*b1387078SMark Brown 
182b545dd92SMark Brown 	ret = snd_soc_dai_set_sysclk(aif2_dai, ARIZONA_CLK_ASYNCCLK, 0, 0);
183b545dd92SMark Brown 	if (ret != 0) {
184b545dd92SMark Brown 		dev_err(aif2_dai->dev, "Failed to set AIF2 clock: %d\n", ret);
185b545dd92SMark Brown 		return ret;
186b545dd92SMark Brown 	}
187b545dd92SMark Brown 
188*b1387078SMark Brown 	if (card->num_rtd == DAI_CODEC_SUB)
189*b1387078SMark Brown 		return 0;
190*b1387078SMark Brown 
191*b1387078SMark Brown 	aif3_dai = card->rtd[DAI_CODEC_SUB].cpu_dai;
192*b1387078SMark Brown 	wm9081_dai = card->rtd[DAI_CODEC_SUB].codec_dai;
193*b1387078SMark Brown 
194b545dd92SMark Brown 	ret = snd_soc_dai_set_sysclk(aif3_dai, ARIZONA_CLK_SYSCLK, 0, 0);
195b545dd92SMark Brown 	if (ret != 0) {
196b545dd92SMark Brown 		dev_err(aif1_dai->dev, "Failed to set AIF1 clock: %d\n", ret);
197b545dd92SMark Brown 		return ret;
198b545dd92SMark Brown 	}
199b545dd92SMark Brown 
200b545dd92SMark Brown 	ret = snd_soc_codec_set_sysclk(wm9081_dai->codec, WM9081_SYSCLK_MCLK,
201344c5edeSMark Brown 				       0, SYS_MCLK_RATE, 0);
202b545dd92SMark Brown 	if (ret != 0) {
203b545dd92SMark Brown 		dev_err(wm9081_dai->dev, "Failed to set MCLK: %d\n", ret);
204b545dd92SMark Brown 		return ret;
205b545dd92SMark Brown 	}
206b545dd92SMark Brown 
207b545dd92SMark Brown 	return 0;
208b545dd92SMark Brown }
209b545dd92SMark Brown 
210b545dd92SMark Brown static const struct snd_soc_pcm_stream baseband_params = {
211b545dd92SMark Brown 	.formats = SNDRV_PCM_FMTBIT_S32_LE,
212b545dd92SMark Brown 	.rate_min = 8000,
213b545dd92SMark Brown 	.rate_max = 8000,
214b545dd92SMark Brown 	.channels_min = 2,
215b545dd92SMark Brown 	.channels_max = 2,
216b545dd92SMark Brown };
217b545dd92SMark Brown 
218b545dd92SMark Brown static const struct snd_soc_pcm_stream sub_params = {
219b545dd92SMark Brown 	.formats = SNDRV_PCM_FMTBIT_S32_LE,
220344c5edeSMark Brown 	.rate_min = SYS_AUDIO_RATE,
221344c5edeSMark Brown 	.rate_max = SYS_AUDIO_RATE,
222b545dd92SMark Brown 	.channels_min = 2,
223b545dd92SMark Brown 	.channels_max = 2,
224b545dd92SMark Brown };
225b545dd92SMark Brown 
226b545dd92SMark Brown static struct snd_soc_dai_link bells_dai_wm5102[] = {
227b545dd92SMark Brown 	{
228344c5edeSMark Brown 		.name = "CPU-DSP",
229344c5edeSMark Brown 		.stream_name = "CPU-DSP",
230b545dd92SMark Brown 		.cpu_dai_name = "samsung-i2s.0",
231344c5edeSMark Brown 		.codec_dai_name = "wm0010-sdi1",
232b545dd92SMark Brown 		.platform_name = "samsung-audio",
233344c5edeSMark Brown 		.codec_name = "spi0.0",
234344c5edeSMark Brown 		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
235344c5edeSMark Brown 				| SND_SOC_DAIFMT_CBM_CFM,
236344c5edeSMark Brown 	},
237344c5edeSMark Brown 	{
238344c5edeSMark Brown 		.name = "DSP-CODEC",
239344c5edeSMark Brown 		.stream_name = "DSP-CODEC",
240344c5edeSMark Brown 		.cpu_dai_name = "wm0010-sdi2",
241344c5edeSMark Brown 		.codec_dai_name = "wm5102-aif1",
242b545dd92SMark Brown 		.codec_name = "wm5102-codec",
243b545dd92SMark Brown 		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
244b545dd92SMark Brown 				| SND_SOC_DAIFMT_CBM_CFM,
245344c5edeSMark Brown 		.params = &sub_params,
246344c5edeSMark Brown 		.ignore_suspend = 1,
247b545dd92SMark Brown 	},
248b545dd92SMark Brown 	{
249b545dd92SMark Brown 		.name = "Baseband",
250b545dd92SMark Brown 		.stream_name = "Baseband",
251b545dd92SMark Brown 		.cpu_dai_name = "wm5102-aif2",
252b545dd92SMark Brown 		.codec_dai_name = "wm1250-ev1",
253b545dd92SMark Brown 		.codec_name = "wm1250-ev1.1-0027",
254b545dd92SMark Brown 		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
255b545dd92SMark Brown 				| SND_SOC_DAIFMT_CBM_CFM,
256b545dd92SMark Brown 		.ignore_suspend = 1,
257b545dd92SMark Brown 		.params = &baseband_params,
258b545dd92SMark Brown 	},
259b545dd92SMark Brown 	{
260b545dd92SMark Brown 		.name = "Sub",
261b545dd92SMark Brown 		.stream_name = "Sub",
262b545dd92SMark Brown 		.cpu_dai_name = "wm5102-aif3",
263b545dd92SMark Brown 		.codec_dai_name = "wm9081-hifi",
264b545dd92SMark Brown 		.codec_name = "wm9081.1-006c",
265b545dd92SMark Brown 		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
266b545dd92SMark Brown 				| SND_SOC_DAIFMT_CBS_CFS,
267b545dd92SMark Brown 		.ignore_suspend = 1,
268b545dd92SMark Brown 		.params = &sub_params,
269b545dd92SMark Brown 	},
270b545dd92SMark Brown };
271b545dd92SMark Brown 
272b545dd92SMark Brown static struct snd_soc_dai_link bells_dai_wm5110[] = {
273b545dd92SMark Brown 	{
274344c5edeSMark Brown 		.name = "CPU-DSP",
275344c5edeSMark Brown 		.stream_name = "CPU-DSP",
276b545dd92SMark Brown 		.cpu_dai_name = "samsung-i2s.0",
277344c5edeSMark Brown 		.codec_dai_name = "wm0010-sdi1",
278b545dd92SMark Brown 		.platform_name = "samsung-audio",
279344c5edeSMark Brown 		.codec_name = "spi0.0",
280344c5edeSMark Brown 		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
281344c5edeSMark Brown 				| SND_SOC_DAIFMT_CBM_CFM,
282344c5edeSMark Brown 	},
283344c5edeSMark Brown 	{
284344c5edeSMark Brown 		.name = "DSP-CODEC",
285344c5edeSMark Brown 		.stream_name = "DSP-CODEC",
286344c5edeSMark Brown 		.cpu_dai_name = "wm0010-sdi2",
287344c5edeSMark Brown 		.codec_dai_name = "wm5110-aif1",
288b545dd92SMark Brown 		.codec_name = "wm5110-codec",
289b545dd92SMark Brown 		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
290b545dd92SMark Brown 				| SND_SOC_DAIFMT_CBM_CFM,
291344c5edeSMark Brown 		.params = &sub_params,
292344c5edeSMark Brown 		.ignore_suspend = 1,
293b545dd92SMark Brown 	},
294b545dd92SMark Brown 	{
295b545dd92SMark Brown 		.name = "Baseband",
296b545dd92SMark Brown 		.stream_name = "Baseband",
297b545dd92SMark Brown 		.cpu_dai_name = "wm5110-aif2",
298b545dd92SMark Brown 		.codec_dai_name = "wm1250-ev1",
299b545dd92SMark Brown 		.codec_name = "wm1250-ev1.1-0027",
300b545dd92SMark Brown 		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
301b545dd92SMark Brown 				| SND_SOC_DAIFMT_CBM_CFM,
302b545dd92SMark Brown 		.ignore_suspend = 1,
303b545dd92SMark Brown 		.params = &baseband_params,
304b545dd92SMark Brown 	},
305b545dd92SMark Brown 	{
306b545dd92SMark Brown 		.name = "Sub",
307b545dd92SMark Brown 		.stream_name = "Sub",
308ffaa839bSMark Brown 		.cpu_dai_name = "wm5110-aif3",
309b545dd92SMark Brown 		.codec_dai_name = "wm9081-hifi",
310b545dd92SMark Brown 		.codec_name = "wm9081.1-006c",
311b545dd92SMark Brown 		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
312b545dd92SMark Brown 				| SND_SOC_DAIFMT_CBS_CFS,
313b545dd92SMark Brown 		.ignore_suspend = 1,
314b545dd92SMark Brown 		.params = &sub_params,
315b545dd92SMark Brown 	},
316b545dd92SMark Brown };
317b545dd92SMark Brown 
318b545dd92SMark Brown static struct snd_soc_codec_conf bells_codec_conf[] = {
319b545dd92SMark Brown 	{
320b545dd92SMark Brown 		.dev_name = "wm9081.1-006c",
321b545dd92SMark Brown 		.name_prefix = "Sub",
322b545dd92SMark Brown 	},
323b545dd92SMark Brown };
324b545dd92SMark Brown 
325b545dd92SMark Brown static struct snd_soc_dapm_route bells_routes[] = {
326b545dd92SMark Brown 	{ "Sub CLK_SYS", NULL, "OPCLK" },
327b545dd92SMark Brown };
328b545dd92SMark Brown 
329b545dd92SMark Brown static struct snd_soc_card bells_cards[] = {
330b545dd92SMark Brown 	{
331b545dd92SMark Brown 		.name = "Bells WM5102",
332b545dd92SMark Brown 		.owner = THIS_MODULE,
333b545dd92SMark Brown 		.dai_link = bells_dai_wm5102,
334b545dd92SMark Brown 		.num_links = ARRAY_SIZE(bells_dai_wm5102),
335b545dd92SMark Brown 		.codec_conf = bells_codec_conf,
336b545dd92SMark Brown 		.num_configs = ARRAY_SIZE(bells_codec_conf),
337b545dd92SMark Brown 
338b545dd92SMark Brown 		.late_probe = bells_late_probe,
339b545dd92SMark Brown 
340b545dd92SMark Brown 		.dapm_routes = bells_routes,
341b545dd92SMark Brown 		.num_dapm_routes = ARRAY_SIZE(bells_routes),
342b545dd92SMark Brown 
343b545dd92SMark Brown 		.set_bias_level = bells_set_bias_level,
344b545dd92SMark Brown 		.set_bias_level_post = bells_set_bias_level_post,
345*b1387078SMark Brown 
346*b1387078SMark Brown 		.drvdata = &wm5102_drvdata,
347b545dd92SMark Brown 	},
348b545dd92SMark Brown 	{
349b545dd92SMark Brown 		.name = "Bells WM5110",
350b545dd92SMark Brown 		.owner = THIS_MODULE,
351b545dd92SMark Brown 		.dai_link = bells_dai_wm5110,
352b545dd92SMark Brown 		.num_links = ARRAY_SIZE(bells_dai_wm5110),
353b545dd92SMark Brown 		.codec_conf = bells_codec_conf,
354b545dd92SMark Brown 		.num_configs = ARRAY_SIZE(bells_codec_conf),
355b545dd92SMark Brown 
356b545dd92SMark Brown 		.late_probe = bells_late_probe,
357b545dd92SMark Brown 
358b545dd92SMark Brown 		.dapm_routes = bells_routes,
359b545dd92SMark Brown 		.num_dapm_routes = ARRAY_SIZE(bells_routes),
360b545dd92SMark Brown 
361b545dd92SMark Brown 		.set_bias_level = bells_set_bias_level,
362b545dd92SMark Brown 		.set_bias_level_post = bells_set_bias_level_post,
363*b1387078SMark Brown 
364*b1387078SMark Brown 		.drvdata = &wm5110_drvdata,
365b545dd92SMark Brown 	},
366b545dd92SMark Brown };
367b545dd92SMark Brown 
368b545dd92SMark Brown 
369b545dd92SMark Brown static __devinit int bells_probe(struct platform_device *pdev)
370b545dd92SMark Brown {
371b545dd92SMark Brown 	int ret;
372b545dd92SMark Brown 
373b545dd92SMark Brown 	bells_cards[pdev->id].dev = &pdev->dev;
374b545dd92SMark Brown 
375b545dd92SMark Brown 	ret = snd_soc_register_card(&bells_cards[pdev->id]);
376b545dd92SMark Brown 	if (ret) {
377b545dd92SMark Brown 		dev_err(&pdev->dev,
378b545dd92SMark Brown 			"snd_soc_register_card(%s) failed: %d\n",
379b545dd92SMark Brown 			bells_cards[pdev->id].name, ret);
380b545dd92SMark Brown 		return ret;
381b545dd92SMark Brown 	}
382b545dd92SMark Brown 
383b545dd92SMark Brown 	return 0;
384b545dd92SMark Brown }
385b545dd92SMark Brown 
386b545dd92SMark Brown static int __devexit bells_remove(struct platform_device *pdev)
387b545dd92SMark Brown {
388b545dd92SMark Brown 	snd_soc_unregister_card(&bells_cards[pdev->id]);
389b545dd92SMark Brown 
390b545dd92SMark Brown 	return 0;
391b545dd92SMark Brown }
392b545dd92SMark Brown 
393b545dd92SMark Brown static struct platform_driver bells_driver = {
394b545dd92SMark Brown 	.driver = {
395b545dd92SMark Brown 		.name = "bells",
396b545dd92SMark Brown 		.owner = THIS_MODULE,
397b545dd92SMark Brown 		.pm = &snd_soc_pm_ops,
398b545dd92SMark Brown 	},
399b545dd92SMark Brown 	.probe = bells_probe,
400b545dd92SMark Brown 	.remove = __devexit_p(bells_remove),
401b545dd92SMark Brown };
402b545dd92SMark Brown 
403b545dd92SMark Brown module_platform_driver(bells_driver);
404b545dd92SMark Brown 
405b545dd92SMark Brown MODULE_DESCRIPTION("Bells audio support");
406b545dd92SMark Brown MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
407b545dd92SMark Brown MODULE_LICENSE("GPL");
408b545dd92SMark Brown MODULE_ALIAS("platform:bells");
409