xref: /linux/sound/soc/codecs/sta32x.c (revision b66a29808e1fac7fc5c8174e3ec0f014bd418280)
1c034abf6SJohannes Stezenbach /*
2c034abf6SJohannes Stezenbach  * Codec driver for ST STA32x 2.1-channel high-efficiency digital audio system
3c034abf6SJohannes Stezenbach  *
4c034abf6SJohannes Stezenbach  * Copyright: 2011 Raumfeld GmbH
5c034abf6SJohannes Stezenbach  * Author: Johannes Stezenbach <js@sig21.net>
6c034abf6SJohannes Stezenbach  *
7c034abf6SJohannes Stezenbach  * based on code from:
8c034abf6SJohannes Stezenbach  *	Wolfson Microelectronics PLC.
9c034abf6SJohannes Stezenbach  *	  Mark Brown <broonie@opensource.wolfsonmicro.com>
10c034abf6SJohannes Stezenbach  *	Freescale Semiconductor, Inc.
11c034abf6SJohannes Stezenbach  *	  Timur Tabi <timur@freescale.com>
12c034abf6SJohannes Stezenbach  *
13c034abf6SJohannes Stezenbach  * This program is free software; you can redistribute  it and/or modify it
14c034abf6SJohannes Stezenbach  * under  the terms of  the GNU General  Public License as published by the
15c034abf6SJohannes Stezenbach  * Free Software Foundation;  either version 2 of the  License, or (at your
16c034abf6SJohannes Stezenbach  * option) any later version.
17c034abf6SJohannes Stezenbach  */
18c034abf6SJohannes Stezenbach 
19c034abf6SJohannes Stezenbach #define pr_fmt(fmt) KBUILD_MODNAME ":%s:%d: " fmt, __func__, __LINE__
20c034abf6SJohannes Stezenbach 
21c034abf6SJohannes Stezenbach #include <linux/module.h>
22c034abf6SJohannes Stezenbach #include <linux/moduleparam.h>
23c034abf6SJohannes Stezenbach #include <linux/init.h>
24c034abf6SJohannes Stezenbach #include <linux/delay.h>
25c034abf6SJohannes Stezenbach #include <linux/pm.h>
26c034abf6SJohannes Stezenbach #include <linux/i2c.h>
2729fdf4fbSMark Brown #include <linux/regmap.h>
28c034abf6SJohannes Stezenbach #include <linux/regulator/consumer.h>
29*b66a2980SThomas Niederprüm #include <linux/gpio/consumer.h>
30c034abf6SJohannes Stezenbach #include <linux/slab.h>
313fb5eac5SJohannes Stezenbach #include <linux/workqueue.h>
32c034abf6SJohannes Stezenbach #include <sound/core.h>
33c034abf6SJohannes Stezenbach #include <sound/pcm.h>
34c034abf6SJohannes Stezenbach #include <sound/pcm_params.h>
35c034abf6SJohannes Stezenbach #include <sound/soc.h>
36c034abf6SJohannes Stezenbach #include <sound/soc-dapm.h>
37c034abf6SJohannes Stezenbach #include <sound/initval.h>
38c034abf6SJohannes Stezenbach #include <sound/tlv.h>
39c034abf6SJohannes Stezenbach 
40e012ba24SJohannes Stezenbach #include <sound/sta32x.h>
41c034abf6SJohannes Stezenbach #include "sta32x.h"
42c034abf6SJohannes Stezenbach 
43c034abf6SJohannes Stezenbach #define STA32X_RATES (SNDRV_PCM_RATE_32000 | \
44c034abf6SJohannes Stezenbach 		      SNDRV_PCM_RATE_44100 | \
45c034abf6SJohannes Stezenbach 		      SNDRV_PCM_RATE_48000 | \
46c034abf6SJohannes Stezenbach 		      SNDRV_PCM_RATE_88200 | \
47c034abf6SJohannes Stezenbach 		      SNDRV_PCM_RATE_96000 | \
48c034abf6SJohannes Stezenbach 		      SNDRV_PCM_RATE_176400 | \
49c034abf6SJohannes Stezenbach 		      SNDRV_PCM_RATE_192000)
50c034abf6SJohannes Stezenbach 
51c034abf6SJohannes Stezenbach #define STA32X_FORMATS \
52c034abf6SJohannes Stezenbach 	(SNDRV_PCM_FMTBIT_S16_LE  | SNDRV_PCM_FMTBIT_S16_BE  | \
53c034abf6SJohannes Stezenbach 	 SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \
54c034abf6SJohannes Stezenbach 	 SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \
55c034abf6SJohannes Stezenbach 	 SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \
56c034abf6SJohannes Stezenbach 	 SNDRV_PCM_FMTBIT_S24_LE  | SNDRV_PCM_FMTBIT_S24_BE  | \
57c034abf6SJohannes Stezenbach 	 SNDRV_PCM_FMTBIT_S32_LE  | SNDRV_PCM_FMTBIT_S32_BE)
58c034abf6SJohannes Stezenbach 
59c034abf6SJohannes Stezenbach /* Power-up register defaults */
6029fdf4fbSMark Brown static const struct reg_default sta32x_regs[] = {
6129fdf4fbSMark Brown 	{  0x0, 0x63 },
6229fdf4fbSMark Brown 	{  0x1, 0x80 },
6329fdf4fbSMark Brown 	{  0x2, 0xc2 },
6429fdf4fbSMark Brown 	{  0x3, 0x40 },
6529fdf4fbSMark Brown 	{  0x4, 0xc2 },
6629fdf4fbSMark Brown 	{  0x5, 0x5c },
6729fdf4fbSMark Brown 	{  0x6, 0x10 },
6829fdf4fbSMark Brown 	{  0x7, 0xff },
6929fdf4fbSMark Brown 	{  0x8, 0x60 },
7029fdf4fbSMark Brown 	{  0x9, 0x60 },
7129fdf4fbSMark Brown 	{  0xa, 0x60 },
7229fdf4fbSMark Brown 	{  0xb, 0x80 },
7329fdf4fbSMark Brown 	{  0xc, 0x00 },
7429fdf4fbSMark Brown 	{  0xd, 0x00 },
7529fdf4fbSMark Brown 	{  0xe, 0x00 },
7629fdf4fbSMark Brown 	{  0xf, 0x40 },
7729fdf4fbSMark Brown 	{ 0x10, 0x80 },
7829fdf4fbSMark Brown 	{ 0x11, 0x77 },
7929fdf4fbSMark Brown 	{ 0x12, 0x6a },
8029fdf4fbSMark Brown 	{ 0x13, 0x69 },
8129fdf4fbSMark Brown 	{ 0x14, 0x6a },
8229fdf4fbSMark Brown 	{ 0x15, 0x69 },
8329fdf4fbSMark Brown 	{ 0x16, 0x00 },
8429fdf4fbSMark Brown 	{ 0x17, 0x00 },
8529fdf4fbSMark Brown 	{ 0x18, 0x00 },
8629fdf4fbSMark Brown 	{ 0x19, 0x00 },
8729fdf4fbSMark Brown 	{ 0x1a, 0x00 },
8829fdf4fbSMark Brown 	{ 0x1b, 0x00 },
8929fdf4fbSMark Brown 	{ 0x1c, 0x00 },
9029fdf4fbSMark Brown 	{ 0x1d, 0x00 },
9129fdf4fbSMark Brown 	{ 0x1e, 0x00 },
9229fdf4fbSMark Brown 	{ 0x1f, 0x00 },
9329fdf4fbSMark Brown 	{ 0x20, 0x00 },
9429fdf4fbSMark Brown 	{ 0x21, 0x00 },
9529fdf4fbSMark Brown 	{ 0x22, 0x00 },
9629fdf4fbSMark Brown 	{ 0x23, 0x00 },
9729fdf4fbSMark Brown 	{ 0x24, 0x00 },
9829fdf4fbSMark Brown 	{ 0x25, 0x00 },
9929fdf4fbSMark Brown 	{ 0x26, 0x00 },
10029fdf4fbSMark Brown 	{ 0x27, 0x2d },
10129fdf4fbSMark Brown 	{ 0x28, 0xc0 },
10229fdf4fbSMark Brown 	{ 0x2b, 0x00 },
10329fdf4fbSMark Brown 	{ 0x2c, 0x0c },
104c034abf6SJohannes Stezenbach };
105c034abf6SJohannes Stezenbach 
106a1be4ceaSThomas Niederprüm static const struct regmap_range sta32x_write_regs_range[] = {
107a1be4ceaSThomas Niederprüm 	regmap_reg_range(STA32X_CONFA,  STA32X_AUTO2),
108a1be4ceaSThomas Niederprüm 	regmap_reg_range(STA32X_C1CFG,  STA32X_FDRC2),
109a1be4ceaSThomas Niederprüm };
110a1be4ceaSThomas Niederprüm 
111a1be4ceaSThomas Niederprüm static const struct regmap_range sta32x_read_regs_range[] = {
112a1be4ceaSThomas Niederprüm 	regmap_reg_range(STA32X_CONFA,  STA32X_AUTO2),
113a1be4ceaSThomas Niederprüm 	regmap_reg_range(STA32X_C1CFG,  STA32X_FDRC2),
114a1be4ceaSThomas Niederprüm };
115a1be4ceaSThomas Niederprüm 
116a1be4ceaSThomas Niederprüm static const struct regmap_range sta32x_volatile_regs_range[] = {
117a1be4ceaSThomas Niederprüm 	regmap_reg_range(STA32X_CFADDR2, STA32X_CFUD),
118a1be4ceaSThomas Niederprüm };
119a1be4ceaSThomas Niederprüm 
120a1be4ceaSThomas Niederprüm static const struct regmap_access_table sta32x_write_regs = {
121a1be4ceaSThomas Niederprüm 	.yes_ranges =	sta32x_write_regs_range,
122a1be4ceaSThomas Niederprüm 	.n_yes_ranges =	ARRAY_SIZE(sta32x_write_regs_range),
123a1be4ceaSThomas Niederprüm };
124a1be4ceaSThomas Niederprüm 
125a1be4ceaSThomas Niederprüm static const struct regmap_access_table sta32x_read_regs = {
126a1be4ceaSThomas Niederprüm 	.yes_ranges =	sta32x_read_regs_range,
127a1be4ceaSThomas Niederprüm 	.n_yes_ranges =	ARRAY_SIZE(sta32x_read_regs_range),
128a1be4ceaSThomas Niederprüm };
129a1be4ceaSThomas Niederprüm 
130a1be4ceaSThomas Niederprüm static const struct regmap_access_table sta32x_volatile_regs = {
131a1be4ceaSThomas Niederprüm 	.yes_ranges =	sta32x_volatile_regs_range,
132a1be4ceaSThomas Niederprüm 	.n_yes_ranges =	ARRAY_SIZE(sta32x_volatile_regs_range),
133a1be4ceaSThomas Niederprüm };
134a1be4ceaSThomas Niederprüm 
135c034abf6SJohannes Stezenbach /* regulator power supply names */
136c034abf6SJohannes Stezenbach static const char *sta32x_supply_names[] = {
137c034abf6SJohannes Stezenbach 	"Vdda",	/* analog supply, 3.3VV */
138c034abf6SJohannes Stezenbach 	"Vdd3",	/* digital supply, 3.3V */
139c034abf6SJohannes Stezenbach 	"Vcc"	/* power amp spply, 10V - 36V */
140c034abf6SJohannes Stezenbach };
141c034abf6SJohannes Stezenbach 
142c034abf6SJohannes Stezenbach /* codec private data */
143c034abf6SJohannes Stezenbach struct sta32x_priv {
14429fdf4fbSMark Brown 	struct regmap *regmap;
145c034abf6SJohannes Stezenbach 	struct regulator_bulk_data supplies[ARRAY_SIZE(sta32x_supply_names)];
146c034abf6SJohannes Stezenbach 	struct snd_soc_codec *codec;
147e012ba24SJohannes Stezenbach 	struct sta32x_platform_data *pdata;
148c034abf6SJohannes Stezenbach 
149c034abf6SJohannes Stezenbach 	unsigned int mclk;
150c034abf6SJohannes Stezenbach 	unsigned int format;
15154dc6cabSJohannes Stezenbach 
15254dc6cabSJohannes Stezenbach 	u32 coef_shadow[STA32X_COEF_COUNT];
1533fb5eac5SJohannes Stezenbach 	struct delayed_work watchdog_work;
1543fb5eac5SJohannes Stezenbach 	int shutdown;
155*b66a2980SThomas Niederprüm 	struct gpio_desc *gpiod_nreset;
156a1be4ceaSThomas Niederprüm 	struct mutex coeff_lock;
157c034abf6SJohannes Stezenbach };
158c034abf6SJohannes Stezenbach 
159c034abf6SJohannes Stezenbach static const DECLARE_TLV_DB_SCALE(mvol_tlv, -12700, 50, 1);
160c034abf6SJohannes Stezenbach static const DECLARE_TLV_DB_SCALE(chvol_tlv, -7950, 50, 1);
161c034abf6SJohannes Stezenbach static const DECLARE_TLV_DB_SCALE(tone_tlv, -120, 200, 0);
162c034abf6SJohannes Stezenbach 
163c034abf6SJohannes Stezenbach static const char *sta32x_drc_ac[] = {
164c034abf6SJohannes Stezenbach 	"Anti-Clipping", "Dynamic Range Compression" };
165c034abf6SJohannes Stezenbach static const char *sta32x_auto_eq_mode[] = {
166c034abf6SJohannes Stezenbach 	"User", "Preset", "Loudness" };
167c034abf6SJohannes Stezenbach static const char *sta32x_auto_gc_mode[] = {
168c034abf6SJohannes Stezenbach 	"User", "AC no clipping", "AC limited clipping (10%)",
169c034abf6SJohannes Stezenbach 	"DRC nighttime listening mode" };
170c034abf6SJohannes Stezenbach static const char *sta32x_auto_xo_mode[] = {
171c034abf6SJohannes Stezenbach 	"User", "80Hz", "100Hz", "120Hz", "140Hz", "160Hz", "180Hz", "200Hz",
172c034abf6SJohannes Stezenbach 	"220Hz", "240Hz", "260Hz", "280Hz", "300Hz", "320Hz", "340Hz", "360Hz" };
173c034abf6SJohannes Stezenbach static const char *sta32x_preset_eq_mode[] = {
174c034abf6SJohannes Stezenbach 	"Flat", "Rock", "Soft Rock", "Jazz", "Classical", "Dance", "Pop", "Soft",
175c034abf6SJohannes Stezenbach 	"Hard", "Party", "Vocal", "Hip-Hop", "Dialog", "Bass-boost #1",
176c034abf6SJohannes Stezenbach 	"Bass-boost #2", "Bass-boost #3", "Loudness 1", "Loudness 2",
177c034abf6SJohannes Stezenbach 	"Loudness 3", "Loudness 4", "Loudness 5", "Loudness 6", "Loudness 7",
178c034abf6SJohannes Stezenbach 	"Loudness 8", "Loudness 9", "Loudness 10", "Loudness 11", "Loudness 12",
179c034abf6SJohannes Stezenbach 	"Loudness 13", "Loudness 14", "Loudness 15", "Loudness 16" };
180c034abf6SJohannes Stezenbach static const char *sta32x_limiter_select[] = {
181c034abf6SJohannes Stezenbach 	"Limiter Disabled", "Limiter #1", "Limiter #2" };
182c034abf6SJohannes Stezenbach static const char *sta32x_limiter_attack_rate[] = {
183c034abf6SJohannes Stezenbach 	"3.1584", "2.7072", "2.2560", "1.8048", "1.3536", "0.9024",
184c034abf6SJohannes Stezenbach 	"0.4512", "0.2256", "0.1504", "0.1123", "0.0902", "0.0752",
185c034abf6SJohannes Stezenbach 	"0.0645", "0.0564", "0.0501", "0.0451" };
186c034abf6SJohannes Stezenbach static const char *sta32x_limiter_release_rate[] = {
187c034abf6SJohannes Stezenbach 	"0.5116", "0.1370", "0.0744", "0.0499", "0.0360", "0.0299",
188c034abf6SJohannes Stezenbach 	"0.0264", "0.0208", "0.0198", "0.0172", "0.0147", "0.0137",
189c034abf6SJohannes Stezenbach 	"0.0134", "0.0117", "0.0110", "0.0104" };
190c034abf6SJohannes Stezenbach 
191c034abf6SJohannes Stezenbach static const unsigned int sta32x_limiter_ac_attack_tlv[] = {
192c034abf6SJohannes Stezenbach 	TLV_DB_RANGE_HEAD(2),
193c034abf6SJohannes Stezenbach 	0, 7, TLV_DB_SCALE_ITEM(-1200, 200, 0),
194c034abf6SJohannes Stezenbach 	8, 16, TLV_DB_SCALE_ITEM(300, 100, 0),
195c034abf6SJohannes Stezenbach };
196c034abf6SJohannes Stezenbach 
197c034abf6SJohannes Stezenbach static const unsigned int sta32x_limiter_ac_release_tlv[] = {
198c034abf6SJohannes Stezenbach 	TLV_DB_RANGE_HEAD(5),
199c034abf6SJohannes Stezenbach 	0, 0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 0),
200c034abf6SJohannes Stezenbach 	1, 1, TLV_DB_SCALE_ITEM(-2900, 0, 0),
201c034abf6SJohannes Stezenbach 	2, 2, TLV_DB_SCALE_ITEM(-2000, 0, 0),
202c034abf6SJohannes Stezenbach 	3, 8, TLV_DB_SCALE_ITEM(-1400, 200, 0),
203c034abf6SJohannes Stezenbach 	8, 16, TLV_DB_SCALE_ITEM(-700, 100, 0),
204c034abf6SJohannes Stezenbach };
205c034abf6SJohannes Stezenbach 
206c034abf6SJohannes Stezenbach static const unsigned int sta32x_limiter_drc_attack_tlv[] = {
207c034abf6SJohannes Stezenbach 	TLV_DB_RANGE_HEAD(3),
208c034abf6SJohannes Stezenbach 	0, 7, TLV_DB_SCALE_ITEM(-3100, 200, 0),
209c034abf6SJohannes Stezenbach 	8, 13, TLV_DB_SCALE_ITEM(-1600, 100, 0),
210c034abf6SJohannes Stezenbach 	14, 16, TLV_DB_SCALE_ITEM(-1000, 300, 0),
211c034abf6SJohannes Stezenbach };
212c034abf6SJohannes Stezenbach 
213c034abf6SJohannes Stezenbach static const unsigned int sta32x_limiter_drc_release_tlv[] = {
214c034abf6SJohannes Stezenbach 	TLV_DB_RANGE_HEAD(5),
215c034abf6SJohannes Stezenbach 	0, 0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 0),
216c034abf6SJohannes Stezenbach 	1, 2, TLV_DB_SCALE_ITEM(-3800, 200, 0),
217c034abf6SJohannes Stezenbach 	3, 4, TLV_DB_SCALE_ITEM(-3300, 200, 0),
218c034abf6SJohannes Stezenbach 	5, 12, TLV_DB_SCALE_ITEM(-3000, 200, 0),
219c034abf6SJohannes Stezenbach 	13, 16, TLV_DB_SCALE_ITEM(-1500, 300, 0),
220c034abf6SJohannes Stezenbach };
221c034abf6SJohannes Stezenbach 
222025c3fa9STakashi Iwai static SOC_ENUM_SINGLE_DECL(sta32x_drc_ac_enum,
223025c3fa9STakashi Iwai 			    STA32X_CONFD, STA32X_CONFD_DRC_SHIFT,
224025c3fa9STakashi Iwai 			    sta32x_drc_ac);
225025c3fa9STakashi Iwai static SOC_ENUM_SINGLE_DECL(sta32x_auto_eq_enum,
226025c3fa9STakashi Iwai 			    STA32X_AUTO1, STA32X_AUTO1_AMEQ_SHIFT,
227025c3fa9STakashi Iwai 			    sta32x_auto_eq_mode);
228025c3fa9STakashi Iwai static SOC_ENUM_SINGLE_DECL(sta32x_auto_gc_enum,
229025c3fa9STakashi Iwai 			    STA32X_AUTO1, STA32X_AUTO1_AMGC_SHIFT,
230025c3fa9STakashi Iwai 			    sta32x_auto_gc_mode);
231025c3fa9STakashi Iwai static SOC_ENUM_SINGLE_DECL(sta32x_auto_xo_enum,
232025c3fa9STakashi Iwai 			    STA32X_AUTO2, STA32X_AUTO2_XO_SHIFT,
233025c3fa9STakashi Iwai 			    sta32x_auto_xo_mode);
234025c3fa9STakashi Iwai static SOC_ENUM_SINGLE_DECL(sta32x_preset_eq_enum,
235025c3fa9STakashi Iwai 			    STA32X_AUTO3, STA32X_AUTO3_PEQ_SHIFT,
236025c3fa9STakashi Iwai 			    sta32x_preset_eq_mode);
237025c3fa9STakashi Iwai static SOC_ENUM_SINGLE_DECL(sta32x_limiter_ch1_enum,
238025c3fa9STakashi Iwai 			    STA32X_C1CFG, STA32X_CxCFG_LS_SHIFT,
239025c3fa9STakashi Iwai 			    sta32x_limiter_select);
240025c3fa9STakashi Iwai static SOC_ENUM_SINGLE_DECL(sta32x_limiter_ch2_enum,
241025c3fa9STakashi Iwai 			    STA32X_C2CFG, STA32X_CxCFG_LS_SHIFT,
242025c3fa9STakashi Iwai 			    sta32x_limiter_select);
243025c3fa9STakashi Iwai static SOC_ENUM_SINGLE_DECL(sta32x_limiter_ch3_enum,
244025c3fa9STakashi Iwai 			    STA32X_C3CFG, STA32X_CxCFG_LS_SHIFT,
245025c3fa9STakashi Iwai 			    sta32x_limiter_select);
246025c3fa9STakashi Iwai static SOC_ENUM_SINGLE_DECL(sta32x_limiter1_attack_rate_enum,
247025c3fa9STakashi Iwai 			    STA32X_L1AR, STA32X_LxA_SHIFT,
248025c3fa9STakashi Iwai 			    sta32x_limiter_attack_rate);
249025c3fa9STakashi Iwai static SOC_ENUM_SINGLE_DECL(sta32x_limiter2_attack_rate_enum,
250025c3fa9STakashi Iwai 			    STA32X_L2AR, STA32X_LxA_SHIFT,
251025c3fa9STakashi Iwai 			    sta32x_limiter_attack_rate);
252025c3fa9STakashi Iwai static SOC_ENUM_SINGLE_DECL(sta32x_limiter1_release_rate_enum,
253025c3fa9STakashi Iwai 			    STA32X_L1AR, STA32X_LxR_SHIFT,
254025c3fa9STakashi Iwai 			    sta32x_limiter_release_rate);
255025c3fa9STakashi Iwai static SOC_ENUM_SINGLE_DECL(sta32x_limiter2_release_rate_enum,
256025c3fa9STakashi Iwai 			    STA32X_L2AR, STA32X_LxR_SHIFT,
257025c3fa9STakashi Iwai 			    sta32x_limiter_release_rate);
25879688439SJohannes Stezenbach 
25979688439SJohannes Stezenbach /* byte array controls for setting biquad, mixer, scaling coefficients;
26079688439SJohannes Stezenbach  * for biquads all five coefficients need to be set in one go,
26179688439SJohannes Stezenbach  * mixer and pre/postscale coefs can be set individually;
26279688439SJohannes Stezenbach  * each coef is 24bit, the bytes are ordered in the same way
26379688439SJohannes Stezenbach  * as given in the STA32x data sheet (big endian; b1, b2, a1, a2, b0)
26479688439SJohannes Stezenbach  */
26579688439SJohannes Stezenbach 
26679688439SJohannes Stezenbach static int sta32x_coefficient_info(struct snd_kcontrol *kcontrol,
26779688439SJohannes Stezenbach 				   struct snd_ctl_elem_info *uinfo)
26879688439SJohannes Stezenbach {
26979688439SJohannes Stezenbach 	int numcoef = kcontrol->private_value >> 16;
27079688439SJohannes Stezenbach 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
27179688439SJohannes Stezenbach 	uinfo->count = 3 * numcoef;
27279688439SJohannes Stezenbach 	return 0;
27379688439SJohannes Stezenbach }
27479688439SJohannes Stezenbach 
27579688439SJohannes Stezenbach static int sta32x_coefficient_get(struct snd_kcontrol *kcontrol,
27679688439SJohannes Stezenbach 				  struct snd_ctl_elem_value *ucontrol)
27779688439SJohannes Stezenbach {
278ea53bf77SLars-Peter Clausen 	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
279a1be4ceaSThomas Niederprüm 	struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
28079688439SJohannes Stezenbach 	int numcoef = kcontrol->private_value >> 16;
28179688439SJohannes Stezenbach 	int index = kcontrol->private_value & 0xffff;
282a1be4ceaSThomas Niederprüm 	unsigned int cfud, val;
283a1be4ceaSThomas Niederprüm 	int i, ret = 0;
284a1be4ceaSThomas Niederprüm 
285a1be4ceaSThomas Niederprüm 	mutex_lock(&sta32x->coeff_lock);
28679688439SJohannes Stezenbach 
28779688439SJohannes Stezenbach 	/* preserve reserved bits in STA32X_CFUD */
288a1be4ceaSThomas Niederprüm 	regmap_read(sta32x->regmap, STA32X_CFUD, &cfud);
289a1be4ceaSThomas Niederprüm 	cfud &= 0xf0;
290a1be4ceaSThomas Niederprüm 	/*
291a1be4ceaSThomas Niederprüm 	 * chip documentation does not say if the bits are self clearing,
292a1be4ceaSThomas Niederprüm 	 * so do it explicitly
293a1be4ceaSThomas Niederprüm 	 */
294a1be4ceaSThomas Niederprüm 	regmap_write(sta32x->regmap, STA32X_CFUD, cfud);
29579688439SJohannes Stezenbach 
296a1be4ceaSThomas Niederprüm 	regmap_write(sta32x->regmap, STA32X_CFADDR2, index);
297a1be4ceaSThomas Niederprüm 	if (numcoef == 1) {
298a1be4ceaSThomas Niederprüm 		regmap_write(sta32x->regmap, STA32X_CFUD, cfud | 0x04);
299a1be4ceaSThomas Niederprüm 	} else if (numcoef == 5) {
300a1be4ceaSThomas Niederprüm 		regmap_write(sta32x->regmap, STA32X_CFUD, cfud | 0x08);
301a1be4ceaSThomas Niederprüm 	} else {
302a1be4ceaSThomas Niederprüm 		ret = -EINVAL;
303a1be4ceaSThomas Niederprüm 		goto exit_unlock;
304a1be4ceaSThomas Niederprüm 	}
30579688439SJohannes Stezenbach 
306a1be4ceaSThomas Niederprüm 	for (i = 0; i < 3 * numcoef; i++) {
307a1be4ceaSThomas Niederprüm 		regmap_read(sta32x->regmap, STA32X_B1CF1 + i, &val);
308a1be4ceaSThomas Niederprüm 		ucontrol->value.bytes.data[i] = val;
309a1be4ceaSThomas Niederprüm 	}
310a1be4ceaSThomas Niederprüm 
311a1be4ceaSThomas Niederprüm exit_unlock:
312a1be4ceaSThomas Niederprüm 	mutex_unlock(&sta32x->coeff_lock);
313a1be4ceaSThomas Niederprüm 
314a1be4ceaSThomas Niederprüm 	return ret;
31579688439SJohannes Stezenbach }
31679688439SJohannes Stezenbach 
31779688439SJohannes Stezenbach static int sta32x_coefficient_put(struct snd_kcontrol *kcontrol,
31879688439SJohannes Stezenbach 				  struct snd_ctl_elem_value *ucontrol)
31979688439SJohannes Stezenbach {
320ea53bf77SLars-Peter Clausen 	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
32154dc6cabSJohannes Stezenbach 	struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
32279688439SJohannes Stezenbach 	int numcoef = kcontrol->private_value >> 16;
32379688439SJohannes Stezenbach 	int index = kcontrol->private_value & 0xffff;
32479688439SJohannes Stezenbach 	unsigned int cfud;
32579688439SJohannes Stezenbach 	int i;
32679688439SJohannes Stezenbach 
32779688439SJohannes Stezenbach 	/* preserve reserved bits in STA32X_CFUD */
328a1be4ceaSThomas Niederprüm 	regmap_read(sta32x->regmap, STA32X_CFUD, &cfud);
329a1be4ceaSThomas Niederprüm 	cfud &= 0xf0;
330a1be4ceaSThomas Niederprüm 	/*
331a1be4ceaSThomas Niederprüm 	 * chip documentation does not say if the bits are self clearing,
332a1be4ceaSThomas Niederprüm 	 * so do it explicitly
333a1be4ceaSThomas Niederprüm 	 */
334a1be4ceaSThomas Niederprüm 	regmap_write(sta32x->regmap, STA32X_CFUD, cfud);
33579688439SJohannes Stezenbach 
336a1be4ceaSThomas Niederprüm 	regmap_write(sta32x->regmap, STA32X_CFADDR2, index);
33754dc6cabSJohannes Stezenbach 	for (i = 0; i < numcoef && (index + i < STA32X_COEF_COUNT); i++)
33854dc6cabSJohannes Stezenbach 		sta32x->coef_shadow[index + i] =
33954dc6cabSJohannes Stezenbach 			  (ucontrol->value.bytes.data[3 * i] << 16)
34054dc6cabSJohannes Stezenbach 			| (ucontrol->value.bytes.data[3 * i + 1] << 8)
34154dc6cabSJohannes Stezenbach 			| (ucontrol->value.bytes.data[3 * i + 2]);
34279688439SJohannes Stezenbach 	for (i = 0; i < 3 * numcoef; i++)
343a1be4ceaSThomas Niederprüm 		regmap_write(sta32x->regmap, STA32X_B1CF1 + i,
34479688439SJohannes Stezenbach 			     ucontrol->value.bytes.data[i]);
34579688439SJohannes Stezenbach 	if (numcoef == 1)
346a1be4ceaSThomas Niederprüm 		regmap_write(sta32x->regmap, STA32X_CFUD, cfud | 0x01);
34779688439SJohannes Stezenbach 	else if (numcoef == 5)
348a1be4ceaSThomas Niederprüm 		regmap_write(sta32x->regmap, STA32X_CFUD, cfud | 0x02);
34979688439SJohannes Stezenbach 	else
35079688439SJohannes Stezenbach 		return -EINVAL;
35179688439SJohannes Stezenbach 
35279688439SJohannes Stezenbach 	return 0;
35379688439SJohannes Stezenbach }
35479688439SJohannes Stezenbach 
355878042d1SMark Brown static int sta32x_sync_coef_shadow(struct snd_soc_codec *codec)
35654dc6cabSJohannes Stezenbach {
35754dc6cabSJohannes Stezenbach 	struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
35854dc6cabSJohannes Stezenbach 	unsigned int cfud;
35954dc6cabSJohannes Stezenbach 	int i;
36054dc6cabSJohannes Stezenbach 
36154dc6cabSJohannes Stezenbach 	/* preserve reserved bits in STA32X_CFUD */
362a1be4ceaSThomas Niederprüm 	regmap_read(sta32x->regmap, STA32X_CFUD, &cfud);
363a1be4ceaSThomas Niederprüm 	cfud &= 0xf0;
36454dc6cabSJohannes Stezenbach 
36554dc6cabSJohannes Stezenbach 	for (i = 0; i < STA32X_COEF_COUNT; i++) {
366a1be4ceaSThomas Niederprüm 		regmap_write(sta32x->regmap, STA32X_CFADDR2, i);
367a1be4ceaSThomas Niederprüm 		regmap_write(sta32x->regmap, STA32X_B1CF1,
36854dc6cabSJohannes Stezenbach 			     (sta32x->coef_shadow[i] >> 16) & 0xff);
369a1be4ceaSThomas Niederprüm 		regmap_write(sta32x->regmap, STA32X_B1CF2,
37054dc6cabSJohannes Stezenbach 			     (sta32x->coef_shadow[i] >> 8) & 0xff);
371a1be4ceaSThomas Niederprüm 		regmap_write(sta32x->regmap, STA32X_B1CF3,
37254dc6cabSJohannes Stezenbach 			     (sta32x->coef_shadow[i]) & 0xff);
373a1be4ceaSThomas Niederprüm 		/*
374a1be4ceaSThomas Niederprüm 		 * chip documentation does not say if the bits are
375a1be4ceaSThomas Niederprüm 		 * self-clearing, so do it explicitly
376a1be4ceaSThomas Niederprüm 		 */
377a1be4ceaSThomas Niederprüm 		regmap_write(sta32x->regmap, STA32X_CFUD, cfud);
378a1be4ceaSThomas Niederprüm 		regmap_write(sta32x->regmap, STA32X_CFUD, cfud | 0x01);
37954dc6cabSJohannes Stezenbach 	}
38054dc6cabSJohannes Stezenbach 	return 0;
38154dc6cabSJohannes Stezenbach }
38254dc6cabSJohannes Stezenbach 
383878042d1SMark Brown static int sta32x_cache_sync(struct snd_soc_codec *codec)
38454dc6cabSJohannes Stezenbach {
38570ff00f8SLars-Peter Clausen 	struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
38654dc6cabSJohannes Stezenbach 	unsigned int mute;
38754dc6cabSJohannes Stezenbach 	int rc;
38854dc6cabSJohannes Stezenbach 
38954dc6cabSJohannes Stezenbach 	/* mute during register sync */
390a1be4ceaSThomas Niederprüm 	regmap_read(sta32x->regmap, STA32X_MMUTE, &mute);
391a1be4ceaSThomas Niederprüm 	regmap_write(sta32x->regmap, STA32X_MMUTE, mute | STA32X_MMUTE_MMUTE);
39254dc6cabSJohannes Stezenbach 	sta32x_sync_coef_shadow(codec);
39329fdf4fbSMark Brown 	rc = regcache_sync(sta32x->regmap);
394a1be4ceaSThomas Niederprüm 	regmap_write(sta32x->regmap, STA32X_MMUTE, mute);
39554dc6cabSJohannes Stezenbach 	return rc;
39654dc6cabSJohannes Stezenbach }
39754dc6cabSJohannes Stezenbach 
3983fb5eac5SJohannes Stezenbach /* work around ESD issue where sta32x resets and loses all configuration */
3993fb5eac5SJohannes Stezenbach static void sta32x_watchdog(struct work_struct *work)
4003fb5eac5SJohannes Stezenbach {
4013fb5eac5SJohannes Stezenbach 	struct sta32x_priv *sta32x = container_of(work, struct sta32x_priv,
4023fb5eac5SJohannes Stezenbach 						  watchdog_work.work);
4033fb5eac5SJohannes Stezenbach 	struct snd_soc_codec *codec = sta32x->codec;
4043fb5eac5SJohannes Stezenbach 	unsigned int confa, confa_cached;
4053fb5eac5SJohannes Stezenbach 
4063fb5eac5SJohannes Stezenbach 	/* check if sta32x has reset itself */
4073fb5eac5SJohannes Stezenbach 	confa_cached = snd_soc_read(codec, STA32X_CONFA);
40829fdf4fbSMark Brown 	regcache_cache_bypass(sta32x->regmap, true);
4093fb5eac5SJohannes Stezenbach 	confa = snd_soc_read(codec, STA32X_CONFA);
41029fdf4fbSMark Brown 	regcache_cache_bypass(sta32x->regmap, false);
4113fb5eac5SJohannes Stezenbach 	if (confa != confa_cached) {
41229fdf4fbSMark Brown 		regcache_mark_dirty(sta32x->regmap);
4133fb5eac5SJohannes Stezenbach 		sta32x_cache_sync(codec);
4143fb5eac5SJohannes Stezenbach 	}
4153fb5eac5SJohannes Stezenbach 
4163fb5eac5SJohannes Stezenbach 	if (!sta32x->shutdown)
417a14d9829SMark Brown 		queue_delayed_work(system_power_efficient_wq,
418a14d9829SMark Brown 				   &sta32x->watchdog_work,
4193fb5eac5SJohannes Stezenbach 				   round_jiffies_relative(HZ));
4203fb5eac5SJohannes Stezenbach }
4213fb5eac5SJohannes Stezenbach 
4223fb5eac5SJohannes Stezenbach static void sta32x_watchdog_start(struct sta32x_priv *sta32x)
4233fb5eac5SJohannes Stezenbach {
4243fb5eac5SJohannes Stezenbach 	if (sta32x->pdata->needs_esd_watchdog) {
4253fb5eac5SJohannes Stezenbach 		sta32x->shutdown = 0;
426a14d9829SMark Brown 		queue_delayed_work(system_power_efficient_wq,
427a14d9829SMark Brown 				   &sta32x->watchdog_work,
4283fb5eac5SJohannes Stezenbach 				   round_jiffies_relative(HZ));
4293fb5eac5SJohannes Stezenbach 	}
4303fb5eac5SJohannes Stezenbach }
4313fb5eac5SJohannes Stezenbach 
4323fb5eac5SJohannes Stezenbach static void sta32x_watchdog_stop(struct sta32x_priv *sta32x)
4333fb5eac5SJohannes Stezenbach {
4343fb5eac5SJohannes Stezenbach 	if (sta32x->pdata->needs_esd_watchdog) {
4353fb5eac5SJohannes Stezenbach 		sta32x->shutdown = 1;
4363fb5eac5SJohannes Stezenbach 		cancel_delayed_work_sync(&sta32x->watchdog_work);
4373fb5eac5SJohannes Stezenbach 	}
4383fb5eac5SJohannes Stezenbach }
4393fb5eac5SJohannes Stezenbach 
44079688439SJohannes Stezenbach #define SINGLE_COEF(xname, index) \
44179688439SJohannes Stezenbach {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
44279688439SJohannes Stezenbach 	.info = sta32x_coefficient_info, \
44379688439SJohannes Stezenbach 	.get = sta32x_coefficient_get,\
44479688439SJohannes Stezenbach 	.put = sta32x_coefficient_put, \
44579688439SJohannes Stezenbach 	.private_value = index | (1 << 16) }
44679688439SJohannes Stezenbach 
44779688439SJohannes Stezenbach #define BIQUAD_COEFS(xname, index) \
44879688439SJohannes Stezenbach {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
44979688439SJohannes Stezenbach 	.info = sta32x_coefficient_info, \
45079688439SJohannes Stezenbach 	.get = sta32x_coefficient_get,\
45179688439SJohannes Stezenbach 	.put = sta32x_coefficient_put, \
45279688439SJohannes Stezenbach 	.private_value = index | (5 << 16) }
45379688439SJohannes Stezenbach 
454c034abf6SJohannes Stezenbach static const struct snd_kcontrol_new sta32x_snd_controls[] = {
455c034abf6SJohannes Stezenbach SOC_SINGLE_TLV("Master Volume", STA32X_MVOL, 0, 0xff, 1, mvol_tlv),
456c034abf6SJohannes Stezenbach SOC_SINGLE("Master Switch", STA32X_MMUTE, 0, 1, 1),
457c034abf6SJohannes Stezenbach SOC_SINGLE("Ch1 Switch", STA32X_MMUTE, 1, 1, 1),
458c034abf6SJohannes Stezenbach SOC_SINGLE("Ch2 Switch", STA32X_MMUTE, 2, 1, 1),
459c034abf6SJohannes Stezenbach SOC_SINGLE("Ch3 Switch", STA32X_MMUTE, 3, 1, 1),
460c034abf6SJohannes Stezenbach SOC_SINGLE_TLV("Ch1 Volume", STA32X_C1VOL, 0, 0xff, 1, chvol_tlv),
461c034abf6SJohannes Stezenbach SOC_SINGLE_TLV("Ch2 Volume", STA32X_C2VOL, 0, 0xff, 1, chvol_tlv),
462c034abf6SJohannes Stezenbach SOC_SINGLE_TLV("Ch3 Volume", STA32X_C3VOL, 0, 0xff, 1, chvol_tlv),
463c034abf6SJohannes Stezenbach SOC_SINGLE("De-emphasis Filter Switch", STA32X_CONFD, STA32X_CONFD_DEMP_SHIFT, 1, 0),
464c034abf6SJohannes Stezenbach SOC_ENUM("Compressor/Limiter Switch", sta32x_drc_ac_enum),
465c034abf6SJohannes Stezenbach SOC_SINGLE("Miami Mode Switch", STA32X_CONFD, STA32X_CONFD_MME_SHIFT, 1, 0),
466c034abf6SJohannes Stezenbach SOC_SINGLE("Zero Cross Switch", STA32X_CONFE, STA32X_CONFE_ZCE_SHIFT, 1, 0),
467c034abf6SJohannes Stezenbach SOC_SINGLE("Soft Ramp Switch", STA32X_CONFE, STA32X_CONFE_SVE_SHIFT, 1, 0),
468c034abf6SJohannes Stezenbach SOC_SINGLE("Auto-Mute Switch", STA32X_CONFF, STA32X_CONFF_IDE_SHIFT, 1, 0),
469c034abf6SJohannes Stezenbach SOC_ENUM("Automode EQ", sta32x_auto_eq_enum),
470c034abf6SJohannes Stezenbach SOC_ENUM("Automode GC", sta32x_auto_gc_enum),
471c034abf6SJohannes Stezenbach SOC_ENUM("Automode XO", sta32x_auto_xo_enum),
472c034abf6SJohannes Stezenbach SOC_ENUM("Preset EQ", sta32x_preset_eq_enum),
473c034abf6SJohannes Stezenbach SOC_SINGLE("Ch1 Tone Control Bypass Switch", STA32X_C1CFG, STA32X_CxCFG_TCB_SHIFT, 1, 0),
474c034abf6SJohannes Stezenbach SOC_SINGLE("Ch2 Tone Control Bypass Switch", STA32X_C2CFG, STA32X_CxCFG_TCB_SHIFT, 1, 0),
475c034abf6SJohannes Stezenbach SOC_SINGLE("Ch1 EQ Bypass Switch", STA32X_C1CFG, STA32X_CxCFG_EQBP_SHIFT, 1, 0),
476c034abf6SJohannes Stezenbach SOC_SINGLE("Ch2 EQ Bypass Switch", STA32X_C2CFG, STA32X_CxCFG_EQBP_SHIFT, 1, 0),
477c034abf6SJohannes Stezenbach SOC_SINGLE("Ch1 Master Volume Bypass Switch", STA32X_C1CFG, STA32X_CxCFG_VBP_SHIFT, 1, 0),
478c034abf6SJohannes Stezenbach SOC_SINGLE("Ch2 Master Volume Bypass Switch", STA32X_C1CFG, STA32X_CxCFG_VBP_SHIFT, 1, 0),
479c034abf6SJohannes Stezenbach SOC_SINGLE("Ch3 Master Volume Bypass Switch", STA32X_C1CFG, STA32X_CxCFG_VBP_SHIFT, 1, 0),
480c034abf6SJohannes Stezenbach SOC_ENUM("Ch1 Limiter Select", sta32x_limiter_ch1_enum),
481c034abf6SJohannes Stezenbach SOC_ENUM("Ch2 Limiter Select", sta32x_limiter_ch2_enum),
482c034abf6SJohannes Stezenbach SOC_ENUM("Ch3 Limiter Select", sta32x_limiter_ch3_enum),
483c034abf6SJohannes Stezenbach SOC_SINGLE_TLV("Bass Tone Control", STA32X_TONE, STA32X_TONE_BTC_SHIFT, 15, 0, tone_tlv),
484c034abf6SJohannes Stezenbach SOC_SINGLE_TLV("Treble Tone Control", STA32X_TONE, STA32X_TONE_TTC_SHIFT, 15, 0, tone_tlv),
485c034abf6SJohannes Stezenbach SOC_ENUM("Limiter1 Attack Rate (dB/ms)", sta32x_limiter1_attack_rate_enum),
486c034abf6SJohannes Stezenbach SOC_ENUM("Limiter2 Attack Rate (dB/ms)", sta32x_limiter2_attack_rate_enum),
487c034abf6SJohannes Stezenbach SOC_ENUM("Limiter1 Release Rate (dB/ms)", sta32x_limiter1_release_rate_enum),
488b3619b28STakashi Iwai SOC_ENUM("Limiter2 Release Rate (dB/ms)", sta32x_limiter2_release_rate_enum),
489c034abf6SJohannes Stezenbach 
490c034abf6SJohannes Stezenbach /* depending on mode, the attack/release thresholds have
491c034abf6SJohannes Stezenbach  * two different enum definitions; provide both
492c034abf6SJohannes Stezenbach  */
493c034abf6SJohannes Stezenbach SOC_SINGLE_TLV("Limiter1 Attack Threshold (AC Mode)", STA32X_L1ATRT, STA32X_LxA_SHIFT,
494c034abf6SJohannes Stezenbach 	       16, 0, sta32x_limiter_ac_attack_tlv),
495c034abf6SJohannes Stezenbach SOC_SINGLE_TLV("Limiter2 Attack Threshold (AC Mode)", STA32X_L2ATRT, STA32X_LxA_SHIFT,
496c034abf6SJohannes Stezenbach 	       16, 0, sta32x_limiter_ac_attack_tlv),
497c034abf6SJohannes Stezenbach SOC_SINGLE_TLV("Limiter1 Release Threshold (AC Mode)", STA32X_L1ATRT, STA32X_LxR_SHIFT,
498c034abf6SJohannes Stezenbach 	       16, 0, sta32x_limiter_ac_release_tlv),
499c034abf6SJohannes Stezenbach SOC_SINGLE_TLV("Limiter2 Release Threshold (AC Mode)", STA32X_L2ATRT, STA32X_LxR_SHIFT,
500c034abf6SJohannes Stezenbach 	       16, 0, sta32x_limiter_ac_release_tlv),
501c034abf6SJohannes Stezenbach SOC_SINGLE_TLV("Limiter1 Attack Threshold (DRC Mode)", STA32X_L1ATRT, STA32X_LxA_SHIFT,
502c034abf6SJohannes Stezenbach 	       16, 0, sta32x_limiter_drc_attack_tlv),
503c034abf6SJohannes Stezenbach SOC_SINGLE_TLV("Limiter2 Attack Threshold (DRC Mode)", STA32X_L2ATRT, STA32X_LxA_SHIFT,
504c034abf6SJohannes Stezenbach 	       16, 0, sta32x_limiter_drc_attack_tlv),
505c034abf6SJohannes Stezenbach SOC_SINGLE_TLV("Limiter1 Release Threshold (DRC Mode)", STA32X_L1ATRT, STA32X_LxR_SHIFT,
506c034abf6SJohannes Stezenbach 	       16, 0, sta32x_limiter_drc_release_tlv),
507c034abf6SJohannes Stezenbach SOC_SINGLE_TLV("Limiter2 Release Threshold (DRC Mode)", STA32X_L2ATRT, STA32X_LxR_SHIFT,
508c034abf6SJohannes Stezenbach 	       16, 0, sta32x_limiter_drc_release_tlv),
50979688439SJohannes Stezenbach 
51079688439SJohannes Stezenbach BIQUAD_COEFS("Ch1 - Biquad 1", 0),
51179688439SJohannes Stezenbach BIQUAD_COEFS("Ch1 - Biquad 2", 5),
51279688439SJohannes Stezenbach BIQUAD_COEFS("Ch1 - Biquad 3", 10),
51379688439SJohannes Stezenbach BIQUAD_COEFS("Ch1 - Biquad 4", 15),
51479688439SJohannes Stezenbach BIQUAD_COEFS("Ch2 - Biquad 1", 20),
51579688439SJohannes Stezenbach BIQUAD_COEFS("Ch2 - Biquad 2", 25),
51679688439SJohannes Stezenbach BIQUAD_COEFS("Ch2 - Biquad 3", 30),
51779688439SJohannes Stezenbach BIQUAD_COEFS("Ch2 - Biquad 4", 35),
51879688439SJohannes Stezenbach BIQUAD_COEFS("High-pass", 40),
51979688439SJohannes Stezenbach BIQUAD_COEFS("Low-pass", 45),
52079688439SJohannes Stezenbach SINGLE_COEF("Ch1 - Prescale", 50),
52179688439SJohannes Stezenbach SINGLE_COEF("Ch2 - Prescale", 51),
52279688439SJohannes Stezenbach SINGLE_COEF("Ch1 - Postscale", 52),
52379688439SJohannes Stezenbach SINGLE_COEF("Ch2 - Postscale", 53),
52479688439SJohannes Stezenbach SINGLE_COEF("Ch3 - Postscale", 54),
52579688439SJohannes Stezenbach SINGLE_COEF("Thermal warning - Postscale", 55),
52679688439SJohannes Stezenbach SINGLE_COEF("Ch1 - Mix 1", 56),
52779688439SJohannes Stezenbach SINGLE_COEF("Ch1 - Mix 2", 57),
52879688439SJohannes Stezenbach SINGLE_COEF("Ch2 - Mix 1", 58),
52979688439SJohannes Stezenbach SINGLE_COEF("Ch2 - Mix 2", 59),
53079688439SJohannes Stezenbach SINGLE_COEF("Ch3 - Mix 1", 60),
53179688439SJohannes Stezenbach SINGLE_COEF("Ch3 - Mix 2", 61),
532c034abf6SJohannes Stezenbach };
533c034abf6SJohannes Stezenbach 
534c034abf6SJohannes Stezenbach static const struct snd_soc_dapm_widget sta32x_dapm_widgets[] = {
535c034abf6SJohannes Stezenbach SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
536c034abf6SJohannes Stezenbach SND_SOC_DAPM_OUTPUT("LEFT"),
537c034abf6SJohannes Stezenbach SND_SOC_DAPM_OUTPUT("RIGHT"),
538c034abf6SJohannes Stezenbach SND_SOC_DAPM_OUTPUT("SUB"),
539c034abf6SJohannes Stezenbach };
540c034abf6SJohannes Stezenbach 
541c034abf6SJohannes Stezenbach static const struct snd_soc_dapm_route sta32x_dapm_routes[] = {
542c034abf6SJohannes Stezenbach 	{ "LEFT", NULL, "DAC" },
543c034abf6SJohannes Stezenbach 	{ "RIGHT", NULL, "DAC" },
544c034abf6SJohannes Stezenbach 	{ "SUB", NULL, "DAC" },
545c034abf6SJohannes Stezenbach };
546c034abf6SJohannes Stezenbach 
547c034abf6SJohannes Stezenbach /* MCLK interpolation ratio per fs */
548c034abf6SJohannes Stezenbach static struct {
549c034abf6SJohannes Stezenbach 	int fs;
550c034abf6SJohannes Stezenbach 	int ir;
551c034abf6SJohannes Stezenbach } interpolation_ratios[] = {
552c034abf6SJohannes Stezenbach 	{ 32000, 0 },
553c034abf6SJohannes Stezenbach 	{ 44100, 0 },
554c034abf6SJohannes Stezenbach 	{ 48000, 0 },
555c034abf6SJohannes Stezenbach 	{ 88200, 1 },
556c034abf6SJohannes Stezenbach 	{ 96000, 1 },
557c034abf6SJohannes Stezenbach 	{ 176400, 2 },
558c034abf6SJohannes Stezenbach 	{ 192000, 2 },
559c034abf6SJohannes Stezenbach };
560c034abf6SJohannes Stezenbach 
561c034abf6SJohannes Stezenbach /* MCLK to fs clock ratios */
562c034abf6SJohannes Stezenbach static struct {
563c034abf6SJohannes Stezenbach 	int ratio;
564c034abf6SJohannes Stezenbach 	int mcs;
565c034abf6SJohannes Stezenbach } mclk_ratios[3][7] = {
566c034abf6SJohannes Stezenbach 	{ { 768, 0 }, { 512, 1 }, { 384, 2 }, { 256, 3 },
567c034abf6SJohannes Stezenbach 	  { 128, 4 }, { 576, 5 }, { 0, 0 } },
568c034abf6SJohannes Stezenbach 	{ { 384, 2 }, { 256, 3 }, { 192, 4 }, { 128, 5 }, {64, 0 }, { 0, 0 } },
569c034abf6SJohannes Stezenbach 	{ { 384, 2 }, { 256, 3 }, { 192, 4 }, { 128, 5 }, {64, 0 }, { 0, 0 } },
570c034abf6SJohannes Stezenbach };
571c034abf6SJohannes Stezenbach 
572c034abf6SJohannes Stezenbach 
573c034abf6SJohannes Stezenbach /**
574c034abf6SJohannes Stezenbach  * sta32x_set_dai_sysclk - configure MCLK
575c034abf6SJohannes Stezenbach  * @codec_dai: the codec DAI
576c034abf6SJohannes Stezenbach  * @clk_id: the clock ID (ignored)
577c034abf6SJohannes Stezenbach  * @freq: the MCLK input frequency
578c034abf6SJohannes Stezenbach  * @dir: the clock direction (ignored)
579c034abf6SJohannes Stezenbach  *
580c034abf6SJohannes Stezenbach  * The value of MCLK is used to determine which sample rates are supported
581c034abf6SJohannes Stezenbach  * by the STA32X, based on the mclk_ratios table.
582c034abf6SJohannes Stezenbach  *
583c034abf6SJohannes Stezenbach  * This function must be called by the machine driver's 'startup' function,
584c034abf6SJohannes Stezenbach  * otherwise the list of supported sample rates will not be available in
585c034abf6SJohannes Stezenbach  * time for ALSA.
586c034abf6SJohannes Stezenbach  *
587c034abf6SJohannes Stezenbach  * For setups with variable MCLKs, pass 0 as 'freq' argument. This will cause
588c034abf6SJohannes Stezenbach  * theoretically possible sample rates to be enabled. Call it again with a
589c034abf6SJohannes Stezenbach  * proper value set one the external clock is set (most probably you would do
590c034abf6SJohannes Stezenbach  * that from a machine's driver 'hw_param' hook.
591c034abf6SJohannes Stezenbach  */
592c034abf6SJohannes Stezenbach static int sta32x_set_dai_sysclk(struct snd_soc_dai *codec_dai,
593c034abf6SJohannes Stezenbach 		int clk_id, unsigned int freq, int dir)
594c034abf6SJohannes Stezenbach {
595c034abf6SJohannes Stezenbach 	struct snd_soc_codec *codec = codec_dai->codec;
596c034abf6SJohannes Stezenbach 	struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
597c034abf6SJohannes Stezenbach 	int i, j, ir, fs;
598c034abf6SJohannes Stezenbach 	unsigned int rates = 0;
599c034abf6SJohannes Stezenbach 	unsigned int rate_min = -1;
600c034abf6SJohannes Stezenbach 	unsigned int rate_max = 0;
601c034abf6SJohannes Stezenbach 
602c034abf6SJohannes Stezenbach 	pr_debug("mclk=%u\n", freq);
603c034abf6SJohannes Stezenbach 	sta32x->mclk = freq;
604c034abf6SJohannes Stezenbach 
605c034abf6SJohannes Stezenbach 	if (sta32x->mclk) {
606c034abf6SJohannes Stezenbach 		for (i = 0; i < ARRAY_SIZE(interpolation_ratios); i++) {
607c034abf6SJohannes Stezenbach 			ir = interpolation_ratios[i].ir;
608c034abf6SJohannes Stezenbach 			fs = interpolation_ratios[i].fs;
609c034abf6SJohannes Stezenbach 			for (j = 0; mclk_ratios[ir][j].ratio; j++) {
610c034abf6SJohannes Stezenbach 				if (mclk_ratios[ir][j].ratio * fs == freq) {
611c034abf6SJohannes Stezenbach 					rates |= snd_pcm_rate_to_rate_bit(fs);
612c034abf6SJohannes Stezenbach 					if (fs < rate_min)
613c034abf6SJohannes Stezenbach 						rate_min = fs;
614c034abf6SJohannes Stezenbach 					if (fs > rate_max)
615c034abf6SJohannes Stezenbach 						rate_max = fs;
6167a748e43SAxel Lin 					break;
617c034abf6SJohannes Stezenbach 				}
618c034abf6SJohannes Stezenbach 			}
619c034abf6SJohannes Stezenbach 		}
620c034abf6SJohannes Stezenbach 		/* FIXME: soc should support a rate list */
621c034abf6SJohannes Stezenbach 		rates &= ~SNDRV_PCM_RATE_KNOT;
622c034abf6SJohannes Stezenbach 
623c034abf6SJohannes Stezenbach 		if (!rates) {
624c034abf6SJohannes Stezenbach 			dev_err(codec->dev, "could not find a valid sample rate\n");
625c034abf6SJohannes Stezenbach 			return -EINVAL;
626c034abf6SJohannes Stezenbach 		}
627c034abf6SJohannes Stezenbach 	} else {
628c034abf6SJohannes Stezenbach 		/* enable all possible rates */
629c034abf6SJohannes Stezenbach 		rates = STA32X_RATES;
630c034abf6SJohannes Stezenbach 		rate_min = 32000;
631c034abf6SJohannes Stezenbach 		rate_max = 192000;
632c034abf6SJohannes Stezenbach 	}
633c034abf6SJohannes Stezenbach 
634c034abf6SJohannes Stezenbach 	codec_dai->driver->playback.rates = rates;
635c034abf6SJohannes Stezenbach 	codec_dai->driver->playback.rate_min = rate_min;
636c034abf6SJohannes Stezenbach 	codec_dai->driver->playback.rate_max = rate_max;
637c034abf6SJohannes Stezenbach 	return 0;
638c034abf6SJohannes Stezenbach }
639c034abf6SJohannes Stezenbach 
640c034abf6SJohannes Stezenbach /**
641c034abf6SJohannes Stezenbach  * sta32x_set_dai_fmt - configure the codec for the selected audio format
642c034abf6SJohannes Stezenbach  * @codec_dai: the codec DAI
643c034abf6SJohannes Stezenbach  * @fmt: a SND_SOC_DAIFMT_x value indicating the data format
644c034abf6SJohannes Stezenbach  *
645c034abf6SJohannes Stezenbach  * This function takes a bitmask of SND_SOC_DAIFMT_x bits and programs the
646c034abf6SJohannes Stezenbach  * codec accordingly.
647c034abf6SJohannes Stezenbach  */
648c034abf6SJohannes Stezenbach static int sta32x_set_dai_fmt(struct snd_soc_dai *codec_dai,
649c034abf6SJohannes Stezenbach 			      unsigned int fmt)
650c034abf6SJohannes Stezenbach {
651c034abf6SJohannes Stezenbach 	struct snd_soc_codec *codec = codec_dai->codec;
652c034abf6SJohannes Stezenbach 	struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
653a1be4ceaSThomas Niederprüm 	u8 confb = 0;
654c034abf6SJohannes Stezenbach 
655c034abf6SJohannes Stezenbach 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
656c034abf6SJohannes Stezenbach 	case SND_SOC_DAIFMT_CBS_CFS:
657c034abf6SJohannes Stezenbach 		break;
658c034abf6SJohannes Stezenbach 	default:
659c034abf6SJohannes Stezenbach 		return -EINVAL;
660c034abf6SJohannes Stezenbach 	}
661c034abf6SJohannes Stezenbach 
662c034abf6SJohannes Stezenbach 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
663c034abf6SJohannes Stezenbach 	case SND_SOC_DAIFMT_I2S:
664c034abf6SJohannes Stezenbach 	case SND_SOC_DAIFMT_RIGHT_J:
665c034abf6SJohannes Stezenbach 	case SND_SOC_DAIFMT_LEFT_J:
666c034abf6SJohannes Stezenbach 		sta32x->format = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
667c034abf6SJohannes Stezenbach 		break;
668c034abf6SJohannes Stezenbach 	default:
669c034abf6SJohannes Stezenbach 		return -EINVAL;
670c034abf6SJohannes Stezenbach 	}
671c034abf6SJohannes Stezenbach 
672c034abf6SJohannes Stezenbach 	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
673c034abf6SJohannes Stezenbach 	case SND_SOC_DAIFMT_NB_NF:
674c034abf6SJohannes Stezenbach 		confb |= STA32X_CONFB_C2IM;
675c034abf6SJohannes Stezenbach 		break;
676c034abf6SJohannes Stezenbach 	case SND_SOC_DAIFMT_NB_IF:
677c034abf6SJohannes Stezenbach 		confb |= STA32X_CONFB_C1IM;
678c034abf6SJohannes Stezenbach 		break;
679c034abf6SJohannes Stezenbach 	default:
680c034abf6SJohannes Stezenbach 		return -EINVAL;
681c034abf6SJohannes Stezenbach 	}
682c034abf6SJohannes Stezenbach 
683a1be4ceaSThomas Niederprüm 	return regmap_update_bits(sta32x->regmap, STA32X_CONFB,
684a1be4ceaSThomas Niederprüm 				  STA32X_CONFB_C1IM | STA32X_CONFB_C2IM, confb);
685c034abf6SJohannes Stezenbach }
686c034abf6SJohannes Stezenbach 
687c034abf6SJohannes Stezenbach /**
688c034abf6SJohannes Stezenbach  * sta32x_hw_params - program the STA32X with the given hardware parameters.
689c034abf6SJohannes Stezenbach  * @substream: the audio stream
690c034abf6SJohannes Stezenbach  * @params: the hardware parameters to set
691c034abf6SJohannes Stezenbach  * @dai: the SOC DAI (ignored)
692c034abf6SJohannes Stezenbach  *
693c034abf6SJohannes Stezenbach  * This function programs the hardware with the values provided.
694c034abf6SJohannes Stezenbach  * Specifically, the sample rate and the data format.
695c034abf6SJohannes Stezenbach  */
696c034abf6SJohannes Stezenbach static int sta32x_hw_params(struct snd_pcm_substream *substream,
697c034abf6SJohannes Stezenbach 			    struct snd_pcm_hw_params *params,
698c034abf6SJohannes Stezenbach 			    struct snd_soc_dai *dai)
699c034abf6SJohannes Stezenbach {
700e6968a17SMark Brown 	struct snd_soc_codec *codec = dai->codec;
701c034abf6SJohannes Stezenbach 	struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
702c034abf6SJohannes Stezenbach 	unsigned int rate;
703c034abf6SJohannes Stezenbach 	int i, mcs = -1, ir = -1;
704a1be4ceaSThomas Niederprüm 	unsigned int confa, confb;
705c034abf6SJohannes Stezenbach 
706c034abf6SJohannes Stezenbach 	rate = params_rate(params);
707c034abf6SJohannes Stezenbach 	pr_debug("rate: %u\n", rate);
708c034abf6SJohannes Stezenbach 	for (i = 0; i < ARRAY_SIZE(interpolation_ratios); i++)
709a595238bSAxel Lin 		if (interpolation_ratios[i].fs == rate) {
710c034abf6SJohannes Stezenbach 			ir = interpolation_ratios[i].ir;
711a595238bSAxel Lin 			break;
712a595238bSAxel Lin 		}
713c034abf6SJohannes Stezenbach 	if (ir < 0)
714c034abf6SJohannes Stezenbach 		return -EINVAL;
715c034abf6SJohannes Stezenbach 	for (i = 0; mclk_ratios[ir][i].ratio; i++)
716a595238bSAxel Lin 		if (mclk_ratios[ir][i].ratio * rate == sta32x->mclk) {
717c034abf6SJohannes Stezenbach 			mcs = mclk_ratios[ir][i].mcs;
718a595238bSAxel Lin 			break;
719a595238bSAxel Lin 		}
720c034abf6SJohannes Stezenbach 	if (mcs < 0)
721c034abf6SJohannes Stezenbach 		return -EINVAL;
722c034abf6SJohannes Stezenbach 
723a1be4ceaSThomas Niederprüm 	confa = (ir << STA32X_CONFA_IR_SHIFT) |
724a1be4ceaSThomas Niederprüm 		(mcs << STA32X_CONFA_MCS_SHIFT);
725a1be4ceaSThomas Niederprüm 	confb = 0;
726c034abf6SJohannes Stezenbach 
727737e0f89SMark Brown 	switch (params_width(params)) {
728737e0f89SMark Brown 	case 24:
729c034abf6SJohannes Stezenbach 		pr_debug("24bit\n");
730c034abf6SJohannes Stezenbach 		/* fall through */
731737e0f89SMark Brown 	case 32:
732c034abf6SJohannes Stezenbach 		pr_debug("24bit or 32bit\n");
733c034abf6SJohannes Stezenbach 		switch (sta32x->format) {
734c034abf6SJohannes Stezenbach 		case SND_SOC_DAIFMT_I2S:
735c034abf6SJohannes Stezenbach 			confb |= 0x0;
736c034abf6SJohannes Stezenbach 			break;
737c034abf6SJohannes Stezenbach 		case SND_SOC_DAIFMT_LEFT_J:
738c034abf6SJohannes Stezenbach 			confb |= 0x1;
739c034abf6SJohannes Stezenbach 			break;
740c034abf6SJohannes Stezenbach 		case SND_SOC_DAIFMT_RIGHT_J:
741c034abf6SJohannes Stezenbach 			confb |= 0x2;
742c034abf6SJohannes Stezenbach 			break;
743c034abf6SJohannes Stezenbach 		}
744c034abf6SJohannes Stezenbach 
745c034abf6SJohannes Stezenbach 		break;
746737e0f89SMark Brown 	case 20:
747c034abf6SJohannes Stezenbach 		pr_debug("20bit\n");
748c034abf6SJohannes Stezenbach 		switch (sta32x->format) {
749c034abf6SJohannes Stezenbach 		case SND_SOC_DAIFMT_I2S:
750c034abf6SJohannes Stezenbach 			confb |= 0x4;
751c034abf6SJohannes Stezenbach 			break;
752c034abf6SJohannes Stezenbach 		case SND_SOC_DAIFMT_LEFT_J:
753c034abf6SJohannes Stezenbach 			confb |= 0x5;
754c034abf6SJohannes Stezenbach 			break;
755c034abf6SJohannes Stezenbach 		case SND_SOC_DAIFMT_RIGHT_J:
756c034abf6SJohannes Stezenbach 			confb |= 0x6;
757c034abf6SJohannes Stezenbach 			break;
758c034abf6SJohannes Stezenbach 		}
759c034abf6SJohannes Stezenbach 
760c034abf6SJohannes Stezenbach 		break;
761737e0f89SMark Brown 	case 18:
762c034abf6SJohannes Stezenbach 		pr_debug("18bit\n");
763c034abf6SJohannes Stezenbach 		switch (sta32x->format) {
764c034abf6SJohannes Stezenbach 		case SND_SOC_DAIFMT_I2S:
765c034abf6SJohannes Stezenbach 			confb |= 0x8;
766c034abf6SJohannes Stezenbach 			break;
767c034abf6SJohannes Stezenbach 		case SND_SOC_DAIFMT_LEFT_J:
768c034abf6SJohannes Stezenbach 			confb |= 0x9;
769c034abf6SJohannes Stezenbach 			break;
770c034abf6SJohannes Stezenbach 		case SND_SOC_DAIFMT_RIGHT_J:
771c034abf6SJohannes Stezenbach 			confb |= 0xa;
772c034abf6SJohannes Stezenbach 			break;
773c034abf6SJohannes Stezenbach 		}
774c034abf6SJohannes Stezenbach 
775c034abf6SJohannes Stezenbach 		break;
776737e0f89SMark Brown 	case 16:
777c034abf6SJohannes Stezenbach 		pr_debug("16bit\n");
778c034abf6SJohannes Stezenbach 		switch (sta32x->format) {
779c034abf6SJohannes Stezenbach 		case SND_SOC_DAIFMT_I2S:
780c034abf6SJohannes Stezenbach 			confb |= 0x0;
781c034abf6SJohannes Stezenbach 			break;
782c034abf6SJohannes Stezenbach 		case SND_SOC_DAIFMT_LEFT_J:
783c034abf6SJohannes Stezenbach 			confb |= 0xd;
784c034abf6SJohannes Stezenbach 			break;
785c034abf6SJohannes Stezenbach 		case SND_SOC_DAIFMT_RIGHT_J:
786c034abf6SJohannes Stezenbach 			confb |= 0xe;
787c034abf6SJohannes Stezenbach 			break;
788c034abf6SJohannes Stezenbach 		}
789c034abf6SJohannes Stezenbach 
790c034abf6SJohannes Stezenbach 		break;
791c034abf6SJohannes Stezenbach 	default:
792c034abf6SJohannes Stezenbach 		return -EINVAL;
793c034abf6SJohannes Stezenbach 	}
794c034abf6SJohannes Stezenbach 
795a1be4ceaSThomas Niederprüm 	ret = regmap_update_bits(sta32x->regmap, STA32X_CONFA,
796a1be4ceaSThomas Niederprüm 				 STA32X_CONFA_MCS_MASK | STA32X_CONFA_IR_MASK,
797a1be4ceaSThomas Niederprüm 				 confa);
798a1be4ceaSThomas Niederprüm 	if (ret < 0)
799a1be4ceaSThomas Niederprüm 		return ret;
800a1be4ceaSThomas Niederprüm 
801a1be4ceaSThomas Niederprüm 	ret = regmap_update_bits(sta32x->regmap, STA32X_CONFB,
802a1be4ceaSThomas Niederprüm 				 STA32X_CONFB_SAI_MASK | STA32X_CONFB_SAIFB,
803a1be4ceaSThomas Niederprüm 				 confb);
804a1be4ceaSThomas Niederprüm 	if (ret < 0)
805a1be4ceaSThomas Niederprüm 		return ret;
806a1be4ceaSThomas Niederprüm 
807a1be4ceaSThomas Niederprüm 	return 0;
808a1be4ceaSThomas Niederprüm }
809*b66a2980SThomas Niederprüm 
810*b66a2980SThomas Niederprüm static int sta32x_startup_sequence(struct sta32x_priv *sta32x)
811*b66a2980SThomas Niederprüm {
812*b66a2980SThomas Niederprüm 	if (sta32x->gpiod_nreset) {
813*b66a2980SThomas Niederprüm 		gpiod_set_value(sta32x->gpiod_nreset, 0);
814*b66a2980SThomas Niederprüm 		mdelay(1);
815*b66a2980SThomas Niederprüm 		gpiod_set_value(sta32x->gpiod_nreset, 1);
816*b66a2980SThomas Niederprüm 		mdelay(1);
817*b66a2980SThomas Niederprüm 	}
818*b66a2980SThomas Niederprüm 
819c034abf6SJohannes Stezenbach 	return 0;
820c034abf6SJohannes Stezenbach }
821c034abf6SJohannes Stezenbach 
822c034abf6SJohannes Stezenbach /**
823c034abf6SJohannes Stezenbach  * sta32x_set_bias_level - DAPM callback
824c034abf6SJohannes Stezenbach  * @codec: the codec device
825c034abf6SJohannes Stezenbach  * @level: DAPM power level
826c034abf6SJohannes Stezenbach  *
827c034abf6SJohannes Stezenbach  * This is called by ALSA to put the codec into low power mode
828c034abf6SJohannes Stezenbach  * or to wake it up.  If the codec is powered off completely
829c034abf6SJohannes Stezenbach  * all registers must be restored after power on.
830c034abf6SJohannes Stezenbach  */
831c034abf6SJohannes Stezenbach static int sta32x_set_bias_level(struct snd_soc_codec *codec,
832c034abf6SJohannes Stezenbach 				 enum snd_soc_bias_level level)
833c034abf6SJohannes Stezenbach {
834c034abf6SJohannes Stezenbach 	int ret;
835c034abf6SJohannes Stezenbach 	struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
836c034abf6SJohannes Stezenbach 
837c034abf6SJohannes Stezenbach 	pr_debug("level = %d\n", level);
838c034abf6SJohannes Stezenbach 	switch (level) {
839c034abf6SJohannes Stezenbach 	case SND_SOC_BIAS_ON:
840c034abf6SJohannes Stezenbach 		break;
841c034abf6SJohannes Stezenbach 
842c034abf6SJohannes Stezenbach 	case SND_SOC_BIAS_PREPARE:
843c034abf6SJohannes Stezenbach 		/* Full power on */
844a1be4ceaSThomas Niederprüm 		regmap_update_bits(sta32x->regmap, STA32X_CONFF,
845c034abf6SJohannes Stezenbach 				    STA32X_CONFF_PWDN | STA32X_CONFF_EAPD,
846c034abf6SJohannes Stezenbach 				    STA32X_CONFF_PWDN | STA32X_CONFF_EAPD);
847c034abf6SJohannes Stezenbach 		break;
848c034abf6SJohannes Stezenbach 
849c034abf6SJohannes Stezenbach 	case SND_SOC_BIAS_STANDBY:
850c034abf6SJohannes Stezenbach 		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
851c034abf6SJohannes Stezenbach 			ret = regulator_bulk_enable(ARRAY_SIZE(sta32x->supplies),
852c034abf6SJohannes Stezenbach 						    sta32x->supplies);
853c034abf6SJohannes Stezenbach 			if (ret != 0) {
854c034abf6SJohannes Stezenbach 				dev_err(codec->dev,
855c034abf6SJohannes Stezenbach 					"Failed to enable supplies: %d\n", ret);
856c034abf6SJohannes Stezenbach 				return ret;
857c034abf6SJohannes Stezenbach 			}
858c034abf6SJohannes Stezenbach 
859*b66a2980SThomas Niederprüm 			sta32x_startup_sequence(sta32x);
86054dc6cabSJohannes Stezenbach 			sta32x_cache_sync(codec);
8613fb5eac5SJohannes Stezenbach 			sta32x_watchdog_start(sta32x);
862c034abf6SJohannes Stezenbach 		}
863c034abf6SJohannes Stezenbach 
864a1be4ceaSThomas Niederprüm 		/* Power down */
865a1be4ceaSThomas Niederprüm 		regmap_update_bits(sta32x->regmap, STA32X_CONFF,
866c034abf6SJohannes Stezenbach 				   STA32X_CONFF_PWDN | STA32X_CONFF_EAPD,
867a1be4ceaSThomas Niederprüm 				   0);
868c034abf6SJohannes Stezenbach 
869c034abf6SJohannes Stezenbach 		break;
870c034abf6SJohannes Stezenbach 
871c034abf6SJohannes Stezenbach 	case SND_SOC_BIAS_OFF:
872c034abf6SJohannes Stezenbach 		/* The chip runs through the power down sequence for us. */
873a1be4ceaSThomas Niederprüm 		regmap_update_bits(sta32x->regmap, STA32X_CONFF,
874a1be4ceaSThomas Niederprüm 				   STA32X_CONFF_PWDN | STA32X_CONFF_EAPD, 0);
875c034abf6SJohannes Stezenbach 		msleep(300);
8763fb5eac5SJohannes Stezenbach 		sta32x_watchdog_stop(sta32x);
877*b66a2980SThomas Niederprüm 
878*b66a2980SThomas Niederprüm 		if (sta32x->gpiod_nreset)
879*b66a2980SThomas Niederprüm 			gpiod_set_value(sta32x->gpiod_nreset, 0);
880*b66a2980SThomas Niederprüm 
881c034abf6SJohannes Stezenbach 		regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies),
882c034abf6SJohannes Stezenbach 				       sta32x->supplies);
883c034abf6SJohannes Stezenbach 		break;
884c034abf6SJohannes Stezenbach 	}
885c034abf6SJohannes Stezenbach 	codec->dapm.bias_level = level;
886c034abf6SJohannes Stezenbach 	return 0;
887c034abf6SJohannes Stezenbach }
888c034abf6SJohannes Stezenbach 
88985e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops sta32x_dai_ops = {
890c034abf6SJohannes Stezenbach 	.hw_params	= sta32x_hw_params,
891c034abf6SJohannes Stezenbach 	.set_sysclk	= sta32x_set_dai_sysclk,
892c034abf6SJohannes Stezenbach 	.set_fmt	= sta32x_set_dai_fmt,
893c034abf6SJohannes Stezenbach };
894c034abf6SJohannes Stezenbach 
895c034abf6SJohannes Stezenbach static struct snd_soc_dai_driver sta32x_dai = {
896c034abf6SJohannes Stezenbach 	.name = "STA32X",
897c034abf6SJohannes Stezenbach 	.playback = {
898c034abf6SJohannes Stezenbach 		.stream_name = "Playback",
899c034abf6SJohannes Stezenbach 		.channels_min = 2,
900c034abf6SJohannes Stezenbach 		.channels_max = 2,
901c034abf6SJohannes Stezenbach 		.rates = STA32X_RATES,
902c034abf6SJohannes Stezenbach 		.formats = STA32X_FORMATS,
903c034abf6SJohannes Stezenbach 	},
904c034abf6SJohannes Stezenbach 	.ops = &sta32x_dai_ops,
905c034abf6SJohannes Stezenbach };
906c034abf6SJohannes Stezenbach 
907c034abf6SJohannes Stezenbach static int sta32x_probe(struct snd_soc_codec *codec)
908c034abf6SJohannes Stezenbach {
909c034abf6SJohannes Stezenbach 	struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
910a1be4ceaSThomas Niederprüm 	struct sta32x_platform_data *pdata = sta32x->pdata;
911e012ba24SJohannes Stezenbach 	int i, ret = 0, thermal = 0;
912c034abf6SJohannes Stezenbach 	ret = regulator_bulk_enable(ARRAY_SIZE(sta32x->supplies),
913c034abf6SJohannes Stezenbach 				    sta32x->supplies);
914c034abf6SJohannes Stezenbach 	if (ret != 0) {
915c034abf6SJohannes Stezenbach 		dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
916aff041afSMark Brown 		return ret;
917c034abf6SJohannes Stezenbach 	}
918c034abf6SJohannes Stezenbach 
919*b66a2980SThomas Niederprüm 	ret = sta32x_startup_sequence(sta32x);
920*b66a2980SThomas Niederprüm 	if (ret < 0) {
921*b66a2980SThomas Niederprüm 		dev_err(codec->dev, "Failed to startup device\n");
922*b66a2980SThomas Niederprüm 		return ret;
923*b66a2980SThomas Niederprüm 	}
924e012ba24SJohannes Stezenbach 	/* set thermal warning adjustment and recovery */
925a1be4ceaSThomas Niederprüm 	if (!pdata->thermal_warning_recovery)
926e012ba24SJohannes Stezenbach 		thermal |= STA32X_CONFA_TWAB;
927a1be4ceaSThomas Niederprüm 	if (!pdata->thermal_warning_adjustment)
928e012ba24SJohannes Stezenbach 		thermal |= STA32X_CONFA_TWRB;
929a1be4ceaSThomas Niederprüm 	regmap_update_bits(sta32x->regmap, STA32X_CONFA,
930e012ba24SJohannes Stezenbach 			   STA32X_CONFA_TWAB | STA32X_CONFA_TWRB,
931e012ba24SJohannes Stezenbach 			   thermal);
932c034abf6SJohannes Stezenbach 
933e012ba24SJohannes Stezenbach 	/* select output configuration  */
934a1be4ceaSThomas Niederprüm 	regmap_update_bits(sta32x->regmap, STA32X_CONFF,
935c034abf6SJohannes Stezenbach 			   STA32X_CONFF_OCFG_MASK,
936a1be4ceaSThomas Niederprüm 			   pdata->output_conf
937e012ba24SJohannes Stezenbach 				<< STA32X_CONFF_OCFG_SHIFT);
938c034abf6SJohannes Stezenbach 
939e012ba24SJohannes Stezenbach 	/* channel to output mapping */
940a1be4ceaSThomas Niederprüm 	regmap_update_bits(sta32x->regmap, STA32X_C1CFG,
941c034abf6SJohannes Stezenbach 			   STA32X_CxCFG_OM_MASK,
942a1be4ceaSThomas Niederprüm 			   pdata->ch1_output_mapping
943e012ba24SJohannes Stezenbach 				<< STA32X_CxCFG_OM_SHIFT);
944a1be4ceaSThomas Niederprüm 	regmap_update_bits(sta32x->regmap, STA32X_C2CFG,
945c034abf6SJohannes Stezenbach 			   STA32X_CxCFG_OM_MASK,
946a1be4ceaSThomas Niederprüm 			   pdata->ch2_output_mapping
947e012ba24SJohannes Stezenbach 				<< STA32X_CxCFG_OM_SHIFT);
948a1be4ceaSThomas Niederprüm 	regmap_update_bits(sta32x->regmap, STA32X_C3CFG,
949c034abf6SJohannes Stezenbach 			   STA32X_CxCFG_OM_MASK,
950a1be4ceaSThomas Niederprüm 			   pdata->ch3_output_mapping
951e012ba24SJohannes Stezenbach 				<< STA32X_CxCFG_OM_SHIFT);
952c034abf6SJohannes Stezenbach 
95354dc6cabSJohannes Stezenbach 	/* initialize coefficient shadow RAM with reset values */
95454dc6cabSJohannes Stezenbach 	for (i = 4; i <= 49; i += 5)
95554dc6cabSJohannes Stezenbach 		sta32x->coef_shadow[i] = 0x400000;
95654dc6cabSJohannes Stezenbach 	for (i = 50; i <= 54; i++)
95754dc6cabSJohannes Stezenbach 		sta32x->coef_shadow[i] = 0x7fffff;
95854dc6cabSJohannes Stezenbach 	sta32x->coef_shadow[55] = 0x5a9df7;
95954dc6cabSJohannes Stezenbach 	sta32x->coef_shadow[56] = 0x7fffff;
96054dc6cabSJohannes Stezenbach 	sta32x->coef_shadow[59] = 0x7fffff;
96154dc6cabSJohannes Stezenbach 	sta32x->coef_shadow[60] = 0x400000;
96254dc6cabSJohannes Stezenbach 	sta32x->coef_shadow[61] = 0x400000;
96354dc6cabSJohannes Stezenbach 
9643fb5eac5SJohannes Stezenbach 	if (sta32x->pdata->needs_esd_watchdog)
9653fb5eac5SJohannes Stezenbach 		INIT_DELAYED_WORK(&sta32x->watchdog_work, sta32x_watchdog);
9663fb5eac5SJohannes Stezenbach 
967c034abf6SJohannes Stezenbach 	sta32x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
968c034abf6SJohannes Stezenbach 	/* Bias level configuration will have done an extra enable */
969c034abf6SJohannes Stezenbach 	regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies);
970c034abf6SJohannes Stezenbach 
971c034abf6SJohannes Stezenbach 	return 0;
972c034abf6SJohannes Stezenbach }
973c034abf6SJohannes Stezenbach 
974c034abf6SJohannes Stezenbach static int sta32x_remove(struct snd_soc_codec *codec)
975c034abf6SJohannes Stezenbach {
976c034abf6SJohannes Stezenbach 	struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
977c034abf6SJohannes Stezenbach 
9783fb5eac5SJohannes Stezenbach 	sta32x_watchdog_stop(sta32x);
979c034abf6SJohannes Stezenbach 	regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies);
980c034abf6SJohannes Stezenbach 
981c034abf6SJohannes Stezenbach 	return 0;
982c034abf6SJohannes Stezenbach }
983c034abf6SJohannes Stezenbach 
984c034abf6SJohannes Stezenbach static const struct snd_soc_codec_driver sta32x_codec = {
985c034abf6SJohannes Stezenbach 	.probe =		sta32x_probe,
986c034abf6SJohannes Stezenbach 	.remove =		sta32x_remove,
987c034abf6SJohannes Stezenbach 	.set_bias_level =	sta32x_set_bias_level,
988815b776cSLars-Peter Clausen 	.suspend_bias_off =	true,
989c034abf6SJohannes Stezenbach 	.controls =		sta32x_snd_controls,
990c034abf6SJohannes Stezenbach 	.num_controls =		ARRAY_SIZE(sta32x_snd_controls),
991c034abf6SJohannes Stezenbach 	.dapm_widgets =		sta32x_dapm_widgets,
992c034abf6SJohannes Stezenbach 	.num_dapm_widgets =	ARRAY_SIZE(sta32x_dapm_widgets),
993c034abf6SJohannes Stezenbach 	.dapm_routes =		sta32x_dapm_routes,
994c034abf6SJohannes Stezenbach 	.num_dapm_routes =	ARRAY_SIZE(sta32x_dapm_routes),
995c034abf6SJohannes Stezenbach };
996c034abf6SJohannes Stezenbach 
99729fdf4fbSMark Brown static const struct regmap_config sta32x_regmap = {
99829fdf4fbSMark Brown 	.reg_bits =		8,
99929fdf4fbSMark Brown 	.val_bits =		8,
100029fdf4fbSMark Brown 	.max_register =		STA32X_FDRC2,
100129fdf4fbSMark Brown 	.reg_defaults =		sta32x_regs,
100229fdf4fbSMark Brown 	.num_reg_defaults =	ARRAY_SIZE(sta32x_regs),
100329fdf4fbSMark Brown 	.cache_type =		REGCACHE_RBTREE,
1004a1be4ceaSThomas Niederprüm 	.wr_table =		&sta32x_write_regs,
1005a1be4ceaSThomas Niederprüm 	.rd_table =		&sta32x_read_regs,
1006a1be4ceaSThomas Niederprüm 	.volatile_table =	&sta32x_volatile_regs,
1007a1be4ceaSThomas Niederprüm };
100829fdf4fbSMark Brown };
100929fdf4fbSMark Brown 
10107a79e94eSBill Pemberton static int sta32x_i2c_probe(struct i2c_client *i2c,
1011c034abf6SJohannes Stezenbach 			    const struct i2c_device_id *id)
1012c034abf6SJohannes Stezenbach {
1013a1be4ceaSThomas Niederprüm 	struct device *dev = &i2c->dev;
1014c034abf6SJohannes Stezenbach 	struct sta32x_priv *sta32x;
1015aff041afSMark Brown 	int ret, i;
1016c034abf6SJohannes Stezenbach 
1017d999c021SAxel Lin 	sta32x = devm_kzalloc(&i2c->dev, sizeof(struct sta32x_priv),
1018d999c021SAxel Lin 			      GFP_KERNEL);
1019c034abf6SJohannes Stezenbach 	if (!sta32x)
1020c034abf6SJohannes Stezenbach 		return -ENOMEM;
1021c034abf6SJohannes Stezenbach 
1022a1be4ceaSThomas Niederprüm 	mutex_init(&sta32x->coeff_lock);
1023a1be4ceaSThomas Niederprüm 	sta32x->pdata = dev_get_platdata(dev);
1024*b66a2980SThomas Niederprüm 
1025*b66a2980SThomas Niederprüm 	/* GPIOs */
1026*b66a2980SThomas Niederprüm 	sta32x->gpiod_nreset = devm_gpiod_get(dev, "reset");
1027*b66a2980SThomas Niederprüm 	if (IS_ERR(sta32x->gpiod_nreset)) {
1028*b66a2980SThomas Niederprüm 		ret = PTR_ERR(sta32x->gpiod_nreset);
1029*b66a2980SThomas Niederprüm 		if (ret != -ENOENT && ret != -ENOSYS)
1030*b66a2980SThomas Niederprüm 			return ret;
1031*b66a2980SThomas Niederprüm 
1032*b66a2980SThomas Niederprüm 		sta32x->gpiod_nreset = NULL;
1033*b66a2980SThomas Niederprüm 	} else {
1034*b66a2980SThomas Niederprüm 		gpiod_direction_output(sta32x->gpiod_nreset, 0);
1035*b66a2980SThomas Niederprüm 	}
1036*b66a2980SThomas Niederprüm 
1037aff041afSMark Brown 	/* regulators */
1038aff041afSMark Brown 	for (i = 0; i < ARRAY_SIZE(sta32x->supplies); i++)
1039aff041afSMark Brown 		sta32x->supplies[i].supply = sta32x_supply_names[i];
1040aff041afSMark Brown 
1041aff041afSMark Brown 	ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(sta32x->supplies),
1042aff041afSMark Brown 				      sta32x->supplies);
1043aff041afSMark Brown 	if (ret != 0) {
1044aff041afSMark Brown 		dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
1045aff041afSMark Brown 		return ret;
1046aff041afSMark Brown 	}
1047aff041afSMark Brown 
104829fdf4fbSMark Brown 	sta32x->regmap = devm_regmap_init_i2c(i2c, &sta32x_regmap);
104929fdf4fbSMark Brown 	if (IS_ERR(sta32x->regmap)) {
105029fdf4fbSMark Brown 		ret = PTR_ERR(sta32x->regmap);
1051a1be4ceaSThomas Niederprüm 		dev_err(dev, "Failed to init regmap: %d\n", ret);
105229fdf4fbSMark Brown 		return ret;
105329fdf4fbSMark Brown 	}
105429fdf4fbSMark Brown 
1055c034abf6SJohannes Stezenbach 	i2c_set_clientdata(i2c, sta32x);
1056c034abf6SJohannes Stezenbach 
1057a1be4ceaSThomas Niederprüm 	ret = snd_soc_register_codec(dev, &sta32x_codec, &sta32x_dai, 1);
1058a1be4ceaSThomas Niederprüm 	if (ret < 0)
1059a1be4ceaSThomas Niederprüm 		dev_err(dev, "Failed to register codec (%d)\n", ret);
1060c034abf6SJohannes Stezenbach 
1061d999c021SAxel Lin 	return ret;
1062c034abf6SJohannes Stezenbach }
1063c034abf6SJohannes Stezenbach 
10647a79e94eSBill Pemberton static int sta32x_i2c_remove(struct i2c_client *client)
1065c034abf6SJohannes Stezenbach {
1066c034abf6SJohannes Stezenbach 	snd_soc_unregister_codec(&client->dev);
1067c034abf6SJohannes Stezenbach 	return 0;
1068c034abf6SJohannes Stezenbach }
1069c034abf6SJohannes Stezenbach 
1070c034abf6SJohannes Stezenbach static const struct i2c_device_id sta32x_i2c_id[] = {
1071c034abf6SJohannes Stezenbach 	{ "sta326", 0 },
1072c034abf6SJohannes Stezenbach 	{ "sta328", 0 },
1073c034abf6SJohannes Stezenbach 	{ "sta329", 0 },
1074c034abf6SJohannes Stezenbach 	{ }
1075c034abf6SJohannes Stezenbach };
1076c034abf6SJohannes Stezenbach MODULE_DEVICE_TABLE(i2c, sta32x_i2c_id);
1077c034abf6SJohannes Stezenbach 
1078c034abf6SJohannes Stezenbach static struct i2c_driver sta32x_i2c_driver = {
1079c034abf6SJohannes Stezenbach 	.driver = {
1080c034abf6SJohannes Stezenbach 		.name = "sta32x",
1081c034abf6SJohannes Stezenbach 		.owner = THIS_MODULE,
1082c034abf6SJohannes Stezenbach 	},
1083c034abf6SJohannes Stezenbach 	.probe =    sta32x_i2c_probe,
10847a79e94eSBill Pemberton 	.remove =   sta32x_i2c_remove,
1085c034abf6SJohannes Stezenbach 	.id_table = sta32x_i2c_id,
1086c034abf6SJohannes Stezenbach };
1087c034abf6SJohannes Stezenbach 
10880ead1136SSachin Kamat module_i2c_driver(sta32x_i2c_driver);
1089c034abf6SJohannes Stezenbach 
1090c034abf6SJohannes Stezenbach MODULE_DESCRIPTION("ASoC STA32X driver");
1091c034abf6SJohannes Stezenbach MODULE_AUTHOR("Johannes Stezenbach <js@sig21.net>");
1092c034abf6SJohannes Stezenbach MODULE_LICENSE("GPL");
1093