diff options
author | Shengjiu Wang <b02247@freescale.com> | 2014-05-12 01:38:52 -0400 |
---|---|---|
committer | Shengjiu Wang <b02247@freescale.com> | 2014-05-13 05:47:53 -0400 |
commit | eabb472ee290efb05703303bb3b941e4e856bea6 (patch) | |
tree | d5bc6721538db1f841c1332b204d62033bd2c39e | |
parent | f70599478297a350f4074d2bb1218fb8c42b172a (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.txt | 29 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/sound/cs42xx8.txt | 28 | ||||
-rw-r--r-- | arch/arm/boot/dts/imx6qdl-sabreauto.dtsi | 2 | ||||
-rw-r--r-- | arch/arm/boot/dts/imx6sx-19x19-arm2.dts | 2 | ||||
-rw-r--r-- | arch/arm/configs/imx_v7_defconfig | 1 | ||||
-rw-r--r-- | sound/soc/codecs/Kconfig | 10 | ||||
-rw-r--r-- | sound/soc/codecs/Makefile | 6 | ||||
-rw-r--r-- | sound/soc/codecs/cs42888.c | 913 | ||||
-rw-r--r-- | sound/soc/codecs/cs42888.h | 123 | ||||
-rw-r--r-- | sound/soc/codecs/cs42xx8-i2c.c | 64 | ||||
-rw-r--r-- | sound/soc/codecs/cs42xx8.c | 602 | ||||
-rw-r--r-- | sound/soc/codecs/cs42xx8.h | 238 | ||||
-rw-r--r-- | sound/soc/fsl/imx-cs42888.c | 8 |
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 @@ | |||
1 | CS42888 audio CODEC | ||
2 | |||
3 | This device supports I2C only. | ||
4 | |||
5 | Required 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 | |||
15 | Note: cs42888 needs a regulators node and a clocks node. | ||
16 | |||
17 | Example: | ||
18 | In this case, the clock is external oscillator. | ||
19 | |||
20 | codec: cs42888@48 { | ||
21 | compatible = "cirrus,cs42888"; | ||
22 | reg = <0x048>; | ||
23 | clocks = <&codec_osc 0>; | ||
24 | clock-names = "codec_osc"; | ||
25 | VA-supply = <®_audio>; | ||
26 | VD-supply = <®_audio>; | ||
27 | VLS-supply = <®_audio>; | ||
28 | VLC-supply = <®_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 @@ | |||
1 | CS42448/CS42888 audio CODEC | ||
2 | |||
3 | Required 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 | |||
17 | Example: | ||
18 | |||
19 | codec: cs42888@48 { | ||
20 | compatible = "cirrus,cs42888"; | ||
21 | reg = <0x48>; | ||
22 | clocks = <&codec_mclk 0>; | ||
23 | clock-names = "mclk"; | ||
24 | VA-supply = <®_audio>; | ||
25 | VD-supply = <®_audio>; | ||
26 | VLS-supply = <®_audio>; | ||
27 | VLC-supply = <®_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 = <®_audio>; | 445 | VA-supply = <®_audio>; |
446 | VD-supply = <®_audio>; | 446 | VD-supply = <®_audio>; |
447 | VLS-supply = <®_audio>; | 447 | VLS-supply = <®_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 = <®_3p3v>; | 305 | VA-supply = <®_3p3v>; |
306 | VD-supply = <®_3p3v>; | 306 | VD-supply = <®_3p3v>; |
307 | VLS-supply = <®_3p3v>; | 307 | VLS-supply = <®_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 | |||
245 | CONFIG_SND_SOC_IMX_MC13783=y | 245 | CONFIG_SND_SOC_IMX_MC13783=y |
246 | CONFIG_SND_SOC_IMX_HDMI=y | 246 | CONFIG_SND_SOC_IMX_HDMI=y |
247 | CONFIG_SND_SOC_IMX_SI476X=y | 247 | CONFIG_SND_SOC_IMX_SI476X=y |
248 | CONFIG_SND_SOC_CS42XX8_I2C=y | ||
248 | CONFIG_USB=y | 249 | CONFIG_USB=y |
249 | CONFIG_USB_EHCI_HCD=y | 250 | CONFIG_USB_EHCI_HCD=y |
250 | CONFIG_USB_STORAGE=y | 251 | CONFIG_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 | |||
241 | config SND_SOC_CS4271 | 241 | config SND_SOC_CS4271 |
242 | tristate | 242 | tristate |
243 | 243 | ||
244 | config SND_SOC_CS42888 | 244 | config SND_SOC_CS42XX8 |
245 | tristate | 245 | tristate |
246 | 246 | ||
247 | config 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 | |||
247 | config SND_SOC_CX20442 | 253 | config 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 | |||
22 | snd-soc-cs42l73-objs := cs42l73.o | 22 | snd-soc-cs42l73-objs := cs42l73.o |
23 | snd-soc-cs4270-objs := cs4270.o | 23 | snd-soc-cs4270-objs := cs4270.o |
24 | snd-soc-cs4271-objs := cs4271.o | 24 | snd-soc-cs4271-objs := cs4271.o |
25 | snd-soc-cs42888-objs := cs42888.o | 25 | snd-soc-cs42xx8-objs := cs42xx8.o |
26 | snd-soc-cs42xx8-i2c-objs := cs42xx8-i2c.o | ||
26 | snd-soc-cx20442-objs := cx20442.o | 27 | snd-soc-cx20442-objs := cx20442.o |
27 | snd-soc-da7210-objs := da7210.o | 28 | snd-soc-da7210-objs := da7210.o |
28 | snd-soc-da7213-objs := da7213.o | 29 | snd-soc-da7213-objs := da7213.o |
@@ -150,7 +151,8 @@ obj-$(CONFIG_SND_SOC_CS42L52) += snd-soc-cs42l52.o | |||
150 | obj-$(CONFIG_SND_SOC_CS42L73) += snd-soc-cs42l73.o | 151 | obj-$(CONFIG_SND_SOC_CS42L73) += snd-soc-cs42l73.o |
151 | obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o | 152 | obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o |
152 | obj-$(CONFIG_SND_SOC_CS4271) += snd-soc-cs4271.o | 153 | obj-$(CONFIG_SND_SOC_CS4271) += snd-soc-cs4271.o |
153 | obj-$(CONFIG_SND_SOC_CS42888) += snd-soc-cs42888.o | 154 | obj-$(CONFIG_SND_SOC_CS42XX8) += snd-soc-cs42xx8.o |
155 | obj-$(CONFIG_SND_SOC_CS42XX8_I2C) += snd-soc-cs42xx8-i2c.o | ||
154 | obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o | 156 | obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o |
155 | obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o | 157 | obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o |
156 | obj-$(CONFIG_SND_SOC_DA7213) += snd-soc-da7213.o | 158 | obj-$(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 | ||
37 | static 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 */ | ||
48 | struct 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 | */ | ||
71 | static 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 | ||
90 | static 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 | ||
113 | static void dump_reg(struct snd_soc_codec *codec) | ||
114 | { | ||
115 | } | ||
116 | #endif | ||
117 | |||
118 | /* -127.5dB to 0dB with step of 0.5dB */ | ||
119 | static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1); | ||
120 | /* -64dB to 24dB with step of 0.5dB */ | ||
121 | static const DECLARE_TLV_DB_SCALE(adc_tlv, -6400, 50, 1); | ||
122 | |||
123 | static 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 | |||
129 | static 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 | |||
142 | static 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 | |||
156 | int 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 | |||
215 | static const char *cs42888_adcfilter[] = { "None", "High Pass" }; | ||
216 | static const char *cs42888_dacinvert[] = { "Disabled", "Enabled" }; | ||
217 | static const char *cs42888_adcinvert[] = { "Disabled", "Enabled" }; | ||
218 | static const char *cs42888_dacamute[] = { "Disabled", "AutoMute" }; | ||
219 | static const char *cs42888_dac_sngvol[] = { "Disabled", "Enabled" }; | ||
220 | static const char *cs42888_dac_szc[] = { "Immediate Change", "Zero Cross", | ||
221 | "Soft Ramp", "Soft Ramp on Zero Cross" }; | ||
222 | static const char *cs42888_mute_adc[] = { "UnMute", "Mute" }; | ||
223 | static const char *cs42888_adc_sngvol[] = { "Disabled", "Enabled" }; | ||
224 | static const char *cs42888_adc_szc[] = { "Immediate Change", "Zero Cross", | ||
225 | "Soft Ramp", "Soft Ramp on Zero Cross" }; | ||
226 | static const char *cs42888_dac_dem[] = { "No-De-Emphasis", "De-Emphasis" }; | ||
227 | static const char *cs42888_adc_single[] = { "Differential", "Single-Ended" }; | ||
228 | |||
229 | static 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 | |||
248 | static 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 | |||
280 | static 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 | |||
307 | static 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 | |||
348 | static 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 | */ | ||
381 | struct cs42888_mode_ratios { | ||
382 | unsigned int ratio; | ||
383 | u8 speed_mode; | ||
384 | u8 mclk; | ||
385 | }; | ||
386 | |||
387 | static 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 | */ | ||
413 | static 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 | */ | ||
436 | static 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 | */ | ||
513 | static 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 | */ | ||
584 | static 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 | |||
602 | static 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 | |||
620 | static 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 | |||
629 | static 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 | */ | ||
655 | static 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; | ||
721 | err: | ||
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 | */ | ||
733 | static 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 | */ | ||
749 | static 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 | */ | ||
765 | static 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 | */ | ||
821 | static 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 | */ | ||
830 | static struct i2c_device_id cs42888_i2c_id[] = { | ||
831 | {"cs42888", 0}, | ||
832 | {} | ||
833 | }; | ||
834 | MODULE_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 | |||
846 | static 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 | |||
854 | static 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 | |||
891 | static const struct of_device_id cs42888_dt_ids[] = { | ||
892 | { .compatible = "cirrus,cs42888", }, | ||
893 | { /* sentinel */ } | ||
894 | }; | ||
895 | |||
896 | static 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 | |||
909 | module_i2c_driver(cs42888_i2c_driver); | ||
910 | |||
911 | MODULE_AUTHOR("Freescale Semiconductor, Inc."); | ||
912 | MODULE_DESCRIPTION("Cirrus Logic CS42888 ALSA SoC Codec Driver"); | ||
913 | MODULE_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 | |||
20 | static 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 | |||
34 | static 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 | |||
42 | static struct i2c_device_id cs42xx8_i2c_id[] = { | ||
43 | {"cs42448", (kernel_ulong_t)&cs42448_data}, | ||
44 | {"cs42888", (kernel_ulong_t)&cs42888_data}, | ||
45 | {} | ||
46 | }; | ||
47 | MODULE_DEVICE_TABLE(i2c, cs42xx8_i2c_id); | ||
48 | |||
49 | static 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 | |||
60 | module_i2c_driver(cs42xx8_i2c_driver); | ||
61 | |||
62 | MODULE_DESCRIPTION("Cirrus Logic CS42448/CS42888 ALSA SoC Codec I2C Driver"); | ||
63 | MODULE_AUTHOR("Freescale Semiconductor, Inc."); | ||
64 | MODULE_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 | ||
26 | static 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 */ | ||
39 | struct 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 */ | ||
50 | static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1); | ||
51 | /* -64dB to 24dB with step of 0.5dB */ | ||
52 | static const DECLARE_TLV_DB_SCALE(adc_tlv, -6400, 50, 0); | ||
53 | |||
54 | static const char *const cs42xx8_adc_single[] = { "Differential", "Single-Ended" }; | ||
55 | static const char *const cs42xx8_szc[] = { "Immediate Change", "Zero Cross", | ||
56 | "Soft Ramp", "Soft Ramp on Zero Cross" }; | ||
57 | |||
58 | static const struct soc_enum adc1_single_enum = | ||
59 | SOC_ENUM_SINGLE(CS42XX8_ADCCTL, 4, 2, cs42xx8_adc_single); | ||
60 | static const struct soc_enum adc2_single_enum = | ||
61 | SOC_ENUM_SINGLE(CS42XX8_ADCCTL, 3, 2, cs42xx8_adc_single); | ||
62 | static const struct soc_enum adc3_single_enum = | ||
63 | SOC_ENUM_SINGLE(CS42XX8_ADCCTL, 2, 2, cs42xx8_adc_single); | ||
64 | static const struct soc_enum dac_szc_enum = | ||
65 | SOC_ENUM_SINGLE(CS42XX8_TXCTL, 5, 4, cs42xx8_szc); | ||
66 | static const struct soc_enum adc_szc_enum = | ||
67 | SOC_ENUM_SINGLE(CS42XX8_TXCTL, 0, 4, cs42xx8_szc); | ||
68 | |||
69 | static 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 | |||
100 | static 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 | |||
107 | static 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 | |||
133 | static 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 | |||
140 | static 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 | |||
168 | static 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 | |||
175 | struct cs42xx8_ratios { | ||
176 | unsigned int ratio; | ||
177 | unsigned char speed; | ||
178 | unsigned char mclk; | ||
179 | }; | ||
180 | |||
181 | static 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 | |||
193 | static 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 | |||
204 | static 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 | |||
247 | static 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 | |||
280 | static 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 | |||
291 | static 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 | |||
298 | static 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 | |||
315 | static 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 | |||
344 | static 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 | |||
354 | static 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 | |||
365 | const 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 | }; | ||
376 | EXPORT_SYMBOL_GPL(cs42xx8_regmap_config); | ||
377 | |||
378 | static 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 | |||
402 | static 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 | |||
414 | const struct cs42xx8_driver_data cs42448_data = { | ||
415 | .name = "cs42448", | ||
416 | .num_adcs = 3, | ||
417 | }; | ||
418 | EXPORT_SYMBOL_GPL(cs42448_data); | ||
419 | |||
420 | const struct cs42xx8_driver_data cs42888_data = { | ||
421 | .name = "cs42888", | ||
422 | .num_adcs = 2, | ||
423 | }; | ||
424 | EXPORT_SYMBOL_GPL(cs42888_data); | ||
425 | |||
426 | const struct of_device_id cs42xx8_of_match[] = { | ||
427 | { .compatible = "cirrus,cs42448", .data = &cs42448_data, }, | ||
428 | { .compatible = "cirrus,cs42888", .data = &cs42888_data, }, | ||
429 | { /* sentinel */ } | ||
430 | }; | ||
431 | MODULE_DEVICE_TABLE(of, cs42xx8_of_match); | ||
432 | EXPORT_SYMBOL_GPL(cs42xx8_of_match); | ||
433 | |||
434 | int 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 | |||
531 | err_enable: | ||
532 | regulator_bulk_disable(ARRAY_SIZE(cs42xx8->supplies), | ||
533 | cs42xx8->supplies); | ||
534 | |||
535 | return ret; | ||
536 | } | ||
537 | EXPORT_SYMBOL_GPL(cs42xx8_probe); | ||
538 | |||
539 | #ifdef CONFIG_PM_RUNTIME | ||
540 | static 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 | |||
571 | err_bulk: | ||
572 | regulator_bulk_disable(ARRAY_SIZE(cs42xx8->supplies), | ||
573 | cs42xx8->supplies); | ||
574 | err_clk: | ||
575 | clk_disable_unprepare(cs42xx8->clk); | ||
576 | |||
577 | return ret; | ||
578 | } | ||
579 | |||
580 | static 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 | |||
595 | const struct dev_pm_ops cs42xx8_pm = { | ||
596 | SET_RUNTIME_PM_OPS(cs42xx8_runtime_suspend, cs42xx8_runtime_resume, NULL) | ||
597 | }; | ||
598 | EXPORT_SYMBOL_GPL(cs42xx8_pm); | ||
599 | |||
600 | MODULE_DESCRIPTION("Cirrus Logic CS42448/CS42888 ALSA SoC Codec Driver"); | ||
601 | MODULE_AUTHOR("Freescale Semiconductor, Inc."); | ||
602 | MODULE_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 | |||
16 | struct cs42xx8_driver_data { | ||
17 | char name[32]; | ||
18 | int num_adcs; | ||
19 | }; | ||
20 | |||
21 | extern const struct dev_pm_ops cs42xx8_pm; | ||
22 | extern const struct cs42xx8_driver_data cs42448_data; | ||
23 | extern const struct cs42xx8_driver_data cs42888_data; | ||
24 | extern const struct regmap_config cs42xx8_regmap_config; | ||
25 | int 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 | ||
140 | static int be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, | 140 | static 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, |