1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2eef5bb24SBrian Austin /*
3eef5bb24SBrian Austin * cs35l32.c -- CS35L32 ALSA SoC audio driver
4eef5bb24SBrian Austin *
5eef5bb24SBrian Austin * Copyright 2014 CirrusLogic, Inc.
6eef5bb24SBrian Austin *
7eef5bb24SBrian Austin * Author: Brian Austin <brian.austin@cirrus.com>
8eef5bb24SBrian Austin */
9eef5bb24SBrian Austin
10eef5bb24SBrian Austin #include <linux/module.h>
11eef5bb24SBrian Austin #include <linux/moduleparam.h>
12eef5bb24SBrian Austin #include <linux/kernel.h>
13eef5bb24SBrian Austin #include <linux/init.h>
14eef5bb24SBrian Austin #include <linux/delay.h>
15eef5bb24SBrian Austin #include <linux/i2c.h>
16eef5bb24SBrian Austin #include <linux/gpio.h>
17eef5bb24SBrian Austin #include <linux/regmap.h>
18eef5bb24SBrian Austin #include <linux/slab.h>
19eef5bb24SBrian Austin #include <linux/platform_device.h>
20eef5bb24SBrian Austin #include <linux/regulator/consumer.h>
21eef5bb24SBrian Austin #include <linux/gpio/consumer.h>
22eef5bb24SBrian Austin #include <linux/of_device.h>
23eef5bb24SBrian Austin #include <sound/core.h>
24eef5bb24SBrian Austin #include <sound/pcm.h>
25eef5bb24SBrian Austin #include <sound/pcm_params.h>
26eef5bb24SBrian Austin #include <sound/soc.h>
27eef5bb24SBrian Austin #include <sound/soc-dapm.h>
28eef5bb24SBrian Austin #include <sound/initval.h>
29eef5bb24SBrian Austin #include <sound/tlv.h>
30eef5bb24SBrian Austin #include <dt-bindings/sound/cs35l32.h>
31eef5bb24SBrian Austin
32eef5bb24SBrian Austin #include "cs35l32.h"
33283160f1SCharles Keepax #include "cirrus_legacy.h"
34eef5bb24SBrian Austin
35eef5bb24SBrian Austin #define CS35L32_NUM_SUPPLIES 2
36eef5bb24SBrian Austin static const char *const cs35l32_supply_names[CS35L32_NUM_SUPPLIES] = {
37eef5bb24SBrian Austin "VA",
38eef5bb24SBrian Austin "VP",
39eef5bb24SBrian Austin };
40eef5bb24SBrian Austin
41eef5bb24SBrian Austin struct cs35l32_private {
42eef5bb24SBrian Austin struct regmap *regmap;
43fa643703SKuninori Morimoto struct snd_soc_component *component;
44eef5bb24SBrian Austin struct regulator_bulk_data supplies[CS35L32_NUM_SUPPLIES];
45eef5bb24SBrian Austin struct cs35l32_platform_data pdata;
46eef5bb24SBrian Austin struct gpio_desc *reset_gpio;
47eef5bb24SBrian Austin };
48eef5bb24SBrian Austin
49eef5bb24SBrian Austin static const struct reg_default cs35l32_reg_defaults[] = {
50eef5bb24SBrian Austin
51eef5bb24SBrian Austin { 0x06, 0x04 }, /* Power Ctl 1 */
52eef5bb24SBrian Austin { 0x07, 0xE8 }, /* Power Ctl 2 */
53eef5bb24SBrian Austin { 0x08, 0x40 }, /* Clock Ctl */
54eef5bb24SBrian Austin { 0x09, 0x20 }, /* Low Battery Threshold */
55eef5bb24SBrian Austin { 0x0A, 0x00 }, /* Voltage Monitor [RO] */
56eef5bb24SBrian Austin { 0x0B, 0x40 }, /* Conv Peak Curr Protection CTL */
57eef5bb24SBrian Austin { 0x0C, 0x07 }, /* IMON Scaling */
58eef5bb24SBrian Austin { 0x0D, 0x03 }, /* Audio/LED Pwr Manager */
59eef5bb24SBrian Austin { 0x0F, 0x20 }, /* Serial Port Control */
60eef5bb24SBrian Austin { 0x10, 0x14 }, /* Class D Amp CTL */
61eef5bb24SBrian Austin { 0x11, 0x00 }, /* Protection Release CTL */
62eef5bb24SBrian Austin { 0x12, 0xFF }, /* Interrupt Mask 1 */
63eef5bb24SBrian Austin { 0x13, 0xFF }, /* Interrupt Mask 2 */
64eef5bb24SBrian Austin { 0x14, 0xFF }, /* Interrupt Mask 3 */
65eef5bb24SBrian Austin { 0x19, 0x00 }, /* LED Flash Mode Current */
66eef5bb24SBrian Austin { 0x1A, 0x00 }, /* LED Movie Mode Current */
67eef5bb24SBrian Austin { 0x1B, 0x20 }, /* LED Flash Timer */
68eef5bb24SBrian Austin { 0x1C, 0x00 }, /* LED Flash Inhibit Current */
69eef5bb24SBrian Austin };
70eef5bb24SBrian Austin
cs35l32_readable_register(struct device * dev,unsigned int reg)71eef5bb24SBrian Austin static bool cs35l32_readable_register(struct device *dev, unsigned int reg)
72eef5bb24SBrian Austin {
73eef5bb24SBrian Austin switch (reg) {
74c1763308SAxel Lin case CS35L32_DEVID_AB ... CS35L32_AUDIO_LED_MNGR:
75c1763308SAxel Lin case CS35L32_ADSP_CTL ... CS35L32_FLASH_INHIBIT:
76eef5bb24SBrian Austin return true;
77eef5bb24SBrian Austin default:
78eef5bb24SBrian Austin return false;
79eef5bb24SBrian Austin }
80eef5bb24SBrian Austin }
81eef5bb24SBrian Austin
cs35l32_volatile_register(struct device * dev,unsigned int reg)82eef5bb24SBrian Austin static bool cs35l32_volatile_register(struct device *dev, unsigned int reg)
83eef5bb24SBrian Austin {
84eef5bb24SBrian Austin switch (reg) {
85c1763308SAxel Lin case CS35L32_DEVID_AB ... CS35L32_REV_ID:
86c1763308SAxel Lin case CS35L32_INT_STATUS_1 ... CS35L32_LED_STATUS:
877eef0855SBrian Austin return true;
88eef5bb24SBrian Austin default:
897eef0855SBrian Austin return false;
90eef5bb24SBrian Austin }
91eef5bb24SBrian Austin }
92eef5bb24SBrian Austin
cs35l32_precious_register(struct device * dev,unsigned int reg)93eef5bb24SBrian Austin static bool cs35l32_precious_register(struct device *dev, unsigned int reg)
94eef5bb24SBrian Austin {
95eef5bb24SBrian Austin switch (reg) {
96c1763308SAxel Lin case CS35L32_INT_STATUS_1 ... CS35L32_LED_STATUS:
977eef0855SBrian Austin return true;
98eef5bb24SBrian Austin default:
997eef0855SBrian Austin return false;
100eef5bb24SBrian Austin }
101eef5bb24SBrian Austin }
102eef5bb24SBrian Austin
103eef5bb24SBrian Austin static DECLARE_TLV_DB_SCALE(classd_ctl_tlv, 900, 300, 0);
104eef5bb24SBrian Austin
105eef5bb24SBrian Austin static const struct snd_kcontrol_new imon_ctl =
106eef5bb24SBrian Austin SOC_DAPM_SINGLE("Switch", CS35L32_PWRCTL2, 6, 1, 1);
107eef5bb24SBrian Austin
108eef5bb24SBrian Austin static const struct snd_kcontrol_new vmon_ctl =
109eef5bb24SBrian Austin SOC_DAPM_SINGLE("Switch", CS35L32_PWRCTL2, 7, 1, 1);
110eef5bb24SBrian Austin
111eef5bb24SBrian Austin static const struct snd_kcontrol_new vpmon_ctl =
112eef5bb24SBrian Austin SOC_DAPM_SINGLE("Switch", CS35L32_PWRCTL2, 5, 1, 1);
113eef5bb24SBrian Austin
114eef5bb24SBrian Austin static const struct snd_kcontrol_new cs35l32_snd_controls[] = {
115eef5bb24SBrian Austin SOC_SINGLE_TLV("Speaker Volume", CS35L32_CLASSD_CTL,
116eef5bb24SBrian Austin 3, 0x04, 1, classd_ctl_tlv),
117eef5bb24SBrian Austin SOC_SINGLE("Zero Cross Switch", CS35L32_CLASSD_CTL, 2, 1, 0),
118eef5bb24SBrian Austin SOC_SINGLE("Gain Manager Switch", CS35L32_AUDIO_LED_MNGR, 3, 1, 0),
119eef5bb24SBrian Austin };
120eef5bb24SBrian Austin
121eef5bb24SBrian Austin static const struct snd_soc_dapm_widget cs35l32_dapm_widgets[] = {
122eef5bb24SBrian Austin
123eef5bb24SBrian Austin SND_SOC_DAPM_SUPPLY("BOOST", CS35L32_PWRCTL1, 2, 1, NULL, 0),
124eef5bb24SBrian Austin SND_SOC_DAPM_OUT_DRV("Speaker", CS35L32_PWRCTL1, 7, 1, NULL, 0),
125eef5bb24SBrian Austin
126eef5bb24SBrian Austin SND_SOC_DAPM_AIF_OUT("SDOUT", NULL, 0, CS35L32_PWRCTL2, 3, 1),
127eef5bb24SBrian Austin
128eef5bb24SBrian Austin SND_SOC_DAPM_INPUT("VP"),
129eef5bb24SBrian Austin SND_SOC_DAPM_INPUT("ISENSE"),
130eef5bb24SBrian Austin SND_SOC_DAPM_INPUT("VSENSE"),
131eef5bb24SBrian Austin
132eef5bb24SBrian Austin SND_SOC_DAPM_SWITCH("VMON ADC", CS35L32_PWRCTL2, 7, 1, &vmon_ctl),
133eef5bb24SBrian Austin SND_SOC_DAPM_SWITCH("IMON ADC", CS35L32_PWRCTL2, 6, 1, &imon_ctl),
134eef5bb24SBrian Austin SND_SOC_DAPM_SWITCH("VPMON ADC", CS35L32_PWRCTL2, 5, 1, &vpmon_ctl),
135eef5bb24SBrian Austin };
136eef5bb24SBrian Austin
137eef5bb24SBrian Austin static const struct snd_soc_dapm_route cs35l32_audio_map[] = {
138eef5bb24SBrian Austin
139eef5bb24SBrian Austin {"Speaker", NULL, "BOOST"},
140eef5bb24SBrian Austin
141eef5bb24SBrian Austin {"VMON ADC", NULL, "VSENSE"},
142eef5bb24SBrian Austin {"IMON ADC", NULL, "ISENSE"},
143eef5bb24SBrian Austin {"VPMON ADC", NULL, "VP"},
144eef5bb24SBrian Austin
145eef5bb24SBrian Austin {"SDOUT", "Switch", "VMON ADC"},
146eef5bb24SBrian Austin {"SDOUT", "Switch", "IMON ADC"},
147eef5bb24SBrian Austin {"SDOUT", "Switch", "VPMON ADC"},
148eef5bb24SBrian Austin
149eef5bb24SBrian Austin {"Capture", NULL, "SDOUT"},
150eef5bb24SBrian Austin };
151eef5bb24SBrian Austin
cs35l32_set_dai_fmt(struct snd_soc_dai * codec_dai,unsigned int fmt)152eef5bb24SBrian Austin static int cs35l32_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
153eef5bb24SBrian Austin {
154fa643703SKuninori Morimoto struct snd_soc_component *component = codec_dai->component;
155eef5bb24SBrian Austin
156eef5bb24SBrian Austin switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
157eef5bb24SBrian Austin case SND_SOC_DAIFMT_CBM_CFM:
158fa643703SKuninori Morimoto snd_soc_component_update_bits(component, CS35L32_ADSP_CTL,
159eef5bb24SBrian Austin CS35L32_ADSP_MASTER_MASK,
160eef5bb24SBrian Austin CS35L32_ADSP_MASTER_MASK);
161eef5bb24SBrian Austin break;
162eef5bb24SBrian Austin case SND_SOC_DAIFMT_CBS_CFS:
163fa643703SKuninori Morimoto snd_soc_component_update_bits(component, CS35L32_ADSP_CTL,
164eef5bb24SBrian Austin CS35L32_ADSP_MASTER_MASK, 0);
165eef5bb24SBrian Austin break;
166eef5bb24SBrian Austin default:
167eef5bb24SBrian Austin return -EINVAL;
168eef5bb24SBrian Austin }
169eef5bb24SBrian Austin
170eef5bb24SBrian Austin return 0;
171eef5bb24SBrian Austin }
172eef5bb24SBrian Austin
cs35l32_set_tristate(struct snd_soc_dai * dai,int tristate)173eef5bb24SBrian Austin static int cs35l32_set_tristate(struct snd_soc_dai *dai, int tristate)
174eef5bb24SBrian Austin {
175fa643703SKuninori Morimoto struct snd_soc_component *component = dai->component;
176eef5bb24SBrian Austin
177fa643703SKuninori Morimoto return snd_soc_component_update_bits(component, CS35L32_PWRCTL2,
178eef5bb24SBrian Austin CS35L32_SDOUT_3ST, tristate << 3);
179eef5bb24SBrian Austin }
180eef5bb24SBrian Austin
181eef5bb24SBrian Austin static const struct snd_soc_dai_ops cs35l32_ops = {
182eef5bb24SBrian Austin .set_fmt = cs35l32_set_dai_fmt,
183eef5bb24SBrian Austin .set_tristate = cs35l32_set_tristate,
184eef5bb24SBrian Austin };
185eef5bb24SBrian Austin
186eef5bb24SBrian Austin static struct snd_soc_dai_driver cs35l32_dai[] = {
187eef5bb24SBrian Austin {
188eef5bb24SBrian Austin .name = "cs35l32-monitor",
189eef5bb24SBrian Austin .id = 0,
190eef5bb24SBrian Austin .capture = {
191eef5bb24SBrian Austin .stream_name = "Capture",
192eef5bb24SBrian Austin .channels_min = 2,
193eef5bb24SBrian Austin .channels_max = 2,
194eef5bb24SBrian Austin .rates = CS35L32_RATES,
195eef5bb24SBrian Austin .formats = CS35L32_FORMATS,
196eef5bb24SBrian Austin },
197eef5bb24SBrian Austin .ops = &cs35l32_ops,
198260b668cSKuninori Morimoto .symmetric_rate = 1,
199eef5bb24SBrian Austin }
200eef5bb24SBrian Austin };
201eef5bb24SBrian Austin
cs35l32_component_set_sysclk(struct snd_soc_component * component,int clk_id,int source,unsigned int freq,int dir)202fa643703SKuninori Morimoto static int cs35l32_component_set_sysclk(struct snd_soc_component *component,
203eef5bb24SBrian Austin int clk_id, int source, unsigned int freq, int dir)
204eef5bb24SBrian Austin {
2055f609f28SAxel Lin unsigned int val;
206eef5bb24SBrian Austin
207eef5bb24SBrian Austin switch (freq) {
208eef5bb24SBrian Austin case 6000000:
2095f609f28SAxel Lin val = CS35L32_MCLK_RATIO;
210eef5bb24SBrian Austin break;
211eef5bb24SBrian Austin case 12000000:
2125f609f28SAxel Lin val = CS35L32_MCLK_DIV2_MASK | CS35L32_MCLK_RATIO;
213eef5bb24SBrian Austin break;
214eef5bb24SBrian Austin case 6144000:
2155f609f28SAxel Lin val = 0;
216eef5bb24SBrian Austin break;
217eef5bb24SBrian Austin case 12288000:
2185f609f28SAxel Lin val = CS35L32_MCLK_DIV2_MASK;
219eef5bb24SBrian Austin break;
220eef5bb24SBrian Austin default:
221eef5bb24SBrian Austin return -EINVAL;
222eef5bb24SBrian Austin }
223eef5bb24SBrian Austin
224fa643703SKuninori Morimoto return snd_soc_component_update_bits(component, CS35L32_CLK_CTL,
2255f609f28SAxel Lin CS35L32_MCLK_DIV2_MASK | CS35L32_MCLK_RATIO_MASK, val);
226eef5bb24SBrian Austin }
227eef5bb24SBrian Austin
228fa643703SKuninori Morimoto static const struct snd_soc_component_driver soc_component_dev_cs35l32 = {
229fa643703SKuninori Morimoto .set_sysclk = cs35l32_component_set_sysclk,
2301728f9d1SKuninori Morimoto .controls = cs35l32_snd_controls,
2311728f9d1SKuninori Morimoto .num_controls = ARRAY_SIZE(cs35l32_snd_controls),
232eef5bb24SBrian Austin .dapm_widgets = cs35l32_dapm_widgets,
233eef5bb24SBrian Austin .num_dapm_widgets = ARRAY_SIZE(cs35l32_dapm_widgets),
234eef5bb24SBrian Austin .dapm_routes = cs35l32_audio_map,
235eef5bb24SBrian Austin .num_dapm_routes = ARRAY_SIZE(cs35l32_audio_map),
236fa643703SKuninori Morimoto .idle_bias_on = 1,
237fa643703SKuninori Morimoto .use_pmdown_time = 1,
238fa643703SKuninori Morimoto .endianness = 1,
239eef5bb24SBrian Austin };
240eef5bb24SBrian Austin
241eef5bb24SBrian Austin /* Current and threshold powerup sequence Pg37 in datasheet */
2428019ff6cSNariman Poushin static const struct reg_sequence cs35l32_monitor_patch[] = {
243eef5bb24SBrian Austin
244eef5bb24SBrian Austin { 0x00, 0x99 },
245eef5bb24SBrian Austin { 0x48, 0x17 },
246eef5bb24SBrian Austin { 0x49, 0x56 },
247eef5bb24SBrian Austin { 0x43, 0x01 },
248eef5bb24SBrian Austin { 0x3B, 0x62 },
249eef5bb24SBrian Austin { 0x3C, 0x80 },
250eef5bb24SBrian Austin { 0x00, 0x00 },
251eef5bb24SBrian Austin };
252eef5bb24SBrian Austin
253d883641fSKrzysztof Kozlowski static const struct regmap_config cs35l32_regmap = {
254eef5bb24SBrian Austin .reg_bits = 8,
255eef5bb24SBrian Austin .val_bits = 8,
256eef5bb24SBrian Austin
257eef5bb24SBrian Austin .max_register = CS35L32_MAX_REGISTER,
258eef5bb24SBrian Austin .reg_defaults = cs35l32_reg_defaults,
259eef5bb24SBrian Austin .num_reg_defaults = ARRAY_SIZE(cs35l32_reg_defaults),
260eef5bb24SBrian Austin .volatile_reg = cs35l32_volatile_register,
261eef5bb24SBrian Austin .readable_reg = cs35l32_readable_register,
262eef5bb24SBrian Austin .precious_reg = cs35l32_precious_register,
263*176bb179SMark Brown .cache_type = REGCACHE_MAPLE,
264b1078e98SCharles Keepax
265b1078e98SCharles Keepax .use_single_read = true,
266b1078e98SCharles Keepax .use_single_write = true,
267eef5bb24SBrian Austin };
268eef5bb24SBrian Austin
cs35l32_handle_of_data(struct i2c_client * i2c_client,struct cs35l32_platform_data * pdata)269eef5bb24SBrian Austin static int cs35l32_handle_of_data(struct i2c_client *i2c_client,
270eef5bb24SBrian Austin struct cs35l32_platform_data *pdata)
271eef5bb24SBrian Austin {
272eef5bb24SBrian Austin struct device_node *np = i2c_client->dev.of_node;
273eef5bb24SBrian Austin unsigned int val;
274eef5bb24SBrian Austin
275eef5bb24SBrian Austin if (of_property_read_u32(np, "cirrus,sdout-share", &val) >= 0)
276eef5bb24SBrian Austin pdata->sdout_share = val;
277eef5bb24SBrian Austin
278dd5dc001SArnd Bergmann if (of_property_read_u32(np, "cirrus,boost-manager", &val))
279dd5dc001SArnd Bergmann val = -1u;
280dd5dc001SArnd Bergmann
281eef5bb24SBrian Austin switch (val) {
282eef5bb24SBrian Austin case CS35L32_BOOST_MGR_AUTO:
283eef5bb24SBrian Austin case CS35L32_BOOST_MGR_AUTO_AUDIO:
284eef5bb24SBrian Austin case CS35L32_BOOST_MGR_BYPASS:
285eef5bb24SBrian Austin case CS35L32_BOOST_MGR_FIXED:
286eef5bb24SBrian Austin pdata->boost_mng = val;
287eef5bb24SBrian Austin break;
288dd5dc001SArnd Bergmann case -1u:
289eef5bb24SBrian Austin default:
290eef5bb24SBrian Austin dev_err(&i2c_client->dev,
291eef5bb24SBrian Austin "Wrong cirrus,boost-manager DT value %d\n", val);
292eef5bb24SBrian Austin pdata->boost_mng = CS35L32_BOOST_MGR_BYPASS;
293eef5bb24SBrian Austin }
294eef5bb24SBrian Austin
295dd5dc001SArnd Bergmann if (of_property_read_u32(np, "cirrus,sdout-datacfg", &val))
296dd5dc001SArnd Bergmann val = -1u;
297eef5bb24SBrian Austin switch (val) {
298eef5bb24SBrian Austin case CS35L32_DATA_CFG_LR_VP:
299eef5bb24SBrian Austin case CS35L32_DATA_CFG_LR_STAT:
300eef5bb24SBrian Austin case CS35L32_DATA_CFG_LR:
301eef5bb24SBrian Austin case CS35L32_DATA_CFG_LR_VPSTAT:
302eef5bb24SBrian Austin pdata->sdout_datacfg = val;
303eef5bb24SBrian Austin break;
304dd5dc001SArnd Bergmann case -1u:
305eef5bb24SBrian Austin default:
306eef5bb24SBrian Austin dev_err(&i2c_client->dev,
307eef5bb24SBrian Austin "Wrong cirrus,sdout-datacfg DT value %d\n", val);
308eef5bb24SBrian Austin pdata->sdout_datacfg = CS35L32_DATA_CFG_LR;
309eef5bb24SBrian Austin }
310eef5bb24SBrian Austin
311dd5dc001SArnd Bergmann if (of_property_read_u32(np, "cirrus,battery-threshold", &val))
312dd5dc001SArnd Bergmann val = -1u;
313eef5bb24SBrian Austin switch (val) {
314eef5bb24SBrian Austin case CS35L32_BATT_THRESH_3_1V:
315eef5bb24SBrian Austin case CS35L32_BATT_THRESH_3_2V:
316eef5bb24SBrian Austin case CS35L32_BATT_THRESH_3_3V:
317eef5bb24SBrian Austin case CS35L32_BATT_THRESH_3_4V:
318eef5bb24SBrian Austin pdata->batt_thresh = val;
319eef5bb24SBrian Austin break;
320dd5dc001SArnd Bergmann case -1u:
321eef5bb24SBrian Austin default:
322eef5bb24SBrian Austin dev_err(&i2c_client->dev,
323eef5bb24SBrian Austin "Wrong cirrus,battery-threshold DT value %d\n", val);
324eef5bb24SBrian Austin pdata->batt_thresh = CS35L32_BATT_THRESH_3_3V;
325eef5bb24SBrian Austin }
326eef5bb24SBrian Austin
327dd5dc001SArnd Bergmann if (of_property_read_u32(np, "cirrus,battery-recovery", &val))
328dd5dc001SArnd Bergmann val = -1u;
329eef5bb24SBrian Austin switch (val) {
330eef5bb24SBrian Austin case CS35L32_BATT_RECOV_3_1V:
331eef5bb24SBrian Austin case CS35L32_BATT_RECOV_3_2V:
332eef5bb24SBrian Austin case CS35L32_BATT_RECOV_3_3V:
333eef5bb24SBrian Austin case CS35L32_BATT_RECOV_3_4V:
334eef5bb24SBrian Austin case CS35L32_BATT_RECOV_3_5V:
335eef5bb24SBrian Austin case CS35L32_BATT_RECOV_3_6V:
336eef5bb24SBrian Austin pdata->batt_recov = val;
337eef5bb24SBrian Austin break;
338dd5dc001SArnd Bergmann case -1u:
339eef5bb24SBrian Austin default:
340eef5bb24SBrian Austin dev_err(&i2c_client->dev,
341eef5bb24SBrian Austin "Wrong cirrus,battery-recovery DT value %d\n", val);
342eef5bb24SBrian Austin pdata->batt_recov = CS35L32_BATT_RECOV_3_4V;
343eef5bb24SBrian Austin }
344eef5bb24SBrian Austin
345eef5bb24SBrian Austin return 0;
346eef5bb24SBrian Austin }
347eef5bb24SBrian Austin
cs35l32_i2c_probe(struct i2c_client * i2c_client)3484a404345SStephen Kitt static int cs35l32_i2c_probe(struct i2c_client *i2c_client)
349eef5bb24SBrian Austin {
350eef5bb24SBrian Austin struct cs35l32_private *cs35l32;
351eef5bb24SBrian Austin struct cs35l32_platform_data *pdata =
352eef5bb24SBrian Austin dev_get_platdata(&i2c_client->dev);
353283160f1SCharles Keepax int ret, i, devid;
354eef5bb24SBrian Austin unsigned int reg;
355eef5bb24SBrian Austin
356b28ad41eSMarkus Elfring cs35l32 = devm_kzalloc(&i2c_client->dev, sizeof(*cs35l32), GFP_KERNEL);
357410afed0SMarkus Elfring if (!cs35l32)
358eef5bb24SBrian Austin return -ENOMEM;
359eef5bb24SBrian Austin
360eef5bb24SBrian Austin i2c_set_clientdata(i2c_client, cs35l32);
361eef5bb24SBrian Austin
362eef5bb24SBrian Austin cs35l32->regmap = devm_regmap_init_i2c(i2c_client, &cs35l32_regmap);
363eef5bb24SBrian Austin if (IS_ERR(cs35l32->regmap)) {
364eef5bb24SBrian Austin ret = PTR_ERR(cs35l32->regmap);
365eef5bb24SBrian Austin dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
366eef5bb24SBrian Austin return ret;
367eef5bb24SBrian Austin }
368eef5bb24SBrian Austin
369eef5bb24SBrian Austin if (pdata) {
370eef5bb24SBrian Austin cs35l32->pdata = *pdata;
371eef5bb24SBrian Austin } else {
372b28ad41eSMarkus Elfring pdata = devm_kzalloc(&i2c_client->dev, sizeof(*pdata),
373eef5bb24SBrian Austin GFP_KERNEL);
374410afed0SMarkus Elfring if (!pdata)
375eef5bb24SBrian Austin return -ENOMEM;
376410afed0SMarkus Elfring
377eef5bb24SBrian Austin if (i2c_client->dev.of_node) {
378eef5bb24SBrian Austin ret = cs35l32_handle_of_data(i2c_client,
379eef5bb24SBrian Austin &cs35l32->pdata);
380eef5bb24SBrian Austin if (ret != 0)
381eef5bb24SBrian Austin return ret;
382eef5bb24SBrian Austin }
383eef5bb24SBrian Austin }
384eef5bb24SBrian Austin
385eef5bb24SBrian Austin for (i = 0; i < ARRAY_SIZE(cs35l32->supplies); i++)
386eef5bb24SBrian Austin cs35l32->supplies[i].supply = cs35l32_supply_names[i];
387eef5bb24SBrian Austin
388eef5bb24SBrian Austin ret = devm_regulator_bulk_get(&i2c_client->dev,
389eef5bb24SBrian Austin ARRAY_SIZE(cs35l32->supplies),
390eef5bb24SBrian Austin cs35l32->supplies);
391eef5bb24SBrian Austin if (ret != 0) {
392eef5bb24SBrian Austin dev_err(&i2c_client->dev,
393eef5bb24SBrian Austin "Failed to request supplies: %d\n", ret);
394eef5bb24SBrian Austin return ret;
395eef5bb24SBrian Austin }
396eef5bb24SBrian Austin
397eef5bb24SBrian Austin ret = regulator_bulk_enable(ARRAY_SIZE(cs35l32->supplies),
398eef5bb24SBrian Austin cs35l32->supplies);
399eef5bb24SBrian Austin if (ret != 0) {
400eef5bb24SBrian Austin dev_err(&i2c_client->dev,
401eef5bb24SBrian Austin "Failed to enable supplies: %d\n", ret);
402eef5bb24SBrian Austin return ret;
403eef5bb24SBrian Austin }
404eef5bb24SBrian Austin
405eef5bb24SBrian Austin /* Reset the Device */
40634d7c390SUwe Kleine-König cs35l32->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev,
40734d7c390SUwe Kleine-König "reset", GPIOD_OUT_LOW);
408283160f1SCharles Keepax if (IS_ERR(cs35l32->reset_gpio)) {
409283160f1SCharles Keepax ret = PTR_ERR(cs35l32->reset_gpio);
410283160f1SCharles Keepax goto err_supplies;
411283160f1SCharles Keepax }
412eef5bb24SBrian Austin
413eef5bb24SBrian Austin gpiod_set_value_cansleep(cs35l32->reset_gpio, 1);
414eef5bb24SBrian Austin
415eef5bb24SBrian Austin /* initialize codec */
416283160f1SCharles Keepax devid = cirrus_read_device_id(cs35l32->regmap, CS35L32_DEVID_AB);
417283160f1SCharles Keepax if (devid < 0) {
418283160f1SCharles Keepax ret = devid;
419283160f1SCharles Keepax dev_err(&i2c_client->dev, "Failed to read device ID: %d\n", ret);
420283160f1SCharles Keepax goto err_disable;
421283160f1SCharles Keepax }
422eef5bb24SBrian Austin
423eef5bb24SBrian Austin if (devid != CS35L32_CHIP_ID) {
424eef5bb24SBrian Austin ret = -ENODEV;
425eef5bb24SBrian Austin dev_err(&i2c_client->dev,
426eef5bb24SBrian Austin "CS35L32 Device ID (%X). Expected %X\n",
427eef5bb24SBrian Austin devid, CS35L32_CHIP_ID);
428283160f1SCharles Keepax goto err_disable;
429eef5bb24SBrian Austin }
430eef5bb24SBrian Austin
431eef5bb24SBrian Austin ret = regmap_read(cs35l32->regmap, CS35L32_REV_ID, ®);
432eef5bb24SBrian Austin if (ret < 0) {
433eef5bb24SBrian Austin dev_err(&i2c_client->dev, "Get Revision ID failed\n");
434283160f1SCharles Keepax goto err_disable;
435eef5bb24SBrian Austin }
436eef5bb24SBrian Austin
437eef5bb24SBrian Austin ret = regmap_register_patch(cs35l32->regmap, cs35l32_monitor_patch,
438eef5bb24SBrian Austin ARRAY_SIZE(cs35l32_monitor_patch));
439eef5bb24SBrian Austin if (ret < 0) {
440eef5bb24SBrian Austin dev_err(&i2c_client->dev, "Failed to apply errata patch\n");
441283160f1SCharles Keepax goto err_disable;
442eef5bb24SBrian Austin }
443eef5bb24SBrian Austin
444eef5bb24SBrian Austin dev_info(&i2c_client->dev,
445eef5bb24SBrian Austin "Cirrus Logic CS35L32, Revision: %02X\n", reg & 0xFF);
446eef5bb24SBrian Austin
447eef5bb24SBrian Austin /* Setup VBOOST Management */
448eef5bb24SBrian Austin if (cs35l32->pdata.boost_mng)
449eef5bb24SBrian Austin regmap_update_bits(cs35l32->regmap, CS35L32_AUDIO_LED_MNGR,
450eef5bb24SBrian Austin CS35L32_BOOST_MASK,
451eef5bb24SBrian Austin cs35l32->pdata.boost_mng);
452eef5bb24SBrian Austin
453eef5bb24SBrian Austin /* Setup ADSP Format Config */
454eef5bb24SBrian Austin if (cs35l32->pdata.sdout_share)
455eef5bb24SBrian Austin regmap_update_bits(cs35l32->regmap, CS35L32_ADSP_CTL,
456eef5bb24SBrian Austin CS35L32_ADSP_SHARE_MASK,
457eef5bb24SBrian Austin cs35l32->pdata.sdout_share << 3);
458eef5bb24SBrian Austin
459eef5bb24SBrian Austin /* Setup ADSP Data Configuration */
460eef5bb24SBrian Austin if (cs35l32->pdata.sdout_datacfg)
461eef5bb24SBrian Austin regmap_update_bits(cs35l32->regmap, CS35L32_ADSP_CTL,
462eef5bb24SBrian Austin CS35L32_ADSP_DATACFG_MASK,
463eef5bb24SBrian Austin cs35l32->pdata.sdout_datacfg << 4);
464eef5bb24SBrian Austin
465eef5bb24SBrian Austin /* Setup Low Battery Recovery */
466eef5bb24SBrian Austin if (cs35l32->pdata.batt_recov)
467eef5bb24SBrian Austin regmap_update_bits(cs35l32->regmap, CS35L32_BATT_THRESHOLD,
468eef5bb24SBrian Austin CS35L32_BATT_REC_MASK,
469eef5bb24SBrian Austin cs35l32->pdata.batt_recov << 1);
470eef5bb24SBrian Austin
471eef5bb24SBrian Austin /* Setup Low Battery Threshold */
472eef5bb24SBrian Austin if (cs35l32->pdata.batt_thresh)
473eef5bb24SBrian Austin regmap_update_bits(cs35l32->regmap, CS35L32_BATT_THRESHOLD,
474eef5bb24SBrian Austin CS35L32_BATT_THRESH_MASK,
475eef5bb24SBrian Austin cs35l32->pdata.batt_thresh << 4);
476eef5bb24SBrian Austin
477eef5bb24SBrian Austin /* Power down the AMP */
478eef5bb24SBrian Austin regmap_update_bits(cs35l32->regmap, CS35L32_PWRCTL1, CS35L32_PDN_AMP,
479eef5bb24SBrian Austin CS35L32_PDN_AMP);
480eef5bb24SBrian Austin
481eef5bb24SBrian Austin /* Clear MCLK Error Bit since we don't have the clock yet */
482283160f1SCharles Keepax regmap_read(cs35l32->regmap, CS35L32_INT_STATUS_1, ®);
483eef5bb24SBrian Austin
484fa643703SKuninori Morimoto ret = devm_snd_soc_register_component(&i2c_client->dev,
485fa643703SKuninori Morimoto &soc_component_dev_cs35l32, cs35l32_dai,
486eef5bb24SBrian Austin ARRAY_SIZE(cs35l32_dai));
487eef5bb24SBrian Austin if (ret < 0)
488eef5bb24SBrian Austin goto err_disable;
489eef5bb24SBrian Austin
490eef5bb24SBrian Austin return 0;
491eef5bb24SBrian Austin
492eef5bb24SBrian Austin err_disable:
493283160f1SCharles Keepax gpiod_set_value_cansleep(cs35l32->reset_gpio, 0);
494283160f1SCharles Keepax err_supplies:
495eef5bb24SBrian Austin regulator_bulk_disable(ARRAY_SIZE(cs35l32->supplies),
496eef5bb24SBrian Austin cs35l32->supplies);
49738f57532SBrian Austin return ret;
498eef5bb24SBrian Austin }
499eef5bb24SBrian Austin
cs35l32_i2c_remove(struct i2c_client * i2c_client)500ed5c2f5fSUwe Kleine-König static void cs35l32_i2c_remove(struct i2c_client *i2c_client)
501eef5bb24SBrian Austin {
502eef5bb24SBrian Austin struct cs35l32_private *cs35l32 = i2c_get_clientdata(i2c_client);
503eef5bb24SBrian Austin
504eef5bb24SBrian Austin /* Hold down reset */
505eef5bb24SBrian Austin gpiod_set_value_cansleep(cs35l32->reset_gpio, 0);
506eef5bb24SBrian Austin }
507eef5bb24SBrian Austin
508641d334bSRafael J. Wysocki #ifdef CONFIG_PM
cs35l32_runtime_suspend(struct device * dev)509eef5bb24SBrian Austin static int cs35l32_runtime_suspend(struct device *dev)
510eef5bb24SBrian Austin {
511eef5bb24SBrian Austin struct cs35l32_private *cs35l32 = dev_get_drvdata(dev);
512eef5bb24SBrian Austin
513eef5bb24SBrian Austin regcache_cache_only(cs35l32->regmap, true);
514eef5bb24SBrian Austin regcache_mark_dirty(cs35l32->regmap);
515eef5bb24SBrian Austin
516eef5bb24SBrian Austin /* Hold down reset */
517eef5bb24SBrian Austin gpiod_set_value_cansleep(cs35l32->reset_gpio, 0);
518eef5bb24SBrian Austin
519eef5bb24SBrian Austin /* remove power */
520eef5bb24SBrian Austin regulator_bulk_disable(ARRAY_SIZE(cs35l32->supplies),
521eef5bb24SBrian Austin cs35l32->supplies);
522eef5bb24SBrian Austin
523eef5bb24SBrian Austin return 0;
524eef5bb24SBrian Austin }
525eef5bb24SBrian Austin
cs35l32_runtime_resume(struct device * dev)526eef5bb24SBrian Austin static int cs35l32_runtime_resume(struct device *dev)
527eef5bb24SBrian Austin {
528eef5bb24SBrian Austin struct cs35l32_private *cs35l32 = dev_get_drvdata(dev);
529eef5bb24SBrian Austin int ret;
530eef5bb24SBrian Austin
531eef5bb24SBrian Austin /* Enable power */
532eef5bb24SBrian Austin ret = regulator_bulk_enable(ARRAY_SIZE(cs35l32->supplies),
533eef5bb24SBrian Austin cs35l32->supplies);
534eef5bb24SBrian Austin if (ret != 0) {
535eef5bb24SBrian Austin dev_err(dev, "Failed to enable supplies: %d\n",
536eef5bb24SBrian Austin ret);
537eef5bb24SBrian Austin return ret;
538eef5bb24SBrian Austin }
539eef5bb24SBrian Austin
540eef5bb24SBrian Austin gpiod_set_value_cansleep(cs35l32->reset_gpio, 1);
541eef5bb24SBrian Austin
542eef5bb24SBrian Austin regcache_cache_only(cs35l32->regmap, false);
543eef5bb24SBrian Austin regcache_sync(cs35l32->regmap);
544eef5bb24SBrian Austin
545eef5bb24SBrian Austin return 0;
546eef5bb24SBrian Austin }
547eef5bb24SBrian Austin #endif
548eef5bb24SBrian Austin
549eef5bb24SBrian Austin static const struct dev_pm_ops cs35l32_runtime_pm = {
550eef5bb24SBrian Austin SET_RUNTIME_PM_OPS(cs35l32_runtime_suspend, cs35l32_runtime_resume,
551eef5bb24SBrian Austin NULL)
552eef5bb24SBrian Austin };
553eef5bb24SBrian Austin
554eef5bb24SBrian Austin static const struct of_device_id cs35l32_of_match[] = {
555eef5bb24SBrian Austin { .compatible = "cirrus,cs35l32", },
556eef5bb24SBrian Austin {},
557eef5bb24SBrian Austin };
558eef5bb24SBrian Austin MODULE_DEVICE_TABLE(of, cs35l32_of_match);
559eef5bb24SBrian Austin
560eef5bb24SBrian Austin
561eef5bb24SBrian Austin static const struct i2c_device_id cs35l32_id[] = {
562eef5bb24SBrian Austin {"cs35l32", 0},
563eef5bb24SBrian Austin {}
564eef5bb24SBrian Austin };
565eef5bb24SBrian Austin
566eef5bb24SBrian Austin MODULE_DEVICE_TABLE(i2c, cs35l32_id);
567eef5bb24SBrian Austin
568eef5bb24SBrian Austin static struct i2c_driver cs35l32_i2c_driver = {
569eef5bb24SBrian Austin .driver = {
570eef5bb24SBrian Austin .name = "cs35l32",
571eef5bb24SBrian Austin .pm = &cs35l32_runtime_pm,
572eef5bb24SBrian Austin .of_match_table = cs35l32_of_match,
573eef5bb24SBrian Austin },
574eef5bb24SBrian Austin .id_table = cs35l32_id,
5759abcd240SUwe Kleine-König .probe = cs35l32_i2c_probe,
576eef5bb24SBrian Austin .remove = cs35l32_i2c_remove,
577eef5bb24SBrian Austin };
578eef5bb24SBrian Austin
579eef5bb24SBrian Austin module_i2c_driver(cs35l32_i2c_driver);
580eef5bb24SBrian Austin
581eef5bb24SBrian Austin MODULE_DESCRIPTION("ASoC CS35L32 driver");
582eef5bb24SBrian Austin MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, <brian.austin@cirrus.com>");
583eef5bb24SBrian Austin MODULE_LICENSE("GPL");
584