xref: /openbmc/linux/sound/soc/codecs/cx20442.c (revision 459dc35233c88d9eb7c5d0e6c086122751e64750)
1*459dc352SJanusz Krzysztofik /*
2*459dc352SJanusz Krzysztofik  * cx20442.c  --  CX20442 ALSA Soc Audio driver
3*459dc352SJanusz Krzysztofik  *
4*459dc352SJanusz Krzysztofik  * Copyright 2009 Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
5*459dc352SJanusz Krzysztofik  *
6*459dc352SJanusz Krzysztofik  * Initially based on sound/soc/codecs/wm8400.c
7*459dc352SJanusz Krzysztofik  * Copyright 2008, 2009 Wolfson Microelectronics PLC.
8*459dc352SJanusz Krzysztofik  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
9*459dc352SJanusz Krzysztofik  *
10*459dc352SJanusz Krzysztofik  *  This program is free software; you can redistribute  it and/or modify it
11*459dc352SJanusz Krzysztofik  *  under  the terms of  the GNU General  Public License as published by the
12*459dc352SJanusz Krzysztofik  *  Free Software Foundation;  either version 2 of the  License, or (at your
13*459dc352SJanusz Krzysztofik  *  option) any later version.
14*459dc352SJanusz Krzysztofik  */
15*459dc352SJanusz Krzysztofik 
16*459dc352SJanusz Krzysztofik #include <sound/core.h>
17*459dc352SJanusz Krzysztofik #include <sound/initval.h>
18*459dc352SJanusz Krzysztofik #include <sound/soc-dapm.h>
19*459dc352SJanusz Krzysztofik 
20*459dc352SJanusz Krzysztofik #include "cx20442.h"
21*459dc352SJanusz Krzysztofik 
22*459dc352SJanusz Krzysztofik 
23*459dc352SJanusz Krzysztofik struct cx20442_priv {
24*459dc352SJanusz Krzysztofik 	struct snd_soc_codec codec;
25*459dc352SJanusz Krzysztofik 	u8 reg_cache[1];
26*459dc352SJanusz Krzysztofik };
27*459dc352SJanusz Krzysztofik 
28*459dc352SJanusz Krzysztofik #define CX20442_PM		0x0
29*459dc352SJanusz Krzysztofik 
30*459dc352SJanusz Krzysztofik #define CX20442_TELIN		0
31*459dc352SJanusz Krzysztofik #define CX20442_TELOUT		1
32*459dc352SJanusz Krzysztofik #define CX20442_MIC		2
33*459dc352SJanusz Krzysztofik #define CX20442_SPKOUT		3
34*459dc352SJanusz Krzysztofik #define CX20442_AGC		4
35*459dc352SJanusz Krzysztofik 
36*459dc352SJanusz Krzysztofik static const struct snd_soc_dapm_widget cx20442_dapm_widgets[] = {
37*459dc352SJanusz Krzysztofik 	SND_SOC_DAPM_OUTPUT("TELOUT"),
38*459dc352SJanusz Krzysztofik 	SND_SOC_DAPM_OUTPUT("SPKOUT"),
39*459dc352SJanusz Krzysztofik 	SND_SOC_DAPM_OUTPUT("AGCOUT"),
40*459dc352SJanusz Krzysztofik 
41*459dc352SJanusz Krzysztofik 	SND_SOC_DAPM_MIXER("SPKOUT Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
42*459dc352SJanusz Krzysztofik 
43*459dc352SJanusz Krzysztofik 	SND_SOC_DAPM_PGA("TELOUT Amp", CX20442_PM, CX20442_TELOUT, 0, NULL, 0),
44*459dc352SJanusz Krzysztofik 	SND_SOC_DAPM_PGA("SPKOUT Amp", CX20442_PM, CX20442_SPKOUT, 0, NULL, 0),
45*459dc352SJanusz Krzysztofik 	SND_SOC_DAPM_PGA("SPKOUT AGC", CX20442_PM, CX20442_AGC, 0, NULL, 0),
46*459dc352SJanusz Krzysztofik 
47*459dc352SJanusz Krzysztofik 	SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
48*459dc352SJanusz Krzysztofik 	SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
49*459dc352SJanusz Krzysztofik 
50*459dc352SJanusz Krzysztofik 	SND_SOC_DAPM_MIXER("Input Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
51*459dc352SJanusz Krzysztofik 
52*459dc352SJanusz Krzysztofik 	SND_SOC_DAPM_MICBIAS("TELIN Bias", CX20442_PM, CX20442_TELIN, 0),
53*459dc352SJanusz Krzysztofik 	SND_SOC_DAPM_MICBIAS("MIC Bias", CX20442_PM, CX20442_MIC, 0),
54*459dc352SJanusz Krzysztofik 
55*459dc352SJanusz Krzysztofik 	SND_SOC_DAPM_PGA("MIC AGC", CX20442_PM, CX20442_AGC, 0, NULL, 0),
56*459dc352SJanusz Krzysztofik 
57*459dc352SJanusz Krzysztofik 	SND_SOC_DAPM_INPUT("TELIN"),
58*459dc352SJanusz Krzysztofik 	SND_SOC_DAPM_INPUT("MIC"),
59*459dc352SJanusz Krzysztofik 	SND_SOC_DAPM_INPUT("AGCIN"),
60*459dc352SJanusz Krzysztofik };
61*459dc352SJanusz Krzysztofik 
62*459dc352SJanusz Krzysztofik static const struct snd_soc_dapm_route cx20442_audio_map[] = {
63*459dc352SJanusz Krzysztofik 	{"TELOUT", NULL, "TELOUT Amp"},
64*459dc352SJanusz Krzysztofik 
65*459dc352SJanusz Krzysztofik 	{"SPKOUT", NULL, "SPKOUT Mixer"},
66*459dc352SJanusz Krzysztofik 	{"SPKOUT Mixer", NULL, "SPKOUT Amp"},
67*459dc352SJanusz Krzysztofik 
68*459dc352SJanusz Krzysztofik 	{"TELOUT Amp", NULL, "DAC"},
69*459dc352SJanusz Krzysztofik 	{"SPKOUT Amp", NULL, "DAC"},
70*459dc352SJanusz Krzysztofik 
71*459dc352SJanusz Krzysztofik 	{"SPKOUT Mixer", NULL, "SPKOUT AGC"},
72*459dc352SJanusz Krzysztofik 	{"SPKOUT AGC", NULL, "AGCIN"},
73*459dc352SJanusz Krzysztofik 
74*459dc352SJanusz Krzysztofik 	{"AGCOUT", NULL, "MIC AGC"},
75*459dc352SJanusz Krzysztofik 	{"MIC AGC", NULL, "MIC"},
76*459dc352SJanusz Krzysztofik 
77*459dc352SJanusz Krzysztofik 	{"MIC Bias", NULL, "MIC"},
78*459dc352SJanusz Krzysztofik 	{"Input Mixer", NULL, "MIC Bias"},
79*459dc352SJanusz Krzysztofik 
80*459dc352SJanusz Krzysztofik 	{"TELIN Bias", NULL, "TELIN"},
81*459dc352SJanusz Krzysztofik 	{"Input Mixer", NULL, "TELIN Bias"},
82*459dc352SJanusz Krzysztofik 
83*459dc352SJanusz Krzysztofik 	{"ADC", NULL, "Input Mixer"},
84*459dc352SJanusz Krzysztofik };
85*459dc352SJanusz Krzysztofik 
86*459dc352SJanusz Krzysztofik static int cx20442_add_widgets(struct snd_soc_codec *codec)
87*459dc352SJanusz Krzysztofik {
88*459dc352SJanusz Krzysztofik 	snd_soc_dapm_new_controls(codec, cx20442_dapm_widgets,
89*459dc352SJanusz Krzysztofik 				  ARRAY_SIZE(cx20442_dapm_widgets));
90*459dc352SJanusz Krzysztofik 
91*459dc352SJanusz Krzysztofik 	snd_soc_dapm_add_routes(codec, cx20442_audio_map,
92*459dc352SJanusz Krzysztofik 				ARRAY_SIZE(cx20442_audio_map));
93*459dc352SJanusz Krzysztofik 
94*459dc352SJanusz Krzysztofik 	snd_soc_dapm_new_widgets(codec);
95*459dc352SJanusz Krzysztofik 	return 0;
96*459dc352SJanusz Krzysztofik }
97*459dc352SJanusz Krzysztofik 
98*459dc352SJanusz Krzysztofik static unsigned int cx20442_read_reg_cache(struct snd_soc_codec *codec,
99*459dc352SJanusz Krzysztofik 							unsigned int reg)
100*459dc352SJanusz Krzysztofik {
101*459dc352SJanusz Krzysztofik 	u8 *reg_cache = codec->reg_cache;
102*459dc352SJanusz Krzysztofik 
103*459dc352SJanusz Krzysztofik 	if (reg >= codec->reg_cache_size)
104*459dc352SJanusz Krzysztofik 		return -EINVAL;
105*459dc352SJanusz Krzysztofik 
106*459dc352SJanusz Krzysztofik 	return reg_cache[reg];
107*459dc352SJanusz Krzysztofik }
108*459dc352SJanusz Krzysztofik 
109*459dc352SJanusz Krzysztofik enum v253_vls {
110*459dc352SJanusz Krzysztofik 	V253_VLS_NONE = 0,
111*459dc352SJanusz Krzysztofik 	V253_VLS_T,
112*459dc352SJanusz Krzysztofik 	V253_VLS_L,
113*459dc352SJanusz Krzysztofik 	V253_VLS_LT,
114*459dc352SJanusz Krzysztofik 	V253_VLS_S,
115*459dc352SJanusz Krzysztofik 	V253_VLS_ST,
116*459dc352SJanusz Krzysztofik 	V253_VLS_M,
117*459dc352SJanusz Krzysztofik 	V253_VLS_MST,
118*459dc352SJanusz Krzysztofik 	V253_VLS_S1,
119*459dc352SJanusz Krzysztofik 	V253_VLS_S1T,
120*459dc352SJanusz Krzysztofik 	V253_VLS_MS1T,
121*459dc352SJanusz Krzysztofik 	V253_VLS_M1,
122*459dc352SJanusz Krzysztofik 	V253_VLS_M1ST,
123*459dc352SJanusz Krzysztofik 	V253_VLS_M1S1T,
124*459dc352SJanusz Krzysztofik 	V253_VLS_H,
125*459dc352SJanusz Krzysztofik 	V253_VLS_HT,
126*459dc352SJanusz Krzysztofik 	V253_VLS_MS,
127*459dc352SJanusz Krzysztofik 	V253_VLS_MS1,
128*459dc352SJanusz Krzysztofik 	V253_VLS_M1S,
129*459dc352SJanusz Krzysztofik 	V253_VLS_M1S1,
130*459dc352SJanusz Krzysztofik 	V253_VLS_TEST,
131*459dc352SJanusz Krzysztofik };
132*459dc352SJanusz Krzysztofik 
133*459dc352SJanusz Krzysztofik static int cx20442_pm_to_v253_vls(u8 value)
134*459dc352SJanusz Krzysztofik {
135*459dc352SJanusz Krzysztofik 	switch(value & ~(1 << CX20442_AGC)) {
136*459dc352SJanusz Krzysztofik 	case 0:
137*459dc352SJanusz Krzysztofik 		return V253_VLS_T;
138*459dc352SJanusz Krzysztofik 	case (1 << CX20442_SPKOUT):
139*459dc352SJanusz Krzysztofik 		return V253_VLS_S1;
140*459dc352SJanusz Krzysztofik 	case (1 << CX20442_MIC):
141*459dc352SJanusz Krzysztofik 		return V253_VLS_M1;
142*459dc352SJanusz Krzysztofik 	case (1 << CX20442_SPKOUT) | (1 << CX20442_MIC):
143*459dc352SJanusz Krzysztofik 		return V253_VLS_M1S1;
144*459dc352SJanusz Krzysztofik 	case (1 << CX20442_TELOUT):
145*459dc352SJanusz Krzysztofik 	case (1 << CX20442_TELIN):
146*459dc352SJanusz Krzysztofik 	case (1 << CX20442_TELOUT) | (1 << CX20442_TELIN):
147*459dc352SJanusz Krzysztofik 		return V253_VLS_L;
148*459dc352SJanusz Krzysztofik 	case (1 << CX20442_TELOUT) | (1 << CX20442_MIC):
149*459dc352SJanusz Krzysztofik 		return V253_VLS_NONE;
150*459dc352SJanusz Krzysztofik 	}
151*459dc352SJanusz Krzysztofik 	return -EINVAL;
152*459dc352SJanusz Krzysztofik }
153*459dc352SJanusz Krzysztofik static int cx20442_pm_to_v253_vsp(u8 value)
154*459dc352SJanusz Krzysztofik {
155*459dc352SJanusz Krzysztofik 	switch(value & ~(1 << CX20442_AGC)) {
156*459dc352SJanusz Krzysztofik 	case (1 << CX20442_SPKOUT):
157*459dc352SJanusz Krzysztofik 	case (1 << CX20442_MIC):
158*459dc352SJanusz Krzysztofik 	case (1 << CX20442_SPKOUT) | (1 << CX20442_MIC):
159*459dc352SJanusz Krzysztofik 		return (bool)(value & (1 << CX20442_AGC));
160*459dc352SJanusz Krzysztofik 	}
161*459dc352SJanusz Krzysztofik 	return (value & (1 << CX20442_AGC)) ? -EINVAL : 0;
162*459dc352SJanusz Krzysztofik }
163*459dc352SJanusz Krzysztofik 
164*459dc352SJanusz Krzysztofik static int cx20442_write(struct snd_soc_codec *codec, unsigned int reg,
165*459dc352SJanusz Krzysztofik 							unsigned int value)
166*459dc352SJanusz Krzysztofik {
167*459dc352SJanusz Krzysztofik 	u8 *reg_cache = codec->reg_cache;
168*459dc352SJanusz Krzysztofik 	int vls, vsp, old, len;
169*459dc352SJanusz Krzysztofik 	char buf[18];
170*459dc352SJanusz Krzysztofik 
171*459dc352SJanusz Krzysztofik 	if (reg >= codec->reg_cache_size)
172*459dc352SJanusz Krzysztofik 		return -EINVAL;
173*459dc352SJanusz Krzysztofik 
174*459dc352SJanusz Krzysztofik 	if (!codec->hw_write || !codec->control_data)
175*459dc352SJanusz Krzysztofik 		return -EIO;
176*459dc352SJanusz Krzysztofik 
177*459dc352SJanusz Krzysztofik 	old = reg_cache[reg];
178*459dc352SJanusz Krzysztofik 	reg_cache[reg] = value;
179*459dc352SJanusz Krzysztofik 
180*459dc352SJanusz Krzysztofik 	vls = cx20442_pm_to_v253_vls(value);
181*459dc352SJanusz Krzysztofik 	if (vls < 0)
182*459dc352SJanusz Krzysztofik 		return vls;
183*459dc352SJanusz Krzysztofik 
184*459dc352SJanusz Krzysztofik 	vsp = cx20442_pm_to_v253_vsp(value);
185*459dc352SJanusz Krzysztofik 	if (vsp < 0 )
186*459dc352SJanusz Krzysztofik 		return vsp;
187*459dc352SJanusz Krzysztofik 
188*459dc352SJanusz Krzysztofik 	if ((vls == V253_VLS_T) ||
189*459dc352SJanusz Krzysztofik 			(vls == cx20442_pm_to_v253_vls(old))) {
190*459dc352SJanusz Krzysztofik 		if (vsp == cx20442_pm_to_v253_vsp(old))
191*459dc352SJanusz Krzysztofik 			return 0;
192*459dc352SJanusz Krzysztofik 		len = snprintf(buf, ARRAY_SIZE(buf), "at+vsp=%d\r", vsp);
193*459dc352SJanusz Krzysztofik 	} else if (vsp == cx20442_pm_to_v253_vsp(old))
194*459dc352SJanusz Krzysztofik 		len = snprintf(buf, ARRAY_SIZE(buf), "at+vls=%d\r", vls);
195*459dc352SJanusz Krzysztofik 	else
196*459dc352SJanusz Krzysztofik 		len = snprintf(buf, ARRAY_SIZE(buf),
197*459dc352SJanusz Krzysztofik 					"at+vls=%d;+vsp=%d\r", vls, vsp);
198*459dc352SJanusz Krzysztofik 
199*459dc352SJanusz Krzysztofik 	if (unlikely(len > (ARRAY_SIZE(buf) - 1)))
200*459dc352SJanusz Krzysztofik 		return -ENOMEM;
201*459dc352SJanusz Krzysztofik 
202*459dc352SJanusz Krzysztofik 	if (codec->hw_write(codec->control_data, buf, len) != len)
203*459dc352SJanusz Krzysztofik 		return -EIO;
204*459dc352SJanusz Krzysztofik 
205*459dc352SJanusz Krzysztofik 	return 0;
206*459dc352SJanusz Krzysztofik }
207*459dc352SJanusz Krzysztofik 
208*459dc352SJanusz Krzysztofik struct snd_soc_dai cx20442_dai = {
209*459dc352SJanusz Krzysztofik 	.name = "CX20442",
210*459dc352SJanusz Krzysztofik 	.playback = {
211*459dc352SJanusz Krzysztofik 		.stream_name = "Playback",
212*459dc352SJanusz Krzysztofik 		.channels_min = 1,
213*459dc352SJanusz Krzysztofik 		.channels_max = 1,
214*459dc352SJanusz Krzysztofik 		.rates = SNDRV_PCM_RATE_8000,
215*459dc352SJanusz Krzysztofik 		.formats = SNDRV_PCM_FMTBIT_S16_LE,
216*459dc352SJanusz Krzysztofik 	},
217*459dc352SJanusz Krzysztofik 	.capture = {
218*459dc352SJanusz Krzysztofik 		.stream_name = "Capture",
219*459dc352SJanusz Krzysztofik 		.channels_min = 1,
220*459dc352SJanusz Krzysztofik 		.channels_max = 1,
221*459dc352SJanusz Krzysztofik 		.rates = SNDRV_PCM_RATE_8000,
222*459dc352SJanusz Krzysztofik 		.formats = SNDRV_PCM_FMTBIT_S16_LE,
223*459dc352SJanusz Krzysztofik 	},
224*459dc352SJanusz Krzysztofik };
225*459dc352SJanusz Krzysztofik EXPORT_SYMBOL_GPL(cx20442_dai);
226*459dc352SJanusz Krzysztofik 
227*459dc352SJanusz Krzysztofik static struct snd_soc_codec *cx20442_codec;
228*459dc352SJanusz Krzysztofik 
229*459dc352SJanusz Krzysztofik static int cx20442_codec_probe(struct platform_device *pdev)
230*459dc352SJanusz Krzysztofik {
231*459dc352SJanusz Krzysztofik 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
232*459dc352SJanusz Krzysztofik 	struct snd_soc_codec *codec;
233*459dc352SJanusz Krzysztofik 	int ret;
234*459dc352SJanusz Krzysztofik 
235*459dc352SJanusz Krzysztofik 	if(!cx20442_codec) {
236*459dc352SJanusz Krzysztofik 		dev_err(&pdev->dev, "cx20442 not yet discovered\n");
237*459dc352SJanusz Krzysztofik 		return -ENODEV;
238*459dc352SJanusz Krzysztofik 	}
239*459dc352SJanusz Krzysztofik 	codec = cx20442_codec;
240*459dc352SJanusz Krzysztofik 
241*459dc352SJanusz Krzysztofik 	socdev->card->codec = codec;
242*459dc352SJanusz Krzysztofik 
243*459dc352SJanusz Krzysztofik 	/* register pcms */
244*459dc352SJanusz Krzysztofik 	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
245*459dc352SJanusz Krzysztofik 	if (ret < 0) {
246*459dc352SJanusz Krzysztofik 		dev_err(&pdev->dev, "failed to create pcms\n");
247*459dc352SJanusz Krzysztofik 		goto pcm_err;
248*459dc352SJanusz Krzysztofik 	}
249*459dc352SJanusz Krzysztofik 
250*459dc352SJanusz Krzysztofik 	cx20442_add_widgets(codec);
251*459dc352SJanusz Krzysztofik 
252*459dc352SJanusz Krzysztofik 	ret = snd_soc_init_card(socdev);
253*459dc352SJanusz Krzysztofik 	if (ret < 0) {
254*459dc352SJanusz Krzysztofik 		dev_err(&pdev->dev, "failed to register card\n");
255*459dc352SJanusz Krzysztofik 		goto card_err;
256*459dc352SJanusz Krzysztofik 	}
257*459dc352SJanusz Krzysztofik 
258*459dc352SJanusz Krzysztofik 	return ret;
259*459dc352SJanusz Krzysztofik 
260*459dc352SJanusz Krzysztofik card_err:
261*459dc352SJanusz Krzysztofik 	snd_soc_free_pcms(socdev);
262*459dc352SJanusz Krzysztofik 	snd_soc_dapm_free(socdev);
263*459dc352SJanusz Krzysztofik pcm_err:
264*459dc352SJanusz Krzysztofik 	return ret;
265*459dc352SJanusz Krzysztofik }
266*459dc352SJanusz Krzysztofik 
267*459dc352SJanusz Krzysztofik /* power down chip */
268*459dc352SJanusz Krzysztofik static int cx20442_codec_remove(struct platform_device *pdev)
269*459dc352SJanusz Krzysztofik {
270*459dc352SJanusz Krzysztofik 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
271*459dc352SJanusz Krzysztofik 
272*459dc352SJanusz Krzysztofik 	snd_soc_free_pcms(socdev);
273*459dc352SJanusz Krzysztofik 	snd_soc_dapm_free(socdev);
274*459dc352SJanusz Krzysztofik 
275*459dc352SJanusz Krzysztofik 	return 0;
276*459dc352SJanusz Krzysztofik }
277*459dc352SJanusz Krzysztofik 
278*459dc352SJanusz Krzysztofik struct snd_soc_codec_device cx20442_codec_dev = {
279*459dc352SJanusz Krzysztofik 	.probe = 	cx20442_codec_probe,
280*459dc352SJanusz Krzysztofik 	.remove = 	cx20442_codec_remove,
281*459dc352SJanusz Krzysztofik };
282*459dc352SJanusz Krzysztofik EXPORT_SYMBOL_GPL(cx20442_codec_dev);
283*459dc352SJanusz Krzysztofik 
284*459dc352SJanusz Krzysztofik static int cx20442_register(struct cx20442_priv *cx20442)
285*459dc352SJanusz Krzysztofik {
286*459dc352SJanusz Krzysztofik 	struct snd_soc_codec *codec = &cx20442->codec;
287*459dc352SJanusz Krzysztofik 	int ret;
288*459dc352SJanusz Krzysztofik 
289*459dc352SJanusz Krzysztofik 	mutex_init(&codec->mutex);
290*459dc352SJanusz Krzysztofik 	INIT_LIST_HEAD(&codec->dapm_widgets);
291*459dc352SJanusz Krzysztofik 	INIT_LIST_HEAD(&codec->dapm_paths);
292*459dc352SJanusz Krzysztofik 
293*459dc352SJanusz Krzysztofik 	codec->name = "CX20442";
294*459dc352SJanusz Krzysztofik 	codec->owner = THIS_MODULE;
295*459dc352SJanusz Krzysztofik 	codec->private_data = cx20442;
296*459dc352SJanusz Krzysztofik 
297*459dc352SJanusz Krzysztofik 	codec->dai = &cx20442_dai;
298*459dc352SJanusz Krzysztofik 	codec->num_dai = 1;
299*459dc352SJanusz Krzysztofik 
300*459dc352SJanusz Krzysztofik 	codec->reg_cache = &cx20442->reg_cache;
301*459dc352SJanusz Krzysztofik 	codec->reg_cache_size = ARRAY_SIZE(cx20442->reg_cache);
302*459dc352SJanusz Krzysztofik 	codec->read = cx20442_read_reg_cache;
303*459dc352SJanusz Krzysztofik 	codec->write = cx20442_write;
304*459dc352SJanusz Krzysztofik 
305*459dc352SJanusz Krzysztofik 	codec->bias_level = SND_SOC_BIAS_OFF;
306*459dc352SJanusz Krzysztofik 
307*459dc352SJanusz Krzysztofik 	cx20442_dai.dev = codec->dev;
308*459dc352SJanusz Krzysztofik 
309*459dc352SJanusz Krzysztofik 	cx20442_codec = codec;
310*459dc352SJanusz Krzysztofik 
311*459dc352SJanusz Krzysztofik 	ret = snd_soc_register_codec(codec);
312*459dc352SJanusz Krzysztofik 	if (ret != 0) {
313*459dc352SJanusz Krzysztofik 		//dev_err(&dev->dev, "Failed to register codec: %d\n", ret);
314*459dc352SJanusz Krzysztofik 		goto err;
315*459dc352SJanusz Krzysztofik 	}
316*459dc352SJanusz Krzysztofik 
317*459dc352SJanusz Krzysztofik 	ret = snd_soc_register_dai(&cx20442_dai);
318*459dc352SJanusz Krzysztofik 	if (ret != 0) {
319*459dc352SJanusz Krzysztofik 		//dev_err(&dev->dev, "Failed to register DAI: %d\n", ret);
320*459dc352SJanusz Krzysztofik 		goto err_codec;
321*459dc352SJanusz Krzysztofik 	}
322*459dc352SJanusz Krzysztofik 
323*459dc352SJanusz Krzysztofik 	return 0;
324*459dc352SJanusz Krzysztofik 
325*459dc352SJanusz Krzysztofik err_codec:
326*459dc352SJanusz Krzysztofik 	snd_soc_unregister_codec(codec);
327*459dc352SJanusz Krzysztofik err:
328*459dc352SJanusz Krzysztofik 	cx20442_codec = NULL;
329*459dc352SJanusz Krzysztofik 	kfree(cx20442);
330*459dc352SJanusz Krzysztofik 	return ret;
331*459dc352SJanusz Krzysztofik }
332*459dc352SJanusz Krzysztofik 
333*459dc352SJanusz Krzysztofik static void cx20442_unregister(struct cx20442_priv *cx20442)
334*459dc352SJanusz Krzysztofik {
335*459dc352SJanusz Krzysztofik 	snd_soc_unregister_dai(&cx20442_dai);
336*459dc352SJanusz Krzysztofik 	snd_soc_unregister_codec(&cx20442->codec);
337*459dc352SJanusz Krzysztofik 
338*459dc352SJanusz Krzysztofik 	cx20442_codec = NULL;
339*459dc352SJanusz Krzysztofik 	kfree(cx20442);
340*459dc352SJanusz Krzysztofik }
341*459dc352SJanusz Krzysztofik 
342*459dc352SJanusz Krzysztofik static int cx20442_platform_probe(struct platform_device *pdev)
343*459dc352SJanusz Krzysztofik {
344*459dc352SJanusz Krzysztofik 	struct cx20442_priv *cx20442;
345*459dc352SJanusz Krzysztofik 	struct snd_soc_codec *codec;
346*459dc352SJanusz Krzysztofik 
347*459dc352SJanusz Krzysztofik 	cx20442 = kzalloc(sizeof(struct cx20442_priv), GFP_KERNEL);
348*459dc352SJanusz Krzysztofik 	if (cx20442 == NULL)
349*459dc352SJanusz Krzysztofik 		return -ENOMEM;
350*459dc352SJanusz Krzysztofik 
351*459dc352SJanusz Krzysztofik 	codec = &cx20442->codec;
352*459dc352SJanusz Krzysztofik 
353*459dc352SJanusz Krzysztofik 	codec->control_data = NULL;
354*459dc352SJanusz Krzysztofik 	codec->hw_write = NULL;
355*459dc352SJanusz Krzysztofik 	codec->pop_time = 0;
356*459dc352SJanusz Krzysztofik 
357*459dc352SJanusz Krzysztofik 	codec->dev = &pdev->dev;
358*459dc352SJanusz Krzysztofik 	platform_set_drvdata(pdev, cx20442);
359*459dc352SJanusz Krzysztofik 
360*459dc352SJanusz Krzysztofik 	return cx20442_register(cx20442);
361*459dc352SJanusz Krzysztofik }
362*459dc352SJanusz Krzysztofik 
363*459dc352SJanusz Krzysztofik static int __exit cx20442_platform_remove(struct platform_device *pdev)
364*459dc352SJanusz Krzysztofik {
365*459dc352SJanusz Krzysztofik 	struct cx20442_priv *cx20442 = platform_get_drvdata(pdev);
366*459dc352SJanusz Krzysztofik 
367*459dc352SJanusz Krzysztofik 	cx20442_unregister(cx20442);
368*459dc352SJanusz Krzysztofik 	return 0;
369*459dc352SJanusz Krzysztofik }
370*459dc352SJanusz Krzysztofik 
371*459dc352SJanusz Krzysztofik static struct platform_driver cx20442_platform_driver = {
372*459dc352SJanusz Krzysztofik 	.driver = {
373*459dc352SJanusz Krzysztofik 		.name = "cx20442",
374*459dc352SJanusz Krzysztofik 		.owner = THIS_MODULE,
375*459dc352SJanusz Krzysztofik 		},
376*459dc352SJanusz Krzysztofik 	.probe = cx20442_platform_probe,
377*459dc352SJanusz Krzysztofik 	.remove = __exit_p(cx20442_platform_remove),
378*459dc352SJanusz Krzysztofik };
379*459dc352SJanusz Krzysztofik 
380*459dc352SJanusz Krzysztofik static int __init cx20442_init(void)
381*459dc352SJanusz Krzysztofik {
382*459dc352SJanusz Krzysztofik 	return platform_driver_register(&cx20442_platform_driver);
383*459dc352SJanusz Krzysztofik }
384*459dc352SJanusz Krzysztofik module_init(cx20442_init);
385*459dc352SJanusz Krzysztofik 
386*459dc352SJanusz Krzysztofik static void __exit cx20442_exit(void)
387*459dc352SJanusz Krzysztofik {
388*459dc352SJanusz Krzysztofik 	platform_driver_unregister(&cx20442_platform_driver);
389*459dc352SJanusz Krzysztofik }
390*459dc352SJanusz Krzysztofik module_exit(cx20442_exit);
391*459dc352SJanusz Krzysztofik 
392*459dc352SJanusz Krzysztofik MODULE_DESCRIPTION("ASoC CX20442-11 voice modem codec driver");
393*459dc352SJanusz Krzysztofik MODULE_AUTHOR("Janusz Krzysztofik");
394*459dc352SJanusz Krzysztofik MODULE_LICENSE("GPL");
395*459dc352SJanusz Krzysztofik MODULE_ALIAS("platform:cx20442-codec");
396