blob: 7a08e2c70e17691668189e5a1ed760fba8aba3f3 [file] [log] [blame]
anish kumar805d1322016-02-17 17:28:49 -08001/*
2 * max9867.c -- max9867 ALSA SoC Audio driver
3 *
4 * Copyright 2013-15 Maxim Integrated Products
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
10
11#include <linux/delay.h>
12#include <linux/i2c.h>
13#include <linux/module.h>
14#include <linux/regmap.h>
15#include <sound/pcm_params.h>
16#include <sound/soc.h>
17#include <sound/tlv.h>
18#include "max9867.h"
19
20static const char *const max9867_spmode[] = {
21 "Stereo Diff", "Mono Diff",
22 "Stereo Cap", "Mono Cap",
23 "Stereo Single", "Mono Single",
24 "Stereo Single Fast", "Mono Single Fast"
25};
anish kumar805d1322016-02-17 17:28:49 -080026static const char *const max9867_filter_text[] = {"IIR", "FIR"};
27
28static SOC_ENUM_SINGLE_DECL(max9867_filter, MAX9867_CODECFLTR, 7,
29 max9867_filter_text);
30static SOC_ENUM_SINGLE_DECL(max9867_spkmode, MAX9867_MODECONFIG, 0,
31 max9867_spmode);
Ladislav Michlbc2610a2018-12-04 19:21:33 +010032static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(max9867_master_tlv,
33 0, 2, TLV_DB_SCALE_ITEM(-8600, 200, 1),
34 3, 17, TLV_DB_SCALE_ITEM(-7800, 400, 0),
35 18, 25, TLV_DB_SCALE_ITEM(-2000, 200, 0),
36 26, 34, TLV_DB_SCALE_ITEM( -500, 100, 0),
37 35, 40, TLV_DB_SCALE_ITEM( 350, 50, 0),
38);
39static DECLARE_TLV_DB_SCALE(max9867_mic_tlv, 0, 100, 0);
40static DECLARE_TLV_DB_SCALE(max9867_line_tlv, -600, 200, 0);
41static DECLARE_TLV_DB_SCALE(max9867_adc_tlv, -1200, 100, 0);
42static DECLARE_TLV_DB_SCALE(max9867_dac_tlv, -1500, 100, 0);
43static DECLARE_TLV_DB_SCALE(max9867_dacboost_tlv, 0, 600, 0);
44static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(max9867_micboost_tlv,
45 0, 2, TLV_DB_SCALE_ITEM(-2000, 2000, 1),
46 3, 3, TLV_DB_SCALE_ITEM(3000, 0, 0),
Takashi Sakamoto349fa182016-09-28 09:29:19 +090047);
anish kumar805d1322016-02-17 17:28:49 -080048
49static const struct snd_kcontrol_new max9867_snd_controls[] = {
Ladislav Michlbc2610a2018-12-04 19:21:33 +010050 SOC_DOUBLE_R_TLV("Master Playback Volume", MAX9867_LEFTVOL,
51 MAX9867_RIGHTVOL, 0, 41, 1, max9867_master_tlv),
52 SOC_DOUBLE_R_TLV("Line Capture Volume", MAX9867_LEFTLINELVL,
53 MAX9867_RIGHTLINELVL, 0, 15, 1, max9867_line_tlv),
54 SOC_DOUBLE_R_TLV("Mic Capture Volume", MAX9867_LEFTMICGAIN,
55 MAX9867_RIGHTMICGAIN, 0, 20, 1, max9867_mic_tlv),
56 SOC_DOUBLE_R_TLV("Mic Boost Capture Volume", MAX9867_LEFTMICGAIN,
57 MAX9867_RIGHTMICGAIN, 5, 4, 0, max9867_micboost_tlv),
58 SOC_SINGLE("Digital Sidetone Volume", MAX9867_SIDETONE, 0, 31, 1),
59 SOC_SINGLE_TLV("Digital Playback Volume", MAX9867_DACLEVEL, 0, 15, 1,
60 max9867_dac_tlv),
61 SOC_SINGLE_TLV("Digital Boost Playback Volume", MAX9867_DACLEVEL, 4, 3, 0,
62 max9867_dacboost_tlv),
63 SOC_DOUBLE_TLV("Digital Capture Volume", MAX9867_ADCLEVEL, 0, 4, 15, 1,
64 max9867_adc_tlv),
anish kumar805d1322016-02-17 17:28:49 -080065 SOC_ENUM("Speaker Mode", max9867_spkmode),
66 SOC_SINGLE("Volume Smoothing Switch", MAX9867_MODECONFIG, 6, 1, 0),
Ladislav Michlbc2610a2018-12-04 19:21:33 +010067 SOC_SINGLE("Line ZC Switch", MAX9867_MODECONFIG, 5, 1, 0),
anish kumar805d1322016-02-17 17:28:49 -080068 SOC_ENUM("DSP Filter", max9867_filter),
69};
70
Ladislav Michlbc2610a2018-12-04 19:21:33 +010071/* Input mixer */
72static const struct snd_kcontrol_new max9867_input_mixer_controls[] = {
73 SOC_DAPM_DOUBLE("Line Capture Switch", MAX9867_INPUTCONFIG, 7, 5, 1, 0),
74 SOC_DAPM_DOUBLE("Mic Capture Switch", MAX9867_INPUTCONFIG, 6, 4, 1, 0),
75};
anish kumar805d1322016-02-17 17:28:49 -080076
Ladislav Michlbc2610a2018-12-04 19:21:33 +010077/* Output mixer */
78static const struct snd_kcontrol_new max9867_output_mixer_controls[] = {
79 SOC_DAPM_DOUBLE_R("Line Bypass Switch",
80 MAX9867_LEFTLINELVL, MAX9867_RIGHTLINELVL, 6, 1, 1),
81};
anish kumar805d1322016-02-17 17:28:49 -080082
Ladislav Michlbc2610a2018-12-04 19:21:33 +010083/* Sidetone mixer */
84static const struct snd_kcontrol_new max9867_sidetone_mixer_controls[] = {
85 SOC_DAPM_DOUBLE("Sidetone Switch", MAX9867_SIDETONE, 6, 7, 1, 0),
86};
anish kumar805d1322016-02-17 17:28:49 -080087
Ladislav Michlbc2610a2018-12-04 19:21:33 +010088/* Line out switch */
89static const struct snd_kcontrol_new max9867_line_out_control =
90 SOC_DAPM_DOUBLE_R("Switch",
91 MAX9867_LEFTVOL, MAX9867_RIGHTVOL, 6, 1, 1);
92
anish kumar805d1322016-02-17 17:28:49 -080093
94static const struct snd_soc_dapm_widget max9867_dapm_widgets[] = {
Ladislav Michlbc2610a2018-12-04 19:21:33 +010095 SND_SOC_DAPM_INPUT("MICL"),
96 SND_SOC_DAPM_INPUT("MICR"),
97 SND_SOC_DAPM_INPUT("LINL"),
98 SND_SOC_DAPM_INPUT("LINR"),
anish kumar805d1322016-02-17 17:28:49 -080099
Ladislav Michlbc2610a2018-12-04 19:21:33 +0100100 SND_SOC_DAPM_PGA("Left Line Input", MAX9867_PWRMAN, 6, 0, NULL, 0),
101 SND_SOC_DAPM_PGA("Right Line Input", MAX9867_PWRMAN, 5, 0, NULL, 0),
102 SND_SOC_DAPM_MIXER_NAMED_CTL("Input Mixer", SND_SOC_NOPM, 0, 0,
103 max9867_input_mixer_controls,
104 ARRAY_SIZE(max9867_input_mixer_controls)),
105 SND_SOC_DAPM_ADC("ADCL", "HiFi Capture", MAX9867_PWRMAN, 1, 0),
106 SND_SOC_DAPM_ADC("ADCR", "HiFi Capture", MAX9867_PWRMAN, 0, 0),
anish kumar805d1322016-02-17 17:28:49 -0800107
Ladislav Michlbc2610a2018-12-04 19:21:33 +0100108 SND_SOC_DAPM_MIXER("Digital", SND_SOC_NOPM, 0, 0,
109 max9867_sidetone_mixer_controls,
110 ARRAY_SIZE(max9867_sidetone_mixer_controls)),
111 SND_SOC_DAPM_MIXER_NAMED_CTL("Output Mixer", SND_SOC_NOPM, 0, 0,
112 max9867_output_mixer_controls,
113 ARRAY_SIZE(max9867_output_mixer_controls)),
114 SND_SOC_DAPM_DAC("DACL", "HiFi Playback", MAX9867_PWRMAN, 3, 0),
115 SND_SOC_DAPM_DAC("DACR", "HiFi Playback", MAX9867_PWRMAN, 2, 0),
116 SND_SOC_DAPM_SWITCH("Master Playback", SND_SOC_NOPM, 0, 0,
117 &max9867_line_out_control),
118 SND_SOC_DAPM_OUTPUT("LOUT"),
119 SND_SOC_DAPM_OUTPUT("ROUT"),
anish kumar805d1322016-02-17 17:28:49 -0800120};
121
122static const struct snd_soc_dapm_route max9867_audio_map[] = {
Ladislav Michlbc2610a2018-12-04 19:21:33 +0100123 {"Left Line Input", NULL, "LINL"},
124 {"Right Line Input", NULL, "LINR"},
125 {"Input Mixer", "Mic Capture Switch", "MICL"},
126 {"Input Mixer", "Mic Capture Switch", "MICR"},
127 {"Input Mixer", "Line Capture Switch", "Left Line Input"},
128 {"Input Mixer", "Line Capture Switch", "Right Line Input"},
129 {"ADCL", NULL, "Input Mixer"},
130 {"ADCR", NULL, "Input Mixer"},
anish kumar805d1322016-02-17 17:28:49 -0800131
Ladislav Michlbc2610a2018-12-04 19:21:33 +0100132 {"Digital", "Sidetone Switch", "ADCL"},
133 {"Digital", "Sidetone Switch", "ADCR"},
134 {"DACL", NULL, "Digital"},
135 {"DACR", NULL, "Digital"},
136
137 {"Output Mixer", "Line Bypass Switch", "Left Line Input"},
138 {"Output Mixer", "Line Bypass Switch", "Right Line Input"},
139 {"Output Mixer", NULL, "DACL"},
140 {"Output Mixer", NULL, "DACR"},
141 {"Master Playback", "Switch", "Output Mixer"},
142 {"LOUT", NULL, "Master Playback"},
143 {"ROUT", NULL, "Master Playback"},
anish kumar805d1322016-02-17 17:28:49 -0800144};
145
Ladislav Michl715ee192018-12-04 19:21:04 +0100146static const unsigned int max9867_rates_44k1[] = {
147 11025, 22050, 44100,
anish kumar805d1322016-02-17 17:28:49 -0800148};
149
Ladislav Michl715ee192018-12-04 19:21:04 +0100150static const struct snd_pcm_hw_constraint_list max9867_constraints_44k1 = {
151 .list = max9867_rates_44k1,
152 .count = ARRAY_SIZE(max9867_rates_44k1),
anish kumar805d1322016-02-17 17:28:49 -0800153};
154
Ladislav Michl715ee192018-12-04 19:21:04 +0100155static const unsigned int max9867_rates_48k[] = {
156 8000, 16000, 32000, 48000,
157};
158
159static const struct snd_pcm_hw_constraint_list max9867_constraints_48k = {
160 .list = max9867_rates_48k,
161 .count = ARRAY_SIZE(max9867_rates_48k),
162};
163
164struct max9867_priv {
165 struct regmap *regmap;
166 const struct snd_pcm_hw_constraint_list *constraints;
167 unsigned int sysclk, pclk;
168 bool master, dsp_a;
169};
170
171static int max9867_startup(struct snd_pcm_substream *substream,
172 struct snd_soc_dai *dai)
anish kumar805d1322016-02-17 17:28:49 -0800173{
Ladislav Michl715ee192018-12-04 19:21:04 +0100174 struct max9867_priv *max9867 =
175 snd_soc_component_get_drvdata(dai->component);
anish kumar805d1322016-02-17 17:28:49 -0800176
Ladislav Michl715ee192018-12-04 19:21:04 +0100177 if (max9867->constraints)
178 snd_pcm_hw_constraint_list(substream->runtime, 0,
179 SNDRV_PCM_HW_PARAM_RATE, max9867->constraints);
anish kumar805d1322016-02-17 17:28:49 -0800180
Ladislav Michl715ee192018-12-04 19:21:04 +0100181 return 0;
anish kumar805d1322016-02-17 17:28:49 -0800182}
183
184static int max9867_dai_hw_params(struct snd_pcm_substream *substream,
185 struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
186{
Ladislav Michl715ee192018-12-04 19:21:04 +0100187 int value;
188 unsigned long int rate, ratio;
Kuninori Morimoto91880a62018-01-29 03:11:46 +0000189 struct snd_soc_component *component = dai->component;
190 struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component);
Ladislav Michl715ee192018-12-04 19:21:04 +0100191 unsigned int ni = DIV_ROUND_CLOSEST_ULL(96ULL * 0x10000 * params_rate(params),
192 max9867->pclk);
anish kumar805d1322016-02-17 17:28:49 -0800193
anish kumar805d1322016-02-17 17:28:49 -0800194 /* set up the ni value */
195 regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKHIGH,
Ladislav Michl715ee192018-12-04 19:21:04 +0100196 MAX9867_NI_HIGH_MASK, (0xFF00 & ni) >> 8);
anish kumar805d1322016-02-17 17:28:49 -0800197 regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKLOW,
Ladislav Michl715ee192018-12-04 19:21:04 +0100198 MAX9867_NI_LOW_MASK, 0x00FF & ni);
199 if (max9867->master) {
200 if (max9867->dsp_a) {
201 value = MAX9867_IFC1B_48X;
202 } else {
203 rate = params_rate(params) * 2 * params_width(params);
204 ratio = max9867->pclk / rate;
205 switch (params_width(params)) {
206 case 8:
207 case 16:
208 switch (ratio) {
209 case 2:
210 value = MAX9867_IFC1B_PCLK_2;
211 break;
212 case 4:
213 value = MAX9867_IFC1B_PCLK_4;
214 break;
215 case 8:
216 value = MAX9867_IFC1B_PCLK_8;
217 break;
218 case 16:
219 value = MAX9867_IFC1B_PCLK_16;
220 break;
221 default:
222 return -EINVAL;
223 }
224 break;
225 case 24:
226 value = MAX9867_IFC1B_48X;
227 break;
228 case 32:
229 value = MAX9867_IFC1B_64X;
230 break;
231 default:
232 return -EINVAL;
233 }
234 }
235 regmap_update_bits(max9867->regmap, MAX9867_IFC1B,
236 MAX9867_IFC1B_BCLK_MASK, value);
237 } else {
anish kumar805d1322016-02-17 17:28:49 -0800238 /*
239 * digital pll locks on to any externally supplied LRCLK signal
240 * and also enable rapid lock mode.
241 */
242 regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKLOW,
243 MAX9867_RAPID_LOCK, MAX9867_RAPID_LOCK);
244 regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKHIGH,
245 MAX9867_PLL, MAX9867_PLL);
anish kumar805d1322016-02-17 17:28:49 -0800246 }
247 return 0;
248}
249
anish kumar805d1322016-02-17 17:28:49 -0800250static int max9867_mute(struct snd_soc_dai *dai, int mute)
251{
Kuninori Morimoto91880a62018-01-29 03:11:46 +0000252 struct snd_soc_component *component = dai->component;
253 struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component);
anish kumar805d1322016-02-17 17:28:49 -0800254
Ladislav Michlbc2610a2018-12-04 19:21:33 +0100255 return regmap_update_bits(max9867->regmap, MAX9867_DACLEVEL,
256 1 << 6, !!mute << 6);
anish kumar805d1322016-02-17 17:28:49 -0800257}
258
259static int max9867_set_dai_sysclk(struct snd_soc_dai *codec_dai,
260 int clk_id, unsigned int freq, int dir)
261{
Kuninori Morimoto91880a62018-01-29 03:11:46 +0000262 struct snd_soc_component *component = codec_dai->component;
263 struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component);
anish kumar805d1322016-02-17 17:28:49 -0800264 int value = 0;
265
266 /* Set the prescaler based on the master clock frequency*/
267 if (freq >= 10000000 && freq <= 20000000) {
268 value |= MAX9867_PSCLK_10_20;
Ladislav Michl933662f2018-11-23 15:27:00 +0100269 max9867->pclk = freq;
anish kumar805d1322016-02-17 17:28:49 -0800270 } else if (freq >= 20000000 && freq <= 40000000) {
271 value |= MAX9867_PSCLK_20_40;
Ladislav Michl933662f2018-11-23 15:27:00 +0100272 max9867->pclk = freq / 2;
anish kumar805d1322016-02-17 17:28:49 -0800273 } else if (freq >= 40000000 && freq <= 60000000) {
274 value |= MAX9867_PSCLK_40_60;
Ladislav Michl933662f2018-11-23 15:27:00 +0100275 max9867->pclk = freq / 4;
anish kumar805d1322016-02-17 17:28:49 -0800276 } else {
Ladislav Michl8b9c7162018-03-01 15:19:53 +0100277 dev_err(component->dev,
278 "Invalid clock frequency %uHz (required 10-60MHz)\n",
279 freq);
anish kumar805d1322016-02-17 17:28:49 -0800280 return -EINVAL;
281 }
Ladislav Michl715ee192018-12-04 19:21:04 +0100282 if (freq % 48000 == 0)
283 max9867->constraints = &max9867_constraints_48k;
284 else if (freq % 44100 == 0)
285 max9867->constraints = &max9867_constraints_44k1;
286 else
287 dev_warn(component->dev,
288 "Unable to set exact rate with %uHz clock frequency\n",
289 freq);
anish kumar805d1322016-02-17 17:28:49 -0800290 max9867->sysclk = freq;
Ladislav Michl715ee192018-12-04 19:21:04 +0100291 value = value << MAX9867_PSCLK_SHIFT;
anish kumar805d1322016-02-17 17:28:49 -0800292 /* exact integer mode is not supported */
293 value &= ~MAX9867_FREQ_MASK;
294 regmap_update_bits(max9867->regmap, MAX9867_SYSCLK,
295 MAX9867_PSCLK_MASK, value);
296 return 0;
297}
298
299static int max9867_dai_set_fmt(struct snd_soc_dai *codec_dai,
300 unsigned int fmt)
301{
Kuninori Morimoto91880a62018-01-29 03:11:46 +0000302 struct snd_soc_component *component = codec_dai->component;
303 struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component);
Ladislav Michl715ee192018-12-04 19:21:04 +0100304 u8 iface1A, iface1B;
anish kumar805d1322016-02-17 17:28:49 -0800305
306 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
307 case SND_SOC_DAIFMT_CBM_CFM:
Ladislav Michl715ee192018-12-04 19:21:04 +0100308 max9867->master = true;
309 iface1A = MAX9867_MASTER;
310 iface1B = MAX9867_IFC1B_48X;
anish kumar805d1322016-02-17 17:28:49 -0800311 break;
312 case SND_SOC_DAIFMT_CBS_CFS:
Ladislav Michl715ee192018-12-04 19:21:04 +0100313 max9867->master = false;
314 iface1A = iface1B = 0;
anish kumar805d1322016-02-17 17:28:49 -0800315 break;
316 default:
317 return -EINVAL;
318 }
319
Ladislav Michl79e13972018-03-01 15:21:30 +0100320 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
321 case SND_SOC_DAIFMT_I2S:
Ladislav Michl715ee192018-12-04 19:21:04 +0100322 max9867->dsp_a = false;
Ladislav Michl79e13972018-03-01 15:21:30 +0100323 iface1A |= MAX9867_I2S_DLY;
324 break;
325 case SND_SOC_DAIFMT_DSP_A:
Ladislav Michl715ee192018-12-04 19:21:04 +0100326 max9867->dsp_a = true;
Ladislav Michl79e13972018-03-01 15:21:30 +0100327 iface1A |= MAX9867_TDM_MODE | MAX9867_SDOUT_HIZ;
328 break;
329 default:
330 return -EINVAL;
331 }
anish kumar805d1322016-02-17 17:28:49 -0800332
333 /* Clock inversion bits, BCI and WCI */
334 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
335 case SND_SOC_DAIFMT_NB_NF:
336 break;
337 case SND_SOC_DAIFMT_IB_IF:
338 iface1A |= MAX9867_WCI_MODE | MAX9867_BCI_MODE;
339 break;
340 case SND_SOC_DAIFMT_IB_NF:
341 iface1A |= MAX9867_BCI_MODE;
342 break;
343 case SND_SOC_DAIFMT_NB_IF:
344 iface1A |= MAX9867_WCI_MODE;
345 break;
346 default:
347 return -EINVAL;
348 }
349
Vinod Koul9fe78b22016-12-08 23:01:32 +0530350 regmap_write(max9867->regmap, MAX9867_IFC1A, iface1A);
351 regmap_write(max9867->regmap, MAX9867_IFC1B, iface1B);
Ladislav Michl715ee192018-12-04 19:21:04 +0100352
anish kumar805d1322016-02-17 17:28:49 -0800353 return 0;
354}
355
Arvind Yadaveb59d732017-08-18 17:35:59 +0530356static const struct snd_soc_dai_ops max9867_dai_ops = {
anish kumar805d1322016-02-17 17:28:49 -0800357 .set_sysclk = max9867_set_dai_sysclk,
Ladislav Michl715ee192018-12-04 19:21:04 +0100358 .set_fmt = max9867_dai_set_fmt,
anish kumar805d1322016-02-17 17:28:49 -0800359 .digital_mute = max9867_mute,
Ladislav Michl715ee192018-12-04 19:21:04 +0100360 .startup = max9867_startup,
361 .hw_params = max9867_dai_hw_params,
anish kumar805d1322016-02-17 17:28:49 -0800362};
363
anish kumar805d1322016-02-17 17:28:49 -0800364static struct snd_soc_dai_driver max9867_dai[] = {
365 {
366 .name = "max9867-aif1",
367 .playback = {
368 .stream_name = "HiFi Playback",
Ladislav Michle6ceb922018-03-01 15:20:56 +0100369 .channels_min = 2,
anish kumar805d1322016-02-17 17:28:49 -0800370 .channels_max = 2,
Ladislav Michl715ee192018-12-04 19:21:04 +0100371 .rates = SNDRV_PCM_RATE_8000_48000,
372 .formats = SNDRV_PCM_FMTBIT_S16_LE,
anish kumar805d1322016-02-17 17:28:49 -0800373 },
374 .capture = {
375 .stream_name = "HiFi Capture",
Ladislav Michle6ceb922018-03-01 15:20:56 +0100376 .channels_min = 2,
anish kumar805d1322016-02-17 17:28:49 -0800377 .channels_max = 2,
Ladislav Michl715ee192018-12-04 19:21:04 +0100378 .rates = SNDRV_PCM_RATE_8000_48000,
379 .formats = SNDRV_PCM_FMTBIT_S16_LE,
anish kumar805d1322016-02-17 17:28:49 -0800380 },
381 .ops = &max9867_dai_ops,
Ladislav Michle6ceb922018-03-01 15:20:56 +0100382 .symmetric_rates = 1,
anish kumar805d1322016-02-17 17:28:49 -0800383 }
384};
385
Ladislav Michl29f58ff2018-12-04 19:19:51 +0100386#ifdef CONFIG_PM
387static int max9867_suspend(struct snd_soc_component *component)
anish kumar805d1322016-02-17 17:28:49 -0800388{
Ladislav Michl29f58ff2018-12-04 19:19:51 +0100389 snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF);
anish kumar805d1322016-02-17 17:28:49 -0800390
anish kumar805d1322016-02-17 17:28:49 -0800391 return 0;
392}
393
Ladislav Michl29f58ff2018-12-04 19:19:51 +0100394static int max9867_resume(struct snd_soc_component *component)
anish kumar805d1322016-02-17 17:28:49 -0800395{
Ladislav Michl29f58ff2018-12-04 19:19:51 +0100396 snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY);
anish kumar805d1322016-02-17 17:28:49 -0800397
anish kumar805d1322016-02-17 17:28:49 -0800398 return 0;
399}
Ladislav Michl29f58ff2018-12-04 19:19:51 +0100400#else
401#define max9867_suspend NULL
402#define max9867_resume NULL
anish kumar805d1322016-02-17 17:28:49 -0800403#endif
404
Ladislav Michl29f58ff2018-12-04 19:19:51 +0100405static int max9867_set_bias_level(struct snd_soc_component *component,
406 enum snd_soc_bias_level level)
407{
408 int err;
409 struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component);
410
411 switch (level) {
412 case SND_SOC_BIAS_STANDBY:
413 if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
414 err = regcache_sync(max9867->regmap);
415 if (err)
416 return err;
417
418 err = regmap_update_bits(max9867->regmap, MAX9867_PWRMAN,
419 MAX9867_SHTDOWN, MAX9867_SHTDOWN);
420 if (err)
421 return err;
422 }
423 break;
424 case SND_SOC_BIAS_OFF:
425 err = regmap_update_bits(max9867->regmap, MAX9867_PWRMAN,
426 MAX9867_SHTDOWN, 0);
427 if (err)
428 return err;
429
430 regcache_mark_dirty(max9867->regmap);
431 break;
432 default:
433 break;
434 }
435
436 return 0;
437}
438
Kuninori Morimoto91880a62018-01-29 03:11:46 +0000439static const struct snd_soc_component_driver max9867_component = {
Kuninori Morimoto91880a62018-01-29 03:11:46 +0000440 .controls = max9867_snd_controls,
441 .num_controls = ARRAY_SIZE(max9867_snd_controls),
442 .dapm_routes = max9867_audio_map,
443 .num_dapm_routes = ARRAY_SIZE(max9867_audio_map),
444 .dapm_widgets = max9867_dapm_widgets,
445 .num_dapm_widgets = ARRAY_SIZE(max9867_dapm_widgets),
Ladislav Michl29f58ff2018-12-04 19:19:51 +0100446 .suspend = max9867_suspend,
447 .resume = max9867_resume,
448 .set_bias_level = max9867_set_bias_level,
Kuninori Morimoto91880a62018-01-29 03:11:46 +0000449 .idle_bias_on = 1,
450 .use_pmdown_time = 1,
451 .endianness = 1,
452 .non_legacy_dai_naming = 1,
anish kumar805d1322016-02-17 17:28:49 -0800453};
454
455static bool max9867_volatile_register(struct device *dev, unsigned int reg)
456{
457 switch (reg) {
458 case MAX9867_STATUS:
459 case MAX9867_JACKSTATUS:
460 case MAX9867_AUXHIGH:
461 case MAX9867_AUXLOW:
462 return true;
463 default:
464 return false;
465 }
466}
467
Axel Lin250a99e2016-02-20 10:54:11 +0800468static const struct reg_default max9867_reg[] = {
anish kumar805d1322016-02-17 17:28:49 -0800469 { 0x04, 0x00 },
470 { 0x05, 0x00 },
471 { 0x06, 0x00 },
472 { 0x07, 0x00 },
473 { 0x08, 0x00 },
474 { 0x09, 0x00 },
475 { 0x0A, 0x00 },
476 { 0x0B, 0x00 },
477 { 0x0C, 0x00 },
478 { 0x0D, 0x00 },
Ladislav Michlbc2610a2018-12-04 19:21:33 +0100479 { 0x0E, 0x40 },
480 { 0x0F, 0x40 },
anish kumar805d1322016-02-17 17:28:49 -0800481 { 0x10, 0x00 },
482 { 0x11, 0x00 },
483 { 0x12, 0x00 },
484 { 0x13, 0x00 },
485 { 0x14, 0x00 },
486 { 0x15, 0x00 },
487 { 0x16, 0x00 },
488 { 0x17, 0x00 },
489};
490
Axel Lin250a99e2016-02-20 10:54:11 +0800491static const struct regmap_config max9867_regmap = {
anish kumar805d1322016-02-17 17:28:49 -0800492 .reg_bits = 8,
493 .val_bits = 8,
494 .max_register = MAX9867_REVISION,
495 .reg_defaults = max9867_reg,
496 .num_reg_defaults = ARRAY_SIZE(max9867_reg),
497 .volatile_reg = max9867_volatile_register,
498 .cache_type = REGCACHE_RBTREE,
499};
500
501static int max9867_i2c_probe(struct i2c_client *i2c,
502 const struct i2c_device_id *id)
503{
504 struct max9867_priv *max9867;
Ladislav Michl8efc1af2018-12-04 19:18:17 +0100505 int ret, reg;
anish kumar805d1322016-02-17 17:28:49 -0800506
Ladislav Michl933662f2018-11-23 15:27:00 +0100507 max9867 = devm_kzalloc(&i2c->dev, sizeof(*max9867), GFP_KERNEL);
anish kumar805d1322016-02-17 17:28:49 -0800508 if (!max9867)
509 return -ENOMEM;
510
511 i2c_set_clientdata(i2c, max9867);
512 max9867->regmap = devm_regmap_init_i2c(i2c, &max9867_regmap);
513 if (IS_ERR(max9867->regmap)) {
514 ret = PTR_ERR(max9867->regmap);
Ladislav Michl8b9c7162018-03-01 15:19:53 +0100515 dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret);
anish kumar805d1322016-02-17 17:28:49 -0800516 return ret;
517 }
Ladislav Michl933662f2018-11-23 15:27:00 +0100518 ret = regmap_read(max9867->regmap, MAX9867_REVISION, &reg);
anish kumar805d1322016-02-17 17:28:49 -0800519 if (ret < 0) {
520 dev_err(&i2c->dev, "Failed to read: %d\n", ret);
521 return ret;
522 }
523 dev_info(&i2c->dev, "device revision: %x\n", reg);
Kuninori Morimoto91880a62018-01-29 03:11:46 +0000524 ret = devm_snd_soc_register_component(&i2c->dev, &max9867_component,
anish kumar805d1322016-02-17 17:28:49 -0800525 max9867_dai, ARRAY_SIZE(max9867_dai));
Ladislav Michl8efc1af2018-12-04 19:18:17 +0100526 if (ret < 0)
Kuninori Morimoto91880a62018-01-29 03:11:46 +0000527 dev_err(&i2c->dev, "Failed to register component: %d\n", ret);
anish kumar805d1322016-02-17 17:28:49 -0800528 return ret;
529}
530
anish kumar805d1322016-02-17 17:28:49 -0800531static const struct i2c_device_id max9867_i2c_id[] = {
532 { "max9867", 0 },
Axel Lin1b36e4a2016-02-20 10:53:15 +0800533 { }
anish kumar805d1322016-02-17 17:28:49 -0800534};
Javier Martinez Canillas56af0e42017-04-04 13:19:39 -0400535MODULE_DEVICE_TABLE(i2c, max9867_i2c_id);
anish kumar805d1322016-02-17 17:28:49 -0800536
537static const struct of_device_id max9867_of_match[] = {
538 { .compatible = "maxim,max9867", },
539 { }
540};
Javier Martinez Canillas56af0e42017-04-04 13:19:39 -0400541MODULE_DEVICE_TABLE(of, max9867_of_match);
anish kumar805d1322016-02-17 17:28:49 -0800542
anish kumar805d1322016-02-17 17:28:49 -0800543static struct i2c_driver max9867_i2c_driver = {
544 .driver = {
545 .name = "max9867",
546 .of_match_table = of_match_ptr(max9867_of_match),
anish kumar805d1322016-02-17 17:28:49 -0800547 },
548 .probe = max9867_i2c_probe,
anish kumar805d1322016-02-17 17:28:49 -0800549 .id_table = max9867_i2c_id,
550};
551
552module_i2c_driver(max9867_i2c_driver);
553
554MODULE_AUTHOR("anish kumar <yesanishhere@gmail.com>");
555MODULE_DESCRIPTION("ALSA SoC MAX9867 driver");
556MODULE_LICENSE("GPL");