1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * max9850.c -- codec driver for max9850
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (C) 2011 taskit GmbH
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun * Author: Christian Glindkamp <christian.glindkamp@taskit.de>
8*4882a593Smuzhiyun *
9*4882a593Smuzhiyun * Initial development of this code was funded by
10*4882a593Smuzhiyun * MICRONIC Computer Systeme GmbH, https://www.mcsberlin.de/
11*4882a593Smuzhiyun */
12*4882a593Smuzhiyun
13*4882a593Smuzhiyun #include <linux/module.h>
14*4882a593Smuzhiyun #include <linux/init.h>
15*4882a593Smuzhiyun #include <linux/i2c.h>
16*4882a593Smuzhiyun #include <linux/regmap.h>
17*4882a593Smuzhiyun #include <linux/slab.h>
18*4882a593Smuzhiyun #include <sound/pcm.h>
19*4882a593Smuzhiyun #include <sound/pcm_params.h>
20*4882a593Smuzhiyun #include <sound/soc.h>
21*4882a593Smuzhiyun #include <sound/tlv.h>
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun #include "max9850.h"
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun struct max9850_priv {
26*4882a593Smuzhiyun struct regmap *regmap;
27*4882a593Smuzhiyun unsigned int sysclk;
28*4882a593Smuzhiyun };
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun /* these registers are not used at the moment but provided for the sake of
31*4882a593Smuzhiyun * completeness */
max9850_volatile_register(struct device * dev,unsigned int reg)32*4882a593Smuzhiyun static bool max9850_volatile_register(struct device *dev, unsigned int reg)
33*4882a593Smuzhiyun {
34*4882a593Smuzhiyun switch (reg) {
35*4882a593Smuzhiyun case MAX9850_STATUSA:
36*4882a593Smuzhiyun case MAX9850_STATUSB:
37*4882a593Smuzhiyun return true;
38*4882a593Smuzhiyun default:
39*4882a593Smuzhiyun return false;
40*4882a593Smuzhiyun }
41*4882a593Smuzhiyun }
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun static const struct regmap_config max9850_regmap = {
44*4882a593Smuzhiyun .reg_bits = 8,
45*4882a593Smuzhiyun .val_bits = 8,
46*4882a593Smuzhiyun
47*4882a593Smuzhiyun .max_register = MAX9850_DIGITAL_AUDIO,
48*4882a593Smuzhiyun .volatile_reg = max9850_volatile_register,
49*4882a593Smuzhiyun .cache_type = REGCACHE_RBTREE,
50*4882a593Smuzhiyun };
51*4882a593Smuzhiyun
52*4882a593Smuzhiyun static const DECLARE_TLV_DB_RANGE(max9850_tlv,
53*4882a593Smuzhiyun 0x18, 0x1f, TLV_DB_SCALE_ITEM(-7450, 400, 0),
54*4882a593Smuzhiyun 0x20, 0x33, TLV_DB_SCALE_ITEM(-4150, 200, 0),
55*4882a593Smuzhiyun 0x34, 0x37, TLV_DB_SCALE_ITEM(-150, 100, 0),
56*4882a593Smuzhiyun 0x38, 0x3f, TLV_DB_SCALE_ITEM(250, 50, 0)
57*4882a593Smuzhiyun );
58*4882a593Smuzhiyun
59*4882a593Smuzhiyun static const struct snd_kcontrol_new max9850_controls[] = {
60*4882a593Smuzhiyun SOC_SINGLE_TLV("Headphone Volume", MAX9850_VOLUME, 0, 0x3f, 1, max9850_tlv),
61*4882a593Smuzhiyun SOC_SINGLE("Headphone Switch", MAX9850_VOLUME, 7, 1, 1),
62*4882a593Smuzhiyun SOC_SINGLE("Mono Switch", MAX9850_GENERAL_PURPOSE, 2, 1, 0),
63*4882a593Smuzhiyun };
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun static const struct snd_kcontrol_new max9850_mixer_controls[] = {
66*4882a593Smuzhiyun SOC_DAPM_SINGLE("Line In Switch", MAX9850_ENABLE, 1, 1, 0),
67*4882a593Smuzhiyun };
68*4882a593Smuzhiyun
69*4882a593Smuzhiyun static const struct snd_soc_dapm_widget max9850_dapm_widgets[] = {
70*4882a593Smuzhiyun SND_SOC_DAPM_SUPPLY("Charge Pump 1", MAX9850_ENABLE, 4, 0, NULL, 0),
71*4882a593Smuzhiyun SND_SOC_DAPM_SUPPLY("Charge Pump 2", MAX9850_ENABLE, 5, 0, NULL, 0),
72*4882a593Smuzhiyun SND_SOC_DAPM_SUPPLY("MCLK", MAX9850_ENABLE, 6, 0, NULL, 0),
73*4882a593Smuzhiyun SND_SOC_DAPM_SUPPLY("SHDN", MAX9850_ENABLE, 7, 0, NULL, 0),
74*4882a593Smuzhiyun SND_SOC_DAPM_MIXER_NAMED_CTL("Output Mixer", MAX9850_ENABLE, 2, 0,
75*4882a593Smuzhiyun &max9850_mixer_controls[0],
76*4882a593Smuzhiyun ARRAY_SIZE(max9850_mixer_controls)),
77*4882a593Smuzhiyun SND_SOC_DAPM_PGA("Headphone Output", MAX9850_ENABLE, 3, 0, NULL, 0),
78*4882a593Smuzhiyun SND_SOC_DAPM_DAC("DAC", "HiFi Playback", MAX9850_ENABLE, 0, 0),
79*4882a593Smuzhiyun SND_SOC_DAPM_OUTPUT("OUTL"),
80*4882a593Smuzhiyun SND_SOC_DAPM_OUTPUT("HPL"),
81*4882a593Smuzhiyun SND_SOC_DAPM_OUTPUT("OUTR"),
82*4882a593Smuzhiyun SND_SOC_DAPM_OUTPUT("HPR"),
83*4882a593Smuzhiyun SND_SOC_DAPM_MIXER("Line Input", SND_SOC_NOPM, 0, 0, NULL, 0),
84*4882a593Smuzhiyun SND_SOC_DAPM_INPUT("INL"),
85*4882a593Smuzhiyun SND_SOC_DAPM_INPUT("INR"),
86*4882a593Smuzhiyun };
87*4882a593Smuzhiyun
88*4882a593Smuzhiyun static const struct snd_soc_dapm_route max9850_dapm_routes[] = {
89*4882a593Smuzhiyun /* output mixer */
90*4882a593Smuzhiyun {"Output Mixer", NULL, "DAC"},
91*4882a593Smuzhiyun {"Output Mixer", "Line In Switch", "Line Input"},
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun /* outputs */
94*4882a593Smuzhiyun {"Headphone Output", NULL, "Output Mixer"},
95*4882a593Smuzhiyun {"HPL", NULL, "Headphone Output"},
96*4882a593Smuzhiyun {"HPR", NULL, "Headphone Output"},
97*4882a593Smuzhiyun {"OUTL", NULL, "Output Mixer"},
98*4882a593Smuzhiyun {"OUTR", NULL, "Output Mixer"},
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun /* inputs */
101*4882a593Smuzhiyun {"Line Input", NULL, "INL"},
102*4882a593Smuzhiyun {"Line Input", NULL, "INR"},
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun /* supplies */
105*4882a593Smuzhiyun {"Output Mixer", NULL, "Charge Pump 1"},
106*4882a593Smuzhiyun {"Output Mixer", NULL, "Charge Pump 2"},
107*4882a593Smuzhiyun {"Output Mixer", NULL, "SHDN"},
108*4882a593Smuzhiyun {"DAC", NULL, "MCLK"},
109*4882a593Smuzhiyun };
110*4882a593Smuzhiyun
max9850_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)111*4882a593Smuzhiyun static int max9850_hw_params(struct snd_pcm_substream *substream,
112*4882a593Smuzhiyun struct snd_pcm_hw_params *params,
113*4882a593Smuzhiyun struct snd_soc_dai *dai)
114*4882a593Smuzhiyun {
115*4882a593Smuzhiyun struct snd_soc_component *component = dai->component;
116*4882a593Smuzhiyun struct max9850_priv *max9850 = snd_soc_component_get_drvdata(component);
117*4882a593Smuzhiyun u64 lrclk_div;
118*4882a593Smuzhiyun u8 sf, da;
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun if (!max9850->sysclk)
121*4882a593Smuzhiyun return -EINVAL;
122*4882a593Smuzhiyun
123*4882a593Smuzhiyun /* lrclk_div = 2^22 * rate / iclk with iclk = mclk / sf */
124*4882a593Smuzhiyun sf = (snd_soc_component_read(component, MAX9850_CLOCK) >> 2) + 1;
125*4882a593Smuzhiyun lrclk_div = (1 << 22);
126*4882a593Smuzhiyun lrclk_div *= params_rate(params);
127*4882a593Smuzhiyun lrclk_div *= sf;
128*4882a593Smuzhiyun do_div(lrclk_div, max9850->sysclk);
129*4882a593Smuzhiyun
130*4882a593Smuzhiyun snd_soc_component_write(component, MAX9850_LRCLK_MSB, (lrclk_div >> 8) & 0x7f);
131*4882a593Smuzhiyun snd_soc_component_write(component, MAX9850_LRCLK_LSB, lrclk_div & 0xff);
132*4882a593Smuzhiyun
133*4882a593Smuzhiyun switch (params_width(params)) {
134*4882a593Smuzhiyun case 16:
135*4882a593Smuzhiyun da = 0;
136*4882a593Smuzhiyun break;
137*4882a593Smuzhiyun case 20:
138*4882a593Smuzhiyun da = 0x2;
139*4882a593Smuzhiyun break;
140*4882a593Smuzhiyun case 24:
141*4882a593Smuzhiyun da = 0x3;
142*4882a593Smuzhiyun break;
143*4882a593Smuzhiyun default:
144*4882a593Smuzhiyun return -EINVAL;
145*4882a593Smuzhiyun }
146*4882a593Smuzhiyun snd_soc_component_update_bits(component, MAX9850_DIGITAL_AUDIO, 0x3, da);
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun return 0;
149*4882a593Smuzhiyun }
150*4882a593Smuzhiyun
max9850_set_dai_sysclk(struct snd_soc_dai * codec_dai,int clk_id,unsigned int freq,int dir)151*4882a593Smuzhiyun static int max9850_set_dai_sysclk(struct snd_soc_dai *codec_dai,
152*4882a593Smuzhiyun int clk_id, unsigned int freq, int dir)
153*4882a593Smuzhiyun {
154*4882a593Smuzhiyun struct snd_soc_component *component = codec_dai->component;
155*4882a593Smuzhiyun struct max9850_priv *max9850 = snd_soc_component_get_drvdata(component);
156*4882a593Smuzhiyun
157*4882a593Smuzhiyun /* calculate mclk -> iclk divider */
158*4882a593Smuzhiyun if (freq <= 13000000)
159*4882a593Smuzhiyun snd_soc_component_write(component, MAX9850_CLOCK, 0x0);
160*4882a593Smuzhiyun else if (freq <= 26000000)
161*4882a593Smuzhiyun snd_soc_component_write(component, MAX9850_CLOCK, 0x4);
162*4882a593Smuzhiyun else if (freq <= 40000000)
163*4882a593Smuzhiyun snd_soc_component_write(component, MAX9850_CLOCK, 0x8);
164*4882a593Smuzhiyun else
165*4882a593Smuzhiyun return -EINVAL;
166*4882a593Smuzhiyun
167*4882a593Smuzhiyun max9850->sysclk = freq;
168*4882a593Smuzhiyun return 0;
169*4882a593Smuzhiyun }
170*4882a593Smuzhiyun
max9850_set_dai_fmt(struct snd_soc_dai * codec_dai,unsigned int fmt)171*4882a593Smuzhiyun static int max9850_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
172*4882a593Smuzhiyun {
173*4882a593Smuzhiyun struct snd_soc_component *component = codec_dai->component;
174*4882a593Smuzhiyun u8 da = 0;
175*4882a593Smuzhiyun
176*4882a593Smuzhiyun /* set master/slave audio interface */
177*4882a593Smuzhiyun switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
178*4882a593Smuzhiyun case SND_SOC_DAIFMT_CBM_CFM:
179*4882a593Smuzhiyun da |= MAX9850_MASTER;
180*4882a593Smuzhiyun break;
181*4882a593Smuzhiyun case SND_SOC_DAIFMT_CBS_CFS:
182*4882a593Smuzhiyun break;
183*4882a593Smuzhiyun default:
184*4882a593Smuzhiyun return -EINVAL;
185*4882a593Smuzhiyun }
186*4882a593Smuzhiyun
187*4882a593Smuzhiyun /* interface format */
188*4882a593Smuzhiyun switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
189*4882a593Smuzhiyun case SND_SOC_DAIFMT_I2S:
190*4882a593Smuzhiyun da |= MAX9850_DLY;
191*4882a593Smuzhiyun break;
192*4882a593Smuzhiyun case SND_SOC_DAIFMT_RIGHT_J:
193*4882a593Smuzhiyun da |= MAX9850_RTJ;
194*4882a593Smuzhiyun break;
195*4882a593Smuzhiyun case SND_SOC_DAIFMT_LEFT_J:
196*4882a593Smuzhiyun break;
197*4882a593Smuzhiyun default:
198*4882a593Smuzhiyun return -EINVAL;
199*4882a593Smuzhiyun }
200*4882a593Smuzhiyun
201*4882a593Smuzhiyun /* clock inversion */
202*4882a593Smuzhiyun switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
203*4882a593Smuzhiyun case SND_SOC_DAIFMT_NB_NF:
204*4882a593Smuzhiyun break;
205*4882a593Smuzhiyun case SND_SOC_DAIFMT_IB_IF:
206*4882a593Smuzhiyun da |= MAX9850_BCINV | MAX9850_INV;
207*4882a593Smuzhiyun break;
208*4882a593Smuzhiyun case SND_SOC_DAIFMT_IB_NF:
209*4882a593Smuzhiyun da |= MAX9850_BCINV;
210*4882a593Smuzhiyun break;
211*4882a593Smuzhiyun case SND_SOC_DAIFMT_NB_IF:
212*4882a593Smuzhiyun da |= MAX9850_INV;
213*4882a593Smuzhiyun break;
214*4882a593Smuzhiyun default:
215*4882a593Smuzhiyun return -EINVAL;
216*4882a593Smuzhiyun }
217*4882a593Smuzhiyun
218*4882a593Smuzhiyun /* set da */
219*4882a593Smuzhiyun snd_soc_component_write(component, MAX9850_DIGITAL_AUDIO, da);
220*4882a593Smuzhiyun
221*4882a593Smuzhiyun return 0;
222*4882a593Smuzhiyun }
223*4882a593Smuzhiyun
max9850_set_bias_level(struct snd_soc_component * component,enum snd_soc_bias_level level)224*4882a593Smuzhiyun static int max9850_set_bias_level(struct snd_soc_component *component,
225*4882a593Smuzhiyun enum snd_soc_bias_level level)
226*4882a593Smuzhiyun {
227*4882a593Smuzhiyun struct max9850_priv *max9850 = snd_soc_component_get_drvdata(component);
228*4882a593Smuzhiyun int ret;
229*4882a593Smuzhiyun
230*4882a593Smuzhiyun switch (level) {
231*4882a593Smuzhiyun case SND_SOC_BIAS_ON:
232*4882a593Smuzhiyun break;
233*4882a593Smuzhiyun case SND_SOC_BIAS_PREPARE:
234*4882a593Smuzhiyun break;
235*4882a593Smuzhiyun case SND_SOC_BIAS_STANDBY:
236*4882a593Smuzhiyun if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
237*4882a593Smuzhiyun ret = regcache_sync(max9850->regmap);
238*4882a593Smuzhiyun if (ret) {
239*4882a593Smuzhiyun dev_err(component->dev,
240*4882a593Smuzhiyun "Failed to sync cache: %d\n", ret);
241*4882a593Smuzhiyun return ret;
242*4882a593Smuzhiyun }
243*4882a593Smuzhiyun }
244*4882a593Smuzhiyun break;
245*4882a593Smuzhiyun case SND_SOC_BIAS_OFF:
246*4882a593Smuzhiyun break;
247*4882a593Smuzhiyun }
248*4882a593Smuzhiyun return 0;
249*4882a593Smuzhiyun }
250*4882a593Smuzhiyun
251*4882a593Smuzhiyun #define MAX9850_RATES SNDRV_PCM_RATE_8000_48000
252*4882a593Smuzhiyun
253*4882a593Smuzhiyun #define MAX9850_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
254*4882a593Smuzhiyun SNDRV_PCM_FMTBIT_S24_LE)
255*4882a593Smuzhiyun
256*4882a593Smuzhiyun static const struct snd_soc_dai_ops max9850_dai_ops = {
257*4882a593Smuzhiyun .hw_params = max9850_hw_params,
258*4882a593Smuzhiyun .set_sysclk = max9850_set_dai_sysclk,
259*4882a593Smuzhiyun .set_fmt = max9850_set_dai_fmt,
260*4882a593Smuzhiyun };
261*4882a593Smuzhiyun
262*4882a593Smuzhiyun static struct snd_soc_dai_driver max9850_dai = {
263*4882a593Smuzhiyun .name = "max9850-hifi",
264*4882a593Smuzhiyun .playback = {
265*4882a593Smuzhiyun .stream_name = "Playback",
266*4882a593Smuzhiyun .channels_min = 1,
267*4882a593Smuzhiyun .channels_max = 2,
268*4882a593Smuzhiyun .rates = MAX9850_RATES,
269*4882a593Smuzhiyun .formats = MAX9850_FORMATS
270*4882a593Smuzhiyun },
271*4882a593Smuzhiyun .ops = &max9850_dai_ops,
272*4882a593Smuzhiyun };
273*4882a593Smuzhiyun
max9850_probe(struct snd_soc_component * component)274*4882a593Smuzhiyun static int max9850_probe(struct snd_soc_component *component)
275*4882a593Smuzhiyun {
276*4882a593Smuzhiyun /* enable zero-detect */
277*4882a593Smuzhiyun snd_soc_component_update_bits(component, MAX9850_GENERAL_PURPOSE, 1, 1);
278*4882a593Smuzhiyun /* enable slew-rate control */
279*4882a593Smuzhiyun snd_soc_component_update_bits(component, MAX9850_VOLUME, 0x40, 0x40);
280*4882a593Smuzhiyun /* set slew-rate 125ms */
281*4882a593Smuzhiyun snd_soc_component_update_bits(component, MAX9850_CHARGE_PUMP, 0xff, 0xc0);
282*4882a593Smuzhiyun
283*4882a593Smuzhiyun return 0;
284*4882a593Smuzhiyun }
285*4882a593Smuzhiyun
286*4882a593Smuzhiyun static const struct snd_soc_component_driver soc_component_dev_max9850 = {
287*4882a593Smuzhiyun .probe = max9850_probe,
288*4882a593Smuzhiyun .set_bias_level = max9850_set_bias_level,
289*4882a593Smuzhiyun .controls = max9850_controls,
290*4882a593Smuzhiyun .num_controls = ARRAY_SIZE(max9850_controls),
291*4882a593Smuzhiyun .dapm_widgets = max9850_dapm_widgets,
292*4882a593Smuzhiyun .num_dapm_widgets = ARRAY_SIZE(max9850_dapm_widgets),
293*4882a593Smuzhiyun .dapm_routes = max9850_dapm_routes,
294*4882a593Smuzhiyun .num_dapm_routes = ARRAY_SIZE(max9850_dapm_routes),
295*4882a593Smuzhiyun .suspend_bias_off = 1,
296*4882a593Smuzhiyun .idle_bias_on = 1,
297*4882a593Smuzhiyun .use_pmdown_time = 1,
298*4882a593Smuzhiyun .endianness = 1,
299*4882a593Smuzhiyun .non_legacy_dai_naming = 1,
300*4882a593Smuzhiyun };
301*4882a593Smuzhiyun
max9850_i2c_probe(struct i2c_client * i2c,const struct i2c_device_id * id)302*4882a593Smuzhiyun static int max9850_i2c_probe(struct i2c_client *i2c,
303*4882a593Smuzhiyun const struct i2c_device_id *id)
304*4882a593Smuzhiyun {
305*4882a593Smuzhiyun struct max9850_priv *max9850;
306*4882a593Smuzhiyun int ret;
307*4882a593Smuzhiyun
308*4882a593Smuzhiyun max9850 = devm_kzalloc(&i2c->dev, sizeof(struct max9850_priv),
309*4882a593Smuzhiyun GFP_KERNEL);
310*4882a593Smuzhiyun if (max9850 == NULL)
311*4882a593Smuzhiyun return -ENOMEM;
312*4882a593Smuzhiyun
313*4882a593Smuzhiyun max9850->regmap = devm_regmap_init_i2c(i2c, &max9850_regmap);
314*4882a593Smuzhiyun if (IS_ERR(max9850->regmap))
315*4882a593Smuzhiyun return PTR_ERR(max9850->regmap);
316*4882a593Smuzhiyun
317*4882a593Smuzhiyun i2c_set_clientdata(i2c, max9850);
318*4882a593Smuzhiyun
319*4882a593Smuzhiyun ret = devm_snd_soc_register_component(&i2c->dev,
320*4882a593Smuzhiyun &soc_component_dev_max9850, &max9850_dai, 1);
321*4882a593Smuzhiyun return ret;
322*4882a593Smuzhiyun }
323*4882a593Smuzhiyun
324*4882a593Smuzhiyun static const struct i2c_device_id max9850_i2c_id[] = {
325*4882a593Smuzhiyun { "max9850", 0 },
326*4882a593Smuzhiyun { }
327*4882a593Smuzhiyun };
328*4882a593Smuzhiyun MODULE_DEVICE_TABLE(i2c, max9850_i2c_id);
329*4882a593Smuzhiyun
330*4882a593Smuzhiyun static struct i2c_driver max9850_i2c_driver = {
331*4882a593Smuzhiyun .driver = {
332*4882a593Smuzhiyun .name = "max9850",
333*4882a593Smuzhiyun },
334*4882a593Smuzhiyun .probe = max9850_i2c_probe,
335*4882a593Smuzhiyun .id_table = max9850_i2c_id,
336*4882a593Smuzhiyun };
337*4882a593Smuzhiyun
338*4882a593Smuzhiyun module_i2c_driver(max9850_i2c_driver);
339*4882a593Smuzhiyun
340*4882a593Smuzhiyun MODULE_AUTHOR("Christian Glindkamp <christian.glindkamp@taskit.de>");
341*4882a593Smuzhiyun MODULE_DESCRIPTION("ASoC MAX9850 codec driver");
342*4882a593Smuzhiyun MODULE_LICENSE("GPL");
343