diff options
-rw-r--r-- | Documentation/devicetree/bindings/sound/st,sta350.txt | 107 | ||||
-rw-r--r-- | include/sound/sta350.h | 52 | ||||
-rw-r--r-- | sound/soc/codecs/Kconfig | 5 | ||||
-rw-r--r-- | sound/soc/codecs/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/codecs/sta350.c | 1266 | ||||
-rw-r--r-- | sound/soc/codecs/sta350.h | 228 |
6 files changed, 1660 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/sound/st,sta350.txt b/Documentation/devicetree/bindings/sound/st,sta350.txt new file mode 100644 index 000000000000..950188891abd --- /dev/null +++ b/Documentation/devicetree/bindings/sound/st,sta350.txt | |||
@@ -0,0 +1,107 @@ | |||
1 | STA350 audio CODEC | ||
2 | |||
3 | The driver for this device only supports I2C. | ||
4 | |||
5 | Required properties: | ||
6 | |||
7 | - compatible: "st,sta350" | ||
8 | - reg: the I2C address of the device for I2C | ||
9 | - reset-gpios: a GPIO spec for the reset pin. If specified, it will be | ||
10 | deasserted before communication to the codec starts. | ||
11 | |||
12 | - power-down-gpios: a GPIO spec for the power down pin. If specified, | ||
13 | it will be deasserted before communication to the codec | ||
14 | starts. | ||
15 | |||
16 | - vdd-dig-supply: regulator spec, providing 3.3V | ||
17 | - vdd-pll-supply: regulator spec, providing 3.3V | ||
18 | - vcc-supply: regulator spec, providing 5V - 26V | ||
19 | |||
20 | Optional properties: | ||
21 | |||
22 | - st,output-conf: number, Selects the output configuration: | ||
23 | 0: 2-channel (full-bridge) power, 2-channel data-out | ||
24 | 1: 2 (half-bridge). 1 (full-bridge) on-board power | ||
25 | 2: 2 Channel (Full-Bridge) Power, 1 Channel FFX | ||
26 | 3: 1 Channel Mono-Parallel | ||
27 | If parameter is missing, mode 0 will be enabled. | ||
28 | |||
29 | - st,ch1-output-mapping: Channel 1 output mapping | ||
30 | - st,ch2-output-mapping: Channel 2 output mapping | ||
31 | - st,ch3-output-mapping: Channel 3 output mapping | ||
32 | 0: Channel 1 | ||
33 | 1: Channel 2 | ||
34 | 2: Channel 3 | ||
35 | If parameter is missing, channel 1 is choosen. | ||
36 | |||
37 | - st,thermal-warning-recover: | ||
38 | If present, thermal warning recovery is enabled. | ||
39 | |||
40 | - st,thermal-warning-adjustment: | ||
41 | If present, thermal warning adjustment is enabled. | ||
42 | |||
43 | - st,fault-detect-recovery: | ||
44 | If present, then fault recovery will be enabled. | ||
45 | |||
46 | - st,ffx-power-output-mode: string | ||
47 | The FFX power output mode selects how the FFX output timing is | ||
48 | configured. Must be one of these values: | ||
49 | - "drop-compensation" | ||
50 | - "tapered-compensation" | ||
51 | - "full-power-mode" | ||
52 | - "variable-drop-compensation" (default) | ||
53 | |||
54 | - st,drop-compensation-ns: number | ||
55 | Only required for "st,ffx-power-output-mode" == | ||
56 | "variable-drop-compensation". | ||
57 | Specifies the drop compensation in nanoseconds. | ||
58 | The value must be in the range of 0..300, and only | ||
59 | multiples of 20 are allowed. Default is 140ns. | ||
60 | |||
61 | - st,overcurrent-warning-adjustment: | ||
62 | If present, overcurrent warning adjustment is enabled. | ||
63 | |||
64 | - st,max-power-use-mpcc: | ||
65 | If present, then MPCC bits are used for MPC coefficients, | ||
66 | otherwise standard MPC coefficients are used. | ||
67 | |||
68 | - st,max-power-corr: | ||
69 | If present, power bridge correction for THD reduction near maximum | ||
70 | power output is enabled. | ||
71 | |||
72 | - st,am-reduction-mode: | ||
73 | If present, FFX mode runs in AM reduction mode, otherwise normal | ||
74 | FFX mode is used. | ||
75 | |||
76 | - st,odd-pwm-speed-mode: | ||
77 | If present, PWM speed mode run on odd speed mode (341.3 kHz) on all | ||
78 | channels. If not present, normal PWM spped mode (384 kHz) will be used. | ||
79 | |||
80 | - st,distortion-compensation: | ||
81 | If present, distortion compensation variable uses DCC coefficient. | ||
82 | If not present, preset DC coefficient is used. | ||
83 | |||
84 | - st,invalid-input-detect-mute: | ||
85 | If not present, automatic invalid input detect mute is enabled. | ||
86 | |||
87 | |||
88 | |||
89 | Example: | ||
90 | |||
91 | codec: sta350@38 { | ||
92 | compatible = "st,sta350"; | ||
93 | reg = <0x1c>; | ||
94 | reset-gpios = <&gpio1 19 0>; | ||
95 | power-down-gpios = <&gpio1 16 0>; | ||
96 | st,output-conf = <0x3>; // set output to 2-channel | ||
97 | // (full-bridge) power, | ||
98 | // 2-channel data-out | ||
99 | st,ch1-output-mapping = <0>; // set channel 1 output ch 1 | ||
100 | st,ch2-output-mapping = <0>; // set channel 2 output ch 1 | ||
101 | st,ch3-output-mapping = <0>; // set channel 3 output ch 1 | ||
102 | st,max-power-correction; // enables power bridge | ||
103 | // correction for THD reduction | ||
104 | // near maximum power output | ||
105 | st,invalid-input-detect-mute; // mute if no valid digital | ||
106 | // audio signal is provided. | ||
107 | }; | ||
diff --git a/include/sound/sta350.h b/include/sound/sta350.h new file mode 100644 index 000000000000..3a3298106b22 --- /dev/null +++ b/include/sound/sta350.h | |||
@@ -0,0 +1,52 @@ | |||
1 | /* | ||
2 | * Platform data for ST STA350 ASoC codec driver. | ||
3 | * | ||
4 | * Copyright: 2014 Raumfeld GmbH | ||
5 | * Author: Sven Brandau <info@brandau.biz> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License as published by the | ||
9 | * Free Software Foundation; either version 2 of the License, or (at your | ||
10 | * option) any later version. | ||
11 | */ | ||
12 | #ifndef __LINUX_SND__STA350_H | ||
13 | #define __LINUX_SND__STA350_H | ||
14 | |||
15 | #define STA350_OCFG_2CH 0 | ||
16 | #define STA350_OCFG_2_1CH 1 | ||
17 | #define STA350_OCFG_1CH 3 | ||
18 | |||
19 | #define STA350_OM_CH1 0 | ||
20 | #define STA350_OM_CH2 1 | ||
21 | #define STA350_OM_CH3 2 | ||
22 | |||
23 | #define STA350_THERMAL_ADJUSTMENT_ENABLE 1 | ||
24 | #define STA350_THERMAL_RECOVERY_ENABLE 2 | ||
25 | #define STA350_FAULT_DETECT_RECOVERY_BYPASS 1 | ||
26 | |||
27 | #define STA350_FFX_PM_DROP_COMP 0 | ||
28 | #define STA350_FFX_PM_TAPERED_COMP 1 | ||
29 | #define STA350_FFX_PM_FULL_POWER 2 | ||
30 | #define STA350_FFX_PM_VARIABLE_DROP_COMP 3 | ||
31 | |||
32 | |||
33 | struct sta350_platform_data { | ||
34 | u8 output_conf; | ||
35 | u8 ch1_output_mapping; | ||
36 | u8 ch2_output_mapping; | ||
37 | u8 ch3_output_mapping; | ||
38 | u8 ffx_power_output_mode; | ||
39 | u8 drop_compensation_ns; | ||
40 | unsigned int thermal_warning_recovery:1; | ||
41 | unsigned int thermal_warning_adjustment:1; | ||
42 | unsigned int fault_detect_recovery:1; | ||
43 | unsigned int oc_warning_adjustment:1; | ||
44 | unsigned int max_power_use_mpcc:1; | ||
45 | unsigned int max_power_correction:1; | ||
46 | unsigned int am_reduction_mode:1; | ||
47 | unsigned int odd_pwm_speed_mode:1; | ||
48 | unsigned int distortion_compensation:1; | ||
49 | unsigned int invalid_input_detect_mute:1; | ||
50 | }; | ||
51 | |||
52 | #endif /* __LINUX_SND__STA350_H */ | ||
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index f0e840137887..c7b853f520cf 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig | |||
@@ -80,6 +80,7 @@ config SND_SOC_ALL_CODECS | |||
80 | select SND_SOC_SSM2602_SPI if SPI_MASTER | 80 | select SND_SOC_SSM2602_SPI if SPI_MASTER |
81 | select SND_SOC_SSM2602_I2C if I2C | 81 | select SND_SOC_SSM2602_I2C if I2C |
82 | select SND_SOC_STA32X if I2C | 82 | select SND_SOC_STA32X if I2C |
83 | select SND_SOC_STA350 if I2C | ||
83 | select SND_SOC_STA529 if I2C | 84 | select SND_SOC_STA529 if I2C |
84 | select SND_SOC_STAC9766 if SND_SOC_AC97_BUS | 85 | select SND_SOC_STAC9766 if SND_SOC_AC97_BUS |
85 | select SND_SOC_TAS5086 if I2C | 86 | select SND_SOC_TAS5086 if I2C |
@@ -435,6 +436,10 @@ config SND_SOC_SSM2602_I2C | |||
435 | config SND_SOC_STA32X | 436 | config SND_SOC_STA32X |
436 | tristate | 437 | tristate |
437 | 438 | ||
439 | config SND_SOC_STA350 | ||
440 | tristate "STA350 speaker amplifier" | ||
441 | depends on I2C | ||
442 | |||
438 | config SND_SOC_STA529 | 443 | config SND_SOC_STA529 |
439 | tristate | 444 | tristate |
440 | 445 | ||
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 3c4d275d064b..efdb4d060201 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile | |||
@@ -74,6 +74,7 @@ snd-soc-ssm2602-objs := ssm2602.o | |||
74 | snd-soc-ssm2602-spi-objs := ssm2602-spi.o | 74 | snd-soc-ssm2602-spi-objs := ssm2602-spi.o |
75 | snd-soc-ssm2602-i2c-objs := ssm2602-i2c.o | 75 | snd-soc-ssm2602-i2c-objs := ssm2602-i2c.o |
76 | snd-soc-sta32x-objs := sta32x.o | 76 | snd-soc-sta32x-objs := sta32x.o |
77 | snd-soc-sta350-objs := sta350.o | ||
77 | snd-soc-sta529-objs := sta529.o | 78 | snd-soc-sta529-objs := sta529.o |
78 | snd-soc-stac9766-objs := stac9766.o | 79 | snd-soc-stac9766-objs := stac9766.o |
79 | snd-soc-tas5086-objs := tas5086.o | 80 | snd-soc-tas5086-objs := tas5086.o |
@@ -221,6 +222,7 @@ obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o | |||
221 | obj-$(CONFIG_SND_SOC_SSM2602_SPI) += snd-soc-ssm2602-spi.o | 222 | obj-$(CONFIG_SND_SOC_SSM2602_SPI) += snd-soc-ssm2602-spi.o |
222 | obj-$(CONFIG_SND_SOC_SSM2602_I2C) += snd-soc-ssm2602-i2c.o | 223 | obj-$(CONFIG_SND_SOC_SSM2602_I2C) += snd-soc-ssm2602-i2c.o |
223 | obj-$(CONFIG_SND_SOC_STA32X) += snd-soc-sta32x.o | 224 | obj-$(CONFIG_SND_SOC_STA32X) += snd-soc-sta32x.o |
225 | obj-$(CONFIG_SND_SOC_STA350) += snd-soc-sta350.o | ||
224 | obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o | 226 | obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o |
225 | obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o | 227 | obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o |
226 | obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o | 228 | obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o |
diff --git a/sound/soc/codecs/sta350.c b/sound/soc/codecs/sta350.c new file mode 100644 index 000000000000..552e92a6b770 --- /dev/null +++ b/sound/soc/codecs/sta350.c | |||
@@ -0,0 +1,1266 @@ | |||
1 | /* | ||
2 | * Codec driver for ST STA350 2.1-channel high-efficiency digital audio system | ||
3 | * | ||
4 | * Copyright: 2014 Raumfeld GmbH | ||
5 | * Author: Sven Brandau <info@brandau.biz> | ||
6 | * | ||
7 | * based on code from: | ||
8 | * Raumfeld GmbH | ||
9 | * Johannes Stezenbach <js@sig21.net> | ||
10 | * Wolfson Microelectronics PLC. | ||
11 | * Mark Brown <broonie@opensource.wolfsonmicro.com> | ||
12 | * Freescale Semiconductor, Inc. | ||
13 | * Timur Tabi <timur@freescale.com> | ||
14 | * | ||
15 | * This program is free software; you can redistribute it and/or modify it | ||
16 | * under the terms of the GNU General Public License as published by the | ||
17 | * Free Software Foundation; either version 2 of the License, or (at your | ||
18 | * option) any later version. | ||
19 | */ | ||
20 | |||
21 | #define pr_fmt(fmt) KBUILD_MODNAME ":%s:%d: " fmt, __func__, __LINE__ | ||
22 | |||
23 | #include <linux/module.h> | ||
24 | #include <linux/moduleparam.h> | ||
25 | #include <linux/init.h> | ||
26 | #include <linux/delay.h> | ||
27 | #include <linux/pm.h> | ||
28 | #include <linux/i2c.h> | ||
29 | #include <linux/of_device.h> | ||
30 | #include <linux/of_gpio.h> | ||
31 | #include <linux/regmap.h> | ||
32 | #include <linux/regulator/consumer.h> | ||
33 | #include <linux/gpio/consumer.h> | ||
34 | #include <linux/slab.h> | ||
35 | #include <sound/core.h> | ||
36 | #include <sound/pcm.h> | ||
37 | #include <sound/pcm_params.h> | ||
38 | #include <sound/soc.h> | ||
39 | #include <sound/soc-dapm.h> | ||
40 | #include <sound/initval.h> | ||
41 | #include <sound/tlv.h> | ||
42 | |||
43 | #include <sound/sta350.h> | ||
44 | #include "sta350.h" | ||
45 | |||
46 | #define STA350_RATES (SNDRV_PCM_RATE_32000 | \ | ||
47 | SNDRV_PCM_RATE_44100 | \ | ||
48 | SNDRV_PCM_RATE_48000 | \ | ||
49 | SNDRV_PCM_RATE_88200 | \ | ||
50 | SNDRV_PCM_RATE_96000 | \ | ||
51 | SNDRV_PCM_RATE_176400 | \ | ||
52 | SNDRV_PCM_RATE_192000) | ||
53 | |||
54 | #define STA350_FORMATS \ | ||
55 | (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \ | ||
56 | SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \ | ||
57 | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \ | ||
58 | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \ | ||
59 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE | \ | ||
60 | SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE) | ||
61 | |||
62 | /* Power-up register defaults */ | ||
63 | static const struct reg_default sta350_regs[] = { | ||
64 | { 0x0, 0x63 }, | ||
65 | { 0x1, 0x80 }, | ||
66 | { 0x2, 0xdf }, | ||
67 | { 0x3, 0x40 }, | ||
68 | { 0x4, 0xc2 }, | ||
69 | { 0x5, 0x5c }, | ||
70 | { 0x6, 0x00 }, | ||
71 | { 0x7, 0xff }, | ||
72 | { 0x8, 0x60 }, | ||
73 | { 0x9, 0x60 }, | ||
74 | { 0xa, 0x60 }, | ||
75 | { 0xb, 0x00 }, | ||
76 | { 0xc, 0x00 }, | ||
77 | { 0xd, 0x00 }, | ||
78 | { 0xe, 0x00 }, | ||
79 | { 0xf, 0x40 }, | ||
80 | { 0x10, 0x80 }, | ||
81 | { 0x11, 0x77 }, | ||
82 | { 0x12, 0x6a }, | ||
83 | { 0x13, 0x69 }, | ||
84 | { 0x14, 0x6a }, | ||
85 | { 0x15, 0x69 }, | ||
86 | { 0x16, 0x00 }, | ||
87 | { 0x17, 0x00 }, | ||
88 | { 0x18, 0x00 }, | ||
89 | { 0x19, 0x00 }, | ||
90 | { 0x1a, 0x00 }, | ||
91 | { 0x1b, 0x00 }, | ||
92 | { 0x1c, 0x00 }, | ||
93 | { 0x1d, 0x00 }, | ||
94 | { 0x1e, 0x00 }, | ||
95 | { 0x1f, 0x00 }, | ||
96 | { 0x20, 0x00 }, | ||
97 | { 0x21, 0x00 }, | ||
98 | { 0x22, 0x00 }, | ||
99 | { 0x23, 0x00 }, | ||
100 | { 0x24, 0x00 }, | ||
101 | { 0x25, 0x00 }, | ||
102 | { 0x26, 0x00 }, | ||
103 | { 0x27, 0x2a }, | ||
104 | { 0x28, 0xc0 }, | ||
105 | { 0x29, 0xf3 }, | ||
106 | { 0x2a, 0x33 }, | ||
107 | { 0x2b, 0x00 }, | ||
108 | { 0x2c, 0x0c }, | ||
109 | { 0x31, 0x00 }, | ||
110 | { 0x36, 0x00 }, | ||
111 | { 0x37, 0x00 }, | ||
112 | { 0x38, 0x00 }, | ||
113 | { 0x39, 0x01 }, | ||
114 | { 0x3a, 0xee }, | ||
115 | { 0x3b, 0xff }, | ||
116 | { 0x3c, 0x7e }, | ||
117 | { 0x3d, 0xc0 }, | ||
118 | { 0x3e, 0x26 }, | ||
119 | { 0x3f, 0x00 }, | ||
120 | { 0x48, 0x00 }, | ||
121 | { 0x49, 0x00 }, | ||
122 | { 0x4a, 0x00 }, | ||
123 | { 0x4b, 0x04 }, | ||
124 | { 0x4c, 0x00 }, | ||
125 | }; | ||
126 | |||
127 | static const struct regmap_range sta350_write_regs_range[] = { | ||
128 | regmap_reg_range(STA350_CONFA, STA350_AUTO2), | ||
129 | regmap_reg_range(STA350_C1CFG, STA350_FDRC2), | ||
130 | regmap_reg_range(STA350_EQCFG, STA350_EVOLRES), | ||
131 | regmap_reg_range(STA350_NSHAPE, STA350_MISC2), | ||
132 | }; | ||
133 | |||
134 | static const struct regmap_range sta350_read_regs_range[] = { | ||
135 | regmap_reg_range(STA350_CONFA, STA350_AUTO2), | ||
136 | regmap_reg_range(STA350_C1CFG, STA350_STATUS), | ||
137 | regmap_reg_range(STA350_EQCFG, STA350_EVOLRES), | ||
138 | regmap_reg_range(STA350_NSHAPE, STA350_MISC2), | ||
139 | }; | ||
140 | |||
141 | static const struct regmap_range sta350_volatile_regs_range[] = { | ||
142 | regmap_reg_range(STA350_CFADDR2, STA350_CFUD), | ||
143 | regmap_reg_range(STA350_STATUS, STA350_STATUS), | ||
144 | }; | ||
145 | |||
146 | static const struct regmap_access_table sta350_write_regs = { | ||
147 | .yes_ranges = sta350_write_regs_range, | ||
148 | .n_yes_ranges = ARRAY_SIZE(sta350_write_regs_range), | ||
149 | }; | ||
150 | |||
151 | static const struct regmap_access_table sta350_read_regs = { | ||
152 | .yes_ranges = sta350_read_regs_range, | ||
153 | .n_yes_ranges = ARRAY_SIZE(sta350_read_regs_range), | ||
154 | }; | ||
155 | |||
156 | static const struct regmap_access_table sta350_volatile_regs = { | ||
157 | .yes_ranges = sta350_volatile_regs_range, | ||
158 | .n_yes_ranges = ARRAY_SIZE(sta350_volatile_regs_range), | ||
159 | }; | ||
160 | |||
161 | /* regulator power supply names */ | ||
162 | static const char * const sta350_supply_names[] = { | ||
163 | "vdd-dig", /* digital supply, 3.3V */ | ||
164 | "vdd-pll", /* pll supply, 3.3V */ | ||
165 | "vcc" /* power amp supply, 5V - 26V */ | ||
166 | }; | ||
167 | |||
168 | /* codec private data */ | ||
169 | struct sta350_priv { | ||
170 | struct regmap *regmap; | ||
171 | struct regulator_bulk_data supplies[ARRAY_SIZE(sta350_supply_names)]; | ||
172 | struct sta350_platform_data *pdata; | ||
173 | |||
174 | unsigned int mclk; | ||
175 | unsigned int format; | ||
176 | |||
177 | u32 coef_shadow[STA350_COEF_COUNT]; | ||
178 | int shutdown; | ||
179 | |||
180 | struct gpio_desc *gpiod_nreset; | ||
181 | struct gpio_desc *gpiod_power_down; | ||
182 | |||
183 | struct mutex coeff_lock; | ||
184 | }; | ||
185 | |||
186 | static const DECLARE_TLV_DB_SCALE(mvol_tlv, -12750, 50, 1); | ||
187 | static const DECLARE_TLV_DB_SCALE(chvol_tlv, -7950, 50, 1); | ||
188 | static const DECLARE_TLV_DB_SCALE(tone_tlv, -1200, 200, 0); | ||
189 | |||
190 | static const char * const sta350_drc_ac[] = { | ||
191 | "Anti-Clipping", "Dynamic Range Compression" | ||
192 | }; | ||
193 | static const char * const sta350_auto_gc_mode[] = { | ||
194 | "User", "AC no clipping", "AC limited clipping (10%)", | ||
195 | "DRC nighttime listening mode" | ||
196 | }; | ||
197 | static const char * const sta350_auto_xo_mode[] = { | ||
198 | "User", "80Hz", "100Hz", "120Hz", "140Hz", "160Hz", "180Hz", | ||
199 | "200Hz", "220Hz", "240Hz", "260Hz", "280Hz", "300Hz", "320Hz", | ||
200 | "340Hz", "360Hz" | ||
201 | }; | ||
202 | static const char * const sta350_binary_output[] = { | ||
203 | "FFX 3-state output - normal operation", "Binary output" | ||
204 | }; | ||
205 | static const char * const sta350_limiter_select[] = { | ||
206 | "Limiter Disabled", "Limiter #1", "Limiter #2" | ||
207 | }; | ||
208 | static const char * const sta350_limiter_attack_rate[] = { | ||
209 | "3.1584", "2.7072", "2.2560", "1.8048", "1.3536", "0.9024", | ||
210 | "0.4512", "0.2256", "0.1504", "0.1123", "0.0902", "0.0752", | ||
211 | "0.0645", "0.0564", "0.0501", "0.0451" | ||
212 | }; | ||
213 | static const char * const sta350_limiter_release_rate[] = { | ||
214 | "0.5116", "0.1370", "0.0744", "0.0499", "0.0360", "0.0299", | ||
215 | "0.0264", "0.0208", "0.0198", "0.0172", "0.0147", "0.0137", | ||
216 | "0.0134", "0.0117", "0.0110", "0.0104" | ||
217 | }; | ||
218 | static const char * const sta350_noise_shaper_type[] = { | ||
219 | "Third order", "Fourth order" | ||
220 | }; | ||
221 | |||
222 | static DECLARE_TLV_DB_RANGE(sta350_limiter_ac_attack_tlv, | ||
223 | 0, 7, TLV_DB_SCALE_ITEM(-1200, 200, 0), | ||
224 | 8, 16, TLV_DB_SCALE_ITEM(300, 100, 0), | ||
225 | ); | ||
226 | |||
227 | static DECLARE_TLV_DB_RANGE(sta350_limiter_ac_release_tlv, | ||
228 | 0, 0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 0), | ||
229 | 1, 1, TLV_DB_SCALE_ITEM(-2900, 0, 0), | ||
230 | 2, 2, TLV_DB_SCALE_ITEM(-2000, 0, 0), | ||
231 | 3, 8, TLV_DB_SCALE_ITEM(-1400, 200, 0), | ||
232 | 8, 16, TLV_DB_SCALE_ITEM(-700, 100, 0), | ||
233 | ); | ||
234 | |||
235 | static DECLARE_TLV_DB_RANGE(sta350_limiter_drc_attack_tlv, | ||
236 | 0, 7, TLV_DB_SCALE_ITEM(-3100, 200, 0), | ||
237 | 8, 13, TLV_DB_SCALE_ITEM(-1600, 100, 0), | ||
238 | 14, 16, TLV_DB_SCALE_ITEM(-1000, 300, 0), | ||
239 | ); | ||
240 | |||
241 | static DECLARE_TLV_DB_RANGE(sta350_limiter_drc_release_tlv, | ||
242 | 0, 0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 0), | ||
243 | 1, 2, TLV_DB_SCALE_ITEM(-3800, 200, 0), | ||
244 | 3, 4, TLV_DB_SCALE_ITEM(-3300, 200, 0), | ||
245 | 5, 12, TLV_DB_SCALE_ITEM(-3000, 200, 0), | ||
246 | 13, 16, TLV_DB_SCALE_ITEM(-1500, 300, 0), | ||
247 | ); | ||
248 | |||
249 | static SOC_ENUM_SINGLE_DECL(sta350_drc_ac_enum, | ||
250 | STA350_CONFD, STA350_CONFD_DRC_SHIFT, | ||
251 | sta350_drc_ac); | ||
252 | static SOC_ENUM_SINGLE_DECL(sta350_noise_shaper_enum, | ||
253 | STA350_CONFE, STA350_CONFE_NSBW_SHIFT, | ||
254 | sta350_noise_shaper_type); | ||
255 | static SOC_ENUM_SINGLE_DECL(sta350_auto_gc_enum, | ||
256 | STA350_AUTO1, STA350_AUTO1_AMGC_SHIFT, | ||
257 | sta350_auto_gc_mode); | ||
258 | static SOC_ENUM_SINGLE_DECL(sta350_auto_xo_enum, | ||
259 | STA350_AUTO2, STA350_AUTO2_XO_SHIFT, | ||
260 | sta350_auto_xo_mode); | ||
261 | static SOC_ENUM_SINGLE_DECL(sta350_binary_output_ch1_enum, | ||
262 | STA350_C1CFG, STA350_CxCFG_BO_SHIFT, | ||
263 | sta350_binary_output); | ||
264 | static SOC_ENUM_SINGLE_DECL(sta350_binary_output_ch2_enum, | ||
265 | STA350_C2CFG, STA350_CxCFG_BO_SHIFT, | ||
266 | sta350_binary_output); | ||
267 | static SOC_ENUM_SINGLE_DECL(sta350_binary_output_ch3_enum, | ||
268 | STA350_C3CFG, STA350_CxCFG_BO_SHIFT, | ||
269 | sta350_binary_output); | ||
270 | static SOC_ENUM_SINGLE_DECL(sta350_limiter_ch1_enum, | ||
271 | STA350_C1CFG, STA350_CxCFG_LS_SHIFT, | ||
272 | sta350_limiter_select); | ||
273 | static SOC_ENUM_SINGLE_DECL(sta350_limiter_ch2_enum, | ||
274 | STA350_C2CFG, STA350_CxCFG_LS_SHIFT, | ||
275 | sta350_limiter_select); | ||
276 | static SOC_ENUM_SINGLE_DECL(sta350_limiter_ch3_enum, | ||
277 | STA350_C3CFG, STA350_CxCFG_LS_SHIFT, | ||
278 | sta350_limiter_select); | ||
279 | static SOC_ENUM_SINGLE_DECL(sta350_limiter1_attack_rate_enum, | ||
280 | STA350_L1AR, STA350_LxA_SHIFT, | ||
281 | sta350_limiter_attack_rate); | ||
282 | static SOC_ENUM_SINGLE_DECL(sta350_limiter2_attack_rate_enum, | ||
283 | STA350_L2AR, STA350_LxA_SHIFT, | ||
284 | sta350_limiter_attack_rate); | ||
285 | static SOC_ENUM_SINGLE_DECL(sta350_limiter1_release_rate_enum, | ||
286 | STA350_L1AR, STA350_LxR_SHIFT, | ||
287 | sta350_limiter_release_rate); | ||
288 | static SOC_ENUM_SINGLE_DECL(sta350_limiter2_release_rate_enum, | ||
289 | STA350_L2AR, STA350_LxR_SHIFT, | ||
290 | sta350_limiter_release_rate); | ||
291 | |||
292 | /* | ||
293 | * byte array controls for setting biquad, mixer, scaling coefficients; | ||
294 | * for biquads all five coefficients need to be set in one go, | ||
295 | * mixer and pre/postscale coefs can be set individually; | ||
296 | * each coef is 24bit, the bytes are ordered in the same way | ||
297 | * as given in the STA350 data sheet (big endian; b1, b2, a1, a2, b0) | ||
298 | */ | ||
299 | |||
300 | static int sta350_coefficient_info(struct snd_kcontrol *kcontrol, | ||
301 | struct snd_ctl_elem_info *uinfo) | ||
302 | { | ||
303 | int numcoef = kcontrol->private_value >> 16; | ||
304 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; | ||
305 | uinfo->count = 3 * numcoef; | ||
306 | return 0; | ||
307 | } | ||
308 | |||
309 | static int sta350_coefficient_get(struct snd_kcontrol *kcontrol, | ||
310 | struct snd_ctl_elem_value *ucontrol) | ||
311 | { | ||
312 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
313 | struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec); | ||
314 | int numcoef = kcontrol->private_value >> 16; | ||
315 | int index = kcontrol->private_value & 0xffff; | ||
316 | unsigned int cfud, val; | ||
317 | int i, ret = 0; | ||
318 | |||
319 | mutex_lock(&sta350->coeff_lock); | ||
320 | |||
321 | /* preserve reserved bits in STA350_CFUD */ | ||
322 | regmap_read(sta350->regmap, STA350_CFUD, &cfud); | ||
323 | cfud &= 0xf0; | ||
324 | /* | ||
325 | * chip documentation does not say if the bits are self clearing, | ||
326 | * so do it explicitly | ||
327 | */ | ||
328 | regmap_write(sta350->regmap, STA350_CFUD, cfud); | ||
329 | |||
330 | regmap_write(sta350->regmap, STA350_CFADDR2, index); | ||
331 | if (numcoef == 1) { | ||
332 | regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x04); | ||
333 | } else if (numcoef == 5) { | ||
334 | regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x08); | ||
335 | } else { | ||
336 | ret = -EINVAL; | ||
337 | goto exit_unlock; | ||
338 | } | ||
339 | |||
340 | for (i = 0; i < 3 * numcoef; i++) { | ||
341 | regmap_read(sta350->regmap, STA350_B1CF1 + i, &val); | ||
342 | ucontrol->value.bytes.data[i] = val; | ||
343 | } | ||
344 | |||
345 | exit_unlock: | ||
346 | mutex_unlock(&sta350->coeff_lock); | ||
347 | |||
348 | return ret; | ||
349 | } | ||
350 | |||
351 | static int sta350_coefficient_put(struct snd_kcontrol *kcontrol, | ||
352 | struct snd_ctl_elem_value *ucontrol) | ||
353 | { | ||
354 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
355 | struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec); | ||
356 | int numcoef = kcontrol->private_value >> 16; | ||
357 | int index = kcontrol->private_value & 0xffff; | ||
358 | unsigned int cfud; | ||
359 | int i; | ||
360 | |||
361 | /* preserve reserved bits in STA350_CFUD */ | ||
362 | regmap_read(sta350->regmap, STA350_CFUD, &cfud); | ||
363 | cfud &= 0xf0; | ||
364 | /* | ||
365 | * chip documentation does not say if the bits are self clearing, | ||
366 | * so do it explicitly | ||
367 | */ | ||
368 | regmap_write(sta350->regmap, STA350_CFUD, cfud); | ||
369 | |||
370 | regmap_write(sta350->regmap, STA350_CFADDR2, index); | ||
371 | for (i = 0; i < numcoef && (index + i < STA350_COEF_COUNT); i++) | ||
372 | sta350->coef_shadow[index + i] = | ||
373 | (ucontrol->value.bytes.data[3 * i] << 16) | ||
374 | | (ucontrol->value.bytes.data[3 * i + 1] << 8) | ||
375 | | (ucontrol->value.bytes.data[3 * i + 2]); | ||
376 | for (i = 0; i < 3 * numcoef; i++) | ||
377 | regmap_write(sta350->regmap, STA350_B1CF1 + i, | ||
378 | ucontrol->value.bytes.data[i]); | ||
379 | if (numcoef == 1) | ||
380 | regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x01); | ||
381 | else if (numcoef == 5) | ||
382 | regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x02); | ||
383 | else | ||
384 | return -EINVAL; | ||
385 | |||
386 | return 0; | ||
387 | } | ||
388 | |||
389 | static int sta350_sync_coef_shadow(struct snd_soc_codec *codec) | ||
390 | { | ||
391 | struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec); | ||
392 | unsigned int cfud; | ||
393 | int i; | ||
394 | |||
395 | /* preserve reserved bits in STA350_CFUD */ | ||
396 | regmap_read(sta350->regmap, STA350_CFUD, &cfud); | ||
397 | cfud &= 0xf0; | ||
398 | |||
399 | for (i = 0; i < STA350_COEF_COUNT; i++) { | ||
400 | regmap_write(sta350->regmap, STA350_CFADDR2, i); | ||
401 | regmap_write(sta350->regmap, STA350_B1CF1, | ||
402 | (sta350->coef_shadow[i] >> 16) & 0xff); | ||
403 | regmap_write(sta350->regmap, STA350_B1CF2, | ||
404 | (sta350->coef_shadow[i] >> 8) & 0xff); | ||
405 | regmap_write(sta350->regmap, STA350_B1CF3, | ||
406 | (sta350->coef_shadow[i]) & 0xff); | ||
407 | /* | ||
408 | * chip documentation does not say if the bits are | ||
409 | * self-clearing, so do it explicitly | ||
410 | */ | ||
411 | regmap_write(sta350->regmap, STA350_CFUD, cfud); | ||
412 | regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x01); | ||
413 | } | ||
414 | return 0; | ||
415 | } | ||
416 | |||
417 | static int sta350_cache_sync(struct snd_soc_codec *codec) | ||
418 | { | ||
419 | struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec); | ||
420 | unsigned int mute; | ||
421 | int rc; | ||
422 | |||
423 | /* mute during register sync */ | ||
424 | regmap_read(sta350->regmap, STA350_CFUD, &mute); | ||
425 | regmap_write(sta350->regmap, STA350_MMUTE, mute | STA350_MMUTE_MMUTE); | ||
426 | sta350_sync_coef_shadow(codec); | ||
427 | rc = regcache_sync(sta350->regmap); | ||
428 | regmap_write(sta350->regmap, STA350_MMUTE, mute); | ||
429 | return rc; | ||
430 | } | ||
431 | |||
432 | #define SINGLE_COEF(xname, index) \ | ||
433 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ | ||
434 | .info = sta350_coefficient_info, \ | ||
435 | .get = sta350_coefficient_get,\ | ||
436 | .put = sta350_coefficient_put, \ | ||
437 | .private_value = index | (1 << 16) } | ||
438 | |||
439 | #define BIQUAD_COEFS(xname, index) \ | ||
440 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ | ||
441 | .info = sta350_coefficient_info, \ | ||
442 | .get = sta350_coefficient_get,\ | ||
443 | .put = sta350_coefficient_put, \ | ||
444 | .private_value = index | (5 << 16) } | ||
445 | |||
446 | static const struct snd_kcontrol_new sta350_snd_controls[] = { | ||
447 | SOC_SINGLE_TLV("Master Volume", STA350_MVOL, 0, 0xff, 1, mvol_tlv), | ||
448 | /* VOL */ | ||
449 | SOC_SINGLE_TLV("Ch1 Volume", STA350_C1VOL, 0, 0xff, 1, chvol_tlv), | ||
450 | SOC_SINGLE_TLV("Ch2 Volume", STA350_C2VOL, 0, 0xff, 1, chvol_tlv), | ||
451 | SOC_SINGLE_TLV("Ch3 Volume", STA350_C3VOL, 0, 0xff, 1, chvol_tlv), | ||
452 | /* CONFD */ | ||
453 | SOC_SINGLE("High Pass Filter Bypass Switch", | ||
454 | STA350_CONFD, STA350_CONFD_HPB_SHIFT, 1, 1), | ||
455 | SOC_SINGLE("De-emphasis Filter Switch", | ||
456 | STA350_CONFD, STA350_CONFD_DEMP_SHIFT, 1, 0), | ||
457 | SOC_SINGLE("DSP Bypass Switch", | ||
458 | STA350_CONFD, STA350_CONFD_DSPB_SHIFT, 1, 0), | ||
459 | SOC_SINGLE("Post-scale Link Switch", | ||
460 | STA350_CONFD, STA350_CONFD_PSL_SHIFT, 1, 0), | ||
461 | SOC_SINGLE("Biquad Coefficient Link Switch", | ||
462 | STA350_CONFD, STA350_CONFD_BQL_SHIFT, 1, 0), | ||
463 | SOC_ENUM("Compressor/Limiter Switch", sta350_drc_ac_enum), | ||
464 | SOC_ENUM("Noise Shaper Bandwidth", sta350_noise_shaper_enum), | ||
465 | SOC_SINGLE("Zero-detect Mute Enable Switch", | ||
466 | STA350_CONFD, STA350_CONFD_ZDE_SHIFT, 1, 0), | ||
467 | SOC_SINGLE("Submix Mode Switch", | ||
468 | STA350_CONFD, STA350_CONFD_SME_SHIFT, 1, 0), | ||
469 | /* CONFE */ | ||
470 | SOC_SINGLE("Zero Cross Switch", STA350_CONFE, STA350_CONFE_ZCE_SHIFT, 1, 0), | ||
471 | SOC_SINGLE("Soft Ramp Switch", STA350_CONFE, STA350_CONFE_SVE_SHIFT, 1, 0), | ||
472 | /* MUTE */ | ||
473 | SOC_SINGLE("Master Switch", STA350_MMUTE, STA350_MMUTE_MMUTE_SHIFT, 1, 1), | ||
474 | SOC_SINGLE("Ch1 Switch", STA350_MMUTE, STA350_MMUTE_C1M_SHIFT, 1, 1), | ||
475 | SOC_SINGLE("Ch2 Switch", STA350_MMUTE, STA350_MMUTE_C2M_SHIFT, 1, 1), | ||
476 | SOC_SINGLE("Ch3 Switch", STA350_MMUTE, STA350_MMUTE_C3M_SHIFT, 1, 1), | ||
477 | /* AUTOx */ | ||
478 | SOC_ENUM("Automode GC", sta350_auto_gc_enum), | ||
479 | SOC_ENUM("Automode XO", sta350_auto_xo_enum), | ||
480 | /* CxCFG */ | ||
481 | SOC_SINGLE("Ch1 Tone Control Bypass Switch", | ||
482 | STA350_C1CFG, STA350_CxCFG_TCB_SHIFT, 1, 0), | ||
483 | SOC_SINGLE("Ch2 Tone Control Bypass Switch", | ||
484 | STA350_C2CFG, STA350_CxCFG_TCB_SHIFT, 1, 0), | ||
485 | SOC_SINGLE("Ch1 EQ Bypass Switch", | ||
486 | STA350_C1CFG, STA350_CxCFG_EQBP_SHIFT, 1, 0), | ||
487 | SOC_SINGLE("Ch2 EQ Bypass Switch", | ||
488 | STA350_C2CFG, STA350_CxCFG_EQBP_SHIFT, 1, 0), | ||
489 | SOC_SINGLE("Ch1 Master Volume Bypass Switch", | ||
490 | STA350_C1CFG, STA350_CxCFG_VBP_SHIFT, 1, 0), | ||
491 | SOC_SINGLE("Ch2 Master Volume Bypass Switch", | ||
492 | STA350_C1CFG, STA350_CxCFG_VBP_SHIFT, 1, 0), | ||
493 | SOC_SINGLE("Ch3 Master Volume Bypass Switch", | ||
494 | STA350_C1CFG, STA350_CxCFG_VBP_SHIFT, 1, 0), | ||
495 | SOC_ENUM("Ch1 Binary Output Select", sta350_binary_output_ch1_enum), | ||
496 | SOC_ENUM("Ch2 Binary Output Select", sta350_binary_output_ch2_enum), | ||
497 | SOC_ENUM("Ch3 Binary Output Select", sta350_binary_output_ch3_enum), | ||
498 | SOC_ENUM("Ch1 Limiter Select", sta350_limiter_ch1_enum), | ||
499 | SOC_ENUM("Ch2 Limiter Select", sta350_limiter_ch2_enum), | ||
500 | SOC_ENUM("Ch3 Limiter Select", sta350_limiter_ch3_enum), | ||
501 | /* TONE */ | ||
502 | SOC_SINGLE_RANGE_TLV("Bass Tone Control Volume", | ||
503 | STA350_TONE, STA350_TONE_BTC_SHIFT, 1, 13, 0, tone_tlv), | ||
504 | SOC_SINGLE_RANGE_TLV("Treble Tone Control Volume", | ||
505 | STA350_TONE, STA350_TONE_TTC_SHIFT, 1, 13, 0, tone_tlv), | ||
506 | SOC_ENUM("Limiter1 Attack Rate (dB/ms)", sta350_limiter1_attack_rate_enum), | ||
507 | SOC_ENUM("Limiter2 Attack Rate (dB/ms)", sta350_limiter2_attack_rate_enum), | ||
508 | SOC_ENUM("Limiter1 Release Rate (dB/ms)", sta350_limiter1_release_rate_enum), | ||
509 | SOC_ENUM("Limiter2 Release Rate (dB/ms)", sta350_limiter2_release_rate_enum), | ||
510 | |||
511 | /* | ||
512 | * depending on mode, the attack/release thresholds have | ||
513 | * two different enum definitions; provide both | ||
514 | */ | ||
515 | SOC_SINGLE_TLV("Limiter1 Attack Threshold (AC Mode)", | ||
516 | STA350_L1ATRT, STA350_LxA_SHIFT, | ||
517 | 16, 0, sta350_limiter_ac_attack_tlv), | ||
518 | SOC_SINGLE_TLV("Limiter2 Attack Threshold (AC Mode)", | ||
519 | STA350_L2ATRT, STA350_LxA_SHIFT, | ||
520 | 16, 0, sta350_limiter_ac_attack_tlv), | ||
521 | SOC_SINGLE_TLV("Limiter1 Release Threshold (AC Mode)", | ||
522 | STA350_L1ATRT, STA350_LxR_SHIFT, | ||
523 | 16, 0, sta350_limiter_ac_release_tlv), | ||
524 | SOC_SINGLE_TLV("Limiter2 Release Threshold (AC Mode)", | ||
525 | STA350_L2ATRT, STA350_LxR_SHIFT, | ||
526 | 16, 0, sta350_limiter_ac_release_tlv), | ||
527 | SOC_SINGLE_TLV("Limiter1 Attack Threshold (DRC Mode)", | ||
528 | STA350_L1ATRT, STA350_LxA_SHIFT, | ||
529 | 16, 0, sta350_limiter_drc_attack_tlv), | ||
530 | SOC_SINGLE_TLV("Limiter2 Attack Threshold (DRC Mode)", | ||
531 | STA350_L2ATRT, STA350_LxA_SHIFT, | ||
532 | 16, 0, sta350_limiter_drc_attack_tlv), | ||
533 | SOC_SINGLE_TLV("Limiter1 Release Threshold (DRC Mode)", | ||
534 | STA350_L1ATRT, STA350_LxR_SHIFT, | ||
535 | 16, 0, sta350_limiter_drc_release_tlv), | ||
536 | SOC_SINGLE_TLV("Limiter2 Release Threshold (DRC Mode)", | ||
537 | STA350_L2ATRT, STA350_LxR_SHIFT, | ||
538 | 16, 0, sta350_limiter_drc_release_tlv), | ||
539 | |||
540 | BIQUAD_COEFS("Ch1 - Biquad 1", 0), | ||
541 | BIQUAD_COEFS("Ch1 - Biquad 2", 5), | ||
542 | BIQUAD_COEFS("Ch1 - Biquad 3", 10), | ||
543 | BIQUAD_COEFS("Ch1 - Biquad 4", 15), | ||
544 | BIQUAD_COEFS("Ch2 - Biquad 1", 20), | ||
545 | BIQUAD_COEFS("Ch2 - Biquad 2", 25), | ||
546 | BIQUAD_COEFS("Ch2 - Biquad 3", 30), | ||
547 | BIQUAD_COEFS("Ch2 - Biquad 4", 35), | ||
548 | BIQUAD_COEFS("High-pass", 40), | ||
549 | BIQUAD_COEFS("Low-pass", 45), | ||
550 | SINGLE_COEF("Ch1 - Prescale", 50), | ||
551 | SINGLE_COEF("Ch2 - Prescale", 51), | ||
552 | SINGLE_COEF("Ch1 - Postscale", 52), | ||
553 | SINGLE_COEF("Ch2 - Postscale", 53), | ||
554 | SINGLE_COEF("Ch3 - Postscale", 54), | ||
555 | SINGLE_COEF("Thermal warning - Postscale", 55), | ||
556 | SINGLE_COEF("Ch1 - Mix 1", 56), | ||
557 | SINGLE_COEF("Ch1 - Mix 2", 57), | ||
558 | SINGLE_COEF("Ch2 - Mix 1", 58), | ||
559 | SINGLE_COEF("Ch2 - Mix 2", 59), | ||
560 | SINGLE_COEF("Ch3 - Mix 1", 60), | ||
561 | SINGLE_COEF("Ch3 - Mix 2", 61), | ||
562 | }; | ||
563 | |||
564 | static const struct snd_soc_dapm_widget sta350_dapm_widgets[] = { | ||
565 | SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0), | ||
566 | SND_SOC_DAPM_OUTPUT("LEFT"), | ||
567 | SND_SOC_DAPM_OUTPUT("RIGHT"), | ||
568 | SND_SOC_DAPM_OUTPUT("SUB"), | ||
569 | }; | ||
570 | |||
571 | static const struct snd_soc_dapm_route sta350_dapm_routes[] = { | ||
572 | { "LEFT", NULL, "DAC" }, | ||
573 | { "RIGHT", NULL, "DAC" }, | ||
574 | { "SUB", NULL, "DAC" }, | ||
575 | { "DAC", NULL, "Playback" }, | ||
576 | }; | ||
577 | |||
578 | /* MCLK interpolation ratio per fs */ | ||
579 | static struct { | ||
580 | int fs; | ||
581 | int ir; | ||
582 | } interpolation_ratios[] = { | ||
583 | { 32000, 0 }, | ||
584 | { 44100, 0 }, | ||
585 | { 48000, 0 }, | ||
586 | { 88200, 1 }, | ||
587 | { 96000, 1 }, | ||
588 | { 176400, 2 }, | ||
589 | { 192000, 2 }, | ||
590 | }; | ||
591 | |||
592 | /* MCLK to fs clock ratios */ | ||
593 | static int mcs_ratio_table[3][6] = { | ||
594 | { 768, 512, 384, 256, 128, 576 }, | ||
595 | { 384, 256, 192, 128, 64, 0 }, | ||
596 | { 192, 128, 96, 64, 32, 0 }, | ||
597 | }; | ||
598 | |||
599 | /** | ||
600 | * sta350_set_dai_sysclk - configure MCLK | ||
601 | * @codec_dai: the codec DAI | ||
602 | * @clk_id: the clock ID (ignored) | ||
603 | * @freq: the MCLK input frequency | ||
604 | * @dir: the clock direction (ignored) | ||
605 | * | ||
606 | * The value of MCLK is used to determine which sample rates are supported | ||
607 | * by the STA350, based on the mcs_ratio_table. | ||
608 | * | ||
609 | * This function must be called by the machine driver's 'startup' function, | ||
610 | * otherwise the list of supported sample rates will not be available in | ||
611 | * time for ALSA. | ||
612 | */ | ||
613 | static int sta350_set_dai_sysclk(struct snd_soc_dai *codec_dai, | ||
614 | int clk_id, unsigned int freq, int dir) | ||
615 | { | ||
616 | struct snd_soc_codec *codec = codec_dai->codec; | ||
617 | struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec); | ||
618 | |||
619 | dev_dbg(codec->dev, "mclk=%u\n", freq); | ||
620 | sta350->mclk = freq; | ||
621 | |||
622 | return 0; | ||
623 | } | ||
624 | |||
625 | /** | ||
626 | * sta350_set_dai_fmt - configure the codec for the selected audio format | ||
627 | * @codec_dai: the codec DAI | ||
628 | * @fmt: a SND_SOC_DAIFMT_x value indicating the data format | ||
629 | * | ||
630 | * This function takes a bitmask of SND_SOC_DAIFMT_x bits and programs the | ||
631 | * codec accordingly. | ||
632 | */ | ||
633 | static int sta350_set_dai_fmt(struct snd_soc_dai *codec_dai, | ||
634 | unsigned int fmt) | ||
635 | { | ||
636 | struct snd_soc_codec *codec = codec_dai->codec; | ||
637 | struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec); | ||
638 | unsigned int confb = 0; | ||
639 | |||
640 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
641 | case SND_SOC_DAIFMT_CBS_CFS: | ||
642 | break; | ||
643 | default: | ||
644 | return -EINVAL; | ||
645 | } | ||
646 | |||
647 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
648 | case SND_SOC_DAIFMT_I2S: | ||
649 | case SND_SOC_DAIFMT_RIGHT_J: | ||
650 | case SND_SOC_DAIFMT_LEFT_J: | ||
651 | sta350->format = fmt & SND_SOC_DAIFMT_FORMAT_MASK; | ||
652 | break; | ||
653 | default: | ||
654 | return -EINVAL; | ||
655 | } | ||
656 | |||
657 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | ||
658 | case SND_SOC_DAIFMT_NB_NF: | ||
659 | confb |= STA350_CONFB_C2IM; | ||
660 | break; | ||
661 | case SND_SOC_DAIFMT_NB_IF: | ||
662 | confb |= STA350_CONFB_C1IM; | ||
663 | break; | ||
664 | default: | ||
665 | return -EINVAL; | ||
666 | } | ||
667 | |||
668 | return regmap_update_bits(sta350->regmap, STA350_CONFB, | ||
669 | STA350_CONFB_C1IM | STA350_CONFB_C2IM, confb); | ||
670 | } | ||
671 | |||
672 | /** | ||
673 | * sta350_hw_params - program the STA350 with the given hardware parameters. | ||
674 | * @substream: the audio stream | ||
675 | * @params: the hardware parameters to set | ||
676 | * @dai: the SOC DAI (ignored) | ||
677 | * | ||
678 | * This function programs the hardware with the values provided. | ||
679 | * Specifically, the sample rate and the data format. | ||
680 | */ | ||
681 | static int sta350_hw_params(struct snd_pcm_substream *substream, | ||
682 | struct snd_pcm_hw_params *params, | ||
683 | struct snd_soc_dai *dai) | ||
684 | { | ||
685 | struct snd_soc_codec *codec = dai->codec; | ||
686 | struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec); | ||
687 | int i, mcs = -EINVAL, ir = -EINVAL; | ||
688 | unsigned int confa, confb; | ||
689 | unsigned int rate, ratio; | ||
690 | int ret; | ||
691 | |||
692 | if (!sta350->mclk) { | ||
693 | dev_err(codec->dev, | ||
694 | "sta350->mclk is unset. Unable to determine ratio\n"); | ||
695 | return -EIO; | ||
696 | } | ||
697 | |||
698 | rate = params_rate(params); | ||
699 | ratio = sta350->mclk / rate; | ||
700 | dev_dbg(codec->dev, "rate: %u, ratio: %u\n", rate, ratio); | ||
701 | |||
702 | for (i = 0; i < ARRAY_SIZE(interpolation_ratios); i++) { | ||
703 | if (interpolation_ratios[i].fs == rate) { | ||
704 | ir = interpolation_ratios[i].ir; | ||
705 | break; | ||
706 | } | ||
707 | } | ||
708 | |||
709 | if (ir < 0) { | ||
710 | dev_err(codec->dev, "Unsupported samplerate: %u\n", rate); | ||
711 | return -EINVAL; | ||
712 | } | ||
713 | |||
714 | for (i = 0; i < 6; i++) { | ||
715 | if (mcs_ratio_table[ir][i] == ratio) { | ||
716 | mcs = i; | ||
717 | break; | ||
718 | } | ||
719 | } | ||
720 | |||
721 | if (mcs < 0) { | ||
722 | dev_err(codec->dev, "Unresolvable ratio: %u\n", ratio); | ||
723 | return -EINVAL; | ||
724 | } | ||
725 | |||
726 | confa = (ir << STA350_CONFA_IR_SHIFT) | | ||
727 | (mcs << STA350_CONFA_MCS_SHIFT); | ||
728 | confb = 0; | ||
729 | |||
730 | switch (params_width(params)) { | ||
731 | case 24: | ||
732 | dev_dbg(codec->dev, "24bit\n"); | ||
733 | /* fall through */ | ||
734 | case 32: | ||
735 | dev_dbg(codec->dev, "24bit or 32bit\n"); | ||
736 | switch (sta350->format) { | ||
737 | case SND_SOC_DAIFMT_I2S: | ||
738 | confb |= 0x0; | ||
739 | break; | ||
740 | case SND_SOC_DAIFMT_LEFT_J: | ||
741 | confb |= 0x1; | ||
742 | break; | ||
743 | case SND_SOC_DAIFMT_RIGHT_J: | ||
744 | confb |= 0x2; | ||
745 | break; | ||
746 | } | ||
747 | |||
748 | break; | ||
749 | case 20: | ||
750 | dev_dbg(codec->dev, "20bit\n"); | ||
751 | switch (sta350->format) { | ||
752 | case SND_SOC_DAIFMT_I2S: | ||
753 | confb |= 0x4; | ||
754 | break; | ||
755 | case SND_SOC_DAIFMT_LEFT_J: | ||
756 | confb |= 0x5; | ||
757 | break; | ||
758 | case SND_SOC_DAIFMT_RIGHT_J: | ||
759 | confb |= 0x6; | ||
760 | break; | ||
761 | } | ||
762 | |||
763 | break; | ||
764 | case 18: | ||
765 | dev_dbg(codec->dev, "18bit\n"); | ||
766 | switch (sta350->format) { | ||
767 | case SND_SOC_DAIFMT_I2S: | ||
768 | confb |= 0x8; | ||
769 | break; | ||
770 | case SND_SOC_DAIFMT_LEFT_J: | ||
771 | confb |= 0x9; | ||
772 | break; | ||
773 | case SND_SOC_DAIFMT_RIGHT_J: | ||
774 | confb |= 0xa; | ||
775 | break; | ||
776 | } | ||
777 | |||
778 | break; | ||
779 | case 16: | ||
780 | dev_dbg(codec->dev, "16bit\n"); | ||
781 | switch (sta350->format) { | ||
782 | case SND_SOC_DAIFMT_I2S: | ||
783 | confb |= 0x0; | ||
784 | break; | ||
785 | case SND_SOC_DAIFMT_LEFT_J: | ||
786 | confb |= 0xd; | ||
787 | break; | ||
788 | case SND_SOC_DAIFMT_RIGHT_J: | ||
789 | confb |= 0xe; | ||
790 | break; | ||
791 | } | ||
792 | |||
793 | break; | ||
794 | default: | ||
795 | return -EINVAL; | ||
796 | } | ||
797 | |||
798 | ret = regmap_update_bits(sta350->regmap, STA350_CONFA, | ||
799 | STA350_CONFA_MCS_MASK | STA350_CONFA_IR_MASK, | ||
800 | confa); | ||
801 | if (ret < 0) | ||
802 | return ret; | ||
803 | |||
804 | ret = regmap_update_bits(sta350->regmap, STA350_CONFB, | ||
805 | STA350_CONFB_SAI_MASK | STA350_CONFB_SAIFB, | ||
806 | confb); | ||
807 | if (ret < 0) | ||
808 | return ret; | ||
809 | |||
810 | return 0; | ||
811 | } | ||
812 | |||
813 | static int sta350_startup_sequence(struct sta350_priv *sta350) | ||
814 | { | ||
815 | if (sta350->gpiod_power_down) | ||
816 | gpiod_set_value(sta350->gpiod_power_down, 1); | ||
817 | |||
818 | if (sta350->gpiod_nreset) { | ||
819 | gpiod_set_value(sta350->gpiod_nreset, 0); | ||
820 | mdelay(1); | ||
821 | gpiod_set_value(sta350->gpiod_nreset, 1); | ||
822 | mdelay(1); | ||
823 | } | ||
824 | |||
825 | return 0; | ||
826 | } | ||
827 | |||
828 | /** | ||
829 | * sta350_set_bias_level - DAPM callback | ||
830 | * @codec: the codec device | ||
831 | * @level: DAPM power level | ||
832 | * | ||
833 | * This is called by ALSA to put the codec into low power mode | ||
834 | * or to wake it up. If the codec is powered off completely | ||
835 | * all registers must be restored after power on. | ||
836 | */ | ||
837 | static int sta350_set_bias_level(struct snd_soc_codec *codec, | ||
838 | enum snd_soc_bias_level level) | ||
839 | { | ||
840 | struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec); | ||
841 | int ret; | ||
842 | |||
843 | dev_dbg(codec->dev, "level = %d\n", level); | ||
844 | switch (level) { | ||
845 | case SND_SOC_BIAS_ON: | ||
846 | break; | ||
847 | |||
848 | case SND_SOC_BIAS_PREPARE: | ||
849 | /* Full power on */ | ||
850 | regmap_update_bits(sta350->regmap, STA350_CONFF, | ||
851 | STA350_CONFF_PWDN | STA350_CONFF_EAPD, | ||
852 | STA350_CONFF_PWDN | STA350_CONFF_EAPD); | ||
853 | break; | ||
854 | |||
855 | case SND_SOC_BIAS_STANDBY: | ||
856 | if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { | ||
857 | ret = regulator_bulk_enable( | ||
858 | ARRAY_SIZE(sta350->supplies), | ||
859 | sta350->supplies); | ||
860 | if (ret < 0) { | ||
861 | dev_err(codec->dev, | ||
862 | "Failed to enable supplies: %d\n", | ||
863 | ret); | ||
864 | return ret; | ||
865 | } | ||
866 | sta350_startup_sequence(sta350); | ||
867 | sta350_cache_sync(codec); | ||
868 | } | ||
869 | |||
870 | /* Power down */ | ||
871 | regmap_update_bits(sta350->regmap, STA350_CONFF, | ||
872 | STA350_CONFF_PWDN | STA350_CONFF_EAPD, | ||
873 | 0); | ||
874 | |||
875 | break; | ||
876 | |||
877 | case SND_SOC_BIAS_OFF: | ||
878 | /* The chip runs through the power down sequence for us */ | ||
879 | regmap_update_bits(sta350->regmap, STA350_CONFF, | ||
880 | STA350_CONFF_PWDN | STA350_CONFF_EAPD, 0); | ||
881 | |||
882 | /* power down: low */ | ||
883 | if (sta350->gpiod_power_down) | ||
884 | gpiod_set_value(sta350->gpiod_power_down, 0); | ||
885 | |||
886 | if (sta350->gpiod_nreset) | ||
887 | gpiod_set_value(sta350->gpiod_nreset, 0); | ||
888 | |||
889 | regulator_bulk_disable(ARRAY_SIZE(sta350->supplies), | ||
890 | sta350->supplies); | ||
891 | break; | ||
892 | } | ||
893 | codec->dapm.bias_level = level; | ||
894 | return 0; | ||
895 | } | ||
896 | |||
897 | static const struct snd_soc_dai_ops sta350_dai_ops = { | ||
898 | .hw_params = sta350_hw_params, | ||
899 | .set_sysclk = sta350_set_dai_sysclk, | ||
900 | .set_fmt = sta350_set_dai_fmt, | ||
901 | }; | ||
902 | |||
903 | static struct snd_soc_dai_driver sta350_dai = { | ||
904 | .name = "sta350-hifi", | ||
905 | .playback = { | ||
906 | .stream_name = "Playback", | ||
907 | .channels_min = 2, | ||
908 | .channels_max = 2, | ||
909 | .rates = STA350_RATES, | ||
910 | .formats = STA350_FORMATS, | ||
911 | }, | ||
912 | .ops = &sta350_dai_ops, | ||
913 | }; | ||
914 | |||
915 | #ifdef CONFIG_PM | ||
916 | static int sta350_suspend(struct snd_soc_codec *codec) | ||
917 | { | ||
918 | sta350_set_bias_level(codec, SND_SOC_BIAS_OFF); | ||
919 | return 0; | ||
920 | } | ||
921 | |||
922 | static int sta350_resume(struct snd_soc_codec *codec) | ||
923 | { | ||
924 | sta350_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | ||
925 | return 0; | ||
926 | } | ||
927 | #else | ||
928 | #define sta350_suspend NULL | ||
929 | #define sta350_resume NULL | ||
930 | #endif | ||
931 | |||
932 | static int sta350_probe(struct snd_soc_codec *codec) | ||
933 | { | ||
934 | struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec); | ||
935 | struct sta350_platform_data *pdata = sta350->pdata; | ||
936 | int i, ret = 0, thermal = 0; | ||
937 | |||
938 | ret = regulator_bulk_enable(ARRAY_SIZE(sta350->supplies), | ||
939 | sta350->supplies); | ||
940 | if (ret < 0) { | ||
941 | dev_err(codec->dev, "Failed to enable supplies: %d\n", ret); | ||
942 | return ret; | ||
943 | } | ||
944 | |||
945 | ret = sta350_startup_sequence(sta350); | ||
946 | if (ret < 0) { | ||
947 | dev_err(codec->dev, "Failed to startup device\n"); | ||
948 | return ret; | ||
949 | } | ||
950 | |||
951 | /* CONFA */ | ||
952 | if (!pdata->thermal_warning_recovery) | ||
953 | thermal |= STA350_CONFA_TWAB; | ||
954 | if (!pdata->thermal_warning_adjustment) | ||
955 | thermal |= STA350_CONFA_TWRB; | ||
956 | if (!pdata->fault_detect_recovery) | ||
957 | thermal |= STA350_CONFA_FDRB; | ||
958 | regmap_update_bits(sta350->regmap, STA350_CONFA, | ||
959 | STA350_CONFA_TWAB | STA350_CONFA_TWRB | | ||
960 | STA350_CONFA_FDRB, | ||
961 | thermal); | ||
962 | |||
963 | /* CONFC */ | ||
964 | regmap_update_bits(sta350->regmap, STA350_CONFC, | ||
965 | STA350_CONFC_OM_MASK, | ||
966 | pdata->ffx_power_output_mode | ||
967 | << STA350_CONFC_OM_SHIFT); | ||
968 | regmap_update_bits(sta350->regmap, STA350_CONFC, | ||
969 | STA350_CONFC_CSZ_MASK, | ||
970 | pdata->drop_compensation_ns | ||
971 | << STA350_CONFC_CSZ_SHIFT); | ||
972 | regmap_update_bits(sta350->regmap, | ||
973 | STA350_CONFC, | ||
974 | STA350_CONFC_OCRB, | ||
975 | pdata->oc_warning_adjustment ? | ||
976 | STA350_CONFC_OCRB : 0); | ||
977 | |||
978 | /* CONFE */ | ||
979 | regmap_update_bits(sta350->regmap, STA350_CONFE, | ||
980 | STA350_CONFE_MPCV, | ||
981 | pdata->max_power_use_mpcc ? | ||
982 | STA350_CONFE_MPCV : 0); | ||
983 | regmap_update_bits(sta350->regmap, STA350_CONFE, | ||
984 | STA350_CONFE_MPC, | ||
985 | pdata->max_power_correction ? | ||
986 | STA350_CONFE_MPC : 0); | ||
987 | regmap_update_bits(sta350->regmap, STA350_CONFE, | ||
988 | STA350_CONFE_AME, | ||
989 | pdata->am_reduction_mode ? | ||
990 | STA350_CONFE_AME : 0); | ||
991 | regmap_update_bits(sta350->regmap, STA350_CONFE, | ||
992 | STA350_CONFE_PWMS, | ||
993 | pdata->odd_pwm_speed_mode ? | ||
994 | STA350_CONFE_PWMS : 0); | ||
995 | regmap_update_bits(sta350->regmap, STA350_CONFE, | ||
996 | STA350_CONFE_DCCV, | ||
997 | pdata->distortion_compensation ? | ||
998 | STA350_CONFE_DCCV : 0); | ||
999 | /* CONFF */ | ||
1000 | regmap_update_bits(sta350->regmap, STA350_CONFF, | ||
1001 | STA350_CONFF_IDE, | ||
1002 | pdata->invalid_input_detect_mute ? | ||
1003 | STA350_CONFF_IDE : 0); | ||
1004 | regmap_update_bits(sta350->regmap, STA350_CONFF, | ||
1005 | STA350_CONFF_OCFG_MASK, | ||
1006 | pdata->output_conf | ||
1007 | << STA350_CONFF_OCFG_SHIFT); | ||
1008 | |||
1009 | /* channel to output mapping */ | ||
1010 | regmap_update_bits(sta350->regmap, STA350_C1CFG, | ||
1011 | STA350_CxCFG_OM_MASK, | ||
1012 | pdata->ch1_output_mapping | ||
1013 | << STA350_CxCFG_OM_SHIFT); | ||
1014 | regmap_update_bits(sta350->regmap, STA350_C2CFG, | ||
1015 | STA350_CxCFG_OM_MASK, | ||
1016 | pdata->ch2_output_mapping | ||
1017 | << STA350_CxCFG_OM_SHIFT); | ||
1018 | regmap_update_bits(sta350->regmap, STA350_C3CFG, | ||
1019 | STA350_CxCFG_OM_MASK, | ||
1020 | pdata->ch3_output_mapping | ||
1021 | << STA350_CxCFG_OM_SHIFT); | ||
1022 | |||
1023 | /* initialize coefficient shadow RAM with reset values */ | ||
1024 | for (i = 4; i <= 49; i += 5) | ||
1025 | sta350->coef_shadow[i] = 0x400000; | ||
1026 | for (i = 50; i <= 54; i++) | ||
1027 | sta350->coef_shadow[i] = 0x7fffff; | ||
1028 | sta350->coef_shadow[55] = 0x5a9df7; | ||
1029 | sta350->coef_shadow[56] = 0x7fffff; | ||
1030 | sta350->coef_shadow[59] = 0x7fffff; | ||
1031 | sta350->coef_shadow[60] = 0x400000; | ||
1032 | sta350->coef_shadow[61] = 0x400000; | ||
1033 | |||
1034 | sta350_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | ||
1035 | /* Bias level configuration will have done an extra enable */ | ||
1036 | regulator_bulk_disable(ARRAY_SIZE(sta350->supplies), sta350->supplies); | ||
1037 | |||
1038 | return 0; | ||
1039 | } | ||
1040 | |||
1041 | static int sta350_remove(struct snd_soc_codec *codec) | ||
1042 | { | ||
1043 | struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec); | ||
1044 | |||
1045 | sta350_set_bias_level(codec, SND_SOC_BIAS_OFF); | ||
1046 | regulator_bulk_disable(ARRAY_SIZE(sta350->supplies), sta350->supplies); | ||
1047 | |||
1048 | return 0; | ||
1049 | } | ||
1050 | |||
1051 | static const struct snd_soc_codec_driver sta350_codec = { | ||
1052 | .probe = sta350_probe, | ||
1053 | .remove = sta350_remove, | ||
1054 | .suspend = sta350_suspend, | ||
1055 | .resume = sta350_resume, | ||
1056 | .set_bias_level = sta350_set_bias_level, | ||
1057 | .controls = sta350_snd_controls, | ||
1058 | .num_controls = ARRAY_SIZE(sta350_snd_controls), | ||
1059 | .dapm_widgets = sta350_dapm_widgets, | ||
1060 | .num_dapm_widgets = ARRAY_SIZE(sta350_dapm_widgets), | ||
1061 | .dapm_routes = sta350_dapm_routes, | ||
1062 | .num_dapm_routes = ARRAY_SIZE(sta350_dapm_routes), | ||
1063 | }; | ||
1064 | |||
1065 | static const struct regmap_config sta350_regmap = { | ||
1066 | .reg_bits = 8, | ||
1067 | .val_bits = 8, | ||
1068 | .max_register = STA350_MISC2, | ||
1069 | .reg_defaults = sta350_regs, | ||
1070 | .num_reg_defaults = ARRAY_SIZE(sta350_regs), | ||
1071 | .cache_type = REGCACHE_RBTREE, | ||
1072 | .wr_table = &sta350_write_regs, | ||
1073 | .rd_table = &sta350_read_regs, | ||
1074 | .volatile_table = &sta350_volatile_regs, | ||
1075 | }; | ||
1076 | |||
1077 | #ifdef CONFIG_OF | ||
1078 | static const struct of_device_id st350_dt_ids[] = { | ||
1079 | { .compatible = "st,sta350", }, | ||
1080 | { } | ||
1081 | }; | ||
1082 | MODULE_DEVICE_TABLE(of, st350_dt_ids); | ||
1083 | |||
1084 | static const char * const sta350_ffx_modes[] = { | ||
1085 | [STA350_FFX_PM_DROP_COMP] = "drop-compensation", | ||
1086 | [STA350_FFX_PM_TAPERED_COMP] = "tapered-compensation", | ||
1087 | [STA350_FFX_PM_FULL_POWER] = "full-power-mode", | ||
1088 | [STA350_FFX_PM_VARIABLE_DROP_COMP] = "variable-drop-compensation", | ||
1089 | }; | ||
1090 | |||
1091 | static int sta350_probe_dt(struct device *dev, struct sta350_priv *sta350) | ||
1092 | { | ||
1093 | struct device_node *np = dev->of_node; | ||
1094 | struct sta350_platform_data *pdata; | ||
1095 | const char *ffx_power_mode; | ||
1096 | u16 tmp; | ||
1097 | |||
1098 | pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); | ||
1099 | if (!pdata) | ||
1100 | return -ENOMEM; | ||
1101 | |||
1102 | of_property_read_u8(np, "st,output-conf", | ||
1103 | &pdata->output_conf); | ||
1104 | of_property_read_u8(np, "st,ch1-output-mapping", | ||
1105 | &pdata->ch1_output_mapping); | ||
1106 | of_property_read_u8(np, "st,ch2-output-mapping", | ||
1107 | &pdata->ch2_output_mapping); | ||
1108 | of_property_read_u8(np, "st,ch3-output-mapping", | ||
1109 | &pdata->ch3_output_mapping); | ||
1110 | |||
1111 | if (of_get_property(np, "st,thermal-warning-recovery", NULL)) | ||
1112 | pdata->thermal_warning_recovery = 1; | ||
1113 | if (of_get_property(np, "st,thermal-warning-adjustment", NULL)) | ||
1114 | pdata->thermal_warning_adjustment = 1; | ||
1115 | if (of_get_property(np, "st,fault-detect-recovery", NULL)) | ||
1116 | pdata->fault_detect_recovery = 1; | ||
1117 | |||
1118 | pdata->ffx_power_output_mode = STA350_FFX_PM_VARIABLE_DROP_COMP; | ||
1119 | if (!of_property_read_string(np, "st,ffx-power-output-mode", | ||
1120 | &ffx_power_mode)) { | ||
1121 | int i, mode = -EINVAL; | ||
1122 | |||
1123 | for (i = 0; i < ARRAY_SIZE(sta350_ffx_modes); i++) | ||
1124 | if (!strcasecmp(ffx_power_mode, sta350_ffx_modes[i])) | ||
1125 | mode = i; | ||
1126 | |||
1127 | if (mode < 0) | ||
1128 | dev_warn(dev, "Unsupported ffx output mode: %s\n", | ||
1129 | ffx_power_mode); | ||
1130 | else | ||
1131 | pdata->ffx_power_output_mode = mode; | ||
1132 | } | ||
1133 | |||
1134 | tmp = 140; | ||
1135 | of_property_read_u16(np, "st,drop-compensation-ns", &tmp); | ||
1136 | pdata->drop_compensation_ns = clamp_t(u16, tmp, 0, 300) / 20; | ||
1137 | |||
1138 | if (of_get_property(np, "st,overcurrent-warning-adjustment", NULL)) | ||
1139 | pdata->oc_warning_adjustment = 1; | ||
1140 | |||
1141 | /* CONFE */ | ||
1142 | if (of_get_property(np, "st,max-power-use-mpcc", NULL)) | ||
1143 | pdata->max_power_use_mpcc = 1; | ||
1144 | |||
1145 | if (of_get_property(np, "st,max-power-correction", NULL)) | ||
1146 | pdata->max_power_correction = 1; | ||
1147 | |||
1148 | if (of_get_property(np, "st,am-reduction-mode", NULL)) | ||
1149 | pdata->am_reduction_mode = 1; | ||
1150 | |||
1151 | if (of_get_property(np, "st,odd-pwm-speed-mode", NULL)) | ||
1152 | pdata->odd_pwm_speed_mode = 1; | ||
1153 | |||
1154 | if (of_get_property(np, "st,distortion-compensation", NULL)) | ||
1155 | pdata->distortion_compensation = 1; | ||
1156 | |||
1157 | /* CONFF */ | ||
1158 | if (of_get_property(np, "st,invalid-input-detect-mute", NULL)) | ||
1159 | pdata->invalid_input_detect_mute = 1; | ||
1160 | |||
1161 | sta350->pdata = pdata; | ||
1162 | |||
1163 | return 0; | ||
1164 | } | ||
1165 | #endif | ||
1166 | |||
1167 | static int sta350_i2c_probe(struct i2c_client *i2c, | ||
1168 | const struct i2c_device_id *id) | ||
1169 | { | ||
1170 | struct device *dev = &i2c->dev; | ||
1171 | struct sta350_priv *sta350; | ||
1172 | int ret, i; | ||
1173 | |||
1174 | sta350 = devm_kzalloc(dev, sizeof(struct sta350_priv), GFP_KERNEL); | ||
1175 | if (!sta350) | ||
1176 | return -ENOMEM; | ||
1177 | |||
1178 | mutex_init(&sta350->coeff_lock); | ||
1179 | sta350->pdata = dev_get_platdata(dev); | ||
1180 | |||
1181 | #ifdef CONFIG_OF | ||
1182 | if (dev->of_node) { | ||
1183 | ret = sta350_probe_dt(dev, sta350); | ||
1184 | if (ret < 0) | ||
1185 | return ret; | ||
1186 | } | ||
1187 | #endif | ||
1188 | |||
1189 | /* GPIOs */ | ||
1190 | sta350->gpiod_nreset = devm_gpiod_get(dev, "reset"); | ||
1191 | if (IS_ERR(sta350->gpiod_nreset)) { | ||
1192 | ret = PTR_ERR(sta350->gpiod_nreset); | ||
1193 | if (ret != -ENOENT && ret != -ENOSYS) | ||
1194 | return ret; | ||
1195 | |||
1196 | sta350->gpiod_nreset = NULL; | ||
1197 | } else { | ||
1198 | gpiod_direction_output(sta350->gpiod_nreset, 0); | ||
1199 | } | ||
1200 | |||
1201 | sta350->gpiod_power_down = devm_gpiod_get(dev, "power-down"); | ||
1202 | if (IS_ERR(sta350->gpiod_power_down)) { | ||
1203 | ret = PTR_ERR(sta350->gpiod_power_down); | ||
1204 | if (ret != -ENOENT && ret != -ENOSYS) | ||
1205 | return ret; | ||
1206 | |||
1207 | sta350->gpiod_power_down = NULL; | ||
1208 | } else { | ||
1209 | gpiod_direction_output(sta350->gpiod_power_down, 0); | ||
1210 | } | ||
1211 | |||
1212 | /* regulators */ | ||
1213 | for (i = 0; i < ARRAY_SIZE(sta350->supplies); i++) | ||
1214 | sta350->supplies[i].supply = sta350_supply_names[i]; | ||
1215 | |||
1216 | ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(sta350->supplies), | ||
1217 | sta350->supplies); | ||
1218 | if (ret < 0) { | ||
1219 | dev_err(dev, "Failed to request supplies: %d\n", ret); | ||
1220 | return ret; | ||
1221 | } | ||
1222 | |||
1223 | sta350->regmap = devm_regmap_init_i2c(i2c, &sta350_regmap); | ||
1224 | if (IS_ERR(sta350->regmap)) { | ||
1225 | ret = PTR_ERR(sta350->regmap); | ||
1226 | dev_err(dev, "Failed to init regmap: %d\n", ret); | ||
1227 | return ret; | ||
1228 | } | ||
1229 | |||
1230 | i2c_set_clientdata(i2c, sta350); | ||
1231 | |||
1232 | ret = snd_soc_register_codec(dev, &sta350_codec, &sta350_dai, 1); | ||
1233 | if (ret < 0) | ||
1234 | dev_err(dev, "Failed to register codec (%d)\n", ret); | ||
1235 | |||
1236 | return ret; | ||
1237 | } | ||
1238 | |||
1239 | static int sta350_i2c_remove(struct i2c_client *client) | ||
1240 | { | ||
1241 | snd_soc_unregister_codec(&client->dev); | ||
1242 | return 0; | ||
1243 | } | ||
1244 | |||
1245 | static const struct i2c_device_id sta350_i2c_id[] = { | ||
1246 | { "sta350", 0 }, | ||
1247 | { } | ||
1248 | }; | ||
1249 | MODULE_DEVICE_TABLE(i2c, sta350_i2c_id); | ||
1250 | |||
1251 | static struct i2c_driver sta350_i2c_driver = { | ||
1252 | .driver = { | ||
1253 | .name = "sta350", | ||
1254 | .owner = THIS_MODULE, | ||
1255 | .of_match_table = of_match_ptr(st350_dt_ids), | ||
1256 | }, | ||
1257 | .probe = sta350_i2c_probe, | ||
1258 | .remove = sta350_i2c_remove, | ||
1259 | .id_table = sta350_i2c_id, | ||
1260 | }; | ||
1261 | |||
1262 | module_i2c_driver(sta350_i2c_driver); | ||
1263 | |||
1264 | MODULE_DESCRIPTION("ASoC STA350 driver"); | ||
1265 | MODULE_AUTHOR("Sven Brandau <info@brandau.biz>"); | ||
1266 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/codecs/sta350.h b/sound/soc/codecs/sta350.h new file mode 100644 index 000000000000..c3248f0fad2c --- /dev/null +++ b/sound/soc/codecs/sta350.h | |||
@@ -0,0 +1,228 @@ | |||
1 | /* | ||
2 | * Codec driver for ST STA350 2.1-channel high-efficiency digital audio system | ||
3 | * | ||
4 | * Copyright: 2011 Raumfeld GmbH | ||
5 | * Author: Sven Brandau <info@brandau.biz> | ||
6 | * | ||
7 | * based on code from: | ||
8 | * Raumfeld GmbH | ||
9 | * Johannes Stezenbach <js@sig21.net> | ||
10 | * Wolfson Microelectronics PLC. | ||
11 | * Mark Brown <broonie@opensource.wolfsonmicro.com> | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or modify it | ||
14 | * under the terms of the GNU General Public License as published by the | ||
15 | * Free Software Foundation; either version 2 of the License, or (at your | ||
16 | * option) any later version. | ||
17 | */ | ||
18 | #ifndef _ASOC_STA_350_H | ||
19 | #define _ASOC_STA_350_H | ||
20 | |||
21 | /* STA50 register addresses */ | ||
22 | |||
23 | #define STA350_REGISTER_COUNT 0x4D | ||
24 | #define STA350_COEF_COUNT 62 | ||
25 | |||
26 | #define STA350_CONFA 0x00 | ||
27 | #define STA350_CONFB 0x01 | ||
28 | #define STA350_CONFC 0x02 | ||
29 | #define STA350_CONFD 0x03 | ||
30 | #define STA350_CONFE 0x04 | ||
31 | #define STA350_CONFF 0x05 | ||
32 | #define STA350_MMUTE 0x06 | ||
33 | #define STA350_MVOL 0x07 | ||
34 | #define STA350_C1VOL 0x08 | ||
35 | #define STA350_C2VOL 0x09 | ||
36 | #define STA350_C3VOL 0x0a | ||
37 | #define STA350_AUTO1 0x0b | ||
38 | #define STA350_AUTO2 0x0c | ||
39 | #define STA350_AUTO3 0x0d | ||
40 | #define STA350_C1CFG 0x0e | ||
41 | #define STA350_C2CFG 0x0f | ||
42 | #define STA350_C3CFG 0x10 | ||
43 | #define STA350_TONE 0x11 | ||
44 | #define STA350_L1AR 0x12 | ||
45 | #define STA350_L1ATRT 0x13 | ||
46 | #define STA350_L2AR 0x14 | ||
47 | #define STA350_L2ATRT 0x15 | ||
48 | #define STA350_CFADDR2 0x16 | ||
49 | #define STA350_B1CF1 0x17 | ||
50 | #define STA350_B1CF2 0x18 | ||
51 | #define STA350_B1CF3 0x19 | ||
52 | #define STA350_B2CF1 0x1a | ||
53 | #define STA350_B2CF2 0x1b | ||
54 | #define STA350_B2CF3 0x1c | ||
55 | #define STA350_A1CF1 0x1d | ||
56 | #define STA350_A1CF2 0x1e | ||
57 | #define STA350_A1CF3 0x1f | ||
58 | #define STA350_A2CF1 0x20 | ||
59 | #define STA350_A2CF2 0x21 | ||
60 | #define STA350_A2CF3 0x22 | ||
61 | #define STA350_B0CF1 0x23 | ||
62 | #define STA350_B0CF2 0x24 | ||
63 | #define STA350_B0CF3 0x25 | ||
64 | #define STA350_CFUD 0x26 | ||
65 | #define STA350_MPCC1 0x27 | ||
66 | #define STA350_MPCC2 0x28 | ||
67 | #define STA350_DCC1 0x29 | ||
68 | #define STA350_DCC2 0x2a | ||
69 | #define STA350_FDRC1 0x2b | ||
70 | #define STA350_FDRC2 0x2c | ||
71 | #define STA350_STATUS 0x2d | ||
72 | /* reserved: 0x2d - 0x30 */ | ||
73 | #define STA350_EQCFG 0x31 | ||
74 | #define STA350_EATH1 0x32 | ||
75 | #define STA350_ERTH1 0x33 | ||
76 | #define STA350_EATH2 0x34 | ||
77 | #define STA350_ERTH2 0x35 | ||
78 | #define STA350_CONFX 0x36 | ||
79 | #define STA350_SVCA 0x37 | ||
80 | #define STA350_SVCB 0x38 | ||
81 | #define STA350_RMS0A 0x39 | ||
82 | #define STA350_RMS0B 0x3a | ||
83 | #define STA350_RMS0C 0x3b | ||
84 | #define STA350_RMS1A 0x3c | ||
85 | #define STA350_RMS1B 0x3d | ||
86 | #define STA350_RMS1C 0x3e | ||
87 | #define STA350_EVOLRES 0x3f | ||
88 | /* reserved: 0x40 - 0x47 */ | ||
89 | #define STA350_NSHAPE 0x48 | ||
90 | #define STA350_CTXB4B1 0x49 | ||
91 | #define STA350_CTXB7B5 0x4a | ||
92 | #define STA350_MISC1 0x4b | ||
93 | #define STA350_MISC2 0x4c | ||
94 | |||
95 | /* 0x00 CONFA */ | ||
96 | #define STA350_CONFA_MCS_MASK 0x03 | ||
97 | #define STA350_CONFA_MCS_SHIFT 0 | ||
98 | #define STA350_CONFA_IR_MASK 0x18 | ||
99 | #define STA350_CONFA_IR_SHIFT 3 | ||
100 | #define STA350_CONFA_TWRB BIT(5) | ||
101 | #define STA350_CONFA_TWAB BIT(6) | ||
102 | #define STA350_CONFA_FDRB BIT(7) | ||
103 | |||
104 | /* 0x01 CONFB */ | ||
105 | #define STA350_CONFB_SAI_MASK 0x0f | ||
106 | #define STA350_CONFB_SAI_SHIFT 0 | ||
107 | #define STA350_CONFB_SAIFB BIT(4) | ||
108 | #define STA350_CONFB_DSCKE BIT(5) | ||
109 | #define STA350_CONFB_C1IM BIT(6) | ||
110 | #define STA350_CONFB_C2IM BIT(7) | ||
111 | |||
112 | /* 0x02 CONFC */ | ||
113 | #define STA350_CONFC_OM_MASK 0x03 | ||
114 | #define STA350_CONFC_OM_SHIFT 0 | ||
115 | #define STA350_CONFC_CSZ_MASK 0x3c | ||
116 | #define STA350_CONFC_CSZ_SHIFT 2 | ||
117 | #define STA350_CONFC_OCRB BIT(7) | ||
118 | |||
119 | /* 0x03 CONFD */ | ||
120 | #define STA350_CONFD_HPB_SHIFT 0 | ||
121 | #define STA350_CONFD_DEMP_SHIFT 1 | ||
122 | #define STA350_CONFD_DSPB_SHIFT 2 | ||
123 | #define STA350_CONFD_PSL_SHIFT 3 | ||
124 | #define STA350_CONFD_BQL_SHIFT 4 | ||
125 | #define STA350_CONFD_DRC_SHIFT 5 | ||
126 | #define STA350_CONFD_ZDE_SHIFT 6 | ||
127 | #define STA350_CONFD_SME_SHIFT 7 | ||
128 | |||
129 | /* 0x04 CONFE */ | ||
130 | #define STA350_CONFE_MPCV BIT(0) | ||
131 | #define STA350_CONFE_MPCV_SHIFT 0 | ||
132 | #define STA350_CONFE_MPC BIT(1) | ||
133 | #define STA350_CONFE_MPC_SHIFT 1 | ||
134 | #define STA350_CONFE_NSBW BIT(2) | ||
135 | #define STA350_CONFE_NSBW_SHIFT 2 | ||
136 | #define STA350_CONFE_AME BIT(3) | ||
137 | #define STA350_CONFE_AME_SHIFT 3 | ||
138 | #define STA350_CONFE_PWMS BIT(4) | ||
139 | #define STA350_CONFE_PWMS_SHIFT 4 | ||
140 | #define STA350_CONFE_DCCV BIT(5) | ||
141 | #define STA350_CONFE_DCCV_SHIFT 5 | ||
142 | #define STA350_CONFE_ZCE BIT(6) | ||
143 | #define STA350_CONFE_ZCE_SHIFT 6 | ||
144 | #define STA350_CONFE_SVE BIT(7) | ||
145 | #define STA350_CONFE_SVE_SHIFT 7 | ||
146 | |||
147 | /* 0x05 CONFF */ | ||
148 | #define STA350_CONFF_OCFG_MASK 0x03 | ||
149 | #define STA350_CONFF_OCFG_SHIFT 0 | ||
150 | #define STA350_CONFF_IDE BIT(2) | ||
151 | #define STA350_CONFF_BCLE BIT(3) | ||
152 | #define STA350_CONFF_LDTE BIT(4) | ||
153 | #define STA350_CONFF_ECLE BIT(5) | ||
154 | #define STA350_CONFF_PWDN BIT(6) | ||
155 | #define STA350_CONFF_EAPD BIT(7) | ||
156 | |||
157 | /* 0x06 MMUTE */ | ||
158 | #define STA350_MMUTE_MMUTE 0x01 | ||
159 | #define STA350_MMUTE_MMUTE_SHIFT 0 | ||
160 | #define STA350_MMUTE_C1M 0x02 | ||
161 | #define STA350_MMUTE_C1M_SHIFT 1 | ||
162 | #define STA350_MMUTE_C2M 0x04 | ||
163 | #define STA350_MMUTE_C2M_SHIFT 2 | ||
164 | #define STA350_MMUTE_C3M 0x08 | ||
165 | #define STA350_MMUTE_C3M_SHIFT 3 | ||
166 | #define STA350_MMUTE_LOC_MASK 0xC0 | ||
167 | #define STA350_MMUTE_LOC_SHIFT 6 | ||
168 | |||
169 | /* 0x0b AUTO1 */ | ||
170 | #define STA350_AUTO1_AMGC_MASK 0x30 | ||
171 | #define STA350_AUTO1_AMGC_SHIFT 4 | ||
172 | |||
173 | /* 0x0c AUTO2 */ | ||
174 | #define STA350_AUTO2_AMAME 0x01 | ||
175 | #define STA350_AUTO2_AMAM_MASK 0x0e | ||
176 | #define STA350_AUTO2_AMAM_SHIFT 1 | ||
177 | #define STA350_AUTO2_XO_MASK 0xf0 | ||
178 | #define STA350_AUTO2_XO_SHIFT 4 | ||
179 | |||
180 | /* 0x0d AUTO3 */ | ||
181 | #define STA350_AUTO3_PEQ_MASK 0x1f | ||
182 | #define STA350_AUTO3_PEQ_SHIFT 0 | ||
183 | |||
184 | /* 0x0e 0x0f 0x10 CxCFG */ | ||
185 | #define STA350_CxCFG_TCB_SHIFT 0 | ||
186 | #define STA350_CxCFG_EQBP_SHIFT 1 | ||
187 | #define STA350_CxCFG_VBP_SHIFT 2 | ||
188 | #define STA350_CxCFG_BO_SHIFT 3 | ||
189 | #define STA350_CxCFG_LS_SHIFT 4 | ||
190 | #define STA350_CxCFG_OM_MASK 0xc0 | ||
191 | #define STA350_CxCFG_OM_SHIFT 6 | ||
192 | |||
193 | /* 0x11 TONE */ | ||
194 | #define STA350_TONE_BTC_SHIFT 0 | ||
195 | #define STA350_TONE_TTC_SHIFT 4 | ||
196 | |||
197 | /* 0x12 0x13 0x14 0x15 limiter attack/release */ | ||
198 | #define STA350_LxA_SHIFT 0 | ||
199 | #define STA350_LxR_SHIFT 4 | ||
200 | |||
201 | /* 0x26 CFUD */ | ||
202 | #define STA350_CFUD_W1 0x01 | ||
203 | #define STA350_CFUD_WA 0x02 | ||
204 | #define STA350_CFUD_R1 0x04 | ||
205 | #define STA350_CFUD_RA 0x08 | ||
206 | |||
207 | |||
208 | /* biquad filter coefficient table offsets */ | ||
209 | #define STA350_C1_BQ_BASE 0 | ||
210 | #define STA350_C2_BQ_BASE 20 | ||
211 | #define STA350_CH_BQ_NUM 4 | ||
212 | #define STA350_BQ_NUM_COEF 5 | ||
213 | #define STA350_XO_HP_BQ_BASE 40 | ||
214 | #define STA350_XO_LP_BQ_BASE 45 | ||
215 | #define STA350_C1_PRESCALE 50 | ||
216 | #define STA350_C2_PRESCALE 51 | ||
217 | #define STA350_C1_POSTSCALE 52 | ||
218 | #define STA350_C2_POSTSCALE 53 | ||
219 | #define STA350_C3_POSTSCALE 54 | ||
220 | #define STA350_TW_POSTSCALE 55 | ||
221 | #define STA350_C1_MIX1 56 | ||
222 | #define STA350_C1_MIX2 57 | ||
223 | #define STA350_C2_MIX1 58 | ||
224 | #define STA350_C2_MIX2 59 | ||
225 | #define STA350_C3_MIX1 60 | ||
226 | #define STA350_C3_MIX2 61 | ||
227 | |||
228 | #endif /* _ASOC_STA_350_H */ | ||