aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShengjiu Wang <b02247@freescale.com>2014-05-12 01:38:52 -0400
committerShengjiu Wang <b02247@freescale.com>2014-05-13 05:47:53 -0400
commiteabb472ee290efb05703303bb3b941e4e856bea6 (patch)
treed5bc6721538db1f841c1332b204d62033bd2c39e
parentf70599478297a350f4074d2bb1218fb8c42b172a (diff)
ENGR00313280-2 ASoC: fsl: Merge upsteamed cs42xx8 driver.
The upsteamed commit is 0c516b4ff85c0be4cee5b30ae59c9565c7f91a00 ASoC: cs42xx8: Add codec driver support for CS42448/CS42888 This patch adds support for the Cirrus Logic CS42448/CS42888 Audio CODEC that has six/four 24-bit AD and eight 24-bit DA converters. [ CS42448/CS42888 supports both I2C and SPI control ports. As initial patch, this patch only adds the support for I2C. ] Signed-off-by: Nicolin Chen <Guangyu.Chen@freescale.com> Acked-by: Brian Austin <brian.austin@cirrus.com> Acked-by: Paul Handrigan <Paul.Handrigan@cirrus.com> Signed-off-by: Mark Brown <broonie@linaro.org> Signed-off-by: Shengjiu Wang <b02247@freescale.com>
-rw-r--r--Documentation/devicetree/bindings/sound/cs42888.txt29
-rw-r--r--Documentation/devicetree/bindings/sound/cs42xx8.txt28
-rw-r--r--arch/arm/boot/dts/imx6qdl-sabreauto.dtsi2
-rw-r--r--arch/arm/boot/dts/imx6sx-19x19-arm2.dts2
-rw-r--r--arch/arm/configs/imx_v7_defconfig1
-rw-r--r--sound/soc/codecs/Kconfig10
-rw-r--r--sound/soc/codecs/Makefile6
-rw-r--r--sound/soc/codecs/cs42888.c913
-rw-r--r--sound/soc/codecs/cs42888.h123
-rw-r--r--sound/soc/codecs/cs42xx8-i2c.c64
-rw-r--r--sound/soc/codecs/cs42xx8.c602
-rw-r--r--sound/soc/codecs/cs42xx8.h238
-rw-r--r--sound/soc/fsl/imx-cs42888.c8
13 files changed, 951 insertions, 1075 deletions
diff --git a/Documentation/devicetree/bindings/sound/cs42888.txt b/Documentation/devicetree/bindings/sound/cs42888.txt
deleted file mode 100644
index e669ef3972ca..000000000000
--- a/Documentation/devicetree/bindings/sound/cs42888.txt
+++ /dev/null
@@ -1,29 +0,0 @@
1CS42888 audio CODEC
2
3This device supports I2C only.
4
5Required properties:
6
7 - compatible: "cirrus,cs42888"
8 - reg: the I2C address of the device.
9 - clocks: Phandle to the clock node.
10 - clock-names: Contains name for each entry in clocks.
11 "codec_osc" : the external oscillator.
12 "esai" : the hckt clock from esai.
13 - <name>-supply: Phandle to the regulator <name>.
14
15Note: cs42888 needs a regulators node and a clocks node.
16
17Example:
18In this case, the clock is external oscillator.
19
20codec: cs42888@48 {
21 compatible = "cirrus,cs42888";
22 reg = <0x048>;
23 clocks = <&codec_osc 0>;
24 clock-names = "codec_osc";
25 VA-supply = <&reg_audio>;
26 VD-supply = <&reg_audio>;
27 VLS-supply = <&reg_audio>;
28 VLC-supply = <&reg_audio>;
29};
diff --git a/Documentation/devicetree/bindings/sound/cs42xx8.txt b/Documentation/devicetree/bindings/sound/cs42xx8.txt
new file mode 100644
index 000000000000..f631fbca6284
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/cs42xx8.txt
@@ -0,0 +1,28 @@
1CS42448/CS42888 audio CODEC
2
3Required properties:
4
5 - compatible : must contain one of "cirrus,cs42448" and "cirrus,cs42888"
6
7 - reg : the I2C address of the device for I2C
8
9 - clocks : a list of phandles + clock-specifiers, one for each entry in
10 clock-names
11
12 - clock-names : must contain "mclk"
13
14 - VA-supply, VD-supply, VLS-supply, VLC-supply: power supplies for the device,
15 as covered in Documentation/devicetree/bindings/regulator/regulator.txt
16
17Example:
18
19codec: cs42888@48 {
20 compatible = "cirrus,cs42888";
21 reg = <0x48>;
22 clocks = <&codec_mclk 0>;
23 clock-names = "mclk";
24 VA-supply = <&reg_audio>;
25 VD-supply = <&reg_audio>;
26 VLS-supply = <&reg_audio>;
27 VLC-supply = <&reg_audio>;
28};
diff --git a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
index f1d882a7503f..30668ecf24d0 100644
--- a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
@@ -441,7 +441,7 @@
441 compatible = "cirrus,cs42888"; 441 compatible = "cirrus,cs42888";
442 reg = <0x048>; 442 reg = <0x048>;
443 clocks = <&codec_osc 0>; 443 clocks = <&codec_osc 0>;
444 clock-names = "codec_osc"; 444 clock-names = "mclk";
445 VA-supply = <&reg_audio>; 445 VA-supply = <&reg_audio>;
446 VD-supply = <&reg_audio>; 446 VD-supply = <&reg_audio>;
447 VLS-supply = <&reg_audio>; 447 VLS-supply = <&reg_audio>;
diff --git a/arch/arm/boot/dts/imx6sx-19x19-arm2.dts b/arch/arm/boot/dts/imx6sx-19x19-arm2.dts
index 901b469ee508..41a4ce274911 100644
--- a/arch/arm/boot/dts/imx6sx-19x19-arm2.dts
+++ b/arch/arm/boot/dts/imx6sx-19x19-arm2.dts
@@ -301,7 +301,7 @@
301 compatible = "cirrus,cs42888"; 301 compatible = "cirrus,cs42888";
302 reg = <0x048>; 302 reg = <0x048>;
303 clocks = <&clks IMX6SX_CLK_ESAI_EXTAL>; 303 clocks = <&clks IMX6SX_CLK_ESAI_EXTAL>;
304 clock-names = "esai_extal"; 304 clock-names = "mclk";
305 VA-supply = <&reg_3p3v>; 305 VA-supply = <&reg_3p3v>;
306 VD-supply = <&reg_3p3v>; 306 VD-supply = <&reg_3p3v>;
307 VLS-supply = <&reg_3p3v>; 307 VLS-supply = <&reg_3p3v>;
diff --git a/arch/arm/configs/imx_v7_defconfig b/arch/arm/configs/imx_v7_defconfig
index abc9957ae4a9..06064d6b59f1 100644
--- a/arch/arm/configs/imx_v7_defconfig
+++ b/arch/arm/configs/imx_v7_defconfig
@@ -245,6 +245,7 @@ CONFIG_SND_SOC_IMX_SPDIF=y
245CONFIG_SND_SOC_IMX_MC13783=y 245CONFIG_SND_SOC_IMX_MC13783=y
246CONFIG_SND_SOC_IMX_HDMI=y 246CONFIG_SND_SOC_IMX_HDMI=y
247CONFIG_SND_SOC_IMX_SI476X=y 247CONFIG_SND_SOC_IMX_SI476X=y
248CONFIG_SND_SOC_CS42XX8_I2C=y
248CONFIG_USB=y 249CONFIG_USB=y
249CONFIG_USB_EHCI_HCD=y 250CONFIG_USB_EHCI_HCD=y
250CONFIG_USB_STORAGE=y 251CONFIG_USB_STORAGE=y
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 7b7801e06b86..60e4fe8b6fe4 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -35,7 +35,7 @@ config SND_SOC_ALL_CODECS
35 select SND_SOC_CS42L73 if I2C 35 select SND_SOC_CS42L73 if I2C
36 select SND_SOC_CS4270 if I2C 36 select SND_SOC_CS4270 if I2C
37 select SND_SOC_CS4271 if SND_SOC_I2C_AND_SPI 37 select SND_SOC_CS4271 if SND_SOC_I2C_AND_SPI
38 select SND_SOC_CS42888 if I2C 38 select SND_SOC_CS42XX8_I2C if I2C
39 select SND_SOC_CX20442 if TTY 39 select SND_SOC_CX20442 if TTY
40 select SND_SOC_DA7210 if I2C 40 select SND_SOC_DA7210 if I2C
41 select SND_SOC_DA7213 if I2C 41 select SND_SOC_DA7213 if I2C
@@ -241,9 +241,15 @@ config SND_SOC_CS4270_VD33_ERRATA
241config SND_SOC_CS4271 241config SND_SOC_CS4271
242 tristate 242 tristate
243 243
244config SND_SOC_CS42888 244config SND_SOC_CS42XX8
245 tristate 245 tristate
246 246
247config SND_SOC_CS42XX8_I2C
248 tristate "Cirrus Logic CS42448/CS42888 CODEC (I2C)"
249 depends on I2C
250 select SND_SOC_CS42XX8
251 select REGMAP_I2C
252
247config SND_SOC_CX20442 253config SND_SOC_CX20442
248 tristate 254 tristate
249 depends on TTY 255 depends on TTY
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index ea9bd25f9a4a..57a05ca96798 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -22,7 +22,8 @@ snd-soc-cs42l52-objs := cs42l52.o
22snd-soc-cs42l73-objs := cs42l73.o 22snd-soc-cs42l73-objs := cs42l73.o
23snd-soc-cs4270-objs := cs4270.o 23snd-soc-cs4270-objs := cs4270.o
24snd-soc-cs4271-objs := cs4271.o 24snd-soc-cs4271-objs := cs4271.o
25snd-soc-cs42888-objs := cs42888.o 25snd-soc-cs42xx8-objs := cs42xx8.o
26snd-soc-cs42xx8-i2c-objs := cs42xx8-i2c.o
26snd-soc-cx20442-objs := cx20442.o 27snd-soc-cx20442-objs := cx20442.o
27snd-soc-da7210-objs := da7210.o 28snd-soc-da7210-objs := da7210.o
28snd-soc-da7213-objs := da7213.o 29snd-soc-da7213-objs := da7213.o
@@ -150,7 +151,8 @@ obj-$(CONFIG_SND_SOC_CS42L52) += snd-soc-cs42l52.o
150obj-$(CONFIG_SND_SOC_CS42L73) += snd-soc-cs42l73.o 151obj-$(CONFIG_SND_SOC_CS42L73) += snd-soc-cs42l73.o
151obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o 152obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o
152obj-$(CONFIG_SND_SOC_CS4271) += snd-soc-cs4271.o 153obj-$(CONFIG_SND_SOC_CS4271) += snd-soc-cs4271.o
153obj-$(CONFIG_SND_SOC_CS42888) += snd-soc-cs42888.o 154obj-$(CONFIG_SND_SOC_CS42XX8) += snd-soc-cs42xx8.o
155obj-$(CONFIG_SND_SOC_CS42XX8_I2C) += snd-soc-cs42xx8-i2c.o
154obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o 156obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o
155obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o 157obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o
156obj-$(CONFIG_SND_SOC_DA7213) += snd-soc-da7213.o 158obj-$(CONFIG_SND_SOC_DA7213) += snd-soc-da7213.o
diff --git a/sound/soc/codecs/cs42888.c b/sound/soc/codecs/cs42888.c
deleted file mode 100644
index 24b68a4de234..000000000000
--- a/sound/soc/codecs/cs42888.c
+++ /dev/null
@@ -1,913 +0,0 @@
1/*
2 * cs42888.c -- CS42888 ALSA SoC Audio Driver
3 * Copyright (C) 2010-2014 Freescale Semiconductor, Inc. All Rights Reserved.
4 */
5/*
6 * The code contained herein is licensed under the GNU General Public
7 * License. You may obtain a copy of the GNU General Public License
8 * Version 2 or later at the following locations:
9 *
10 * http://www.opensource.org/licenses/gpl-license.html
11 * http://www.gnu.org/copyleft/gpl.html
12 */
13
14#include <linux/module.h>
15#include <linux/moduleparam.h>
16#include <linux/kernel.h>
17#include <linux/init.h>
18#include <linux/clk.h>
19#include <linux/delay.h>
20#include <linux/pm.h>
21#include <linux/i2c.h>
22#include <linux/spi/spi.h>
23#include <linux/platform_device.h>
24#include <linux/regulator/consumer.h>
25
26#include <sound/core.h>
27#include <sound/pcm.h>
28#include <sound/pcm_params.h>
29#include <sound/soc.h>
30#include <sound/soc-dapm.h>
31#include <sound/tlv.h>
32#include <sound/initval.h>
33#include <asm/div64.h>
34#include "cs42888.h"
35
36#define CS42888_NUM_SUPPLIES 4
37static const char *cs42888_supply_names[CS42888_NUM_SUPPLIES] = {
38 "VA",
39 "VD",
40 "VLS",
41 "VLC",
42};
43
44#define CS42888_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
45 SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
46
47/* Private data for the CS42888 */
48struct cs42888_private {
49 struct clk *clk;
50 struct snd_soc_codec *codec;
51 u8 reg_cache[CS42888_NUMREGS + 1];
52 unsigned int mclk; /* Input frequency of the MCLK pin */
53 unsigned int slave_mode;
54 struct regulator_bulk_data supplies[CS42888_NUM_SUPPLIES];
55};
56
57/**
58 * cs42888_fill_cache - pre-fill the CS42888 register cache.
59 * @codec: the codec for this CS42888
60 *
61 * This function fills in the CS42888 register cache by reading the register
62 * values from the hardware.
63 *
64 * This CS42888 registers are cached to avoid excessive I2C I/O operations.
65 * After the initial read to pre-fill the cache, the CS42888 never updates
66 * the register values, so we won't have a cache coherency problem.
67 *
68 * We use the auto-increment feature of the CS42888 to read all registers in
69 * one shot.
70 */
71static int cs42888_fill_cache(struct snd_soc_codec *codec)
72{
73 u8 *cache = codec->reg_cache;
74 struct i2c_client *i2c_client = to_i2c_client(codec->dev);
75 s32 length;
76
77 length = i2c_smbus_read_i2c_block_data(i2c_client,
78 CS42888_FIRSTREG | CS42888_I2C_INCR, CS42888_NUMREGS, \
79 cache + 1);
80
81 if (length != CS42888_NUMREGS) {
82 dev_err(codec->dev, "i2c read failure, addr=0x%x\n",
83 i2c_client->addr);
84 return -EIO;
85 }
86 return 0;
87}
88
89#ifdef DEBUG
90static void dump_reg(struct snd_soc_codec *codec)
91{
92 int i, reg;
93 int ret;
94 u8 *cache = codec->reg_cache + 1;
95
96 dev_dbg(codec->dev, "dump begin\n");
97 dev_dbg(codec->dev, "reg value in cache\n");
98 for (i = 0; i < CS42888_NUMREGS; i++)
99 dev_dbg(codec->dev, "reg[%d] = 0x%x\n", i, cache[i]);
100
101 dev_dbg(codec->dev, "real reg value\n");
102 ret = cs42888_fill_cache(codec);
103 if (ret < 0) {
104 dev_err(codec->dev, "failed to fill register cache\n");
105 return ret;
106 }
107 for (i = 0; i < CS42888_NUMREGS; i++)
108 dev_dbg(codec->dev, "reg[%d] = 0x%x\n", i, cache[i]);
109
110 dev_dbg(codec->dev, "dump end\n");
111}
112#else
113static void dump_reg(struct snd_soc_codec *codec)
114{
115}
116#endif
117
118/* -127.5dB to 0dB with step of 0.5dB */
119static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1);
120/* -64dB to 24dB with step of 0.5dB */
121static const DECLARE_TLV_DB_SCALE(adc_tlv, -6400, 50, 1);
122
123static int cs42888_out_vu(struct snd_kcontrol *kcontrol,
124 struct snd_ctl_elem_value *ucontrol)
125{
126 return snd_soc_put_volsw_2r(kcontrol, ucontrol);
127}
128
129static int cs42888_info_volsw_s8(struct snd_kcontrol *kcontrol,
130 struct snd_ctl_elem_info *uinfo)
131{
132 struct soc_mixer_control *mc =
133 (struct soc_mixer_control *)kcontrol->private_value;
134
135 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
136 uinfo->count = 2;
137 uinfo->value.integer.min = 0;
138 uinfo->value.integer.max = mc->max - mc->min;
139 return 0;
140}
141
142static int cs42888_get_volsw_s8(struct snd_kcontrol *kcontrol,
143 struct snd_ctl_elem_value *ucontrol)
144{
145 struct soc_mixer_control *mc =
146 (struct soc_mixer_control *)kcontrol->private_value;
147 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
148 s8 val = snd_soc_read(codec, mc->reg);
149 ucontrol->value.integer.value[0] = val - mc->min;
150
151 val = snd_soc_read(codec, mc->rreg);
152 ucontrol->value.integer.value[1] = val - mc->min;
153 return 0;
154}
155
156int cs42888_put_volsw_s8(struct snd_kcontrol *kcontrol,
157 struct snd_ctl_elem_value *ucontrol)
158{
159 struct soc_mixer_control *mc =
160 (struct soc_mixer_control *)kcontrol->private_value;
161 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
162 unsigned short val;
163 int ret;
164
165 val = ucontrol->value.integer.value[0] + mc->min;
166 ret = snd_soc_write(codec, mc->reg, val);
167 if (ret < 0) {
168 dev_err(codec->dev, "i2c write failed\n");
169 return ret;
170 }
171
172 val = ucontrol->value.integer.value[1] + mc->min;
173 ret = snd_soc_write(codec, mc->rreg, val);
174 if (ret < 0) {
175 dev_err(codec->dev, "i2c write failed\n");
176 return ret;
177 }
178 return 0;
179}
180
181#define SOC_CS42888_DOUBLE_R_TLV(xname, reg_left, reg_right, xshift, xmax, \
182 xinvert, tlv_array) \
183{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
184 .name = (xname), \
185 .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
186 SNDRV_CTL_ELEM_ACCESS_READWRITE, \
187 .tlv.p = (tlv_array), \
188 .info = snd_soc_info_volsw, \
189 .get = snd_soc_get_volsw, \
190 .put = cs42888_out_vu, \
191 .private_value = (unsigned long)&(struct soc_mixer_control) \
192 {.reg = reg_left, \
193 .rreg = reg_right, \
194 .shift = xshift, \
195 .max = xmax, \
196 .invert = xinvert} \
197}
198
199#define SOC_CS42888_DOUBLE_R_S8_TLV(xname, reg_left, reg_right, xmin, xmax, \
200 tlv_array) \
201{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
202 .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
203 SNDRV_CTL_ELEM_ACCESS_READWRITE, \
204 .tlv.p = (tlv_array), \
205 .info = cs42888_info_volsw_s8, \
206 .get = cs42888_get_volsw_s8, \
207 .put = cs42888_put_volsw_s8, \
208 .private_value = (unsigned long)&(struct soc_mixer_control) \
209 {.reg = reg_left, \
210 .rreg = reg_right, \
211 .min = xmin, \
212 .max = xmax} \
213}
214
215static const char *cs42888_adcfilter[] = { "None", "High Pass" };
216static const char *cs42888_dacinvert[] = { "Disabled", "Enabled" };
217static const char *cs42888_adcinvert[] = { "Disabled", "Enabled" };
218static const char *cs42888_dacamute[] = { "Disabled", "AutoMute" };
219static const char *cs42888_dac_sngvol[] = { "Disabled", "Enabled" };
220static const char *cs42888_dac_szc[] = { "Immediate Change", "Zero Cross",
221 "Soft Ramp", "Soft Ramp on Zero Cross" };
222static const char *cs42888_mute_adc[] = { "UnMute", "Mute" };
223static const char *cs42888_adc_sngvol[] = { "Disabled", "Enabled" };
224static const char *cs42888_adc_szc[] = { "Immediate Change", "Zero Cross",
225 "Soft Ramp", "Soft Ramp on Zero Cross" };
226static const char *cs42888_dac_dem[] = { "No-De-Emphasis", "De-Emphasis" };
227static const char *cs42888_adc_single[] = { "Differential", "Single-Ended" };
228
229static const struct soc_enum cs42888_enum[] = {
230 SOC_ENUM_SINGLE(CS42888_ADCCTL, 7, 2, cs42888_adcfilter),
231 SOC_ENUM_DOUBLE(CS42888_DACINV, 0, 1, 2, cs42888_dacinvert),
232 SOC_ENUM_DOUBLE(CS42888_DACINV, 2, 3, 2, cs42888_dacinvert),
233 SOC_ENUM_DOUBLE(CS42888_DACINV, 4, 5, 2, cs42888_dacinvert),
234 SOC_ENUM_DOUBLE(CS42888_DACINV, 6, 7, 2, cs42888_dacinvert),
235 SOC_ENUM_DOUBLE(CS42888_ADCINV, 0, 1, 2, cs42888_adcinvert),
236 SOC_ENUM_DOUBLE(CS42888_ADCINV, 2, 3, 2, cs42888_adcinvert),
237 SOC_ENUM_SINGLE(CS42888_TRANS, 4, 2, cs42888_dacamute),
238 SOC_ENUM_SINGLE(CS42888_TRANS, 7, 2, cs42888_dac_sngvol),
239 SOC_ENUM_SINGLE(CS42888_TRANS, 5, 4, cs42888_dac_szc),
240 SOC_ENUM_SINGLE(CS42888_TRANS, 3, 2, cs42888_mute_adc),
241 SOC_ENUM_SINGLE(CS42888_TRANS, 2, 2, cs42888_adc_sngvol),
242 SOC_ENUM_SINGLE(CS42888_TRANS, 0, 4, cs42888_adc_szc),
243 SOC_ENUM_SINGLE(CS42888_ADCCTL, 5, 2, cs42888_dac_dem),
244 SOC_ENUM_SINGLE(CS42888_ADCCTL, 4, 2, cs42888_adc_single),
245 SOC_ENUM_SINGLE(CS42888_ADCCTL, 3, 2, cs42888_adc_single),
246};
247
248static const struct snd_kcontrol_new cs42888_snd_controls[] = {
249 SOC_CS42888_DOUBLE_R_TLV("DAC1 Playback Volume", CS42888_VOLAOUT1,
250 CS42888_VOLAOUT2, 0, 0xff, 1, dac_tlv),
251 SOC_CS42888_DOUBLE_R_TLV("DAC2 Playback Volume", CS42888_VOLAOUT3,
252 CS42888_VOLAOUT4, 0, 0xff, 1, dac_tlv),
253 SOC_CS42888_DOUBLE_R_TLV("DAC3 Playback Volume", CS42888_VOLAOUT5,
254 CS42888_VOLAOUT6, 0, 0xff, 1, dac_tlv),
255 SOC_CS42888_DOUBLE_R_TLV("DAC4 Playback Volume", CS42888_VOLAOUT7,
256 CS42888_VOLAOUT8, 0, 0xff, 1, dac_tlv),
257 SOC_CS42888_DOUBLE_R_S8_TLV("ADC1 Capture Volume", CS42888_VOLAIN1,
258 CS42888_VOLAIN2, -128, 48, adc_tlv),
259 SOC_CS42888_DOUBLE_R_S8_TLV("ADC2 Capture Volume", CS42888_VOLAIN3,
260 CS42888_VOLAIN4, -128, 48, adc_tlv),
261 SOC_ENUM("ADC High-Pass Filter Switch", cs42888_enum[0]),
262 SOC_ENUM("DAC1 Invert Switch", cs42888_enum[1]),
263 SOC_ENUM("DAC2 Invert Switch", cs42888_enum[2]),
264 SOC_ENUM("DAC3 Invert Switch", cs42888_enum[3]),
265 SOC_ENUM("DAC4 Invert Switch", cs42888_enum[4]),
266 SOC_ENUM("ADC1 Invert Switch", cs42888_enum[5]),
267 SOC_ENUM("ADC2 Invert Switch", cs42888_enum[6]),
268 SOC_ENUM("DAC Auto Mute Switch", cs42888_enum[7]),
269 SOC_ENUM("DAC Single Volume Control Switch", cs42888_enum[8]),
270 SOC_ENUM("DAC Soft Ramp and Zero Cross Control Switch", cs42888_enum[9]),
271 SOC_ENUM("Mute ADC Serial Port Switch", cs42888_enum[10]),
272 SOC_ENUM("ADC Single Volume Control Switch", cs42888_enum[11]),
273 SOC_ENUM("ADC Soft Ramp and Zero Cross Control Switch", cs42888_enum[12]),
274 SOC_ENUM("DAC Deemphasis Switch", cs42888_enum[13]),
275 SOC_ENUM("ADC1 Single Ended Mode Switch", cs42888_enum[14]),
276 SOC_ENUM("ADC2 Single Ended Mode Switch", cs42888_enum[15]),
277};
278
279
280static const struct snd_soc_dapm_widget cs42888_dapm_widgets[] = {
281 SND_SOC_DAPM_DAC("DAC1", "codec-Playback", CS42888_PWRCTL, 1, 1),
282 SND_SOC_DAPM_DAC("DAC2", "codec-Playback", CS42888_PWRCTL, 2, 1),
283 SND_SOC_DAPM_DAC("DAC3", "codec-Playback", CS42888_PWRCTL, 3, 1),
284 SND_SOC_DAPM_DAC("DAC4", "codec-Playback", CS42888_PWRCTL, 4, 1),
285
286 SND_SOC_DAPM_OUTPUT("AOUT1L"),
287 SND_SOC_DAPM_OUTPUT("AOUT1R"),
288 SND_SOC_DAPM_OUTPUT("AOUT2L"),
289 SND_SOC_DAPM_OUTPUT("AOUT2R"),
290 SND_SOC_DAPM_OUTPUT("AOUT3L"),
291 SND_SOC_DAPM_OUTPUT("AOUT3R"),
292 SND_SOC_DAPM_OUTPUT("AOUT4L"),
293 SND_SOC_DAPM_OUTPUT("AOUT4R"),
294
295 SND_SOC_DAPM_ADC("ADC1", "codec-Capture", CS42888_PWRCTL, 5, 1),
296 SND_SOC_DAPM_ADC("ADC2", "codec-Capture", CS42888_PWRCTL, 6, 1),
297
298 SND_SOC_DAPM_INPUT("AIN1L"),
299 SND_SOC_DAPM_INPUT("AIN1R"),
300 SND_SOC_DAPM_INPUT("AIN2L"),
301 SND_SOC_DAPM_INPUT("AIN2R"),
302
303 SND_SOC_DAPM_PGA_E("PWR", CS42888_PWRCTL, 0, 1, NULL, 0,
304 NULL, 0),
305};
306
307static const struct snd_soc_dapm_route audio_map[] = {
308 /* Playback */
309 { "PWR", NULL, "DAC1" },
310 { "PWR", NULL, "DAC1" },
311
312 { "PWR", NULL, "DAC2" },
313 { "PWR", NULL, "DAC2" },
314
315 { "PWR", NULL, "DAC3" },
316 { "PWR", NULL, "DAC3" },
317
318 { "PWR", NULL, "DAC4" },
319 { "PWR", NULL, "DAC4" },
320
321 { "AOUT1L", NULL, "PWR" },
322 { "AOUT1R", NULL, "PWR" },
323
324 { "AOUT2L", NULL, "PWR" },
325 { "AOUT2R", NULL, "PWR" },
326
327 { "AOUT3L", NULL, "PWR" },
328 { "AOUT3R", NULL, "PWR" },
329
330 { "AOUT4L", NULL, "PWR" },
331 { "AOUT4R", NULL, "PWR" },
332
333 /* Capture */
334 { "PWR", NULL, "AIN1L" },
335 { "PWR", NULL, "AIN1R" },
336
337 { "PWR", NULL, "AIN2L" },
338 { "PWR", NULL, "AIN2R" },
339
340 { "ADC1", NULL, "PWR" },
341 { "ADC1", NULL, "PWR" },
342
343 { "ADC2", NULL, "PWR" },
344 { "ADC2", NULL, "PWR" },
345};
346
347
348static int cs42888_add_widgets(struct snd_soc_codec *codec)
349{
350 snd_soc_dapm_new_controls(&codec->dapm, cs42888_dapm_widgets,
351 ARRAY_SIZE(cs42888_dapm_widgets));
352
353 snd_soc_dapm_add_routes(&codec->dapm, audio_map, ARRAY_SIZE(audio_map));
354
355 snd_soc_dapm_new_widgets(&codec->dapm);
356 return 0;
357}
358
359/**
360 * struct cs42888_mode_ratios - clock ratio tables
361 * @ratio: the ratio of MCLK to the sample rate
362 * @speed_mode: the Speed Mode bits to set in the Mode Control register for
363 * this ratio
364 * @mclk: the Ratio Select bits to set in the Mode Control register for this
365 * ratio
366 *
367 * The data for this chart is taken from Table 10 of the CS42888 reference
368 * manual.
369 *
370 * This table is used to determine how to program the Functional Mode register.
371 * It is also used by cs42888_set_dai_sysclk() to tell ALSA which sampling
372 * rates the CS42888 currently supports.
373 *
374 * @speed_mode is the corresponding bit pattern to be written to the
375 * MODE bits of the Mode Control Register
376 *
377 * @mclk is the corresponding bit pattern to be wirten to the MCLK bits of
378 * the Mode Control Register.
379 *
380 */
381struct cs42888_mode_ratios {
382 unsigned int ratio;
383 u8 speed_mode;
384 u8 mclk;
385};
386
387static struct cs42888_mode_ratios cs42888_mode_ratios[] = {
388 {64, CS42888_MODE_4X, CS42888_MODE_DIV1},
389 {96, CS42888_MODE_4X, CS42888_MODE_DIV2},
390 {128, CS42888_MODE_2X, CS42888_MODE_DIV1},
391 {192, CS42888_MODE_2X, CS42888_MODE_DIV2},
392 {256, CS42888_MODE_1X, CS42888_MODE_DIV1},
393 {384, CS42888_MODE_2X, CS42888_MODE_DIV4},
394 {512, CS42888_MODE_1X, CS42888_MODE_DIV3},
395 {768, CS42888_MODE_1X, CS42888_MODE_DIV4},
396 {1024, CS42888_MODE_1X, CS42888_MODE_DIV5}
397};
398
399/* The number of MCLK/LRCK ratios supported by the CS42888 */
400#define NUM_MCLK_RATIOS ARRAY_SIZE(cs42888_mode_ratios)
401
402/**
403 * cs42888_set_dai_sysclk - determine the CS42888 samples rates.
404 * @codec_dai: the codec DAI
405 * @clk_id: the clock ID (ignored)
406 * @freq: the MCLK input frequency
407 * @dir: the clock direction (ignored)
408 *
409 * This function is used to tell the codec driver what the input MCLK
410 * frequency is.
411 *
412 */
413static int cs42888_set_dai_sysclk(struct snd_soc_dai *codec_dai,
414 int clk_id, unsigned int freq, int dir)
415{
416 struct snd_soc_codec *codec = codec_dai->codec;
417 struct cs42888_private *cs42888 = snd_soc_codec_get_drvdata(codec);
418
419 cs42888->mclk = freq;
420 return 0;
421}
422
423/**
424 * cs42888_set_dai_fmt - configure the codec for the selected audio format
425 * @codec_dai: the codec DAI
426 * @format: a SND_SOC_DAIFMT_x value indicating the data format
427 *
428 * This function takes a bitmask of SND_SOC_DAIFMT_x bits and programs the
429 * codec accordingly.
430 *
431 * Currently, this function only supports SND_SOC_DAIFMT_I2S and
432 * SND_SOC_DAIFMT_LEFT_J. The CS42888 codec also supports right-justified
433 * data for playback only, but ASoC currently does not support different
434 * formats for playback vs. record.
435 */
436static int cs42888_set_dai_fmt(struct snd_soc_dai *codec_dai,
437 unsigned int format)
438{
439 struct snd_soc_codec *codec = codec_dai->codec;
440 struct cs42888_private *cs42888 = snd_soc_codec_get_drvdata(codec);
441 int ret = 0;
442 u8 val;
443
444 val = snd_soc_read(codec, CS42888_FORMAT);
445 val &= ~CS42888_FORMAT_DAC_DIF_MASK;
446 val &= ~CS42888_FORMAT_ADC_DIF_MASK;
447 /* set DAI format */
448 switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
449 case SND_SOC_DAIFMT_LEFT_J:
450 val |= DIF_LEFT_J << CS42888_FORMAT_DAC_DIF_OFFSET;
451 val |= DIF_LEFT_J << CS42888_FORMAT_ADC_DIF_OFFSET;
452 break;
453 case SND_SOC_DAIFMT_I2S:
454 val |= DIF_I2S << CS42888_FORMAT_DAC_DIF_OFFSET;
455 val |= DIF_I2S << CS42888_FORMAT_ADC_DIF_OFFSET;
456 break;
457 case SND_SOC_DAIFMT_RIGHT_J:
458 val |= DIF_RIGHT_J << CS42888_FORMAT_DAC_DIF_OFFSET;
459 val |= DIF_RIGHT_J << CS42888_FORMAT_ADC_DIF_OFFSET;
460 break;
461 default:
462 dev_err(codec->dev, "invalid dai format\n");
463 return -EINVAL;
464 }
465
466 ret = snd_soc_write(codec, CS42888_FORMAT, val);
467 if (ret < 0) {
468 dev_err(codec->dev, "i2c write failed\n");
469 return ret;
470 }
471
472 val = snd_soc_read(codec, CS42888_MODE);
473 /* set master/slave audio interface */
474 switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
475 case SND_SOC_DAIFMT_CBS_CFS:
476 cs42888->slave_mode = 1;
477 val &= ~CS42888_MODE_SPEED_MASK;
478 val |= CS42888_MODE_SLAVE;
479 break;
480 case SND_SOC_DAIFMT_CBM_CFM:
481 cs42888->slave_mode = 0;
482 break;
483 default:
484 /* all other modes are unsupported by the hardware */
485 return -EINVAL;
486 }
487
488 ret = snd_soc_write(codec, CS42888_MODE, val);
489 if (ret < 0) {
490 dev_err(codec->dev, "i2c write failed\n");
491 return ret;
492 }
493
494 dump_reg(codec);
495 return ret;
496}
497
498/**
499 * cs42888_hw_params - program the CS42888 with the given hardware parameters.
500 * @substream: the audio stream
501 * @params: the hardware parameters to set
502
503 * @dai: the SOC DAI (ignored)
504 *
505 * This function programs the hardware with the values provided.
506 * Specifically, the sample rate and the data format.
507 *
508 * The .ops functions are used to provide board-specific data, like input
509 * frequencies, to this driver. This function takes that information,
510 * combines it with the hardware parameters provided, and programs the
511 * hardware accordingly.
512 */
513static int cs42888_hw_params(struct snd_pcm_substream *substream,
514 struct snd_pcm_hw_params *params,
515 struct snd_soc_dai *dai)
516{
517 struct snd_soc_pcm_runtime *rtd = substream->private_data;
518 struct snd_soc_codec *codec = rtd->codec;
519 struct cs42888_private *cs42888 = snd_soc_codec_get_drvdata(codec);
520 int ret;
521 u32 i, rate, ratio, val;
522
523 rate = params_rate(params); /* Sampling rate, in Hz */
524 ratio = cs42888->mclk / rate; /* MCLK/LRCK ratio */
525 for (i = 0; i < NUM_MCLK_RATIOS; i++) {
526 if (cs42888_mode_ratios[i].ratio == ratio)
527 break;
528 }
529
530 if (i == NUM_MCLK_RATIOS) {
531 /* We did not find a matching ratio */
532 dev_err(codec->dev, "could not find matching ratio\n");
533 return -EINVAL;
534 }
535
536 if (!cs42888->slave_mode) {
537 val = snd_soc_read(codec, CS42888_MODE);
538 val &= ~CS42888_MODE_SPEED_MASK;
539 val |= cs42888_mode_ratios[i].speed_mode;
540 val &= ~CS42888_MODE_DIV_MASK;
541 val |= cs42888_mode_ratios[i].mclk;
542 } else {
543 val = snd_soc_read(codec, CS42888_MODE);
544 val &= ~CS42888_MODE_SPEED_MASK;
545 val |= CS42888_MODE_SLAVE;
546 val &= ~CS42888_MODE_DIV_MASK;
547 val |= cs42888_mode_ratios[i].mclk;
548 }
549 ret = snd_soc_write(codec, CS42888_MODE, val);
550 if (ret < 0) {
551 dev_err(codec->dev, "i2c write failed\n");
552 return ret;
553 }
554
555 /* Unmute all the channels */
556 val = snd_soc_read(codec, CS42888_MUTE);
557 val &= ~CS42888_MUTE_ALL;
558 ret = snd_soc_write(codec, CS42888_MUTE, val);
559 if (ret < 0) {
560 dev_err(codec->dev, "i2c write failed\n");
561 return ret;
562 }
563
564 ret = cs42888_fill_cache(codec);
565 if (ret < 0) {
566 dev_err(codec->dev, "failed to fill register cache\n");
567 return ret;
568 }
569
570 dump_reg(codec);
571 return ret;
572}
573
574/**
575 * cs42888_shutdown - cs42888 enters into low power mode again.
576 * @substream: the audio stream
577 * @dai: the SOC DAI (ignored)
578 *
579 * The .ops functions are used to provide board-specific data, like input
580 * frequencies, to this driver. This function takes that information,
581 * combines it with the hardware parameters provided, and programs the
582 * hardware accordingly.
583 */
584static void cs42888_shutdown(struct snd_pcm_substream *substream,
585 struct snd_soc_dai *dai)
586{
587 struct snd_soc_pcm_runtime *rtd = substream->private_data;
588 struct snd_soc_codec *codec = rtd->codec;
589 int ret;
590 u8 val;
591
592 /* Mute all the channels */
593 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
594 val = snd_soc_read(codec, CS42888_MUTE);
595 val |= CS42888_MUTE_ALL;
596 ret = snd_soc_write(codec, CS42888_MUTE, val);
597 if (ret < 0)
598 dev_err(codec->dev, "i2c write failed\n");
599 }
600}
601
602static int cs42888_prepare(struct snd_pcm_substream *substream,
603 struct snd_soc_dai *dai)
604{
605 struct snd_soc_pcm_runtime *rtd = substream->private_data;
606 struct snd_soc_card *card = rtd->card;
607 struct snd_soc_dai *tmp_codec_dai;
608 struct snd_soc_pcm_runtime *tmp_rtd;
609 u32 i;
610
611 for (i = 0; i < card->num_rtd; i++) {
612 tmp_codec_dai = card->rtd[i].codec_dai;
613 tmp_rtd = (struct snd_soc_pcm_runtime *)(card->rtd + i);
614 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
615 cancel_delayed_work(&tmp_rtd->delayed_work);
616 }
617 return 0;
618}
619
620static struct snd_soc_dai_ops cs42888_dai_ops = {
621 .set_fmt = cs42888_set_dai_fmt,
622 .set_sysclk = cs42888_set_dai_sysclk,
623 .hw_params = cs42888_hw_params,
624 .shutdown = cs42888_shutdown,
625 .prepare = cs42888_prepare,
626};
627
628
629static struct snd_soc_dai_driver cs42888_dai = {
630 .name = "CS42888",
631 .playback = {
632 .stream_name = "codec-Playback",
633 .channels_min = 2,
634 .channels_max = 8,
635 .rates = SNDRV_PCM_RATE_8000_192000,
636 .formats = CS42888_FORMATS,
637 },
638 .capture = {
639 .stream_name = "codec-Capture",
640 .channels_min = 2,
641 .channels_max = 4,
642 .rates = SNDRV_PCM_RATE_8000_192000,
643 .formats = CS42888_FORMATS,
644 },
645 .ops = &cs42888_dai_ops,
646};
647
648/**
649 * cs42888_probe - ASoC probe function
650 * @pdev: platform device
651 *
652 * This function is called when ASoC has all the pieces it needs to
653 * instantiate a sound driver.
654 */
655static int cs42888_probe(struct snd_soc_codec *codec)
656{
657 struct cs42888_private *cs42888 = snd_soc_codec_get_drvdata(codec);
658 int ret, i, val;
659
660 cs42888->codec = codec;
661 /* setup i2c data ops */
662 ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
663 if (ret < 0) {
664 dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
665 return ret;
666 }
667
668 for (i = 0; i < ARRAY_SIZE(cs42888->supplies); i++)
669 cs42888->supplies[i].supply = cs42888_supply_names[i];
670
671 ret = devm_regulator_bulk_get(codec->dev,
672 ARRAY_SIZE(cs42888->supplies), cs42888->supplies);
673 if (ret) {
674 dev_err(codec->dev, "Failed to request supplies: %d\n",
675 ret);
676 return ret;
677 }
678
679 ret = regulator_bulk_enable(ARRAY_SIZE(cs42888->supplies),
680 cs42888->supplies);
681 if (ret) {
682 dev_err(codec->dev, "Failed to enable supplies: %d\n",
683 ret);
684 goto err;
685 }
686 msleep(1);
687
688 /* The I2C interface is set up, so pre-fill our register cache */
689 ret = cs42888_fill_cache(codec);
690 if (ret < 0) {
691 dev_err(codec->dev, "failed to fill register cache\n");
692 goto err;
693 }
694
695 /* Enter low power state */
696 val = snd_soc_read(codec, CS42888_PWRCTL);
697 val |= CS42888_PWRCTL_PDN_MASK;
698 ret = snd_soc_write(codec, CS42888_PWRCTL, val);
699 if (ret < 0) {
700 dev_err(codec->dev, "i2c write failed\n");
701 goto err;
702 }
703
704 /* Disable auto-mute */
705 val = snd_soc_read(codec, CS42888_TRANS);
706 val &= ~CS42888_TRANS_AMUTE_MASK;
707 val &= ~CS42888_TRANS_DAC_SZC_MASK;
708 val |= CS42888_TRANS_DAC_SZC_SR;
709 ret = snd_soc_write(codec, CS42888_TRANS, val);
710 if (ret < 0) {
711 dev_err(codec->dev, "i2c write failed\n");
712 goto err;
713 }
714 /* Add the non-DAPM controls */
715 snd_soc_add_codec_controls(codec, cs42888_snd_controls,
716 ARRAY_SIZE(cs42888_snd_controls));
717
718 /* Add DAPM controls */
719 cs42888_add_widgets(codec);
720 return 0;
721err:
722 regulator_bulk_disable(ARRAY_SIZE(cs42888->supplies),
723 cs42888->supplies);
724 return ret;
725}
726
727/**
728 * cs42888_remove - ASoC remove function
729 * @pdev: platform device
730 *
731 * This function is the counterpart to cs42888_probe().
732 */
733static int cs42888_remove(struct snd_soc_codec *codec)
734{
735 struct cs42888_private *cs42888 = snd_soc_codec_get_drvdata(codec);
736
737 regulator_bulk_disable(ARRAY_SIZE(cs42888->supplies),
738 cs42888->supplies);
739
740 return 0;
741};
742
743/*
744 * ASoC codec device structure
745 *
746 * Assign this variable to the codec_dev field of the machine driver's
747 * snd_soc_device structure.
748 */
749static struct snd_soc_codec_driver cs42888_driver = {
750 .probe = cs42888_probe,
751 .remove = cs42888_remove,
752 .reg_cache_size = CS42888_NUMREGS + 1,
753 .reg_word_size = sizeof(u8),
754 .reg_cache_step = 1,
755};
756
757/**
758 * cs42888_i2c_probe - initialize the I2C interface of the CS42888
759 * @i2c_client: the I2C client object
760 * @id: the I2C device ID (ignored)
761 *
762 * This function is called whenever the I2C subsystem finds a device that
763 * matches the device ID given via a prior call to i2c_add_driver().
764 */
765static int cs42888_i2c_probe(struct i2c_client *i2c_client,
766 const struct i2c_device_id *id)
767{
768 struct cs42888_private *cs42888;
769 int ret, val;
770
771 /* Verify that we have a CS42888 */
772 val = i2c_smbus_read_byte_data(i2c_client, CS42888_CHIPID);
773 if (val < 0) {
774 dev_err(&i2c_client->dev, "Device with ID register %x is not a CS42888", val);
775 return -ENODEV;
776 }
777 /* The top four bits of the chip ID should be 0000. */
778 if ((val & CS42888_CHIPID_ID_MASK) != 0x00) {
779 dev_err(&i2c_client->dev, "device is not a CS42888\n");
780 return -ENODEV;
781 }
782
783 dev_info(&i2c_client->dev, "found device at i2c address %X\n",
784 i2c_client->addr);
785 dev_info(&i2c_client->dev, "hardware revision %X\n", val & 0xF);
786
787 /* Allocate enough space for the snd_soc_codec structure
788 and our private data together. */
789 cs42888 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs42888_private), GFP_KERNEL);
790 if (!cs42888) {
791 dev_err(&i2c_client->dev, "could not allocate codec\n");
792 return -ENOMEM;
793 }
794
795 i2c_set_clientdata(i2c_client, cs42888);
796
797 cs42888->clk = devm_clk_get(&i2c_client->dev, NULL);
798 if (IS_ERR(cs42888->clk)) {
799 ret = PTR_ERR(cs42888->clk);
800 dev_err(&i2c_client->dev, "Cannot get the clock: %d\n", ret);
801 return ret;
802 }
803
804 cs42888->mclk = clk_get_rate(cs42888->clk);
805
806 ret = snd_soc_register_codec(&i2c_client->dev,
807 &cs42888_driver, &cs42888_dai, 1);
808 if (ret) {
809 dev_err(&i2c_client->dev, "Failed to register codec:%d\n", ret);
810 return ret;
811 }
812 return 0;
813}
814
815/**
816 * cs42888_i2c_remove - remove an I2C device
817 * @i2c_client: the I2C client object
818 *
819 * This function is the counterpart to cs42888_i2c_probe().
820 */
821static int cs42888_i2c_remove(struct i2c_client *i2c_client)
822{
823 snd_soc_unregister_codec(&i2c_client->dev);
824 return 0;
825}
826
827/*
828 * cs42888_i2c_id - I2C device IDs supported by this driver
829 */
830static struct i2c_device_id cs42888_i2c_id[] = {
831 {"cs42888", 0},
832 {}
833};
834MODULE_DEVICE_TABLE(i2c, cs42888_i2c_id);
835
836#ifdef CONFIG_PM
837/* This suspend/resume implementation can handle both - a simple standby
838 * where the codec remains powered, and a full suspend, where the voltage
839 * domain the codec is connected to is teared down and/or any other hardware
840 * reset condition is asserted.
841 *
842 * The codec's own power saving features are enabled in the suspend callback,
843 * and all registers are written back to the hardware when resuming.
844 */
845
846static int cs42888_i2c_suspend(struct i2c_client *client, pm_message_t mesg)
847{
848 struct cs42888_private *cs42888 = i2c_get_clientdata(client);
849 struct snd_soc_codec *codec = cs42888->codec;
850 int reg = snd_soc_read(codec, CS42888_PWRCTL) | CS42888_PWRCTL_PDN_MASK;
851 return snd_soc_write(codec, CS42888_PWRCTL, reg);
852}
853
854static int cs42888_i2c_resume(struct i2c_client *client)
855{
856 struct cs42888_private *cs42888 = i2c_get_clientdata(client);
857 struct snd_soc_codec *codec = cs42888->codec;
858 int reg;
859
860 /* In case the device was put to hard reset during sleep, we need to
861 * wait 500ns here before any I2C communication. */
862 ndelay(500);
863
864 /* first restore the entire register cache ... */
865 for (reg = CS42888_FIRSTREG; reg <= CS42888_LASTREG; reg++) {
866 u8 val = snd_soc_read(codec, reg);
867
868 if (i2c_smbus_write_byte_data(client, reg, val)) {
869 dev_err(codec->dev, "i2c write failed\n");
870 return -EIO;
871 }
872 }
873
874 /* ... then disable the power-down bits */
875 reg = snd_soc_read(codec, CS42888_PWRCTL);
876 reg &= ~CS42888_PWRCTL_PDN_MASK;
877 return snd_soc_write(codec, CS42888_PWRCTL, reg);
878}
879#else
880#define cs42888_i2c_suspend NULL
881#define cs42888_i2c_resume NULL
882#endif /* CONFIG_PM */
883
884/*
885 * cs42888_i2c_driver - I2C device identification
886 *
887 * This structure tells the I2C subsystem how to identify and support a
888 * given I2C device type.
889 */
890
891static const struct of_device_id cs42888_dt_ids[] = {
892 { .compatible = "cirrus,cs42888", },
893 { /* sentinel */ }
894};
895
896static struct i2c_driver cs42888_i2c_driver = {
897 .driver = {
898 .name = "cs42888",
899 .owner = THIS_MODULE,
900 .of_match_table = cs42888_dt_ids,
901 },
902 .probe = cs42888_i2c_probe,
903 .remove = cs42888_i2c_remove,
904 .suspend = cs42888_i2c_suspend,
905 .resume = cs42888_i2c_resume,
906 .id_table = cs42888_i2c_id,
907};
908
909module_i2c_driver(cs42888_i2c_driver);
910
911MODULE_AUTHOR("Freescale Semiconductor, Inc.");
912MODULE_DESCRIPTION("Cirrus Logic CS42888 ALSA SoC Codec Driver");
913MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs42888.h b/sound/soc/codecs/cs42888.h
deleted file mode 100644
index d06d78196b9a..000000000000
--- a/sound/soc/codecs/cs42888.h
+++ /dev/null
@@ -1,123 +0,0 @@
1/*
2 * Copyright (C) 2010-2013 Freescale Semiconductor, Inc. All Rights Reserved.
3 */
4
5/*
6 * The code contained herein is licensed under the GNU General Public
7 * License. You may obtain a copy of the GNU General Public License
8 * Version 2 or later at the following locations:
9 *
10 * http://www.opensource.org/licenses/gpl-license.html
11 * http://www.gnu.org/copyleft/gpl.html
12 */
13
14#ifndef _CS42888_H
15#define _CS42888_H
16
17/* CS42888 registers addresses */
18#define CS42888_CHIPID 0x01 /* Chip ID */
19#define CS42888_PWRCTL 0x02 /* Power Control */
20#define CS42888_MODE 0x03 /* Functional Mode */
21#define CS42888_FORMAT 0x04 /* Interface Formats */
22#define CS42888_ADCCTL 0x05 /* ADC Control */
23#define CS42888_TRANS 0x06 /* Transition Control */
24#define CS42888_MUTE 0x07 /* Mute Control */
25#define CS42888_VOLAOUT1 0x08 /* Volume Control AOUT1*/
26#define CS42888_VOLAOUT2 0x09 /* Volume Control AOUT2*/
27#define CS42888_VOLAOUT3 0x0A /* Volume Control AOUT3*/
28#define CS42888_VOLAOUT4 0x0B /* Volume Control AOUT4*/
29#define CS42888_VOLAOUT5 0x0C /* Volume Control AOUT5*/
30#define CS42888_VOLAOUT6 0x0D /* Volume Control AOUT6*/
31#define CS42888_VOLAOUT7 0x0E /* Volume Control AOUT7*/
32#define CS42888_VOLAOUT8 0x0F /* Volume Control AOUT8*/
33#define CS42888_DACINV 0x10 /* DAC Channel Invert */
34#define CS42888_VOLAIN1 0x11 /* Volume Control AIN1 */
35#define CS42888_VOLAIN2 0x12 /* Volume Control AIN2 */
36#define CS42888_VOLAIN3 0x13 /* Volume Control AIN3 */
37#define CS42888_VOLAIN4 0x14 /* Volume Control AIN4 */
38#define CS42888_ADCINV 0x17 /* ADC Channel Invert */
39#define CS42888_STATUSCTL 0x18 /* Status Control */
40#define CS42888_STATUS 0x19 /* Status */
41#define CS42888_STATUSMASK 0x1A /* Status Mask */
42
43#define CS42888_FIRSTREG 0x01
44#define CS42888_LASTREG 0x1A
45#define CS42888_NUMREGS (CS42888_LASTREG - CS42888_FIRSTREG + 1)
46#define CS42888_I2C_INCR 0x80
47
48/* Bit masks for the CS42888 registers */
49#define CS42888_CHIPID_ID_MASK 0xF0
50#define CS42888_CHIPID_REV 0x0F
51#define CS42888_PWRCTL_PDN_ADC2_OFFSET 6
52#define CS42888_PWRCTL_PDN_ADC1_OFFSET 5
53#define CS42888_PWRCTL_PDN_DAC4_OFFSET 4
54#define CS42888_PWRCTL_PDN_DAC3_OFFSET 3
55#define CS42888_PWRCTL_PDN_DAC2_OFFSET 2
56#define CS42888_PWRCTL_PDN_DAC1_OFFSET 1
57#define CS42888_PWRCTL_PDN_OFFSET 0
58#define CS42888_PWRCTL_PDN_ADC2_MASK (1 << CS42888_PWRCTL_PDN_ADC2_OFFSET)
59#define CS42888_PWRCTL_PDN_ADC1_MASK (1 << CS42888_PWRCTL_PDN_ADC1_OFFSET)
60#define CS42888_PWRCTL_PDN_DAC4_MASK (1 << CS42888_PWRCTL_PDN_DAC4_OFFSET)
61#define CS42888_PWRCTL_PDN_DAC3_MASK (1 << CS42888_PWRCTL_PDN_DAC3_OFFSET)
62#define CS42888_PWRCTL_PDN_DAC2_MASK (1 << CS42888_PWRCTL_PDN_DAC2_OFFSET)
63#define CS42888_PWRCTL_PDN_DAC1_MASK (1 << CS42888_PWRCTL_PDN_DAC1_OFFSET)
64#define CS42888_PWRCTL_PDN_MASK (1 << CS42888_PWRCTL_PDN_OFFSET)
65
66#define CS42888_MODE_SPEED_MASK 0xF0
67#define CS42888_MODE_1X 0x00
68#define CS42888_MODE_2X 0x50
69#define CS42888_MODE_4X 0xA0
70#define CS42888_MODE_SLAVE 0xF0
71#define CS42888_MODE_DIV_MASK 0x0E
72#define CS42888_MODE_DIV1 0x00
73#define CS42888_MODE_DIV2 0x02
74#define CS42888_MODE_DIV3 0x04
75#define CS42888_MODE_DIV4 0x06
76#define CS42888_MODE_DIV5 0x08
77
78#define CS42888_FORMAT_FREEZE_OFFSET 7
79#define CS42888_FORMAT_AUX_DIF_OFFSET 6
80#define CS42888_FORMAT_DAC_DIF_OFFSET 3
81#define CS42888_FORMAT_ADC_DIF_OFFSET 0
82#define CS42888_FORMAT_FREEZE_MASK (1 << CS42888_FORMAT_FREEZE_OFFSET)
83#define CS42888_FORMAT_AUX_DIF_MASK (1 << CS42888_FORMAT_AUX_DIF_OFFSET)
84#define CS42888_FORMAT_DAC_DIF_MASK (7 << CS42888_FORMAT_DAC_DIF_OFFSET)
85#define CS42888_FORMAT_ADC_DIF_MASK (7 << CS42888_FORMAT_ADC_DIF_OFFSET)
86
87#define CS42888_TRANS_DAC_SNGVOL_OFFSET 7
88#define CS42888_TRANS_DAC_SZC_OFFSET 5
89#define CS42888_TRANS_AMUTE_OFFSET 4
90#define CS42888_TRANS_MUTE_ADC_SP_OFFSET 3
91#define CS42888_TRANS_ADC_SNGVOL_OFFSET 2
92#define CS42888_TRANS_ADC_SZC_OFFSET 0
93#define CS42888_TRANS_DAC_SNGVOL_MASK (1 << CS42888_TRANS_DAC_SNGVOL_OFFSET)
94#define CS42888_TRANS_DAC_SZC_MASK (3 << CS42888_TRANS_DAC_SZC_OFFSET)
95#define CS42888_TRANS_AMUTE_MASK (1 << CS42888_TRANS_AMUTE_OFFSET)
96#define CS42888_TRANS_MUTE_ADC_SP_MASK (1 << CS42888_TRANS_MUTE_ADC_SP_OFFSET)
97#define CS42888_TRANS_ADC_SNGVOL_MASK (1 << CS42888_TRANS_ADC_SNGVOL_OFFSET)
98#define CS42888_TRANS_ADC_SZC_MASK (3 << CS42888_TRANS_ADC_SZC_OFFSET)
99#define CS42888_TRANS_DAC_SZC_IC (0 << CS42888_TRANS_DAC_SZC_OFFSET)
100#define CS42888_TRANS_DAC_SZC_ZC (1 << CS42888_TRANS_DAC_SZC_OFFSET)
101#define CS42888_TRANS_DAC_SZC_SR (2 << CS42888_TRANS_DAC_SZC_OFFSET)
102#define CS42888_TRANS_DAC_SZC_SRZC (3 << CS42888_TRANS_DAC_SZC_OFFSET)
103
104#define CS42888_MUTE_AOUT8 (0x1 << 7)
105#define CS42888_MUTE_AOUT7 (0x1 << 6)
106#define CS42888_MUTE_AOUT6 (0x1 << 5)
107#define CS42888_MUTE_AOUT5 (0x1 << 4)
108#define CS42888_MUTE_AOUT4 (0x1 << 3)
109#define CS42888_MUTE_AOUT3 (0x1 << 2)
110#define CS42888_MUTE_AOUT2 (0x1 << 1)
111#define CS42888_MUTE_AOUT1 (0x1 << 0)
112#define CS42888_MUTE_ALL (CS42888_MUTE_AOUT1 | CS42888_MUTE_AOUT2 | \
113 CS42888_MUTE_AOUT3 | CS42888_MUTE_AOUT4 | \
114 CS42888_MUTE_AOUT5 | CS42888_MUTE_AOUT6 | \
115 CS42888_MUTE_AOUT7 | CS42888_MUTE_AOUT8)
116
117#define DIF_LEFT_J 0
118#define DIF_I2S 1
119#define DIF_RIGHT_J 2
120#define DIF_TDM 6
121
122
123#endif
diff --git a/sound/soc/codecs/cs42xx8-i2c.c b/sound/soc/codecs/cs42xx8-i2c.c
new file mode 100644
index 000000000000..657dce27eade
--- /dev/null
+++ b/sound/soc/codecs/cs42xx8-i2c.c
@@ -0,0 +1,64 @@
1/*
2 * Cirrus Logic CS42448/CS42888 Audio CODEC DAI I2C driver
3 *
4 * Copyright (C) 2014 Freescale Semiconductor, Inc.
5 *
6 * Author: Nicolin Chen <Guangyu.Chen@freescale.com>
7 *
8 * This file is licensed under the terms of the GNU General Public License
9 * version 2. This program is licensed "as is" without any warranty of any
10 * kind, whether express or implied.
11 */
12
13#include <linux/i2c.h>
14#include <linux/module.h>
15#include <linux/pm_runtime.h>
16#include <sound/soc.h>
17
18#include "cs42xx8.h"
19
20static int cs42xx8_i2c_probe(struct i2c_client *i2c,
21 const struct i2c_device_id *id)
22{
23 u32 ret = cs42xx8_probe(&i2c->dev,
24 devm_regmap_init_i2c(i2c, &cs42xx8_regmap_config));
25 if (ret)
26 return ret;
27
28 pm_runtime_enable(&i2c->dev);
29 pm_request_idle(&i2c->dev);
30
31 return 0;
32}
33
34static int cs42xx8_i2c_remove(struct i2c_client *i2c)
35{
36 snd_soc_unregister_codec(&i2c->dev);
37 pm_runtime_disable(&i2c->dev);
38
39 return 0;
40}
41
42static struct i2c_device_id cs42xx8_i2c_id[] = {
43 {"cs42448", (kernel_ulong_t)&cs42448_data},
44 {"cs42888", (kernel_ulong_t)&cs42888_data},
45 {}
46};
47MODULE_DEVICE_TABLE(i2c, cs42xx8_i2c_id);
48
49static struct i2c_driver cs42xx8_i2c_driver = {
50 .driver = {
51 .name = "cs42xx8",
52 .owner = THIS_MODULE,
53 .pm = &cs42xx8_pm,
54 },
55 .probe = cs42xx8_i2c_probe,
56 .remove = cs42xx8_i2c_remove,
57 .id_table = cs42xx8_i2c_id,
58};
59
60module_i2c_driver(cs42xx8_i2c_driver);
61
62MODULE_DESCRIPTION("Cirrus Logic CS42448/CS42888 ALSA SoC Codec I2C Driver");
63MODULE_AUTHOR("Freescale Semiconductor, Inc.");
64MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs42xx8.c b/sound/soc/codecs/cs42xx8.c
new file mode 100644
index 000000000000..082299a4e2fa
--- /dev/null
+++ b/sound/soc/codecs/cs42xx8.c
@@ -0,0 +1,602 @@
1/*
2 * Cirrus Logic CS42448/CS42888 Audio CODEC Digital Audio Interface (DAI) driver
3 *
4 * Copyright (C) 2014 Freescale Semiconductor, Inc.
5 *
6 * Author: Nicolin Chen <Guangyu.Chen@freescale.com>
7 *
8 * This file is licensed under the terms of the GNU General Public License
9 * version 2. This program is licensed "as is" without any warranty of any
10 * kind, whether express or implied.
11 */
12
13#include <linux/clk.h>
14#include <linux/delay.h>
15#include <linux/module.h>
16#include <linux/of_device.h>
17#include <linux/pm_runtime.h>
18#include <linux/regulator/consumer.h>
19#include <sound/pcm_params.h>
20#include <sound/soc.h>
21#include <sound/tlv.h>
22
23#include "cs42xx8.h"
24
25#define CS42XX8_NUM_SUPPLIES 4
26static const char *const cs42xx8_supply_names[CS42XX8_NUM_SUPPLIES] = {
27 "VA",
28 "VD",
29 "VLS",
30 "VLC",
31};
32
33#define CS42XX8_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
34 SNDRV_PCM_FMTBIT_S20_3LE | \
35 SNDRV_PCM_FMTBIT_S24_LE | \
36 SNDRV_PCM_FMTBIT_S32_LE)
37
38/* codec private data */
39struct cs42xx8_priv {
40 struct regulator_bulk_data supplies[CS42XX8_NUM_SUPPLIES];
41 const struct cs42xx8_driver_data *drvdata;
42 struct regmap *regmap;
43 struct clk *clk;
44
45 bool slave_mode;
46 unsigned long sysclk;
47};
48
49/* -127.5dB to 0dB with step of 0.5dB */
50static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1);
51/* -64dB to 24dB with step of 0.5dB */
52static const DECLARE_TLV_DB_SCALE(adc_tlv, -6400, 50, 0);
53
54static const char *const cs42xx8_adc_single[] = { "Differential", "Single-Ended" };
55static const char *const cs42xx8_szc[] = { "Immediate Change", "Zero Cross",
56 "Soft Ramp", "Soft Ramp on Zero Cross" };
57
58static const struct soc_enum adc1_single_enum =
59 SOC_ENUM_SINGLE(CS42XX8_ADCCTL, 4, 2, cs42xx8_adc_single);
60static const struct soc_enum adc2_single_enum =
61 SOC_ENUM_SINGLE(CS42XX8_ADCCTL, 3, 2, cs42xx8_adc_single);
62static const struct soc_enum adc3_single_enum =
63 SOC_ENUM_SINGLE(CS42XX8_ADCCTL, 2, 2, cs42xx8_adc_single);
64static const struct soc_enum dac_szc_enum =
65 SOC_ENUM_SINGLE(CS42XX8_TXCTL, 5, 4, cs42xx8_szc);
66static const struct soc_enum adc_szc_enum =
67 SOC_ENUM_SINGLE(CS42XX8_TXCTL, 0, 4, cs42xx8_szc);
68
69static const struct snd_kcontrol_new cs42xx8_snd_controls[] = {
70 SOC_DOUBLE_R_TLV("DAC1 Playback Volume", CS42XX8_VOLAOUT1,
71 CS42XX8_VOLAOUT2, 0, 0xff, 1, dac_tlv),
72 SOC_DOUBLE_R_TLV("DAC2 Playback Volume", CS42XX8_VOLAOUT3,
73 CS42XX8_VOLAOUT4, 0, 0xff, 1, dac_tlv),
74 SOC_DOUBLE_R_TLV("DAC3 Playback Volume", CS42XX8_VOLAOUT5,
75 CS42XX8_VOLAOUT6, 0, 0xff, 1, dac_tlv),
76 SOC_DOUBLE_R_TLV("DAC4 Playback Volume", CS42XX8_VOLAOUT7,
77 CS42XX8_VOLAOUT8, 0, 0xff, 1, dac_tlv),
78 SOC_DOUBLE_R_S_TLV("ADC1 Capture Volume", CS42XX8_VOLAIN1,
79 CS42XX8_VOLAIN2, 0, -0x80, 0x30, 7, 0, adc_tlv),
80 SOC_DOUBLE_R_S_TLV("ADC2 Capture Volume", CS42XX8_VOLAIN3,
81 CS42XX8_VOLAIN4, 0, -0x80, 0x30, 7, 0, adc_tlv),
82 SOC_DOUBLE("DAC1 Invert Switch", CS42XX8_DACINV, 0, 1, 1, 0),
83 SOC_DOUBLE("DAC2 Invert Switch", CS42XX8_DACINV, 2, 3, 1, 0),
84 SOC_DOUBLE("DAC3 Invert Switch", CS42XX8_DACINV, 4, 5, 1, 0),
85 SOC_DOUBLE("DAC4 Invert Switch", CS42XX8_DACINV, 6, 7, 1, 0),
86 SOC_DOUBLE("ADC1 Invert Switch", CS42XX8_ADCINV, 0, 1, 1, 0),
87 SOC_DOUBLE("ADC2 Invert Switch", CS42XX8_ADCINV, 2, 3, 1, 0),
88 SOC_SINGLE("ADC High-Pass Filter Switch", CS42XX8_ADCCTL, 7, 1, 1),
89 SOC_SINGLE("DAC De-emphasis Switch", CS42XX8_ADCCTL, 5, 1, 0),
90 SOC_ENUM("ADC1 Single Ended Mode Switch", adc1_single_enum),
91 SOC_ENUM("ADC2 Single Ended Mode Switch", adc2_single_enum),
92 SOC_SINGLE("DAC Single Volume Control Switch", CS42XX8_TXCTL, 7, 1, 0),
93 SOC_ENUM("DAC Soft Ramp & Zero Cross Control Switch", dac_szc_enum),
94 SOC_SINGLE("DAC Auto Mute Switch", CS42XX8_TXCTL, 4, 1, 0),
95 SOC_SINGLE("Mute ADC Serial Port Switch", CS42XX8_TXCTL, 3, 1, 0),
96 SOC_SINGLE("ADC Single Volume Control Switch", CS42XX8_TXCTL, 2, 1, 0),
97 SOC_ENUM("ADC Soft Ramp & Zero Cross Control Switch", adc_szc_enum),
98};
99
100static const struct snd_kcontrol_new cs42xx8_adc3_snd_controls[] = {
101 SOC_DOUBLE_R_S_TLV("ADC3 Capture Volume", CS42XX8_VOLAIN5,
102 CS42XX8_VOLAIN6, 0, -0x80, 0x30, 7, 0, adc_tlv),
103 SOC_DOUBLE("ADC3 Invert Switch", CS42XX8_ADCINV, 4, 5, 1, 0),
104 SOC_ENUM("ADC3 Single Ended Mode Switch", adc3_single_enum),
105};
106
107static const struct snd_soc_dapm_widget cs42xx8_dapm_widgets[] = {
108 SND_SOC_DAPM_DAC("DAC1", "Playback", CS42XX8_PWRCTL, 1, 1),
109 SND_SOC_DAPM_DAC("DAC2", "Playback", CS42XX8_PWRCTL, 2, 1),
110 SND_SOC_DAPM_DAC("DAC3", "Playback", CS42XX8_PWRCTL, 3, 1),
111 SND_SOC_DAPM_DAC("DAC4", "Playback", CS42XX8_PWRCTL, 4, 1),
112
113 SND_SOC_DAPM_OUTPUT("AOUT1L"),
114 SND_SOC_DAPM_OUTPUT("AOUT1R"),
115 SND_SOC_DAPM_OUTPUT("AOUT2L"),
116 SND_SOC_DAPM_OUTPUT("AOUT2R"),
117 SND_SOC_DAPM_OUTPUT("AOUT3L"),
118 SND_SOC_DAPM_OUTPUT("AOUT3R"),
119 SND_SOC_DAPM_OUTPUT("AOUT4L"),
120 SND_SOC_DAPM_OUTPUT("AOUT4R"),
121
122 SND_SOC_DAPM_ADC("ADC1", "Capture", CS42XX8_PWRCTL, 5, 1),
123 SND_SOC_DAPM_ADC("ADC2", "Capture", CS42XX8_PWRCTL, 6, 1),
124
125 SND_SOC_DAPM_INPUT("AIN1L"),
126 SND_SOC_DAPM_INPUT("AIN1R"),
127 SND_SOC_DAPM_INPUT("AIN2L"),
128 SND_SOC_DAPM_INPUT("AIN2R"),
129
130 SND_SOC_DAPM_SUPPLY("PWR", CS42XX8_PWRCTL, 0, 1, NULL, 0),
131};
132
133static const struct snd_soc_dapm_widget cs42xx8_adc3_dapm_widgets[] = {
134 SND_SOC_DAPM_ADC("ADC3", "Capture", CS42XX8_PWRCTL, 7, 1),
135
136 SND_SOC_DAPM_INPUT("AIN3L"),
137 SND_SOC_DAPM_INPUT("AIN3R"),
138};
139
140static const struct snd_soc_dapm_route cs42xx8_dapm_routes[] = {
141 /* Playback */
142 { "AOUT1L", NULL, "DAC1" },
143 { "AOUT1R", NULL, "DAC1" },
144 { "DAC1", NULL, "PWR" },
145
146 { "AOUT2L", NULL, "DAC2" },
147 { "AOUT2R", NULL, "DAC2" },
148 { "DAC2", NULL, "PWR" },
149
150 { "AOUT3L", NULL, "DAC3" },
151 { "AOUT3R", NULL, "DAC3" },
152 { "DAC3", NULL, "PWR" },
153
154 { "AOUT4L", NULL, "DAC4" },
155 { "AOUT4R", NULL, "DAC4" },
156 { "DAC4", NULL, "PWR" },
157
158 /* Capture */
159 { "ADC1", NULL, "AIN1L" },
160 { "ADC1", NULL, "AIN1R" },
161 { "ADC1", NULL, "PWR" },
162
163 { "ADC2", NULL, "AIN2L" },
164 { "ADC2", NULL, "AIN2R" },
165 { "ADC2", NULL, "PWR" },
166};
167
168static const struct snd_soc_dapm_route cs42xx8_adc3_dapm_routes[] = {
169 /* Capture */
170 { "ADC3", NULL, "AIN3L" },
171 { "ADC3", NULL, "AIN3R" },
172 { "ADC3", NULL, "PWR" },
173};
174
175struct cs42xx8_ratios {
176 unsigned int ratio;
177 unsigned char speed;
178 unsigned char mclk;
179};
180
181static const struct cs42xx8_ratios cs42xx8_ratios[] = {
182 { 64, CS42XX8_FM_QUAD, CS42XX8_FUNCMOD_MFREQ_256(4) },
183 { 96, CS42XX8_FM_QUAD, CS42XX8_FUNCMOD_MFREQ_384(4) },
184 { 128, CS42XX8_FM_QUAD, CS42XX8_FUNCMOD_MFREQ_512(4) },
185 { 192, CS42XX8_FM_QUAD, CS42XX8_FUNCMOD_MFREQ_768(4) },
186 { 256, CS42XX8_FM_SINGLE, CS42XX8_FUNCMOD_MFREQ_256(1) },
187 { 384, CS42XX8_FM_SINGLE, CS42XX8_FUNCMOD_MFREQ_384(1) },
188 { 512, CS42XX8_FM_SINGLE, CS42XX8_FUNCMOD_MFREQ_512(1) },
189 { 768, CS42XX8_FM_SINGLE, CS42XX8_FUNCMOD_MFREQ_768(1) },
190 { 1024, CS42XX8_FM_SINGLE, CS42XX8_FUNCMOD_MFREQ_1024(1) }
191};
192
193static int cs42xx8_set_dai_sysclk(struct snd_soc_dai *codec_dai,
194 int clk_id, unsigned int freq, int dir)
195{
196 struct snd_soc_codec *codec = codec_dai->codec;
197 struct cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec);
198
199 cs42xx8->sysclk = freq;
200
201 return 0;
202}
203
204static int cs42xx8_set_dai_fmt(struct snd_soc_dai *codec_dai,
205 unsigned int format)
206{
207 struct snd_soc_codec *codec = codec_dai->codec;
208 struct cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec);
209 u32 val;
210
211 /* Set DAI format */
212 switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
213 case SND_SOC_DAIFMT_LEFT_J:
214 val = CS42XX8_INTF_DAC_DIF_LEFTJ | CS42XX8_INTF_ADC_DIF_LEFTJ;
215 break;
216 case SND_SOC_DAIFMT_I2S:
217 val = CS42XX8_INTF_DAC_DIF_I2S | CS42XX8_INTF_ADC_DIF_I2S;
218 break;
219 case SND_SOC_DAIFMT_RIGHT_J:
220 val = CS42XX8_INTF_DAC_DIF_RIGHTJ | CS42XX8_INTF_ADC_DIF_RIGHTJ;
221 break;
222 default:
223 dev_err(codec->dev, "unsupported dai format\n");
224 return -EINVAL;
225 }
226
227 regmap_update_bits(cs42xx8->regmap, CS42XX8_INTF,
228 CS42XX8_INTF_DAC_DIF_MASK |
229 CS42XX8_INTF_ADC_DIF_MASK, val);
230
231 /* Set master/slave audio interface */
232 switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
233 case SND_SOC_DAIFMT_CBS_CFS:
234 cs42xx8->slave_mode = true;
235 break;
236 case SND_SOC_DAIFMT_CBM_CFM:
237 cs42xx8->slave_mode = false;
238 break;
239 default:
240 dev_err(codec->dev, "unsupported master/slave mode\n");
241 return -EINVAL;
242 }
243
244 return 0;
245}
246
247static int cs42xx8_hw_params(struct snd_pcm_substream *substream,
248 struct snd_pcm_hw_params *params,
249 struct snd_soc_dai *dai)
250{
251 struct snd_soc_pcm_runtime *rtd = substream->private_data;
252 struct snd_soc_codec *codec = rtd->codec;
253 struct cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec);
254 bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
255 u32 ratio = cs42xx8->sysclk / params_rate(params);
256 u32 i, fm, val, mask;
257
258 for (i = 0; i < ARRAY_SIZE(cs42xx8_ratios); i++) {
259 if (cs42xx8_ratios[i].ratio == ratio)
260 break;
261 }
262
263 if (i == ARRAY_SIZE(cs42xx8_ratios)) {
264 dev_err(codec->dev, "unsupported sysclk ratio\n");
265 return -EINVAL;
266 }
267
268 mask = CS42XX8_FUNCMOD_MFREQ_MASK;
269 val = cs42xx8_ratios[i].mclk;
270
271 fm = cs42xx8->slave_mode ? CS42XX8_FM_AUTO : cs42xx8_ratios[i].speed;
272
273 regmap_update_bits(cs42xx8->regmap, CS42XX8_FUNCMOD,
274 CS42XX8_FUNCMOD_xC_FM_MASK(tx) | mask,
275 CS42XX8_FUNCMOD_xC_FM(tx, fm) | val);
276
277 return 0;
278}
279
280static int cs42xx8_digital_mute(struct snd_soc_dai *dai, int mute)
281{
282 struct snd_soc_codec *codec = dai->codec;
283 struct cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec);
284
285 regmap_update_bits(cs42xx8->regmap, CS42XX8_DACMUTE,
286 CS42XX8_DACMUTE_ALL, mute ? CS42XX8_DACMUTE_ALL : 0);
287
288 return 0;
289}
290
291static const struct snd_soc_dai_ops cs42xx8_dai_ops = {
292 .set_fmt = cs42xx8_set_dai_fmt,
293 .set_sysclk = cs42xx8_set_dai_sysclk,
294 .hw_params = cs42xx8_hw_params,
295 .digital_mute = cs42xx8_digital_mute,
296};
297
298static struct snd_soc_dai_driver cs42xx8_dai = {
299 .playback = {
300 .stream_name = "Playback",
301 .channels_min = 1,
302 .channels_max = 8,
303 .rates = SNDRV_PCM_RATE_8000_192000,
304 .formats = CS42XX8_FORMATS,
305 },
306 .capture = {
307 .stream_name = "Capture",
308 .channels_min = 1,
309 .rates = SNDRV_PCM_RATE_8000_192000,
310 .formats = CS42XX8_FORMATS,
311 },
312 .ops = &cs42xx8_dai_ops,
313};
314
315static const struct reg_default cs42xx8_reg[] = {
316 { 0x01, 0x01 }, /* Chip I.D. and Revision Register */
317 { 0x02, 0x00 }, /* Power Control */
318 { 0x03, 0xF0 }, /* Functional Mode */
319 { 0x04, 0x46 }, /* Interface Formats */
320 { 0x05, 0x00 }, /* ADC Control & DAC De-Emphasis */
321 { 0x06, 0x10 }, /* Transition Control */
322 { 0x07, 0x00 }, /* DAC Channel Mute */
323 { 0x08, 0x00 }, /* Volume Control AOUT1 */
324 { 0x09, 0x00 }, /* Volume Control AOUT2 */
325 { 0x0a, 0x00 }, /* Volume Control AOUT3 */
326 { 0x0b, 0x00 }, /* Volume Control AOUT4 */
327 { 0x0c, 0x00 }, /* Volume Control AOUT5 */
328 { 0x0d, 0x00 }, /* Volume Control AOUT6 */
329 { 0x0e, 0x00 }, /* Volume Control AOUT7 */
330 { 0x0f, 0x00 }, /* Volume Control AOUT8 */
331 { 0x10, 0x00 }, /* DAC Channel Invert */
332 { 0x11, 0x00 }, /* Volume Control AIN1 */
333 { 0x12, 0x00 }, /* Volume Control AIN2 */
334 { 0x13, 0x00 }, /* Volume Control AIN3 */
335 { 0x14, 0x00 }, /* Volume Control AIN4 */
336 { 0x15, 0x00 }, /* Volume Control AIN5 */
337 { 0x16, 0x00 }, /* Volume Control AIN6 */
338 { 0x17, 0x00 }, /* ADC Channel Invert */
339 { 0x18, 0x00 }, /* Status Control */
340 { 0x1a, 0x00 }, /* Status Mask */
341 { 0x1b, 0x00 }, /* MUTEC Pin Control */
342};
343
344static bool cs42xx8_volatile_register(struct device *dev, unsigned int reg)
345{
346 switch (reg) {
347 case CS42XX8_STATUS:
348 return true;
349 default:
350 return false;
351 }
352}
353
354static bool cs42xx8_writeable_register(struct device *dev, unsigned int reg)
355{
356 switch (reg) {
357 case CS42XX8_CHIPID:
358 case CS42XX8_STATUS:
359 return false;
360 default:
361 return true;
362 }
363}
364
365const struct regmap_config cs42xx8_regmap_config = {
366 .reg_bits = 8,
367 .val_bits = 8,
368
369 .max_register = CS42XX8_LASTREG,
370 .reg_defaults = cs42xx8_reg,
371 .num_reg_defaults = ARRAY_SIZE(cs42xx8_reg),
372 .volatile_reg = cs42xx8_volatile_register,
373 .writeable_reg = cs42xx8_writeable_register,
374 .cache_type = REGCACHE_RBTREE,
375};
376EXPORT_SYMBOL_GPL(cs42xx8_regmap_config);
377
378static int cs42xx8_codec_probe(struct snd_soc_codec *codec)
379{
380 struct cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec);
381 struct snd_soc_dapm_context *dapm = &codec->dapm;
382
383 switch (cs42xx8->drvdata->num_adcs) {
384 case 3:
385 snd_soc_add_codec_controls(codec, cs42xx8_adc3_snd_controls,
386 ARRAY_SIZE(cs42xx8_adc3_snd_controls));
387 snd_soc_dapm_new_controls(dapm, cs42xx8_adc3_dapm_widgets,
388 ARRAY_SIZE(cs42xx8_adc3_dapm_widgets));
389 snd_soc_dapm_add_routes(dapm, cs42xx8_adc3_dapm_routes,
390 ARRAY_SIZE(cs42xx8_adc3_dapm_routes));
391 break;
392 default:
393 break;
394 }
395
396 /* Mute all DAC channels */
397 regmap_write(cs42xx8->regmap, CS42XX8_DACMUTE, CS42XX8_DACMUTE_ALL);
398
399 return 0;
400}
401
402static const struct snd_soc_codec_driver cs42xx8_driver = {
403 .probe = cs42xx8_codec_probe,
404 .idle_bias_off = true,
405
406 .controls = cs42xx8_snd_controls,
407 .num_controls = ARRAY_SIZE(cs42xx8_snd_controls),
408 .dapm_widgets = cs42xx8_dapm_widgets,
409 .num_dapm_widgets = ARRAY_SIZE(cs42xx8_dapm_widgets),
410 .dapm_routes = cs42xx8_dapm_routes,
411 .num_dapm_routes = ARRAY_SIZE(cs42xx8_dapm_routes),
412};
413
414const struct cs42xx8_driver_data cs42448_data = {
415 .name = "cs42448",
416 .num_adcs = 3,
417};
418EXPORT_SYMBOL_GPL(cs42448_data);
419
420const struct cs42xx8_driver_data cs42888_data = {
421 .name = "cs42888",
422 .num_adcs = 2,
423};
424EXPORT_SYMBOL_GPL(cs42888_data);
425
426const struct of_device_id cs42xx8_of_match[] = {
427 { .compatible = "cirrus,cs42448", .data = &cs42448_data, },
428 { .compatible = "cirrus,cs42888", .data = &cs42888_data, },
429 { /* sentinel */ }
430};
431MODULE_DEVICE_TABLE(of, cs42xx8_of_match);
432EXPORT_SYMBOL_GPL(cs42xx8_of_match);
433
434int cs42xx8_probe(struct device *dev, struct regmap *regmap)
435{
436 const struct of_device_id *of_id = of_match_device(cs42xx8_of_match, dev);
437 struct cs42xx8_priv *cs42xx8;
438 int ret, val, i;
439
440 cs42xx8 = devm_kzalloc(dev, sizeof(*cs42xx8), GFP_KERNEL);
441 if (cs42xx8 == NULL)
442 return -ENOMEM;
443
444 dev_set_drvdata(dev, cs42xx8);
445
446 if (of_id)
447 cs42xx8->drvdata = of_id->data;
448
449 if (!cs42xx8->drvdata) {
450 dev_err(dev, "failed to find driver data\n");
451 return -EINVAL;
452 }
453
454 cs42xx8->clk = devm_clk_get(dev, "mclk");
455 if (IS_ERR(cs42xx8->clk)) {
456 dev_err(dev, "failed to get the clock: %ld\n",
457 PTR_ERR(cs42xx8->clk));
458 return -EINVAL;
459 }
460
461 cs42xx8->sysclk = clk_get_rate(cs42xx8->clk);
462
463 for (i = 0; i < ARRAY_SIZE(cs42xx8->supplies); i++)
464 cs42xx8->supplies[i].supply = cs42xx8_supply_names[i];
465
466 ret = devm_regulator_bulk_get(dev,
467 ARRAY_SIZE(cs42xx8->supplies), cs42xx8->supplies);
468 if (ret) {
469 dev_err(dev, "failed to request supplies: %d\n", ret);
470 return ret;
471 }
472
473 ret = regulator_bulk_enable(ARRAY_SIZE(cs42xx8->supplies),
474 cs42xx8->supplies);
475 if (ret) {
476 dev_err(dev, "failed to enable supplies: %d\n", ret);
477 return ret;
478 }
479
480 /* Make sure hardware reset done */
481 msleep(5);
482
483 cs42xx8->regmap = regmap;
484 if (IS_ERR(cs42xx8->regmap)) {
485 ret = PTR_ERR(cs42xx8->regmap);
486 dev_err(dev, "failed to allocate regmap: %d\n", ret);
487 goto err_enable;
488 }
489
490 /*
491 * We haven't marked the chip revision as volatile due to
492 * sharing a register with the right input volume; explicitly
493 * bypass the cache to read it.
494 */
495 regcache_cache_bypass(cs42xx8->regmap, true);
496
497 /* Validate the chip ID */
498 regmap_read(cs42xx8->regmap, CS42XX8_CHIPID, &val);
499 if (val < 0) {
500 dev_err(dev, "failed to get device ID: %x", val);
501 ret = -EINVAL;
502 goto err_enable;
503 }
504
505 /* The top four bits of the chip ID should be 0000 */
506 if ((val & CS42XX8_CHIPID_CHIP_ID_MASK) != 0x00) {
507 dev_err(dev, "unmatched chip ID: %d\n",
508 val & CS42XX8_CHIPID_CHIP_ID_MASK);
509 ret = -EINVAL;
510 goto err_enable;
511 }
512
513 dev_info(dev, "found device, revision %X\n",
514 val & CS42XX8_CHIPID_REV_ID_MASK);
515
516 regcache_cache_bypass(cs42xx8->regmap, false);
517
518 cs42xx8_dai.name = cs42xx8->drvdata->name;
519
520 /* Each adc supports stereo input */
521 cs42xx8_dai.capture.channels_max = cs42xx8->drvdata->num_adcs * 2;
522
523 ret = snd_soc_register_codec(dev, &cs42xx8_driver, &cs42xx8_dai, 1);
524 if (ret) {
525 dev_err(dev, "failed to register codec:%d\n", ret);
526 goto err_enable;
527 }
528
529 regcache_cache_only(cs42xx8->regmap, true);
530
531err_enable:
532 regulator_bulk_disable(ARRAY_SIZE(cs42xx8->supplies),
533 cs42xx8->supplies);
534
535 return ret;
536}
537EXPORT_SYMBOL_GPL(cs42xx8_probe);
538
539#ifdef CONFIG_PM_RUNTIME
540static int cs42xx8_runtime_resume(struct device *dev)
541{
542 struct cs42xx8_priv *cs42xx8 = dev_get_drvdata(dev);
543 int ret;
544
545 ret = clk_prepare_enable(cs42xx8->clk);
546 if (ret) {
547 dev_err(dev, "failed to enable mclk: %d\n", ret);
548 return ret;
549 }
550
551 ret = regulator_bulk_enable(ARRAY_SIZE(cs42xx8->supplies),
552 cs42xx8->supplies);
553 if (ret) {
554 dev_err(dev, "failed to enable supplies: %d\n", ret);
555 goto err_clk;
556 }
557
558 /* Make sure hardware reset done */
559 msleep(5);
560
561 regcache_cache_only(cs42xx8->regmap, false);
562
563 ret = regcache_sync(cs42xx8->regmap);
564 if (ret) {
565 dev_err(dev, "failed to sync regmap: %d\n", ret);
566 goto err_bulk;
567 }
568
569 return 0;
570
571err_bulk:
572 regulator_bulk_disable(ARRAY_SIZE(cs42xx8->supplies),
573 cs42xx8->supplies);
574err_clk:
575 clk_disable_unprepare(cs42xx8->clk);
576
577 return ret;
578}
579
580static int cs42xx8_runtime_suspend(struct device *dev)
581{
582 struct cs42xx8_priv *cs42xx8 = dev_get_drvdata(dev);
583
584 regcache_cache_only(cs42xx8->regmap, true);
585
586 regulator_bulk_disable(ARRAY_SIZE(cs42xx8->supplies),
587 cs42xx8->supplies);
588
589 clk_disable_unprepare(cs42xx8->clk);
590
591 return 0;
592}
593#endif
594
595const struct dev_pm_ops cs42xx8_pm = {
596 SET_RUNTIME_PM_OPS(cs42xx8_runtime_suspend, cs42xx8_runtime_resume, NULL)
597};
598EXPORT_SYMBOL_GPL(cs42xx8_pm);
599
600MODULE_DESCRIPTION("Cirrus Logic CS42448/CS42888 ALSA SoC Codec Driver");
601MODULE_AUTHOR("Freescale Semiconductor, Inc.");
602MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs42xx8.h b/sound/soc/codecs/cs42xx8.h
new file mode 100644
index 000000000000..da0b94aee419
--- /dev/null
+++ b/sound/soc/codecs/cs42xx8.h
@@ -0,0 +1,238 @@
1/*
2 * cs42xx8.h - Cirrus Logic CS42448/CS42888 Audio CODEC driver header file
3 *
4 * Copyright (C) 2014 Freescale Semiconductor, Inc.
5 *
6 * Author: Nicolin Chen <Guangyu.Chen@freescale.com>
7 *
8 * This file is licensed under the terms of the GNU General Public License
9 * version 2. This program is licensed "as is" without any warranty of any
10 * kind, whether express or implied.
11 */
12
13#ifndef _CS42XX8_H
14#define _CS42XX8_H
15
16struct cs42xx8_driver_data {
17 char name[32];
18 int num_adcs;
19};
20
21extern const struct dev_pm_ops cs42xx8_pm;
22extern const struct cs42xx8_driver_data cs42448_data;
23extern const struct cs42xx8_driver_data cs42888_data;
24extern const struct regmap_config cs42xx8_regmap_config;
25int cs42xx8_probe(struct device *dev, struct regmap *regmap);
26
27/* CS42888 register map */
28#define CS42XX8_CHIPID 0x01 /* Chip ID */
29#define CS42XX8_PWRCTL 0x02 /* Power Control */
30#define CS42XX8_FUNCMOD 0x03 /* Functional Mode */
31#define CS42XX8_INTF 0x04 /* Interface Formats */
32#define CS42XX8_ADCCTL 0x05 /* ADC Control */
33#define CS42XX8_TXCTL 0x06 /* Transition Control */
34#define CS42XX8_DACMUTE 0x07 /* DAC Mute Control */
35#define CS42XX8_VOLAOUT1 0x08 /* Volume Control AOUT1 */
36#define CS42XX8_VOLAOUT2 0x09 /* Volume Control AOUT2 */
37#define CS42XX8_VOLAOUT3 0x0A /* Volume Control AOUT3 */
38#define CS42XX8_VOLAOUT4 0x0B /* Volume Control AOUT4 */
39#define CS42XX8_VOLAOUT5 0x0C /* Volume Control AOUT5 */
40#define CS42XX8_VOLAOUT6 0x0D /* Volume Control AOUT6 */
41#define CS42XX8_VOLAOUT7 0x0E /* Volume Control AOUT7 */
42#define CS42XX8_VOLAOUT8 0x0F /* Volume Control AOUT8 */
43#define CS42XX8_DACINV 0x10 /* DAC Channel Invert */
44#define CS42XX8_VOLAIN1 0x11 /* Volume Control AIN1 */
45#define CS42XX8_VOLAIN2 0x12 /* Volume Control AIN2 */
46#define CS42XX8_VOLAIN3 0x13 /* Volume Control AIN3 */
47#define CS42XX8_VOLAIN4 0x14 /* Volume Control AIN4 */
48#define CS42XX8_VOLAIN5 0x15 /* Volume Control AIN5 */
49#define CS42XX8_VOLAIN6 0x16 /* Volume Control AIN6 */
50#define CS42XX8_ADCINV 0x17 /* ADC Channel Invert */
51#define CS42XX8_STATUSCTL 0x18 /* Status Control */
52#define CS42XX8_STATUS 0x19 /* Status */
53#define CS42XX8_STATUSM 0x1A /* Status Mask */
54#define CS42XX8_MUTEC 0x1B /* MUTEC Pin Control */
55
56#define CS42XX8_FIRSTREG CS42XX8_CHIPID
57#define CS42XX8_LASTREG CS42XX8_MUTEC
58#define CS42XX8_NUMREGS (CS42XX8_LASTREG - CS42XX8_FIRSTREG + 1)
59#define CS42XX8_I2C_INCR 0x80
60
61/* Chip I.D. and Revision Register (Address 01h) */
62#define CS42XX8_CHIPID_CHIP_ID_MASK 0xF0
63#define CS42XX8_CHIPID_REV_ID_MASK 0x0F
64
65/* Power Control (Address 02h) */
66#define CS42XX8_PWRCTL_PDN_ADC3_SHIFT 7
67#define CS42XX8_PWRCTL_PDN_ADC3_MASK (1 << CS42XX8_PWRCTL_PDN_ADC3_SHIFT)
68#define CS42XX8_PWRCTL_PDN_ADC3 (1 << CS42XX8_PWRCTL_PDN_ADC3_SHIFT)
69#define CS42XX8_PWRCTL_PDN_ADC2_SHIFT 6
70#define CS42XX8_PWRCTL_PDN_ADC2_MASK (1 << CS42XX8_PWRCTL_PDN_ADC2_SHIFT)
71#define CS42XX8_PWRCTL_PDN_ADC2 (1 << CS42XX8_PWRCTL_PDN_ADC2_SHIFT)
72#define CS42XX8_PWRCTL_PDN_ADC1_SHIFT 5
73#define CS42XX8_PWRCTL_PDN_ADC1_MASK (1 << CS42XX8_PWRCTL_PDN_ADC1_SHIFT)
74#define CS42XX8_PWRCTL_PDN_ADC1 (1 << CS42XX8_PWRCTL_PDN_ADC1_SHIFT)
75#define CS42XX8_PWRCTL_PDN_DAC4_SHIFT 4
76#define CS42XX8_PWRCTL_PDN_DAC4_MASK (1 << CS42XX8_PWRCTL_PDN_DAC4_SHIFT)
77#define CS42XX8_PWRCTL_PDN_DAC4 (1 << CS42XX8_PWRCTL_PDN_DAC4_SHIFT)
78#define CS42XX8_PWRCTL_PDN_DAC3_SHIFT 3
79#define CS42XX8_PWRCTL_PDN_DAC3_MASK (1 << CS42XX8_PWRCTL_PDN_DAC3_SHIFT)
80#define CS42XX8_PWRCTL_PDN_DAC3 (1 << CS42XX8_PWRCTL_PDN_DAC3_SHIFT)
81#define CS42XX8_PWRCTL_PDN_DAC2_SHIFT 2
82#define CS42XX8_PWRCTL_PDN_DAC2_MASK (1 << CS42XX8_PWRCTL_PDN_DAC2_SHIFT)
83#define CS42XX8_PWRCTL_PDN_DAC2 (1 << CS42XX8_PWRCTL_PDN_DAC2_SHIFT)
84#define CS42XX8_PWRCTL_PDN_DAC1_SHIFT 1
85#define CS42XX8_PWRCTL_PDN_DAC1_MASK (1 << CS42XX8_PWRCTL_PDN_DAC1_SHIFT)
86#define CS42XX8_PWRCTL_PDN_DAC1 (1 << CS42XX8_PWRCTL_PDN_DAC1_SHIFT)
87#define CS42XX8_PWRCTL_PDN_SHIFT 0
88#define CS42XX8_PWRCTL_PDN_MASK (1 << CS42XX8_PWRCTL_PDN_SHIFT)
89#define CS42XX8_PWRCTL_PDN (1 << CS42XX8_PWRCTL_PDN_SHIFT)
90
91/* Functional Mode (Address 03h) */
92#define CS42XX8_FUNCMOD_DAC_FM_SHIFT 6
93#define CS42XX8_FUNCMOD_DAC_FM_WIDTH 2
94#define CS42XX8_FUNCMOD_DAC_FM_MASK (((1 << CS42XX8_FUNCMOD_DAC_FM_WIDTH) - 1) << CS42XX8_FUNCMOD_DAC_FM_SHIFT)
95#define CS42XX8_FUNCMOD_DAC_FM(v) ((v) << CS42XX8_FUNCMOD_DAC_FM_SHIFT)
96#define CS42XX8_FUNCMOD_ADC_FM_SHIFT 4
97#define CS42XX8_FUNCMOD_ADC_FM_WIDTH 2
98#define CS42XX8_FUNCMOD_ADC_FM_MASK (((1 << CS42XX8_FUNCMOD_ADC_FM_WIDTH) - 1) << CS42XX8_FUNCMOD_ADC_FM_SHIFT)
99#define CS42XX8_FUNCMOD_ADC_FM(v) ((v) << CS42XX8_FUNCMOD_ADC_FM_SHIFT)
100#define CS42XX8_FUNCMOD_xC_FM_MASK(x) ((x) ? CS42XX8_FUNCMOD_DAC_FM_MASK : CS42XX8_FUNCMOD_ADC_FM_MASK)
101#define CS42XX8_FUNCMOD_xC_FM(x, v) ((x) ? CS42XX8_FUNCMOD_DAC_FM(v) : CS42XX8_FUNCMOD_ADC_FM(v))
102#define CS42XX8_FUNCMOD_MFREQ_SHIFT 1
103#define CS42XX8_FUNCMOD_MFREQ_WIDTH 3
104#define CS42XX8_FUNCMOD_MFREQ_MASK (((1 << CS42XX8_FUNCMOD_MFREQ_WIDTH) - 1) << CS42XX8_FUNCMOD_MFREQ_SHIFT)
105#define CS42XX8_FUNCMOD_MFREQ_256(s) ((0 << CS42XX8_FUNCMOD_MFREQ_SHIFT) >> (s >> 1))
106#define CS42XX8_FUNCMOD_MFREQ_384(s) ((1 << CS42XX8_FUNCMOD_MFREQ_SHIFT) >> (s >> 1))
107#define CS42XX8_FUNCMOD_MFREQ_512(s) ((2 << CS42XX8_FUNCMOD_MFREQ_SHIFT) >> (s >> 1))
108#define CS42XX8_FUNCMOD_MFREQ_768(s) ((3 << CS42XX8_FUNCMOD_MFREQ_SHIFT) >> (s >> 1))
109#define CS42XX8_FUNCMOD_MFREQ_1024(s) ((4 << CS42XX8_FUNCMOD_MFREQ_SHIFT) >> (s >> 1))
110
111#define CS42XX8_FM_SINGLE 0
112#define CS42XX8_FM_DOUBLE 1
113#define CS42XX8_FM_QUAD 2
114#define CS42XX8_FM_AUTO 3
115
116/* Interface Formats (Address 04h) */
117#define CS42XX8_INTF_FREEZE_SHIFT 7
118#define CS42XX8_INTF_FREEZE_MASK (1 << CS42XX8_INTF_FREEZE_SHIFT)
119#define CS42XX8_INTF_FREEZE (1 << CS42XX8_INTF_FREEZE_SHIFT)
120#define CS42XX8_INTF_AUX_DIF_SHIFT 6
121#define CS42XX8_INTF_AUX_DIF_MASK (1 << CS42XX8_INTF_AUX_DIF_SHIFT)
122#define CS42XX8_INTF_AUX_DIF (1 << CS42XX8_INTF_AUX_DIF_SHIFT)
123#define CS42XX8_INTF_DAC_DIF_SHIFT 3
124#define CS42XX8_INTF_DAC_DIF_WIDTH 3
125#define CS42XX8_INTF_DAC_DIF_MASK (((1 << CS42XX8_INTF_DAC_DIF_WIDTH) - 1) << CS42XX8_INTF_DAC_DIF_SHIFT)
126#define CS42XX8_INTF_DAC_DIF_LEFTJ (0 << CS42XX8_INTF_DAC_DIF_SHIFT)
127#define CS42XX8_INTF_DAC_DIF_I2S (1 << CS42XX8_INTF_DAC_DIF_SHIFT)
128#define CS42XX8_INTF_DAC_DIF_RIGHTJ (2 << CS42XX8_INTF_DAC_DIF_SHIFT)
129#define CS42XX8_INTF_DAC_DIF_RIGHTJ_16 (3 << CS42XX8_INTF_DAC_DIF_SHIFT)
130#define CS42XX8_INTF_DAC_DIF_ONELINE_20 (4 << CS42XX8_INTF_DAC_DIF_SHIFT)
131#define CS42XX8_INTF_DAC_DIF_ONELINE_24 (6 << CS42XX8_INTF_DAC_DIF_SHIFT)
132#define CS42XX8_INTF_DAC_DIF_TDM (7 << CS42XX8_INTF_DAC_DIF_SHIFT)
133#define CS42XX8_INTF_ADC_DIF_SHIFT 0
134#define CS42XX8_INTF_ADC_DIF_WIDTH 3
135#define CS42XX8_INTF_ADC_DIF_MASK (((1 << CS42XX8_INTF_ADC_DIF_WIDTH) - 1) << CS42XX8_INTF_ADC_DIF_SHIFT)
136#define CS42XX8_INTF_ADC_DIF_LEFTJ (0 << CS42XX8_INTF_ADC_DIF_SHIFT)
137#define CS42XX8_INTF_ADC_DIF_I2S (1 << CS42XX8_INTF_ADC_DIF_SHIFT)
138#define CS42XX8_INTF_ADC_DIF_RIGHTJ (2 << CS42XX8_INTF_ADC_DIF_SHIFT)
139#define CS42XX8_INTF_ADC_DIF_RIGHTJ_16 (3 << CS42XX8_INTF_ADC_DIF_SHIFT)
140#define CS42XX8_INTF_ADC_DIF_ONELINE_20 (4 << CS42XX8_INTF_ADC_DIF_SHIFT)
141#define CS42XX8_INTF_ADC_DIF_ONELINE_24 (6 << CS42XX8_INTF_ADC_DIF_SHIFT)
142#define CS42XX8_INTF_ADC_DIF_TDM (7 << CS42XX8_INTF_ADC_DIF_SHIFT)
143
144/* ADC Control & DAC De-Emphasis (Address 05h) */
145#define CS42XX8_ADCCTL_ADC_HPF_FREEZE_SHIFT 7
146#define CS42XX8_ADCCTL_ADC_HPF_FREEZE_MASK (1 << CS42XX8_ADCCTL_ADC_HPF_FREEZE_SHIFT)
147#define CS42XX8_ADCCTL_ADC_HPF_FREEZE (1 << CS42XX8_ADCCTL_ADC_HPF_FREEZE_SHIFT)
148#define CS42XX8_ADCCTL_DAC_DEM_SHIFT 5
149#define CS42XX8_ADCCTL_DAC_DEM_MASK (1 << CS42XX8_ADCCTL_DAC_DEM_SHIFT)
150#define CS42XX8_ADCCTL_DAC_DEM (1 << CS42XX8_ADCCTL_DAC_DEM_SHIFT)
151#define CS42XX8_ADCCTL_ADC1_SINGLE_SHIFT 4
152#define CS42XX8_ADCCTL_ADC1_SINGLE_MASK (1 << CS42XX8_ADCCTL_ADC1_SINGLE_SHIFT)
153#define CS42XX8_ADCCTL_ADC1_SINGLE (1 << CS42XX8_ADCCTL_ADC1_SINGLE_SHIFT)
154#define CS42XX8_ADCCTL_ADC2_SINGLE_SHIFT 3
155#define CS42XX8_ADCCTL_ADC2_SINGLE_MASK (1 << CS42XX8_ADCCTL_ADC2_SINGLE_SHIFT)
156#define CS42XX8_ADCCTL_ADC2_SINGLE (1 << CS42XX8_ADCCTL_ADC2_SINGLE_SHIFT)
157#define CS42XX8_ADCCTL_ADC3_SINGLE_SHIFT 2
158#define CS42XX8_ADCCTL_ADC3_SINGLE_MASK (1 << CS42XX8_ADCCTL_ADC3_SINGLE_SHIFT)
159#define CS42XX8_ADCCTL_ADC3_SINGLE (1 << CS42XX8_ADCCTL_ADC3_SINGLE_SHIFT)
160#define CS42XX8_ADCCTL_AIN5_MUX_SHIFT 1
161#define CS42XX8_ADCCTL_AIN5_MUX_MASK (1 << CS42XX8_ADCCTL_AIN5_MUX_SHIFT)
162#define CS42XX8_ADCCTL_AIN5_MUX (1 << CS42XX8_ADCCTL_AIN5_MUX_SHIFT)
163#define CS42XX8_ADCCTL_AIN6_MUX_SHIFT 0
164#define CS42XX8_ADCCTL_AIN6_MUX_MASK (1 << CS42XX8_ADCCTL_AIN6_MUX_SHIFT)
165#define CS42XX8_ADCCTL_AIN6_MUX (1 << CS42XX8_ADCCTL_AIN6_MUX_SHIFT)
166
167/* Transition Control (Address 06h) */
168#define CS42XX8_TXCTL_DAC_SNGVOL_SHIFT 7
169#define CS42XX8_TXCTL_DAC_SNGVOL_MASK (1 << CS42XX8_TXCTL_DAC_SNGVOL_SHIFT)
170#define CS42XX8_TXCTL_DAC_SNGVOL (1 << CS42XX8_TXCTL_DAC_SNGVOL_SHIFT)
171#define CS42XX8_TXCTL_DAC_SZC_SHIFT 5
172#define CS42XX8_TXCTL_DAC_SZC_WIDTH 2
173#define CS42XX8_TXCTL_DAC_SZC_MASK (((1 << CS42XX8_TXCTL_DAC_SZC_WIDTH) - 1) << CS42XX8_TXCTL_DAC_SZC_SHIFT)
174#define CS42XX8_TXCTL_DAC_SZC_IC (0 << CS42XX8_TXCTL_DAC_SZC_SHIFT)
175#define CS42XX8_TXCTL_DAC_SZC_ZC (1 << CS42XX8_TXCTL_DAC_SZC_SHIFT)
176#define CS42XX8_TXCTL_DAC_SZC_SR (2 << CS42XX8_TXCTL_DAC_SZC_SHIFT)
177#define CS42XX8_TXCTL_DAC_SZC_SRZC (3 << CS42XX8_TXCTL_DAC_SZC_SHIFT)
178#define CS42XX8_TXCTL_AMUTE_SHIFT 4
179#define CS42XX8_TXCTL_AMUTE_MASK (1 << CS42XX8_TXCTL_AMUTE_SHIFT)
180#define CS42XX8_TXCTL_AMUTE (1 << CS42XX8_TXCTL_AMUTE_SHIFT)
181#define CS42XX8_TXCTL_MUTE_ADC_SP_SHIFT 3
182#define CS42XX8_TXCTL_MUTE_ADC_SP_MASK (1 << CS42XX8_TXCTL_MUTE_ADC_SP_SHIFT)
183#define CS42XX8_TXCTL_MUTE_ADC_SP (1 << CS42XX8_TXCTL_MUTE_ADC_SP_SHIFT)
184#define CS42XX8_TXCTL_ADC_SNGVOL_SHIFT 2
185#define CS42XX8_TXCTL_ADC_SNGVOL_MASK (1 << CS42XX8_TXCTL_ADC_SNGVOL_SHIFT)
186#define CS42XX8_TXCTL_ADC_SNGVOL (1 << CS42XX8_TXCTL_ADC_SNGVOL_SHIFT)
187#define CS42XX8_TXCTL_ADC_SZC_SHIFT 0
188#define CS42XX8_TXCTL_ADC_SZC_MASK (((1 << CS42XX8_TXCTL_ADC_SZC_WIDTH) - 1) << CS42XX8_TXCTL_ADC_SZC_SHIFT)
189#define CS42XX8_TXCTL_ADC_SZC_IC (0 << CS42XX8_TXCTL_ADC_SZC_SHIFT)
190#define CS42XX8_TXCTL_ADC_SZC_ZC (1 << CS42XX8_TXCTL_ADC_SZC_SHIFT)
191#define CS42XX8_TXCTL_ADC_SZC_SR (2 << CS42XX8_TXCTL_ADC_SZC_SHIFT)
192#define CS42XX8_TXCTL_ADC_SZC_SRZC (3 << CS42XX8_TXCTL_ADC_SZC_SHIFT)
193
194/* DAC Channel Mute (Address 07h) */
195#define CS42XX8_DACMUTE_AOUT(n) (0x1 << n)
196#define CS42XX8_DACMUTE_ALL 0xff
197
198/* Status Control (Address 18h)*/
199#define CS42XX8_STATUSCTL_INI_SHIFT 2
200#define CS42XX8_STATUSCTL_INI_WIDTH 2
201#define CS42XX8_STATUSCTL_INI_MASK (((1 << CS42XX8_STATUSCTL_INI_WIDTH) - 1) << CS42XX8_STATUSCTL_INI_SHIFT)
202#define CS42XX8_STATUSCTL_INT_ACTIVE_HIGH (0 << CS42XX8_STATUSCTL_INI_SHIFT)
203#define CS42XX8_STATUSCTL_INT_ACTIVE_LOW (1 << CS42XX8_STATUSCTL_INI_SHIFT)
204#define CS42XX8_STATUSCTL_INT_OPEN_DRAIN (2 << CS42XX8_STATUSCTL_INI_SHIFT)
205
206/* Status (Address 19h)*/
207#define CS42XX8_STATUS_DAC_CLK_ERR_SHIFT 4
208#define CS42XX8_STATUS_DAC_CLK_ERR_MASK (1 << CS42XX8_STATUS_DAC_CLK_ERR_SHIFT)
209#define CS42XX8_STATUS_ADC_CLK_ERR_SHIFT 3
210#define CS42XX8_STATUS_ADC_CLK_ERR_MASK (1 << CS42XX8_STATUS_ADC_CLK_ERR_SHIFT)
211#define CS42XX8_STATUS_ADC3_OVFL_SHIFT 2
212#define CS42XX8_STATUS_ADC3_OVFL_MASK (1 << CS42XX8_STATUS_ADC3_OVFL_SHIFT)
213#define CS42XX8_STATUS_ADC2_OVFL_SHIFT 1
214#define CS42XX8_STATUS_ADC2_OVFL_MASK (1 << CS42XX8_STATUS_ADC2_OVFL_SHIFT)
215#define CS42XX8_STATUS_ADC1_OVFL_SHIFT 0
216#define CS42XX8_STATUS_ADC1_OVFL_MASK (1 << CS42XX8_STATUS_ADC1_OVFL_SHIFT)
217
218/* Status Mask (Address 1Ah) */
219#define CS42XX8_STATUS_DAC_CLK_ERR_M_SHIFT 4
220#define CS42XX8_STATUS_DAC_CLK_ERR_M_MASK (1 << CS42XX8_STATUS_DAC_CLK_ERR_M_SHIFT)
221#define CS42XX8_STATUS_ADC_CLK_ERR_M_SHIFT 3
222#define CS42XX8_STATUS_ADC_CLK_ERR_M_MASK (1 << CS42XX8_STATUS_ADC_CLK_ERR_M_SHIFT)
223#define CS42XX8_STATUS_ADC3_OVFL_M_SHIFT 2
224#define CS42XX8_STATUS_ADC3_OVFL_M_MASK (1 << CS42XX8_STATUS_ADC3_OVFL_M_SHIFT)
225#define CS42XX8_STATUS_ADC2_OVFL_M_SHIFT 1
226#define CS42XX8_STATUS_ADC2_OVFL_M_MASK (1 << CS42XX8_STATUS_ADC2_OVFL_M_SHIFT)
227#define CS42XX8_STATUS_ADC1_OVFL_M_SHIFT 0
228#define CS42XX8_STATUS_ADC1_OVFL_M_MASK (1 << CS42XX8_STATUS_ADC1_OVFL_M_SHIFT)
229
230/* MUTEC Pin Control (Address 1Bh) */
231#define CS42XX8_MUTEC_MCPOLARITY_SHIFT 1
232#define CS42XX8_MUTEC_MCPOLARITY_MASK (1 << CS42XX8_MUTEC_MCPOLARITY_SHIFT)
233#define CS42XX8_MUTEC_MCPOLARITY_ACTIVE_LOW (0 << CS42XX8_MUTEC_MCPOLARITY_SHIFT)
234#define CS42XX8_MUTEC_MCPOLARITY_ACTIVE_HIGH (1 << CS42XX8_MUTEC_MCPOLARITY_SHIFT)
235#define CS42XX8_MUTEC_MUTEC_ACTIVE_SHIFT 0
236#define CS42XX8_MUTEC_MUTEC_ACTIVE_MASK (1 << CS42XX8_MUTEC_MUTEC_ACTIVE_SHIFT)
237#define CS42XX8_MUTEC_MUTEC_ACTIVE (1 << CS42XX8_MUTEC_MUTEC_ACTIVE_SHIFT)
238#endif /* _CS42XX8_H */
diff --git a/sound/soc/fsl/imx-cs42888.c b/sound/soc/fsl/imx-cs42888.c
index 3750cd917465..357933ecf989 100644
--- a/sound/soc/fsl/imx-cs42888.c
+++ b/sound/soc/fsl/imx-cs42888.c
@@ -132,9 +132,9 @@ static const struct snd_soc_dapm_route audio_map[] = {
132 {"AIN2L", NULL, "Line In Jack"}, 132 {"AIN2L", NULL, "Line In Jack"},
133 {"AIN2R", NULL, "Line In Jack"}, 133 {"AIN2R", NULL, "Line In Jack"},
134 {"esai-Playback", NULL, "asrc-Playback"}, 134 {"esai-Playback", NULL, "asrc-Playback"},
135 {"codec-Playback", NULL, "esai-Playback"},/* dai route for be and fe */ 135 {"Playback", NULL, "esai-Playback"},/* dai route for be and fe */
136 {"asrc-Capture", NULL, "esai-Capture"}, 136 {"asrc-Capture", NULL, "esai-Capture"},
137 {"esai-Capture", NULL, "codec-Capture"}, 137 {"esai-Capture", NULL, "Capture"},
138}; 138};
139 139
140static int be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, 140static int be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
@@ -158,7 +158,7 @@ static struct snd_soc_dai_link imx_cs42888_dai[] = {
158 { 158 {
159 .name = "HiFi", 159 .name = "HiFi",
160 .stream_name = "HiFi", 160 .stream_name = "HiFi",
161 .codec_dai_name = "CS42888", 161 .codec_dai_name = "cs42888",
162 .ops = &imx_cs42888_surround_ops, 162 .ops = &imx_cs42888_surround_ops,
163 }, 163 },
164 { 164 {
@@ -171,7 +171,7 @@ static struct snd_soc_dai_link imx_cs42888_dai[] = {
171 { 171 {
172 .name = "HiFi-ASRC-BE", 172 .name = "HiFi-ASRC-BE",
173 .stream_name = "HiFi-ASRC-BE", 173 .stream_name = "HiFi-ASRC-BE",
174 .codec_dai_name = "CS42888", 174 .codec_dai_name = "cs42888",
175 .platform_name = "snd-soc-dummy", 175 .platform_name = "snd-soc-dummy",
176 .no_pcm = 1, 176 .no_pcm = 1,
177 .ops = &imx_cs42888_surround_ops_be, 177 .ops = &imx_cs42888_surround_ops_be,