blob: dcedd7442ec7090cdaafa56a4758cf46bd40b5ff [file] [log] [blame]
Cliff Caib7138212008-09-05 18:09:57 +08001/*
2 * File: sound/soc/codecs/ssm2602.c
3 * Author: Cliff Cai <Cliff.Cai@analog.com>
4 *
5 * Created: Tue June 06 2008
6 * Description: Driver for ssm2602 sound chip
7 *
8 * Modified:
9 * Copyright 2008 Analog Devices Inc.
10 *
11 * Bugs: Enter bugs at http://blackfin.uclinux.org/
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, see the file COPYING, or write
25 * to the Free Software Foundation, Inc.,
26 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
27 */
28
29#include <linux/module.h>
30#include <linux/moduleparam.h>
31#include <linux/init.h>
32#include <linux/delay.h>
33#include <linux/pm.h>
34#include <linux/i2c.h>
Mike Frysingerb39e2852011-04-07 02:05:11 -040035#include <linux/spi/spi.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090036#include <linux/slab.h>
Cliff Caib7138212008-09-05 18:09:57 +080037#include <sound/core.h>
38#include <sound/pcm.h>
39#include <sound/pcm_params.h>
40#include <sound/soc.h>
Cliff Caib7138212008-09-05 18:09:57 +080041#include <sound/initval.h>
Lars-Peter Clausenf3eee002011-05-08 09:24:46 -070042#include <sound/tlv.h>
Cliff Caib7138212008-09-05 18:09:57 +080043
44#include "ssm2602.h"
45
Cliff Caib7138212008-09-05 18:09:57 +080046#define SSM2602_VERSION "0.1"
47
Lars-Peter Clausenb1f7b2b2011-05-08 09:24:43 -070048enum ssm2602_type {
49 SSM2602,
50 SSM2604,
51};
52
Cliff Caib7138212008-09-05 18:09:57 +080053/* codec private data */
54struct ssm2602_priv {
55 unsigned int sysclk;
Lars-Peter Clausen8b3f39d2012-04-25 11:42:46 +020056 struct snd_pcm_hw_constraint_list *sysclk_constraints;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +000057 enum snd_soc_control_type control_type;
Cliff Caib7138212008-09-05 18:09:57 +080058 struct snd_pcm_substream *master_substream;
59 struct snd_pcm_substream *slave_substream;
Lars-Peter Clausenb1f7b2b2011-05-08 09:24:43 -070060
61 enum ssm2602_type type;
Lars-Peter Clausen02890532011-09-27 11:08:48 +020062 unsigned int clk_out_pwr;
Cliff Caib7138212008-09-05 18:09:57 +080063};
64
65/*
66 * ssm2602 register cache
67 * We can't read the ssm2602 register space when we are
68 * using 2 wire for device control, so we cache them instead.
69 * There is no point in caching the reset register
70 */
71static const u16 ssm2602_reg[SSM2602_CACHEREGNUM] = {
Lars-Peter Clausen7164bdb2011-05-08 09:24:41 -070072 0x0097, 0x0097, 0x0079, 0x0079,
73 0x000a, 0x0008, 0x009f, 0x000a,
Cliff Caib7138212008-09-05 18:09:57 +080074 0x0000, 0x0000
75};
76
Cliff Cai93547e82011-03-27 17:22:57 -040077#define ssm2602_reset(c) snd_soc_write(c, SSM2602_RESET, 0)
Cliff Caib7138212008-09-05 18:09:57 +080078
79/*Appending several "None"s just for OSS mixer use*/
80static const char *ssm2602_input_select[] = {
81 "Line", "Mic", "None", "None", "None",
82 "None", "None", "None",
83};
84
85static const char *ssm2602_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
86
87static const struct soc_enum ssm2602_enum[] = {
88 SOC_ENUM_SINGLE(SSM2602_APANA, 2, 2, ssm2602_input_select),
89 SOC_ENUM_SINGLE(SSM2602_APDIGI, 1, 4, ssm2602_deemph),
90};
91
Lars-Peter Clausenf3eee002011-05-08 09:24:46 -070092static const unsigned int ssm260x_outmix_tlv[] = {
93 TLV_DB_RANGE_HEAD(2),
94 0, 47, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 0),
95 48, 127, TLV_DB_SCALE_ITEM(-7400, 100, 0),
96};
97
98static const DECLARE_TLV_DB_SCALE(ssm260x_inpga_tlv, -3450, 150, 0);
99static const DECLARE_TLV_DB_SCALE(ssm260x_sidetone_tlv, -1500, 300, 0);
100
Lars-Peter Clausenb1f7b2b2011-05-08 09:24:43 -0700101static const struct snd_kcontrol_new ssm260x_snd_controls[] = {
Lars-Peter Clausenf3eee002011-05-08 09:24:46 -0700102SOC_DOUBLE_R_TLV("Capture Volume", SSM2602_LINVOL, SSM2602_RINVOL, 0, 45, 0,
103 ssm260x_inpga_tlv),
Cliff Caib7138212008-09-05 18:09:57 +0800104SOC_DOUBLE_R("Capture Switch", SSM2602_LINVOL, SSM2602_RINVOL, 7, 1, 1),
105
Cliff Caib7138212008-09-05 18:09:57 +0800106SOC_SINGLE("ADC High Pass Filter Switch", SSM2602_APDIGI, 0, 1, 1),
107SOC_SINGLE("Store DC Offset Switch", SSM2602_APDIGI, 4, 1, 0),
108
Cliff Caib7138212008-09-05 18:09:57 +0800109SOC_ENUM("Playback De-emphasis", ssm2602_enum[1]),
110};
111
Lars-Peter Clausenb1f7b2b2011-05-08 09:24:43 -0700112static const struct snd_kcontrol_new ssm2602_snd_controls[] = {
Lars-Peter Clausenf3eee002011-05-08 09:24:46 -0700113SOC_DOUBLE_R_TLV("Master Playback Volume", SSM2602_LOUT1V, SSM2602_ROUT1V,
114 0, 127, 0, ssm260x_outmix_tlv),
Lars-Peter Clausenb1f7b2b2011-05-08 09:24:43 -0700115SOC_DOUBLE_R("Master Playback ZC Switch", SSM2602_LOUT1V, SSM2602_ROUT1V,
116 7, 1, 0),
Lars-Peter Clausenf3eee002011-05-08 09:24:46 -0700117SOC_SINGLE_TLV("Sidetone Playback Volume", SSM2602_APANA, 6, 3, 1,
118 ssm260x_sidetone_tlv),
Lars-Peter Clausenb1f7b2b2011-05-08 09:24:43 -0700119
120SOC_SINGLE("Mic Boost (+20dB)", SSM2602_APANA, 0, 1, 0),
121SOC_SINGLE("Mic Boost2 (+20dB)", SSM2602_APANA, 8, 1, 0),
122SOC_SINGLE("Mic Switch", SSM2602_APANA, 1, 1, 1),
123};
124
Cliff Caib7138212008-09-05 18:09:57 +0800125/* Output Mixer */
Lars-Peter Clausenb1f7b2b2011-05-08 09:24:43 -0700126static const struct snd_kcontrol_new ssm260x_output_mixer_controls[] = {
Cliff Caib7138212008-09-05 18:09:57 +0800127SOC_DAPM_SINGLE("Line Bypass Switch", SSM2602_APANA, 3, 1, 0),
Cliff Caib7138212008-09-05 18:09:57 +0800128SOC_DAPM_SINGLE("HiFi Playback Switch", SSM2602_APANA, 4, 1, 0),
Lars-Peter Clausenb1f7b2b2011-05-08 09:24:43 -0700129SOC_DAPM_SINGLE("Mic Sidetone Switch", SSM2602_APANA, 5, 1, 0),
Cliff Caib7138212008-09-05 18:09:57 +0800130};
131
132/* Input mux */
133static const struct snd_kcontrol_new ssm2602_input_mux_controls =
134SOC_DAPM_ENUM("Input Select", ssm2602_enum[0]);
135
Lars-Peter Clausenb1f7b2b2011-05-08 09:24:43 -0700136static const struct snd_soc_dapm_widget ssm260x_dapm_widgets[] = {
Cliff Caib7138212008-09-05 18:09:57 +0800137SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SSM2602_PWR, 3, 1),
Cliff Caib7138212008-09-05 18:09:57 +0800138SND_SOC_DAPM_ADC("ADC", "HiFi Capture", SSM2602_PWR, 2, 1),
Cliff Caib7138212008-09-05 18:09:57 +0800139SND_SOC_DAPM_PGA("Line Input", SSM2602_PWR, 0, 1, NULL, 0),
Lars-Peter Clausenb1f7b2b2011-05-08 09:24:43 -0700140
Mark Brown3afb1b3e2011-05-11 00:01:58 +0200141SND_SOC_DAPM_SUPPLY("Digital Core Power", SSM2602_ACTIVE, 0, 0, NULL, 0),
Lars-Peter Clausen2a438012011-05-08 09:24:45 -0700142
Lars-Peter Clausenb1f7b2b2011-05-08 09:24:43 -0700143SND_SOC_DAPM_OUTPUT("LOUT"),
144SND_SOC_DAPM_OUTPUT("ROUT"),
Cliff Caib7138212008-09-05 18:09:57 +0800145SND_SOC_DAPM_INPUT("RLINEIN"),
146SND_SOC_DAPM_INPUT("LLINEIN"),
147};
148
Lars-Peter Clausenb1f7b2b2011-05-08 09:24:43 -0700149static const struct snd_soc_dapm_widget ssm2602_dapm_widgets[] = {
150SND_SOC_DAPM_MIXER("Output Mixer", SSM2602_PWR, 4, 1,
151 ssm260x_output_mixer_controls,
152 ARRAY_SIZE(ssm260x_output_mixer_controls)),
153
154SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, &ssm2602_input_mux_controls),
155SND_SOC_DAPM_MICBIAS("Mic Bias", SSM2602_PWR, 1, 1),
156
157SND_SOC_DAPM_OUTPUT("LHPOUT"),
158SND_SOC_DAPM_OUTPUT("RHPOUT"),
159SND_SOC_DAPM_INPUT("MICIN"),
160};
161
162static const struct snd_soc_dapm_widget ssm2604_dapm_widgets[] = {
163SND_SOC_DAPM_MIXER("Output Mixer", SND_SOC_NOPM, 0, 0,
164 ssm260x_output_mixer_controls,
165 ARRAY_SIZE(ssm260x_output_mixer_controls) - 1), /* Last element is the mic */
166};
167
168static const struct snd_soc_dapm_route ssm260x_routes[] = {
Lars-Peter Clausen2a438012011-05-08 09:24:45 -0700169 {"DAC", NULL, "Digital Core Power"},
170 {"ADC", NULL, "Digital Core Power"},
171
Cliff Caib7138212008-09-05 18:09:57 +0800172 {"Output Mixer", "Line Bypass Switch", "Line Input"},
173 {"Output Mixer", "HiFi Playback Switch", "DAC"},
Cliff Caib7138212008-09-05 18:09:57 +0800174
Cliff Caib7138212008-09-05 18:09:57 +0800175 {"ROUT", NULL, "Output Mixer"},
Cliff Caib7138212008-09-05 18:09:57 +0800176 {"LOUT", NULL, "Output Mixer"},
177
Lars-Peter Clausenb1f7b2b2011-05-08 09:24:43 -0700178 {"Line Input", NULL, "LLINEIN"},
179 {"Line Input", NULL, "RLINEIN"},
180};
181
182static const struct snd_soc_dapm_route ssm2602_routes[] = {
183 {"Output Mixer", "Mic Sidetone Switch", "Mic Bias"},
184
185 {"RHPOUT", NULL, "Output Mixer"},
186 {"LHPOUT", NULL, "Output Mixer"},
187
Cliff Caib7138212008-09-05 18:09:57 +0800188 {"Input Mux", "Line", "Line Input"},
189 {"Input Mux", "Mic", "Mic Bias"},
190 {"ADC", NULL, "Input Mux"},
191
Cliff Caib7138212008-09-05 18:09:57 +0800192 {"Mic Bias", NULL, "MICIN"},
193};
194
Lars-Peter Clausenb1f7b2b2011-05-08 09:24:43 -0700195static const struct snd_soc_dapm_route ssm2604_routes[] = {
196 {"ADC", NULL, "Line Input"},
197};
Cliff Caib7138212008-09-05 18:09:57 +0800198
Lars-Peter Clausen8b3f39d2012-04-25 11:42:46 +0200199static const unsigned int ssm2602_rates_12288000[] = {
200 8000, 32000, 48000, 96000,
201};
202
203static struct snd_pcm_hw_constraint_list ssm2602_constraints_12288000 = {
204 .list = ssm2602_rates_12288000,
205 .count = ARRAY_SIZE(ssm2602_rates_12288000),
206};
207
208static const unsigned int ssm2602_rates_11289600[] = {
209 8000, 44100, 88200,
210};
211
212static struct snd_pcm_hw_constraint_list ssm2602_constraints_11289600 = {
213 .list = ssm2602_rates_11289600,
214 .count = ARRAY_SIZE(ssm2602_rates_11289600),
215};
216
Lars-Peter Clausen0b4cd2e2011-05-05 16:59:10 +0200217struct ssm2602_coeff {
Cliff Caib7138212008-09-05 18:09:57 +0800218 u32 mclk;
219 u32 rate;
Lars-Peter Clausen0b4cd2e2011-05-05 16:59:10 +0200220 u8 srate;
Cliff Caib7138212008-09-05 18:09:57 +0800221};
222
Lars-Peter Clausen0b4cd2e2011-05-05 16:59:10 +0200223#define SSM2602_COEFF_SRATE(sr, bosr, usb) (((sr) << 2) | ((bosr) << 1) | (usb))
224
225/* codec mclk clock coefficients */
226static const struct ssm2602_coeff ssm2602_coeff_table[] = {
Cliff Caib7138212008-09-05 18:09:57 +0800227 /* 48k */
Lars-Peter Clausen0b4cd2e2011-05-05 16:59:10 +0200228 {12288000, 48000, SSM2602_COEFF_SRATE(0x0, 0x0, 0x0)},
229 {18432000, 48000, SSM2602_COEFF_SRATE(0x0, 0x1, 0x0)},
230 {12000000, 48000, SSM2602_COEFF_SRATE(0x0, 0x0, 0x1)},
Cliff Caib7138212008-09-05 18:09:57 +0800231
232 /* 32k */
Lars-Peter Clausen0b4cd2e2011-05-05 16:59:10 +0200233 {12288000, 32000, SSM2602_COEFF_SRATE(0x6, 0x0, 0x0)},
234 {18432000, 32000, SSM2602_COEFF_SRATE(0x6, 0x1, 0x0)},
235 {12000000, 32000, SSM2602_COEFF_SRATE(0x6, 0x0, 0x1)},
Cliff Caib7138212008-09-05 18:09:57 +0800236
237 /* 8k */
Lars-Peter Clausen0b4cd2e2011-05-05 16:59:10 +0200238 {12288000, 8000, SSM2602_COEFF_SRATE(0x3, 0x0, 0x0)},
239 {18432000, 8000, SSM2602_COEFF_SRATE(0x3, 0x1, 0x0)},
240 {11289600, 8000, SSM2602_COEFF_SRATE(0xb, 0x0, 0x0)},
241 {16934400, 8000, SSM2602_COEFF_SRATE(0xb, 0x1, 0x0)},
242 {12000000, 8000, SSM2602_COEFF_SRATE(0x3, 0x0, 0x1)},
Cliff Caib7138212008-09-05 18:09:57 +0800243
244 /* 96k */
Lars-Peter Clausen0b4cd2e2011-05-05 16:59:10 +0200245 {12288000, 96000, SSM2602_COEFF_SRATE(0x7, 0x0, 0x0)},
246 {18432000, 96000, SSM2602_COEFF_SRATE(0x7, 0x1, 0x0)},
247 {12000000, 96000, SSM2602_COEFF_SRATE(0x7, 0x0, 0x1)},
Cliff Caib7138212008-09-05 18:09:57 +0800248
249 /* 44.1k */
Lars-Peter Clausen0b4cd2e2011-05-05 16:59:10 +0200250 {11289600, 44100, SSM2602_COEFF_SRATE(0x8, 0x0, 0x0)},
251 {16934400, 44100, SSM2602_COEFF_SRATE(0x8, 0x1, 0x0)},
252 {12000000, 44100, SSM2602_COEFF_SRATE(0x8, 0x1, 0x1)},
Cliff Caib7138212008-09-05 18:09:57 +0800253
254 /* 88.2k */
Lars-Peter Clausen0b4cd2e2011-05-05 16:59:10 +0200255 {11289600, 88200, SSM2602_COEFF_SRATE(0xf, 0x0, 0x0)},
256 {16934400, 88200, SSM2602_COEFF_SRATE(0xf, 0x1, 0x0)},
257 {12000000, 88200, SSM2602_COEFF_SRATE(0xf, 0x1, 0x1)},
Cliff Caib7138212008-09-05 18:09:57 +0800258};
259
Lars-Peter Clausen0b4cd2e2011-05-05 16:59:10 +0200260static inline int ssm2602_get_coeff(int mclk, int rate)
Cliff Caib7138212008-09-05 18:09:57 +0800261{
262 int i;
263
Lars-Peter Clausen0b4cd2e2011-05-05 16:59:10 +0200264 for (i = 0; i < ARRAY_SIZE(ssm2602_coeff_table); i++) {
265 if (ssm2602_coeff_table[i].rate == rate &&
266 ssm2602_coeff_table[i].mclk == mclk)
267 return ssm2602_coeff_table[i].srate;
Cliff Caib7138212008-09-05 18:09:57 +0800268 }
Lars-Peter Clausen0b4cd2e2011-05-05 16:59:10 +0200269 return -EINVAL;
Cliff Caib7138212008-09-05 18:09:57 +0800270}
271
272static int ssm2602_hw_params(struct snd_pcm_substream *substream,
Mark Browndee89c42008-11-18 22:11:38 +0000273 struct snd_pcm_hw_params *params,
274 struct snd_soc_dai *dai)
Cliff Caib7138212008-09-05 18:09:57 +0800275{
Mark Browne6968a12012-04-04 15:58:16 +0100276 struct snd_soc_codec *codec = dai->codec;
Mark Brownb2c812e2010-04-14 15:35:19 +0900277 struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
Cliff Cai93547e82011-03-27 17:22:57 -0400278 u16 iface = snd_soc_read(codec, SSM2602_IFACE) & 0xfff3;
Lars-Peter Clausen0b4cd2e2011-05-05 16:59:10 +0200279 int srate = ssm2602_get_coeff(ssm2602->sysclk, params_rate(params));
Cliff Caib7138212008-09-05 18:09:57 +0800280
Karl Beldanfaab5a32008-11-20 15:39:27 +0100281 if (substream == ssm2602->slave_substream) {
Cliff Cai93547e82011-03-27 17:22:57 -0400282 dev_dbg(codec->dev, "Ignoring hw_params for slave substream\n");
Karl Beldanfaab5a32008-11-20 15:39:27 +0100283 return 0;
284 }
285
Lars-Peter Clausen0b4cd2e2011-05-05 16:59:10 +0200286 if (srate < 0)
287 return srate;
Cliff Caib7138212008-09-05 18:09:57 +0800288
Cliff Cai93547e82011-03-27 17:22:57 -0400289 snd_soc_write(codec, SSM2602_SRATE, srate);
Cliff Caib7138212008-09-05 18:09:57 +0800290
291 /* bit size */
292 switch (params_format(params)) {
293 case SNDRV_PCM_FORMAT_S16_LE:
294 break;
295 case SNDRV_PCM_FORMAT_S20_3LE:
296 iface |= 0x0004;
297 break;
298 case SNDRV_PCM_FORMAT_S24_LE:
299 iface |= 0x0008;
300 break;
301 case SNDRV_PCM_FORMAT_S32_LE:
302 iface |= 0x000c;
303 break;
304 }
Cliff Cai93547e82011-03-27 17:22:57 -0400305 snd_soc_write(codec, SSM2602_IFACE, iface);
Cliff Caib7138212008-09-05 18:09:57 +0800306 return 0;
307}
308
Mark Browndee89c42008-11-18 22:11:38 +0000309static int ssm2602_startup(struct snd_pcm_substream *substream,
310 struct snd_soc_dai *dai)
Cliff Caib7138212008-09-05 18:09:57 +0800311{
Mark Browne6968a12012-04-04 15:58:16 +0100312 struct snd_soc_codec *codec = dai->codec;
Mark Brownb2c812e2010-04-14 15:35:19 +0900313 struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
Cliff Caib7138212008-09-05 18:09:57 +0800314 struct snd_pcm_runtime *master_runtime;
315
316 /* The DAI has shared clocks so if we already have a playback or
317 * capture going then constrain this substream to match it.
Karl Beldanfaab5a32008-11-20 15:39:27 +0100318 * TODO: the ssm2602 allows pairs of non-matching PB/REC rates
Cliff Caib7138212008-09-05 18:09:57 +0800319 */
320 if (ssm2602->master_substream) {
321 master_runtime = ssm2602->master_substream->runtime;
Lars-Peter Clausen26806a42011-09-20 08:19:58 +0200322 dev_dbg(codec->dev, "Constraining to %d bits at %dHz\n",
Karl Beldanfaab5a32008-11-20 15:39:27 +0100323 master_runtime->sample_bits,
324 master_runtime->rate);
325
Cliff Caif692fce2009-06-02 00:18:54 -0400326 if (master_runtime->rate != 0)
327 snd_pcm_hw_constraint_minmax(substream->runtime,
328 SNDRV_PCM_HW_PARAM_RATE,
329 master_runtime->rate,
330 master_runtime->rate);
Cliff Caib7138212008-09-05 18:09:57 +0800331
Cliff Caif692fce2009-06-02 00:18:54 -0400332 if (master_runtime->sample_bits != 0)
333 snd_pcm_hw_constraint_minmax(substream->runtime,
334 SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
335 master_runtime->sample_bits,
336 master_runtime->sample_bits);
Cliff Caib7138212008-09-05 18:09:57 +0800337
338 ssm2602->slave_substream = substream;
339 } else
340 ssm2602->master_substream = substream;
341
Lars-Peter Clausen8b3f39d2012-04-25 11:42:46 +0200342 if (ssm2602->sysclk_constraints) {
343 snd_pcm_hw_constraint_list(substream->runtime, 0,
344 SNDRV_PCM_HW_PARAM_RATE,
345 ssm2602->sysclk_constraints);
346 }
347
Cliff Caib7138212008-09-05 18:09:57 +0800348 return 0;
349}
350
Mark Browndee89c42008-11-18 22:11:38 +0000351static void ssm2602_shutdown(struct snd_pcm_substream *substream,
352 struct snd_soc_dai *dai)
Cliff Caib7138212008-09-05 18:09:57 +0800353{
Mark Browne6968a12012-04-04 15:58:16 +0100354 struct snd_soc_codec *codec = dai->codec;
Mark Brownb2c812e2010-04-14 15:35:19 +0900355 struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
Cliff Caif692fce2009-06-02 00:18:54 -0400356
Karl Beldanfaab5a32008-11-20 15:39:27 +0100357 if (ssm2602->master_substream == substream)
358 ssm2602->master_substream = ssm2602->slave_substream;
359
360 ssm2602->slave_substream = NULL;
Cliff Caib7138212008-09-05 18:09:57 +0800361}
362
Lars-Peter Clausen2a438012011-05-08 09:24:45 -0700363
Cliff Caib7138212008-09-05 18:09:57 +0800364static int ssm2602_mute(struct snd_soc_dai *dai, int mute)
365{
366 struct snd_soc_codec *codec = dai->codec;
Axel Lin2ee9c182011-10-19 14:07:31 +0800367
Cliff Caib7138212008-09-05 18:09:57 +0800368 if (mute)
Axel Lin2ee9c182011-10-19 14:07:31 +0800369 snd_soc_update_bits(codec, SSM2602_APDIGI,
370 APDIGI_ENABLE_DAC_MUTE,
371 APDIGI_ENABLE_DAC_MUTE);
Cliff Caib7138212008-09-05 18:09:57 +0800372 else
Axel Lin2ee9c182011-10-19 14:07:31 +0800373 snd_soc_update_bits(codec, SSM2602_APDIGI,
374 APDIGI_ENABLE_DAC_MUTE, 0);
Cliff Caib7138212008-09-05 18:09:57 +0800375 return 0;
376}
377
378static int ssm2602_set_dai_sysclk(struct snd_soc_dai *codec_dai,
379 int clk_id, unsigned int freq, int dir)
380{
381 struct snd_soc_codec *codec = codec_dai->codec;
Mark Brownb2c812e2010-04-14 15:35:19 +0900382 struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
Lars-Peter Clausen02890532011-09-27 11:08:48 +0200383
384 if (dir == SND_SOC_CLOCK_IN) {
385 if (clk_id != SSM2602_SYSCLK)
386 return -EINVAL;
387
388 switch (freq) {
Lars-Peter Clausen02890532011-09-27 11:08:48 +0200389 case 12288000:
Lars-Peter Clausen02890532011-09-27 11:08:48 +0200390 case 18432000:
Lars-Peter Clausen8b3f39d2012-04-25 11:42:46 +0200391 ssm2602->sysclk_constraints = &ssm2602_constraints_12288000;
392 break;
393 case 11289600:
394 case 16934400:
395 ssm2602->sysclk_constraints = &ssm2602_constraints_11289600;
396 break;
397 case 12000000:
398 ssm2602->sysclk_constraints = NULL;
Lars-Peter Clausen02890532011-09-27 11:08:48 +0200399 break;
400 default:
401 return -EINVAL;
402 }
Lars-Peter Clausen8b3f39d2012-04-25 11:42:46 +0200403 ssm2602->sysclk = freq;
Lars-Peter Clausen02890532011-09-27 11:08:48 +0200404 } else {
405 unsigned int mask;
406
407 switch (clk_id) {
408 case SSM2602_CLK_CLKOUT:
409 mask = PWR_CLK_OUT_PDN;
410 break;
411 case SSM2602_CLK_XTO:
412 mask = PWR_OSC_PDN;
413 break;
414 default:
415 return -EINVAL;
416 }
417
418 if (freq == 0)
419 ssm2602->clk_out_pwr |= mask;
420 else
421 ssm2602->clk_out_pwr &= ~mask;
422
423 snd_soc_update_bits(codec, SSM2602_PWR,
424 PWR_CLK_OUT_PDN | PWR_OSC_PDN, ssm2602->clk_out_pwr);
Cliff Caib7138212008-09-05 18:09:57 +0800425 }
Lars-Peter Clausen02890532011-09-27 11:08:48 +0200426
427 return 0;
Cliff Caib7138212008-09-05 18:09:57 +0800428}
429
430static int ssm2602_set_dai_fmt(struct snd_soc_dai *codec_dai,
431 unsigned int fmt)
432{
433 struct snd_soc_codec *codec = codec_dai->codec;
434 u16 iface = 0;
435
436 /* set master/slave audio interface */
437 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
438 case SND_SOC_DAIFMT_CBM_CFM:
439 iface |= 0x0040;
440 break;
441 case SND_SOC_DAIFMT_CBS_CFS:
442 break;
443 default:
444 return -EINVAL;
445 }
446
447 /* interface format */
448 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
449 case SND_SOC_DAIFMT_I2S:
450 iface |= 0x0002;
451 break;
452 case SND_SOC_DAIFMT_RIGHT_J:
453 break;
454 case SND_SOC_DAIFMT_LEFT_J:
455 iface |= 0x0001;
456 break;
457 case SND_SOC_DAIFMT_DSP_A:
Jarkko Nikulac6913482008-12-22 10:57:33 +0200458 iface |= 0x0013;
Cliff Caib7138212008-09-05 18:09:57 +0800459 break;
460 case SND_SOC_DAIFMT_DSP_B:
Jarkko Nikulac6913482008-12-22 10:57:33 +0200461 iface |= 0x0003;
Cliff Caib7138212008-09-05 18:09:57 +0800462 break;
463 default:
464 return -EINVAL;
465 }
466
467 /* clock inversion */
468 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
469 case SND_SOC_DAIFMT_NB_NF:
470 break;
471 case SND_SOC_DAIFMT_IB_IF:
472 iface |= 0x0090;
473 break;
474 case SND_SOC_DAIFMT_IB_NF:
475 iface |= 0x0080;
476 break;
477 case SND_SOC_DAIFMT_NB_IF:
478 iface |= 0x0010;
479 break;
480 default:
481 return -EINVAL;
482 }
483
484 /* set iface */
Cliff Cai93547e82011-03-27 17:22:57 -0400485 snd_soc_write(codec, SSM2602_IFACE, iface);
Cliff Caib7138212008-09-05 18:09:57 +0800486 return 0;
487}
488
489static int ssm2602_set_bias_level(struct snd_soc_codec *codec,
490 enum snd_soc_bias_level level)
491{
Lars-Peter Clausen02890532011-09-27 11:08:48 +0200492 struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
Cliff Caib7138212008-09-05 18:09:57 +0800493
494 switch (level) {
495 case SND_SOC_BIAS_ON:
Lars-Peter Clausen02890532011-09-27 11:08:48 +0200496 /* vref/mid on, osc and clkout on if enabled */
497 snd_soc_update_bits(codec, SSM2602_PWR,
498 PWR_POWER_OFF | PWR_CLK_OUT_PDN | PWR_OSC_PDN,
499 ssm2602->clk_out_pwr);
Cliff Caib7138212008-09-05 18:09:57 +0800500 break;
501 case SND_SOC_BIAS_PREPARE:
502 break;
503 case SND_SOC_BIAS_STANDBY:
504 /* everything off except vref/vmid, */
Lars-Peter Clausen02890532011-09-27 11:08:48 +0200505 snd_soc_update_bits(codec, SSM2602_PWR,
506 PWR_POWER_OFF | PWR_CLK_OUT_PDN | PWR_OSC_PDN,
507 PWR_CLK_OUT_PDN | PWR_OSC_PDN);
Cliff Caib7138212008-09-05 18:09:57 +0800508 break;
509 case SND_SOC_BIAS_OFF:
Lars-Peter Clausen02890532011-09-27 11:08:48 +0200510 /* everything off */
511 snd_soc_update_bits(codec, SSM2602_PWR,
512 PWR_POWER_OFF, PWR_POWER_OFF);
Cliff Caib7138212008-09-05 18:09:57 +0800513 break;
514
515 }
Liam Girdwoodce6120c2010-11-05 15:53:46 +0200516 codec->dapm.bias_level = level;
Cliff Caib7138212008-09-05 18:09:57 +0800517 return 0;
518}
519
Cliff Cai2552a712009-06-02 00:18:53 -0400520#define SSM2602_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_32000 |\
521 SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
522 SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
Cliff Caib7138212008-09-05 18:09:57 +0800523
Karl Beldan5de27b62008-11-20 15:39:31 +0100524#define SSM2602_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
525 SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
526
Lars-Peter Clausen85e76522011-11-23 11:40:40 +0100527static const struct snd_soc_dai_ops ssm2602_dai_ops = {
Eric Miao6335d052009-03-03 09:41:00 +0800528 .startup = ssm2602_startup,
Eric Miao6335d052009-03-03 09:41:00 +0800529 .hw_params = ssm2602_hw_params,
530 .shutdown = ssm2602_shutdown,
531 .digital_mute = ssm2602_mute,
532 .set_sysclk = ssm2602_set_dai_sysclk,
533 .set_fmt = ssm2602_set_dai_fmt,
534};
535
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000536static struct snd_soc_dai_driver ssm2602_dai = {
537 .name = "ssm2602-hifi",
Cliff Caib7138212008-09-05 18:09:57 +0800538 .playback = {
539 .stream_name = "Playback",
540 .channels_min = 2,
541 .channels_max = 2,
542 .rates = SSM2602_RATES,
Karl Beldan5de27b62008-11-20 15:39:31 +0100543 .formats = SSM2602_FORMATS,},
Cliff Caib7138212008-09-05 18:09:57 +0800544 .capture = {
545 .stream_name = "Capture",
546 .channels_min = 2,
547 .channels_max = 2,
548 .rates = SSM2602_RATES,
Karl Beldan5de27b62008-11-20 15:39:31 +0100549 .formats = SSM2602_FORMATS,},
Eric Miao6335d052009-03-03 09:41:00 +0800550 .ops = &ssm2602_dai_ops,
Cliff Caib7138212008-09-05 18:09:57 +0800551};
Cliff Caib7138212008-09-05 18:09:57 +0800552
Lars-Peter Clausen84b315e2011-12-02 10:18:28 +0100553static int ssm2602_suspend(struct snd_soc_codec *codec)
Cliff Caib7138212008-09-05 18:09:57 +0800554{
Cliff Caib7138212008-09-05 18:09:57 +0800555 ssm2602_set_bias_level(codec, SND_SOC_BIAS_OFF);
556 return 0;
557}
558
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000559static int ssm2602_resume(struct snd_soc_codec *codec)
Cliff Caib7138212008-09-05 18:09:57 +0800560{
Cliff Cai93547e82011-03-27 17:22:57 -0400561 snd_soc_cache_sync(codec);
Cliff Caib7138212008-09-05 18:09:57 +0800562
Cliff Caib7138212008-09-05 18:09:57 +0800563 ssm2602_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
Cliff Cai93547e82011-03-27 17:22:57 -0400564
Cliff Caib7138212008-09-05 18:09:57 +0800565 return 0;
566}
567
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000568static int ssm2602_probe(struct snd_soc_codec *codec)
Cliff Caib7138212008-09-05 18:09:57 +0800569{
Lars-Peter Clausenb1f7b2b2011-05-08 09:24:43 -0700570 struct snd_soc_dapm_context *dapm = &codec->dapm;
Axel Lin2ee9c182011-10-19 14:07:31 +0800571 int ret;
Lars-Peter Clausenb1f7b2b2011-05-08 09:24:43 -0700572
Axel Lin2ee9c182011-10-19 14:07:31 +0800573 snd_soc_update_bits(codec, SSM2602_LOUT1V,
574 LOUT1V_LRHP_BOTH, LOUT1V_LRHP_BOTH);
575 snd_soc_update_bits(codec, SSM2602_ROUT1V,
576 ROUT1V_RLHP_BOTH, ROUT1V_RLHP_BOTH);
Lars-Peter Clausenb1f7b2b2011-05-08 09:24:43 -0700577
Liam Girdwood022658b2012-02-03 17:43:09 +0000578 ret = snd_soc_add_codec_controls(codec, ssm2602_snd_controls,
Lars-Peter Clausenb1f7b2b2011-05-08 09:24:43 -0700579 ARRAY_SIZE(ssm2602_snd_controls));
580 if (ret)
581 return ret;
582
583 ret = snd_soc_dapm_new_controls(dapm, ssm2602_dapm_widgets,
584 ARRAY_SIZE(ssm2602_dapm_widgets));
585 if (ret)
586 return ret;
587
588 return snd_soc_dapm_add_routes(dapm, ssm2602_routes,
589 ARRAY_SIZE(ssm2602_routes));
590}
591
592static int ssm2604_probe(struct snd_soc_codec *codec)
593{
594 struct snd_soc_dapm_context *dapm = &codec->dapm;
595 int ret;
596
597 ret = snd_soc_dapm_new_controls(dapm, ssm2604_dapm_widgets,
598 ARRAY_SIZE(ssm2604_dapm_widgets));
599 if (ret)
600 return ret;
601
602 return snd_soc_dapm_add_routes(dapm, ssm2604_routes,
603 ARRAY_SIZE(ssm2604_routes));
604}
605
606static int ssm260x_probe(struct snd_soc_codec *codec)
607{
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000608 struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
Axel Lin2ee9c182011-10-19 14:07:31 +0800609 int ret;
Cliff Caib7138212008-09-05 18:09:57 +0800610
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000611 pr_info("ssm2602 Audio Codec %s", SSM2602_VERSION);
612
Cliff Cai93547e82011-03-27 17:22:57 -0400613 ret = snd_soc_codec_set_cache_io(codec, 7, 9, ssm2602->control_type);
614 if (ret < 0) {
615 dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
616 return ret;
617 }
Cliff Caib7138212008-09-05 18:09:57 +0800618
Cliff Cai93547e82011-03-27 17:22:57 -0400619 ret = ssm2602_reset(codec);
620 if (ret < 0) {
621 dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
622 return ret;
623 }
Cliff Caib7138212008-09-05 18:09:57 +0800624
Cliff Caib7138212008-09-05 18:09:57 +0800625 /* set the update bits */
Axel Lin2ee9c182011-10-19 14:07:31 +0800626 snd_soc_update_bits(codec, SSM2602_LINVOL,
627 LINVOL_LRIN_BOTH, LINVOL_LRIN_BOTH);
628 snd_soc_update_bits(codec, SSM2602_RINVOL,
629 RINVOL_RLIN_BOTH, RINVOL_RLIN_BOTH);
Cliff Caib7138212008-09-05 18:09:57 +0800630 /*select Line in as default input*/
Cliff Cai93547e82011-03-27 17:22:57 -0400631 snd_soc_write(codec, SSM2602_APANA, APANA_SELECT_DAC |
Cliff Caib7138212008-09-05 18:09:57 +0800632 APANA_ENABLE_MIC_BOOST);
Cliff Caib7138212008-09-05 18:09:57 +0800633
Lars-Peter Clausenb1f7b2b2011-05-08 09:24:43 -0700634 switch (ssm2602->type) {
635 case SSM2602:
636 ret = ssm2602_probe(codec);
637 break;
638 case SSM2604:
639 ret = ssm2604_probe(codec);
640 break;
641 }
Cliff Caib7138212008-09-05 18:09:57 +0800642
Lars-Peter Clausena9d19742011-09-27 11:08:47 +0200643 if (ret)
644 return ret;
645
646 ssm2602_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
647
648 return 0;
Cliff Caib7138212008-09-05 18:09:57 +0800649}
650
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000651/* remove everything here */
652static int ssm2602_remove(struct snd_soc_codec *codec)
653{
654 ssm2602_set_bias_level(codec, SND_SOC_BIAS_OFF);
655 return 0;
656}
657
658static struct snd_soc_codec_driver soc_codec_dev_ssm2602 = {
Lars-Peter Clausenb1f7b2b2011-05-08 09:24:43 -0700659 .probe = ssm260x_probe,
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000660 .remove = ssm2602_remove,
661 .suspend = ssm2602_suspend,
662 .resume = ssm2602_resume,
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000663 .set_bias_level = ssm2602_set_bias_level,
Lars-Peter Clausen8fc63fe2011-05-05 16:59:14 +0200664 .reg_cache_size = ARRAY_SIZE(ssm2602_reg),
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000665 .reg_word_size = sizeof(u16),
666 .reg_cache_default = ssm2602_reg,
Lars-Peter Clausenb1f7b2b2011-05-08 09:24:43 -0700667
668 .controls = ssm260x_snd_controls,
669 .num_controls = ARRAY_SIZE(ssm260x_snd_controls),
670 .dapm_widgets = ssm260x_dapm_widgets,
671 .num_dapm_widgets = ARRAY_SIZE(ssm260x_dapm_widgets),
672 .dapm_routes = ssm260x_routes,
673 .num_dapm_routes = ARRAY_SIZE(ssm260x_routes),
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000674};
Cliff Caib7138212008-09-05 18:09:57 +0800675
Mike Frysingerb39e2852011-04-07 02:05:11 -0400676#if defined(CONFIG_SPI_MASTER)
677static int __devinit ssm2602_spi_probe(struct spi_device *spi)
678{
679 struct ssm2602_priv *ssm2602;
680 int ret;
681
Axel Lin8eeffe92011-12-29 12:05:20 +0800682 ssm2602 = devm_kzalloc(&spi->dev, sizeof(struct ssm2602_priv),
683 GFP_KERNEL);
Mike Frysingerb39e2852011-04-07 02:05:11 -0400684 if (ssm2602 == NULL)
685 return -ENOMEM;
686
687 spi_set_drvdata(spi, ssm2602);
688 ssm2602->control_type = SND_SOC_SPI;
Lars-Peter Clausenb1f7b2b2011-05-08 09:24:43 -0700689 ssm2602->type = SSM2602;
Mike Frysingerb39e2852011-04-07 02:05:11 -0400690
691 ret = snd_soc_register_codec(&spi->dev,
692 &soc_codec_dev_ssm2602, &ssm2602_dai, 1);
Mike Frysingerb39e2852011-04-07 02:05:11 -0400693 return ret;
694}
695
696static int __devexit ssm2602_spi_remove(struct spi_device *spi)
697{
698 snd_soc_unregister_codec(&spi->dev);
Mike Frysingerb39e2852011-04-07 02:05:11 -0400699 return 0;
700}
701
702static struct spi_driver ssm2602_spi_driver = {
703 .driver = {
704 .name = "ssm2602",
705 .owner = THIS_MODULE,
706 },
707 .probe = ssm2602_spi_probe,
708 .remove = __devexit_p(ssm2602_spi_remove),
709};
710#endif
711
Cliff Caib7138212008-09-05 18:09:57 +0800712#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
713/*
714 * ssm2602 2 wire address is determined by GPIO5
715 * state during powerup.
716 * low = 0x1a
717 * high = 0x1b
718 */
Lars-Peter Clausen04b89452011-05-05 16:59:12 +0200719static int __devinit ssm2602_i2c_probe(struct i2c_client *i2c,
Cliff Caib7138212008-09-05 18:09:57 +0800720 const struct i2c_device_id *id)
721{
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000722 struct ssm2602_priv *ssm2602;
Cliff Caib7138212008-09-05 18:09:57 +0800723 int ret;
724
Axel Lin8eeffe92011-12-29 12:05:20 +0800725 ssm2602 = devm_kzalloc(&i2c->dev, sizeof(struct ssm2602_priv),
726 GFP_KERNEL);
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000727 if (ssm2602 == NULL)
728 return -ENOMEM;
Cliff Caib7138212008-09-05 18:09:57 +0800729
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000730 i2c_set_clientdata(i2c, ssm2602);
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000731 ssm2602->control_type = SND_SOC_I2C;
Lars-Peter Clausenb1f7b2b2011-05-08 09:24:43 -0700732 ssm2602->type = id->driver_data;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000733
734 ret = snd_soc_register_codec(&i2c->dev,
735 &soc_codec_dev_ssm2602, &ssm2602_dai, 1);
Cliff Caib7138212008-09-05 18:09:57 +0800736 return ret;
737}
738
Lars-Peter Clausen04b89452011-05-05 16:59:12 +0200739static int __devexit ssm2602_i2c_remove(struct i2c_client *client)
Cliff Caib7138212008-09-05 18:09:57 +0800740{
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000741 snd_soc_unregister_codec(&client->dev);
Cliff Caib7138212008-09-05 18:09:57 +0800742 return 0;
743}
744
745static const struct i2c_device_id ssm2602_i2c_id[] = {
Lars-Peter Clausenb1f7b2b2011-05-08 09:24:43 -0700746 { "ssm2602", SSM2602 },
Lars-Peter Clausen7dcf2762011-05-08 09:24:44 -0700747 { "ssm2603", SSM2602 },
Lars-Peter Clausenb1f7b2b2011-05-08 09:24:43 -0700748 { "ssm2604", SSM2604 },
Cliff Caib7138212008-09-05 18:09:57 +0800749 { }
750};
751MODULE_DEVICE_TABLE(i2c, ssm2602_i2c_id);
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000752
Cliff Caib7138212008-09-05 18:09:57 +0800753/* corgi i2c codec control layer */
754static struct i2c_driver ssm2602_i2c_driver = {
755 .driver = {
Mike Frysinger2a161012011-03-27 00:44:10 -0400756 .name = "ssm2602",
Cliff Caib7138212008-09-05 18:09:57 +0800757 .owner = THIS_MODULE,
758 },
759 .probe = ssm2602_i2c_probe,
Lars-Peter Clausen04b89452011-05-05 16:59:12 +0200760 .remove = __devexit_p(ssm2602_i2c_remove),
Cliff Caib7138212008-09-05 18:09:57 +0800761 .id_table = ssm2602_i2c_id,
762};
Cliff Caib7138212008-09-05 18:09:57 +0800763#endif
764
Cliff Caib7138212008-09-05 18:09:57 +0800765
Takashi Iwaic9b3a402008-12-10 07:47:22 +0100766static int __init ssm2602_modinit(void)
Mark Brown64089b82008-12-08 19:17:58 +0000767{
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000768 int ret = 0;
Mike Frysingerb39e2852011-04-07 02:05:11 -0400769
770#if defined(CONFIG_SPI_MASTER)
771 ret = spi_register_driver(&ssm2602_spi_driver);
772 if (ret)
773 return ret;
774#endif
775
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000776#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
777 ret = i2c_add_driver(&ssm2602_i2c_driver);
Mike Frysingerb39e2852011-04-07 02:05:11 -0400778 if (ret)
779 return ret;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000780#endif
Mike Frysingerb39e2852011-04-07 02:05:11 -0400781
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000782 return ret;
Mark Brown64089b82008-12-08 19:17:58 +0000783}
784module_init(ssm2602_modinit);
785
786static void __exit ssm2602_exit(void)
787{
Mike Frysingerb39e2852011-04-07 02:05:11 -0400788#if defined(CONFIG_SPI_MASTER)
789 spi_unregister_driver(&ssm2602_spi_driver);
790#endif
791
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000792#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
793 i2c_del_driver(&ssm2602_i2c_driver);
794#endif
Mark Brown64089b82008-12-08 19:17:58 +0000795}
796module_exit(ssm2602_exit);
797
Lars-Peter Clausen7dcf2762011-05-08 09:24:44 -0700798MODULE_DESCRIPTION("ASoC SSM2602/SSM2603/SSM2604 driver");
Cliff Caib7138212008-09-05 18:09:57 +0800799MODULE_AUTHOR("Cliff Cai");
800MODULE_LICENSE("GPL");