xref: /openbmc/linux/sound/soc/codecs/cx20442.c (revision ad120dae127f5cb14b4c4ed0b98313fb13b9c846)
1459dc352SJanusz Krzysztofik /*
2459dc352SJanusz Krzysztofik  * cx20442.c  --  CX20442 ALSA Soc Audio driver
3459dc352SJanusz Krzysztofik  *
4459dc352SJanusz Krzysztofik  * Copyright 2009 Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
5459dc352SJanusz Krzysztofik  *
6459dc352SJanusz Krzysztofik  * Initially based on sound/soc/codecs/wm8400.c
7459dc352SJanusz Krzysztofik  * Copyright 2008, 2009 Wolfson Microelectronics PLC.
8459dc352SJanusz Krzysztofik  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
9459dc352SJanusz Krzysztofik  *
10459dc352SJanusz Krzysztofik  *  This program is free software; you can redistribute  it and/or modify it
11459dc352SJanusz Krzysztofik  *  under  the terms of  the GNU General  Public License as published by the
12459dc352SJanusz Krzysztofik  *  Free Software Foundation;  either version 2 of the  License, or (at your
13459dc352SJanusz Krzysztofik  *  option) any later version.
14459dc352SJanusz Krzysztofik  */
15459dc352SJanusz Krzysztofik 
16*ad120daeSJanusz Krzysztofik #include <linux/tty.h>
17*ad120daeSJanusz Krzysztofik 
18459dc352SJanusz Krzysztofik #include <sound/core.h>
19459dc352SJanusz Krzysztofik #include <sound/initval.h>
20459dc352SJanusz Krzysztofik #include <sound/soc-dapm.h>
21459dc352SJanusz Krzysztofik 
22459dc352SJanusz Krzysztofik #include "cx20442.h"
23459dc352SJanusz Krzysztofik 
24459dc352SJanusz Krzysztofik 
25459dc352SJanusz Krzysztofik struct cx20442_priv {
26459dc352SJanusz Krzysztofik 	struct snd_soc_codec codec;
27459dc352SJanusz Krzysztofik 	u8 reg_cache[1];
28459dc352SJanusz Krzysztofik };
29459dc352SJanusz Krzysztofik 
30459dc352SJanusz Krzysztofik #define CX20442_PM		0x0
31459dc352SJanusz Krzysztofik 
32459dc352SJanusz Krzysztofik #define CX20442_TELIN		0
33459dc352SJanusz Krzysztofik #define CX20442_TELOUT		1
34459dc352SJanusz Krzysztofik #define CX20442_MIC		2
35459dc352SJanusz Krzysztofik #define CX20442_SPKOUT		3
36459dc352SJanusz Krzysztofik #define CX20442_AGC		4
37459dc352SJanusz Krzysztofik 
38459dc352SJanusz Krzysztofik static const struct snd_soc_dapm_widget cx20442_dapm_widgets[] = {
39459dc352SJanusz Krzysztofik 	SND_SOC_DAPM_OUTPUT("TELOUT"),
40459dc352SJanusz Krzysztofik 	SND_SOC_DAPM_OUTPUT("SPKOUT"),
41459dc352SJanusz Krzysztofik 	SND_SOC_DAPM_OUTPUT("AGCOUT"),
42459dc352SJanusz Krzysztofik 
43459dc352SJanusz Krzysztofik 	SND_SOC_DAPM_MIXER("SPKOUT Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
44459dc352SJanusz Krzysztofik 
45459dc352SJanusz Krzysztofik 	SND_SOC_DAPM_PGA("TELOUT Amp", CX20442_PM, CX20442_TELOUT, 0, NULL, 0),
46459dc352SJanusz Krzysztofik 	SND_SOC_DAPM_PGA("SPKOUT Amp", CX20442_PM, CX20442_SPKOUT, 0, NULL, 0),
47459dc352SJanusz Krzysztofik 	SND_SOC_DAPM_PGA("SPKOUT AGC", CX20442_PM, CX20442_AGC, 0, NULL, 0),
48459dc352SJanusz Krzysztofik 
49459dc352SJanusz Krzysztofik 	SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
50459dc352SJanusz Krzysztofik 	SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
51459dc352SJanusz Krzysztofik 
52459dc352SJanusz Krzysztofik 	SND_SOC_DAPM_MIXER("Input Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
53459dc352SJanusz Krzysztofik 
54459dc352SJanusz Krzysztofik 	SND_SOC_DAPM_MICBIAS("TELIN Bias", CX20442_PM, CX20442_TELIN, 0),
55459dc352SJanusz Krzysztofik 	SND_SOC_DAPM_MICBIAS("MIC Bias", CX20442_PM, CX20442_MIC, 0),
56459dc352SJanusz Krzysztofik 
57459dc352SJanusz Krzysztofik 	SND_SOC_DAPM_PGA("MIC AGC", CX20442_PM, CX20442_AGC, 0, NULL, 0),
58459dc352SJanusz Krzysztofik 
59459dc352SJanusz Krzysztofik 	SND_SOC_DAPM_INPUT("TELIN"),
60459dc352SJanusz Krzysztofik 	SND_SOC_DAPM_INPUT("MIC"),
61459dc352SJanusz Krzysztofik 	SND_SOC_DAPM_INPUT("AGCIN"),
62459dc352SJanusz Krzysztofik };
63459dc352SJanusz Krzysztofik 
64459dc352SJanusz Krzysztofik static const struct snd_soc_dapm_route cx20442_audio_map[] = {
65459dc352SJanusz Krzysztofik 	{"TELOUT", NULL, "TELOUT Amp"},
66459dc352SJanusz Krzysztofik 
67459dc352SJanusz Krzysztofik 	{"SPKOUT", NULL, "SPKOUT Mixer"},
68459dc352SJanusz Krzysztofik 	{"SPKOUT Mixer", NULL, "SPKOUT Amp"},
69459dc352SJanusz Krzysztofik 
70459dc352SJanusz Krzysztofik 	{"TELOUT Amp", NULL, "DAC"},
71459dc352SJanusz Krzysztofik 	{"SPKOUT Amp", NULL, "DAC"},
72459dc352SJanusz Krzysztofik 
73459dc352SJanusz Krzysztofik 	{"SPKOUT Mixer", NULL, "SPKOUT AGC"},
74459dc352SJanusz Krzysztofik 	{"SPKOUT AGC", NULL, "AGCIN"},
75459dc352SJanusz Krzysztofik 
76459dc352SJanusz Krzysztofik 	{"AGCOUT", NULL, "MIC AGC"},
77459dc352SJanusz Krzysztofik 	{"MIC AGC", NULL, "MIC"},
78459dc352SJanusz Krzysztofik 
79459dc352SJanusz Krzysztofik 	{"MIC Bias", NULL, "MIC"},
80459dc352SJanusz Krzysztofik 	{"Input Mixer", NULL, "MIC Bias"},
81459dc352SJanusz Krzysztofik 
82459dc352SJanusz Krzysztofik 	{"TELIN Bias", NULL, "TELIN"},
83459dc352SJanusz Krzysztofik 	{"Input Mixer", NULL, "TELIN Bias"},
84459dc352SJanusz Krzysztofik 
85459dc352SJanusz Krzysztofik 	{"ADC", NULL, "Input Mixer"},
86459dc352SJanusz Krzysztofik };
87459dc352SJanusz Krzysztofik 
88459dc352SJanusz Krzysztofik static int cx20442_add_widgets(struct snd_soc_codec *codec)
89459dc352SJanusz Krzysztofik {
90459dc352SJanusz Krzysztofik 	snd_soc_dapm_new_controls(codec, cx20442_dapm_widgets,
91459dc352SJanusz Krzysztofik 				  ARRAY_SIZE(cx20442_dapm_widgets));
92459dc352SJanusz Krzysztofik 
93459dc352SJanusz Krzysztofik 	snd_soc_dapm_add_routes(codec, cx20442_audio_map,
94459dc352SJanusz Krzysztofik 				ARRAY_SIZE(cx20442_audio_map));
95459dc352SJanusz Krzysztofik 
96459dc352SJanusz Krzysztofik 	snd_soc_dapm_new_widgets(codec);
97459dc352SJanusz Krzysztofik 	return 0;
98459dc352SJanusz Krzysztofik }
99459dc352SJanusz Krzysztofik 
100459dc352SJanusz Krzysztofik static unsigned int cx20442_read_reg_cache(struct snd_soc_codec *codec,
101459dc352SJanusz Krzysztofik 							unsigned int reg)
102459dc352SJanusz Krzysztofik {
103459dc352SJanusz Krzysztofik 	u8 *reg_cache = codec->reg_cache;
104459dc352SJanusz Krzysztofik 
105459dc352SJanusz Krzysztofik 	if (reg >= codec->reg_cache_size)
106459dc352SJanusz Krzysztofik 		return -EINVAL;
107459dc352SJanusz Krzysztofik 
108459dc352SJanusz Krzysztofik 	return reg_cache[reg];
109459dc352SJanusz Krzysztofik }
110459dc352SJanusz Krzysztofik 
111459dc352SJanusz Krzysztofik enum v253_vls {
112459dc352SJanusz Krzysztofik 	V253_VLS_NONE = 0,
113459dc352SJanusz Krzysztofik 	V253_VLS_T,
114459dc352SJanusz Krzysztofik 	V253_VLS_L,
115459dc352SJanusz Krzysztofik 	V253_VLS_LT,
116459dc352SJanusz Krzysztofik 	V253_VLS_S,
117459dc352SJanusz Krzysztofik 	V253_VLS_ST,
118459dc352SJanusz Krzysztofik 	V253_VLS_M,
119459dc352SJanusz Krzysztofik 	V253_VLS_MST,
120459dc352SJanusz Krzysztofik 	V253_VLS_S1,
121459dc352SJanusz Krzysztofik 	V253_VLS_S1T,
122459dc352SJanusz Krzysztofik 	V253_VLS_MS1T,
123459dc352SJanusz Krzysztofik 	V253_VLS_M1,
124459dc352SJanusz Krzysztofik 	V253_VLS_M1ST,
125459dc352SJanusz Krzysztofik 	V253_VLS_M1S1T,
126459dc352SJanusz Krzysztofik 	V253_VLS_H,
127459dc352SJanusz Krzysztofik 	V253_VLS_HT,
128459dc352SJanusz Krzysztofik 	V253_VLS_MS,
129459dc352SJanusz Krzysztofik 	V253_VLS_MS1,
130459dc352SJanusz Krzysztofik 	V253_VLS_M1S,
131459dc352SJanusz Krzysztofik 	V253_VLS_M1S1,
132459dc352SJanusz Krzysztofik 	V253_VLS_TEST,
133459dc352SJanusz Krzysztofik };
134459dc352SJanusz Krzysztofik 
135459dc352SJanusz Krzysztofik static int cx20442_pm_to_v253_vls(u8 value)
136459dc352SJanusz Krzysztofik {
137459dc352SJanusz Krzysztofik 	switch (value & ~(1 << CX20442_AGC)) {
138459dc352SJanusz Krzysztofik 	case 0:
139459dc352SJanusz Krzysztofik 		return V253_VLS_T;
140459dc352SJanusz Krzysztofik 	case (1 << CX20442_SPKOUT):
141459dc352SJanusz Krzysztofik 		return V253_VLS_S1;
142459dc352SJanusz Krzysztofik 	case (1 << CX20442_MIC):
143459dc352SJanusz Krzysztofik 		return V253_VLS_M1;
144459dc352SJanusz Krzysztofik 	case (1 << CX20442_SPKOUT) | (1 << CX20442_MIC):
145459dc352SJanusz Krzysztofik 		return V253_VLS_M1S1;
146459dc352SJanusz Krzysztofik 	case (1 << CX20442_TELOUT):
147459dc352SJanusz Krzysztofik 	case (1 << CX20442_TELIN):
148459dc352SJanusz Krzysztofik 	case (1 << CX20442_TELOUT) | (1 << CX20442_TELIN):
149459dc352SJanusz Krzysztofik 		return V253_VLS_L;
150459dc352SJanusz Krzysztofik 	case (1 << CX20442_TELOUT) | (1 << CX20442_MIC):
151459dc352SJanusz Krzysztofik 		return V253_VLS_NONE;
152459dc352SJanusz Krzysztofik 	}
153459dc352SJanusz Krzysztofik 	return -EINVAL;
154459dc352SJanusz Krzysztofik }
155459dc352SJanusz Krzysztofik static int cx20442_pm_to_v253_vsp(u8 value)
156459dc352SJanusz Krzysztofik {
157459dc352SJanusz Krzysztofik 	switch (value & ~(1 << CX20442_AGC)) {
158459dc352SJanusz Krzysztofik 	case (1 << CX20442_SPKOUT):
159459dc352SJanusz Krzysztofik 	case (1 << CX20442_MIC):
160459dc352SJanusz Krzysztofik 	case (1 << CX20442_SPKOUT) | (1 << CX20442_MIC):
161459dc352SJanusz Krzysztofik 		return (bool)(value & (1 << CX20442_AGC));
162459dc352SJanusz Krzysztofik 	}
163459dc352SJanusz Krzysztofik 	return (value & (1 << CX20442_AGC)) ? -EINVAL : 0;
164459dc352SJanusz Krzysztofik }
165459dc352SJanusz Krzysztofik 
166459dc352SJanusz Krzysztofik static int cx20442_write(struct snd_soc_codec *codec, unsigned int reg,
167459dc352SJanusz Krzysztofik 							unsigned int value)
168459dc352SJanusz Krzysztofik {
169459dc352SJanusz Krzysztofik 	u8 *reg_cache = codec->reg_cache;
170459dc352SJanusz Krzysztofik 	int vls, vsp, old, len;
171459dc352SJanusz Krzysztofik 	char buf[18];
172459dc352SJanusz Krzysztofik 
173459dc352SJanusz Krzysztofik 	if (reg >= codec->reg_cache_size)
174459dc352SJanusz Krzysztofik 		return -EINVAL;
175459dc352SJanusz Krzysztofik 
176b84eab08SJanusz Krzysztofik 	/* hw_write and control_data pointers required for talking to the modem
177*ad120daeSJanusz Krzysztofik 	 * are expected to be set by the line discipline initialization code */
178459dc352SJanusz Krzysztofik 	if (!codec->hw_write || !codec->control_data)
179459dc352SJanusz Krzysztofik 		return -EIO;
180459dc352SJanusz Krzysztofik 
181459dc352SJanusz Krzysztofik 	old = reg_cache[reg];
182459dc352SJanusz Krzysztofik 	reg_cache[reg] = value;
183459dc352SJanusz Krzysztofik 
184459dc352SJanusz Krzysztofik 	vls = cx20442_pm_to_v253_vls(value);
185459dc352SJanusz Krzysztofik 	if (vls < 0)
186459dc352SJanusz Krzysztofik 		return vls;
187459dc352SJanusz Krzysztofik 
188459dc352SJanusz Krzysztofik 	vsp = cx20442_pm_to_v253_vsp(value);
189459dc352SJanusz Krzysztofik 	if (vsp < 0)
190459dc352SJanusz Krzysztofik 		return vsp;
191459dc352SJanusz Krzysztofik 
192459dc352SJanusz Krzysztofik 	if ((vls == V253_VLS_T) ||
193459dc352SJanusz Krzysztofik 			(vls == cx20442_pm_to_v253_vls(old))) {
194459dc352SJanusz Krzysztofik 		if (vsp == cx20442_pm_to_v253_vsp(old))
195459dc352SJanusz Krzysztofik 			return 0;
196459dc352SJanusz Krzysztofik 		len = snprintf(buf, ARRAY_SIZE(buf), "at+vsp=%d\r", vsp);
197459dc352SJanusz Krzysztofik 	} else if (vsp == cx20442_pm_to_v253_vsp(old))
198459dc352SJanusz Krzysztofik 		len = snprintf(buf, ARRAY_SIZE(buf), "at+vls=%d\r", vls);
199459dc352SJanusz Krzysztofik 	else
200459dc352SJanusz Krzysztofik 		len = snprintf(buf, ARRAY_SIZE(buf),
201459dc352SJanusz Krzysztofik 					"at+vls=%d;+vsp=%d\r", vls, vsp);
202459dc352SJanusz Krzysztofik 
203459dc352SJanusz Krzysztofik 	if (unlikely(len > (ARRAY_SIZE(buf) - 1)))
204459dc352SJanusz Krzysztofik 		return -ENOMEM;
205459dc352SJanusz Krzysztofik 
206459dc352SJanusz Krzysztofik 	if (codec->hw_write(codec->control_data, buf, len) != len)
207459dc352SJanusz Krzysztofik 		return -EIO;
208459dc352SJanusz Krzysztofik 
209459dc352SJanusz Krzysztofik 	return 0;
210459dc352SJanusz Krzysztofik }
211459dc352SJanusz Krzysztofik 
212*ad120daeSJanusz Krzysztofik 
213*ad120daeSJanusz Krzysztofik /* Moved up here as line discipline referres it during initialization */
214*ad120daeSJanusz Krzysztofik static struct snd_soc_codec *cx20442_codec;
215*ad120daeSJanusz Krzysztofik 
216*ad120daeSJanusz Krzysztofik 
217*ad120daeSJanusz Krzysztofik /*
218*ad120daeSJanusz Krzysztofik  * Line discpline related code
219*ad120daeSJanusz Krzysztofik  *
220*ad120daeSJanusz Krzysztofik  * Any of the callback functions below can be used in two ways:
221*ad120daeSJanusz Krzysztofik  * 1) registerd by a machine driver as one of line discipline operations,
222*ad120daeSJanusz Krzysztofik  * 2) called from a machine's provided line discipline callback function
223*ad120daeSJanusz Krzysztofik  *    in case when extra machine specific code must be run as well.
224*ad120daeSJanusz Krzysztofik  */
225*ad120daeSJanusz Krzysztofik 
226*ad120daeSJanusz Krzysztofik /* Modem init: echo off, digital speaker off, quiet off, voice mode */
227*ad120daeSJanusz Krzysztofik static const char *v253_init = "ate0m0q0+fclass=8\r";
228*ad120daeSJanusz Krzysztofik 
229*ad120daeSJanusz Krzysztofik /* Line discipline .open() */
230*ad120daeSJanusz Krzysztofik static int v253_open(struct tty_struct *tty)
231*ad120daeSJanusz Krzysztofik {
232*ad120daeSJanusz Krzysztofik 	struct snd_soc_codec *codec = cx20442_codec;
233*ad120daeSJanusz Krzysztofik 	int ret, len = strlen(v253_init);
234*ad120daeSJanusz Krzysztofik 
235*ad120daeSJanusz Krzysztofik 	/* Doesn't make sense without write callback */
236*ad120daeSJanusz Krzysztofik 	if (!tty->ops->write)
237*ad120daeSJanusz Krzysztofik 		return -EINVAL;
238*ad120daeSJanusz Krzysztofik 
239*ad120daeSJanusz Krzysztofik 	/* Pass the codec structure address for use by other ldisc callbacks */
240*ad120daeSJanusz Krzysztofik 	tty->disc_data = codec;
241*ad120daeSJanusz Krzysztofik 
242*ad120daeSJanusz Krzysztofik 	if (tty->ops->write(tty, v253_init, len) != len) {
243*ad120daeSJanusz Krzysztofik 		ret = -EIO;
244*ad120daeSJanusz Krzysztofik 		goto err;
245*ad120daeSJanusz Krzysztofik 	}
246*ad120daeSJanusz Krzysztofik 	/* Actual setup will be performed after the modem responds. */
247*ad120daeSJanusz Krzysztofik 	return 0;
248*ad120daeSJanusz Krzysztofik err:
249*ad120daeSJanusz Krzysztofik 	tty->disc_data = NULL;
250*ad120daeSJanusz Krzysztofik 	return ret;
251*ad120daeSJanusz Krzysztofik }
252*ad120daeSJanusz Krzysztofik 
253*ad120daeSJanusz Krzysztofik /* Line discipline .close() */
254*ad120daeSJanusz Krzysztofik static void v253_close(struct tty_struct *tty)
255*ad120daeSJanusz Krzysztofik {
256*ad120daeSJanusz Krzysztofik 	struct snd_soc_codec *codec = tty->disc_data;
257*ad120daeSJanusz Krzysztofik 
258*ad120daeSJanusz Krzysztofik 	tty->disc_data = NULL;
259*ad120daeSJanusz Krzysztofik 
260*ad120daeSJanusz Krzysztofik 	if (!codec)
261*ad120daeSJanusz Krzysztofik 		return;
262*ad120daeSJanusz Krzysztofik 
263*ad120daeSJanusz Krzysztofik 	/* Prevent the codec driver from further accessing the modem */
264*ad120daeSJanusz Krzysztofik 	codec->hw_write = NULL;
265*ad120daeSJanusz Krzysztofik 	codec->control_data = NULL;
266*ad120daeSJanusz Krzysztofik 	codec->pop_time = 0;
267*ad120daeSJanusz Krzysztofik }
268*ad120daeSJanusz Krzysztofik 
269*ad120daeSJanusz Krzysztofik /* Line discipline .hangup() */
270*ad120daeSJanusz Krzysztofik static int v253_hangup(struct tty_struct *tty)
271*ad120daeSJanusz Krzysztofik {
272*ad120daeSJanusz Krzysztofik 	v253_close(tty);
273*ad120daeSJanusz Krzysztofik 	return 0;
274*ad120daeSJanusz Krzysztofik }
275*ad120daeSJanusz Krzysztofik 
276*ad120daeSJanusz Krzysztofik /* Line discipline .receive_buf() */
277*ad120daeSJanusz Krzysztofik static void v253_receive(struct tty_struct *tty,
278*ad120daeSJanusz Krzysztofik 				const unsigned char *cp, char *fp, int count)
279*ad120daeSJanusz Krzysztofik {
280*ad120daeSJanusz Krzysztofik 	struct snd_soc_codec *codec = tty->disc_data;
281*ad120daeSJanusz Krzysztofik 
282*ad120daeSJanusz Krzysztofik 	if (!codec)
283*ad120daeSJanusz Krzysztofik 		return;
284*ad120daeSJanusz Krzysztofik 
285*ad120daeSJanusz Krzysztofik 	if (!codec->control_data) {
286*ad120daeSJanusz Krzysztofik 		/* First modem response, complete setup procedure */
287*ad120daeSJanusz Krzysztofik 
288*ad120daeSJanusz Krzysztofik 		/* Set up codec driver access to modem controls */
289*ad120daeSJanusz Krzysztofik 		codec->control_data = tty;
290*ad120daeSJanusz Krzysztofik 		codec->hw_write = (hw_write_t)tty->ops->write;
291*ad120daeSJanusz Krzysztofik 		codec->pop_time = 1;
292*ad120daeSJanusz Krzysztofik 	}
293*ad120daeSJanusz Krzysztofik }
294*ad120daeSJanusz Krzysztofik 
295*ad120daeSJanusz Krzysztofik /* Line discipline .write_wakeup() */
296*ad120daeSJanusz Krzysztofik static void v253_wakeup(struct tty_struct *tty)
297*ad120daeSJanusz Krzysztofik {
298*ad120daeSJanusz Krzysztofik }
299*ad120daeSJanusz Krzysztofik 
300*ad120daeSJanusz Krzysztofik struct tty_ldisc_ops v253_ops = {
301*ad120daeSJanusz Krzysztofik 	.magic = TTY_LDISC_MAGIC,
302*ad120daeSJanusz Krzysztofik 	.name = "cx20442",
303*ad120daeSJanusz Krzysztofik 	.owner = THIS_MODULE,
304*ad120daeSJanusz Krzysztofik 	.open = v253_open,
305*ad120daeSJanusz Krzysztofik 	.close = v253_close,
306*ad120daeSJanusz Krzysztofik 	.hangup = v253_hangup,
307*ad120daeSJanusz Krzysztofik 	.receive_buf = v253_receive,
308*ad120daeSJanusz Krzysztofik 	.write_wakeup = v253_wakeup,
309*ad120daeSJanusz Krzysztofik };
310*ad120daeSJanusz Krzysztofik EXPORT_SYMBOL_GPL(v253_ops);
311*ad120daeSJanusz Krzysztofik 
312*ad120daeSJanusz Krzysztofik 
313*ad120daeSJanusz Krzysztofik /*
314*ad120daeSJanusz Krzysztofik  * Codec DAI
315*ad120daeSJanusz Krzysztofik  */
316*ad120daeSJanusz Krzysztofik 
317459dc352SJanusz Krzysztofik struct snd_soc_dai cx20442_dai = {
318459dc352SJanusz Krzysztofik 	.name = "CX20442",
319459dc352SJanusz Krzysztofik 	.playback = {
320459dc352SJanusz Krzysztofik 		.stream_name = "Playback",
321459dc352SJanusz Krzysztofik 		.channels_min = 1,
322459dc352SJanusz Krzysztofik 		.channels_max = 1,
323459dc352SJanusz Krzysztofik 		.rates = SNDRV_PCM_RATE_8000,
324459dc352SJanusz Krzysztofik 		.formats = SNDRV_PCM_FMTBIT_S16_LE,
325459dc352SJanusz Krzysztofik 	},
326459dc352SJanusz Krzysztofik 	.capture = {
327459dc352SJanusz Krzysztofik 		.stream_name = "Capture",
328459dc352SJanusz Krzysztofik 		.channels_min = 1,
329459dc352SJanusz Krzysztofik 		.channels_max = 1,
330459dc352SJanusz Krzysztofik 		.rates = SNDRV_PCM_RATE_8000,
331459dc352SJanusz Krzysztofik 		.formats = SNDRV_PCM_FMTBIT_S16_LE,
332459dc352SJanusz Krzysztofik 	},
333459dc352SJanusz Krzysztofik };
334459dc352SJanusz Krzysztofik EXPORT_SYMBOL_GPL(cx20442_dai);
335459dc352SJanusz Krzysztofik 
336459dc352SJanusz Krzysztofik static int cx20442_codec_probe(struct platform_device *pdev)
337459dc352SJanusz Krzysztofik {
338459dc352SJanusz Krzysztofik 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
339459dc352SJanusz Krzysztofik 	struct snd_soc_codec *codec;
340459dc352SJanusz Krzysztofik 	int ret;
341459dc352SJanusz Krzysztofik 
342459dc352SJanusz Krzysztofik 	if (!cx20442_codec) {
343459dc352SJanusz Krzysztofik 		dev_err(&pdev->dev, "cx20442 not yet discovered\n");
344459dc352SJanusz Krzysztofik 		return -ENODEV;
345459dc352SJanusz Krzysztofik 	}
346459dc352SJanusz Krzysztofik 	codec = cx20442_codec;
347459dc352SJanusz Krzysztofik 
348459dc352SJanusz Krzysztofik 	socdev->card->codec = codec;
349459dc352SJanusz Krzysztofik 
350459dc352SJanusz Krzysztofik 	/* register pcms */
351459dc352SJanusz Krzysztofik 	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
352459dc352SJanusz Krzysztofik 	if (ret < 0) {
353459dc352SJanusz Krzysztofik 		dev_err(&pdev->dev, "failed to create pcms\n");
354459dc352SJanusz Krzysztofik 		goto pcm_err;
355459dc352SJanusz Krzysztofik 	}
356459dc352SJanusz Krzysztofik 
357459dc352SJanusz Krzysztofik 	cx20442_add_widgets(codec);
358459dc352SJanusz Krzysztofik 
359459dc352SJanusz Krzysztofik 	ret = snd_soc_init_card(socdev);
360459dc352SJanusz Krzysztofik 	if (ret < 0) {
361459dc352SJanusz Krzysztofik 		dev_err(&pdev->dev, "failed to register card\n");
362459dc352SJanusz Krzysztofik 		goto card_err;
363459dc352SJanusz Krzysztofik 	}
364459dc352SJanusz Krzysztofik 
365459dc352SJanusz Krzysztofik 	return ret;
366459dc352SJanusz Krzysztofik 
367459dc352SJanusz Krzysztofik card_err:
368459dc352SJanusz Krzysztofik 	snd_soc_free_pcms(socdev);
369459dc352SJanusz Krzysztofik 	snd_soc_dapm_free(socdev);
370459dc352SJanusz Krzysztofik pcm_err:
371459dc352SJanusz Krzysztofik 	return ret;
372459dc352SJanusz Krzysztofik }
373459dc352SJanusz Krzysztofik 
374459dc352SJanusz Krzysztofik /* power down chip */
375459dc352SJanusz Krzysztofik static int cx20442_codec_remove(struct platform_device *pdev)
376459dc352SJanusz Krzysztofik {
377459dc352SJanusz Krzysztofik 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
378459dc352SJanusz Krzysztofik 
379459dc352SJanusz Krzysztofik 	snd_soc_free_pcms(socdev);
380459dc352SJanusz Krzysztofik 	snd_soc_dapm_free(socdev);
381459dc352SJanusz Krzysztofik 
382459dc352SJanusz Krzysztofik 	return 0;
383459dc352SJanusz Krzysztofik }
384459dc352SJanusz Krzysztofik 
385459dc352SJanusz Krzysztofik struct snd_soc_codec_device cx20442_codec_dev = {
386459dc352SJanusz Krzysztofik 	.probe = 	cx20442_codec_probe,
387459dc352SJanusz Krzysztofik 	.remove = 	cx20442_codec_remove,
388459dc352SJanusz Krzysztofik };
389459dc352SJanusz Krzysztofik EXPORT_SYMBOL_GPL(cx20442_codec_dev);
390459dc352SJanusz Krzysztofik 
391459dc352SJanusz Krzysztofik static int cx20442_register(struct cx20442_priv *cx20442)
392459dc352SJanusz Krzysztofik {
393459dc352SJanusz Krzysztofik 	struct snd_soc_codec *codec = &cx20442->codec;
394459dc352SJanusz Krzysztofik 	int ret;
395459dc352SJanusz Krzysztofik 
396459dc352SJanusz Krzysztofik 	mutex_init(&codec->mutex);
397459dc352SJanusz Krzysztofik 	INIT_LIST_HEAD(&codec->dapm_widgets);
398459dc352SJanusz Krzysztofik 	INIT_LIST_HEAD(&codec->dapm_paths);
399459dc352SJanusz Krzysztofik 
400459dc352SJanusz Krzysztofik 	codec->name = "CX20442";
401459dc352SJanusz Krzysztofik 	codec->owner = THIS_MODULE;
402459dc352SJanusz Krzysztofik 	codec->private_data = cx20442;
403459dc352SJanusz Krzysztofik 
404459dc352SJanusz Krzysztofik 	codec->dai = &cx20442_dai;
405459dc352SJanusz Krzysztofik 	codec->num_dai = 1;
406459dc352SJanusz Krzysztofik 
407459dc352SJanusz Krzysztofik 	codec->reg_cache = &cx20442->reg_cache;
408459dc352SJanusz Krzysztofik 	codec->reg_cache_size = ARRAY_SIZE(cx20442->reg_cache);
409459dc352SJanusz Krzysztofik 	codec->read = cx20442_read_reg_cache;
410459dc352SJanusz Krzysztofik 	codec->write = cx20442_write;
411459dc352SJanusz Krzysztofik 
412459dc352SJanusz Krzysztofik 	codec->bias_level = SND_SOC_BIAS_OFF;
413459dc352SJanusz Krzysztofik 
414459dc352SJanusz Krzysztofik 	cx20442_dai.dev = codec->dev;
415459dc352SJanusz Krzysztofik 
416459dc352SJanusz Krzysztofik 	cx20442_codec = codec;
417459dc352SJanusz Krzysztofik 
418459dc352SJanusz Krzysztofik 	ret = snd_soc_register_codec(codec);
419459dc352SJanusz Krzysztofik 	if (ret != 0) {
420*ad120daeSJanusz Krzysztofik 		dev_err(codec->dev, "Failed to register codec: %d\n", ret);
421459dc352SJanusz Krzysztofik 		goto err;
422459dc352SJanusz Krzysztofik 	}
423459dc352SJanusz Krzysztofik 
424459dc352SJanusz Krzysztofik 	ret = snd_soc_register_dai(&cx20442_dai);
425459dc352SJanusz Krzysztofik 	if (ret != 0) {
426*ad120daeSJanusz Krzysztofik 		dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
427459dc352SJanusz Krzysztofik 		goto err_codec;
428459dc352SJanusz Krzysztofik 	}
429459dc352SJanusz Krzysztofik 
430459dc352SJanusz Krzysztofik 	return 0;
431459dc352SJanusz Krzysztofik 
432459dc352SJanusz Krzysztofik err_codec:
433459dc352SJanusz Krzysztofik 	snd_soc_unregister_codec(codec);
434459dc352SJanusz Krzysztofik err:
435459dc352SJanusz Krzysztofik 	cx20442_codec = NULL;
436459dc352SJanusz Krzysztofik 	kfree(cx20442);
437459dc352SJanusz Krzysztofik 	return ret;
438459dc352SJanusz Krzysztofik }
439459dc352SJanusz Krzysztofik 
440459dc352SJanusz Krzysztofik static void cx20442_unregister(struct cx20442_priv *cx20442)
441459dc352SJanusz Krzysztofik {
442459dc352SJanusz Krzysztofik 	snd_soc_unregister_dai(&cx20442_dai);
443459dc352SJanusz Krzysztofik 	snd_soc_unregister_codec(&cx20442->codec);
444459dc352SJanusz Krzysztofik 
445459dc352SJanusz Krzysztofik 	cx20442_codec = NULL;
446459dc352SJanusz Krzysztofik 	kfree(cx20442);
447459dc352SJanusz Krzysztofik }
448459dc352SJanusz Krzysztofik 
449459dc352SJanusz Krzysztofik static int cx20442_platform_probe(struct platform_device *pdev)
450459dc352SJanusz Krzysztofik {
451459dc352SJanusz Krzysztofik 	struct cx20442_priv *cx20442;
452459dc352SJanusz Krzysztofik 	struct snd_soc_codec *codec;
453459dc352SJanusz Krzysztofik 
454459dc352SJanusz Krzysztofik 	cx20442 = kzalloc(sizeof(struct cx20442_priv), GFP_KERNEL);
455459dc352SJanusz Krzysztofik 	if (cx20442 == NULL)
456459dc352SJanusz Krzysztofik 		return -ENOMEM;
457459dc352SJanusz Krzysztofik 
458459dc352SJanusz Krzysztofik 	codec = &cx20442->codec;
459459dc352SJanusz Krzysztofik 
460459dc352SJanusz Krzysztofik 	codec->control_data = NULL;
461459dc352SJanusz Krzysztofik 	codec->hw_write = NULL;
462459dc352SJanusz Krzysztofik 	codec->pop_time = 0;
463459dc352SJanusz Krzysztofik 
464459dc352SJanusz Krzysztofik 	codec->dev = &pdev->dev;
465459dc352SJanusz Krzysztofik 	platform_set_drvdata(pdev, cx20442);
466459dc352SJanusz Krzysztofik 
467459dc352SJanusz Krzysztofik 	return cx20442_register(cx20442);
468459dc352SJanusz Krzysztofik }
469459dc352SJanusz Krzysztofik 
470459dc352SJanusz Krzysztofik static int __exit cx20442_platform_remove(struct platform_device *pdev)
471459dc352SJanusz Krzysztofik {
472459dc352SJanusz Krzysztofik 	struct cx20442_priv *cx20442 = platform_get_drvdata(pdev);
473459dc352SJanusz Krzysztofik 
474459dc352SJanusz Krzysztofik 	cx20442_unregister(cx20442);
475459dc352SJanusz Krzysztofik 	return 0;
476459dc352SJanusz Krzysztofik }
477459dc352SJanusz Krzysztofik 
478459dc352SJanusz Krzysztofik static struct platform_driver cx20442_platform_driver = {
479459dc352SJanusz Krzysztofik 	.driver = {
480459dc352SJanusz Krzysztofik 		.name = "cx20442",
481459dc352SJanusz Krzysztofik 		.owner = THIS_MODULE,
482459dc352SJanusz Krzysztofik 		},
483459dc352SJanusz Krzysztofik 	.probe = cx20442_platform_probe,
484459dc352SJanusz Krzysztofik 	.remove = __exit_p(cx20442_platform_remove),
485459dc352SJanusz Krzysztofik };
486459dc352SJanusz Krzysztofik 
487459dc352SJanusz Krzysztofik static int __init cx20442_init(void)
488459dc352SJanusz Krzysztofik {
489459dc352SJanusz Krzysztofik 	return platform_driver_register(&cx20442_platform_driver);
490459dc352SJanusz Krzysztofik }
491459dc352SJanusz Krzysztofik module_init(cx20442_init);
492459dc352SJanusz Krzysztofik 
493459dc352SJanusz Krzysztofik static void __exit cx20442_exit(void)
494459dc352SJanusz Krzysztofik {
495459dc352SJanusz Krzysztofik 	platform_driver_unregister(&cx20442_platform_driver);
496459dc352SJanusz Krzysztofik }
497459dc352SJanusz Krzysztofik module_exit(cx20442_exit);
498459dc352SJanusz Krzysztofik 
499459dc352SJanusz Krzysztofik MODULE_DESCRIPTION("ASoC CX20442-11 voice modem codec driver");
500459dc352SJanusz Krzysztofik MODULE_AUTHOR("Janusz Krzysztofik");
501459dc352SJanusz Krzysztofik MODULE_LICENSE("GPL");
502b84eab08SJanusz Krzysztofik MODULE_ALIAS("platform:cx20442");
503