1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Driver of Inno codec for rk3036 by Rockchip Inc.
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Author: Rockchip Inc.
6*4882a593Smuzhiyun * Author: Zheng ShunQian<zhengsq@rock-chips.com>
7*4882a593Smuzhiyun */
8*4882a593Smuzhiyun
9*4882a593Smuzhiyun #include <sound/soc.h>
10*4882a593Smuzhiyun #include <sound/tlv.h>
11*4882a593Smuzhiyun #include <sound/soc-dapm.h>
12*4882a593Smuzhiyun #include <sound/soc-dai.h>
13*4882a593Smuzhiyun #include <sound/pcm.h>
14*4882a593Smuzhiyun #include <sound/pcm_params.h>
15*4882a593Smuzhiyun
16*4882a593Smuzhiyun #include <linux/platform_device.h>
17*4882a593Smuzhiyun #include <linux/of.h>
18*4882a593Smuzhiyun #include <linux/of_gpio.h>
19*4882a593Smuzhiyun #include <linux/clk.h>
20*4882a593Smuzhiyun #include <linux/regmap.h>
21*4882a593Smuzhiyun #include <linux/device.h>
22*4882a593Smuzhiyun #include <linux/mfd/syscon.h>
23*4882a593Smuzhiyun #include <linux/module.h>
24*4882a593Smuzhiyun #include <linux/io.h>
25*4882a593Smuzhiyun
26*4882a593Smuzhiyun #include "inno_rk3036.h"
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun struct rk3036_codec_priv {
29*4882a593Smuzhiyun void __iomem *base;
30*4882a593Smuzhiyun struct clk *pclk;
31*4882a593Smuzhiyun struct regmap *regmap;
32*4882a593Smuzhiyun struct device *dev;
33*4882a593Smuzhiyun struct gpio_desc *pa_ctl;
34*4882a593Smuzhiyun };
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun static const DECLARE_TLV_DB_MINMAX(rk3036_codec_hp_tlv, -39, 0);
37*4882a593Smuzhiyun
rk3036_codec_antipop_event(struct snd_soc_dapm_widget * w,struct snd_kcontrol * kcontrol,int event)38*4882a593Smuzhiyun static int rk3036_codec_antipop_event(struct snd_soc_dapm_widget *w,
39*4882a593Smuzhiyun struct snd_kcontrol *kcontrol, int event)
40*4882a593Smuzhiyun {
41*4882a593Smuzhiyun struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
42*4882a593Smuzhiyun int val, ret, regmsk;
43*4882a593Smuzhiyun
44*4882a593Smuzhiyun regmsk = INNO_R09_HP_ANTIPOP_MSK << w->shift;
45*4882a593Smuzhiyun switch (event) {
46*4882a593Smuzhiyun case SND_SOC_DAPM_PRE_PMU:
47*4882a593Smuzhiyun val = INNO_R09_HP_ANTIPOP_ON << w->shift;
48*4882a593Smuzhiyun break;
49*4882a593Smuzhiyun case SND_SOC_DAPM_POST_PMD:
50*4882a593Smuzhiyun val = INNO_R09_HP_ANTIPOP_OFF << w->shift;
51*4882a593Smuzhiyun break;
52*4882a593Smuzhiyun default:
53*4882a593Smuzhiyun return 0;
54*4882a593Smuzhiyun }
55*4882a593Smuzhiyun
56*4882a593Smuzhiyun ret = snd_soc_component_update_bits(component, INNO_R09,
57*4882a593Smuzhiyun regmsk, val);
58*4882a593Smuzhiyun if (ret < 0)
59*4882a593Smuzhiyun return ret;
60*4882a593Smuzhiyun
61*4882a593Smuzhiyun /* Need to wait POP Sound VCM is stable */
62*4882a593Smuzhiyun msleep(50);
63*4882a593Smuzhiyun
64*4882a593Smuzhiyun return 0;
65*4882a593Smuzhiyun }
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun static const struct snd_kcontrol_new rk3036_codec_dapm_controls[] = {
68*4882a593Smuzhiyun SOC_DOUBLE_R_RANGE_TLV("Headphone Volume", INNO_R07, INNO_R08,
69*4882a593Smuzhiyun INNO_HP_GAIN_SHIFT, INNO_HP_GAIN_N39DB,
70*4882a593Smuzhiyun INNO_HP_GAIN_0DB, 0, rk3036_codec_hp_tlv),
71*4882a593Smuzhiyun SOC_DOUBLE("Zero Cross Switch", INNO_R06, INNO_R06_VOUTL_CZ_SHIFT,
72*4882a593Smuzhiyun INNO_R06_VOUTR_CZ_SHIFT, 1, 0),
73*4882a593Smuzhiyun SOC_DOUBLE("Headphone Switch", INNO_R09, INNO_R09_HPL_MUTE_SHIFT,
74*4882a593Smuzhiyun INNO_R09_HPR_MUTE_SHIFT, 1, 0),
75*4882a593Smuzhiyun };
76*4882a593Smuzhiyun
77*4882a593Smuzhiyun static const struct snd_soc_dapm_widget rk3036_codec_dapm_widgets[] = {
78*4882a593Smuzhiyun /* Using S3(Step3) as the starting step by datasheet */
79*4882a593Smuzhiyun SND_SOC_DAPM_SUPPLY_S("DAC PWR", 0, INNO_R06,
80*4882a593Smuzhiyun INNO_R06_DAC_EN_SHIFT, 0, NULL,
81*4882a593Smuzhiyun SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
82*4882a593Smuzhiyun SND_SOC_DAPM_SUPPLY_S("DACL VREF", 1, INNO_R04,
83*4882a593Smuzhiyun INNO_R04_DACL_VREF_SHIFT, 0, NULL,
84*4882a593Smuzhiyun SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
85*4882a593Smuzhiyun SND_SOC_DAPM_SUPPLY_S("DACR VREF", 1, INNO_R04,
86*4882a593Smuzhiyun INNO_R04_DACR_VREF_SHIFT, 0, NULL,
87*4882a593Smuzhiyun SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
88*4882a593Smuzhiyun SND_SOC_DAPM_SUPPLY_S("DACL ANTI-POP", 2, SND_SOC_NOPM,
89*4882a593Smuzhiyun INNO_R09_HPL_ANITPOP_SHIFT, 0, rk3036_codec_antipop_event,
90*4882a593Smuzhiyun SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
91*4882a593Smuzhiyun SND_SOC_DAPM_SUPPLY_S("DACR ANTI-POP", 2, SND_SOC_NOPM,
92*4882a593Smuzhiyun INNO_R09_HPR_ANITPOP_SHIFT, 0, rk3036_codec_antipop_event,
93*4882a593Smuzhiyun SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
94*4882a593Smuzhiyun SND_SOC_DAPM_SUPPLY_S("HPL OUT EN", 3, INNO_R05,
95*4882a593Smuzhiyun INNO_R05_HPL_EN_SHIFT, 0, NULL,
96*4882a593Smuzhiyun SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
97*4882a593Smuzhiyun SND_SOC_DAPM_SUPPLY_S("HPR OUT EN", 3, INNO_R05,
98*4882a593Smuzhiyun INNO_R05_HPR_EN_SHIFT, 0, NULL,
99*4882a593Smuzhiyun SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
100*4882a593Smuzhiyun SND_SOC_DAPM_SUPPLY_S("HPL OUT WORK", 4, INNO_R05,
101*4882a593Smuzhiyun INNO_R05_HPL_WORK_SHIFT, 0, NULL,
102*4882a593Smuzhiyun SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
103*4882a593Smuzhiyun SND_SOC_DAPM_SUPPLY_S("HPR OUT WORK", 4, INNO_R05,
104*4882a593Smuzhiyun INNO_R05_HPR_WORK_SHIFT, 0, NULL,
105*4882a593Smuzhiyun SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
106*4882a593Smuzhiyun SND_SOC_DAPM_SUPPLY_S("DACL HiLo VREF", 5, INNO_R06,
107*4882a593Smuzhiyun INNO_R06_DACL_HILO_VREF_SHIFT, 0, NULL,
108*4882a593Smuzhiyun SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
109*4882a593Smuzhiyun SND_SOC_DAPM_SUPPLY_S("DACR HiLo VREF", 5, INNO_R06,
110*4882a593Smuzhiyun INNO_R06_DACR_HILO_VREF_SHIFT, 0, NULL,
111*4882a593Smuzhiyun SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
112*4882a593Smuzhiyun SND_SOC_DAPM_SUPPLY_S("DACL CLK", 6, INNO_R04,
113*4882a593Smuzhiyun INNO_R04_DACL_CLK_SHIFT, 0, NULL,
114*4882a593Smuzhiyun SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
115*4882a593Smuzhiyun SND_SOC_DAPM_SUPPLY_S("DACR CLK", 6, INNO_R04,
116*4882a593Smuzhiyun INNO_R04_DACR_CLK_SHIFT, 0, NULL,
117*4882a593Smuzhiyun SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
118*4882a593Smuzhiyun SND_SOC_DAPM_SUPPLY_S("DACL WORK", 7, INNO_R04,
119*4882a593Smuzhiyun INNO_R04_DACL_SW_SHIFT, 0, NULL,
120*4882a593Smuzhiyun SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
121*4882a593Smuzhiyun SND_SOC_DAPM_SUPPLY_S("DACR WORK", 7, INNO_R04,
122*4882a593Smuzhiyun INNO_R04_DACR_SW_SHIFT, 0, NULL,
123*4882a593Smuzhiyun SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
124*4882a593Smuzhiyun
125*4882a593Smuzhiyun SND_SOC_DAPM_DAC_E("DACL", "Left Playback", INNO_R09,
126*4882a593Smuzhiyun INNO_R09_DACL_SWITCH_SHIFT, 0, NULL,
127*4882a593Smuzhiyun SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
128*4882a593Smuzhiyun SND_SOC_DAPM_DAC_E("DACR", "Right Playback", INNO_R09,
129*4882a593Smuzhiyun INNO_R09_DACR_SWITCH_SHIFT, 0, NULL,
130*4882a593Smuzhiyun SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
131*4882a593Smuzhiyun
132*4882a593Smuzhiyun SND_SOC_DAPM_AIF_IN("DAI-IN", "Playback", 0, SND_SOC_NOPM, 0, 0),
133*4882a593Smuzhiyun
134*4882a593Smuzhiyun SND_SOC_DAPM_OUTPUT("HPL"),
135*4882a593Smuzhiyun SND_SOC_DAPM_OUTPUT("HPR"),
136*4882a593Smuzhiyun };
137*4882a593Smuzhiyun
138*4882a593Smuzhiyun static const struct snd_soc_dapm_route rk3036_codec_dapm_routes[] = {
139*4882a593Smuzhiyun {"DACL VREF", NULL, "DAC PWR"},
140*4882a593Smuzhiyun {"DACR VREF", NULL, "DAC PWR"},
141*4882a593Smuzhiyun {"DACL ANTI-POP", NULL, "DAC PWR"},
142*4882a593Smuzhiyun {"DACR ANTI-POP", NULL, "DAC PWR"},
143*4882a593Smuzhiyun {"HPL OUT EN", NULL, "DAC PWR"},
144*4882a593Smuzhiyun {"HPR OUT EN", NULL, "DAC PWR"},
145*4882a593Smuzhiyun {"HPL OUT WORK", NULL, "DAC PWR"},
146*4882a593Smuzhiyun {"HPR OUT WORK", NULL, "DAC PWR"},
147*4882a593Smuzhiyun {"DACL HiLo VREF", NULL, "DAC PWR"},
148*4882a593Smuzhiyun {"DACR HiLo VREF", NULL, "DAC PWR"},
149*4882a593Smuzhiyun {"DACL CLK", NULL, "DAC PWR"},
150*4882a593Smuzhiyun {"DACR CLK", NULL, "DAC PWR"},
151*4882a593Smuzhiyun {"DACL WORK", NULL, "DAC PWR"},
152*4882a593Smuzhiyun {"DACR WORK", NULL, "DAC PWR"},
153*4882a593Smuzhiyun
154*4882a593Smuzhiyun {"DACL", NULL, "DACL VREF"},
155*4882a593Smuzhiyun {"DACL", NULL, "DACL ANTI-POP"},
156*4882a593Smuzhiyun {"DACL", NULL, "HPL OUT EN"},
157*4882a593Smuzhiyun {"DACL", NULL, "HPL OUT WORK"},
158*4882a593Smuzhiyun {"DACL", NULL, "DACL HiLo VREF"},
159*4882a593Smuzhiyun {"DACL", NULL, "DACL CLK"},
160*4882a593Smuzhiyun {"DACL", NULL, "DACL WORK"},
161*4882a593Smuzhiyun {"DACR", NULL, "DACR VREF"},
162*4882a593Smuzhiyun {"DACR", NULL, "DACR ANTI-POP"},
163*4882a593Smuzhiyun {"DACR", NULL, "HPR OUT EN"},
164*4882a593Smuzhiyun {"DACR", NULL, "HPR OUT WORK"},
165*4882a593Smuzhiyun {"DACR", NULL, "DACR HiLo VREF"},
166*4882a593Smuzhiyun {"DACR", NULL, "DACR CLK"},
167*4882a593Smuzhiyun {"DACR", NULL, "DACR WORK"},
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun {"DACL", NULL, "DAI-IN"},
170*4882a593Smuzhiyun {"DACR", NULL, "DAI-IN"},
171*4882a593Smuzhiyun
172*4882a593Smuzhiyun {"HPL", NULL, "DACL"},
173*4882a593Smuzhiyun {"HPR", NULL, "DACR"},
174*4882a593Smuzhiyun };
175*4882a593Smuzhiyun
rk3036_codec_dai_set_fmt(struct snd_soc_dai * dai,unsigned int fmt)176*4882a593Smuzhiyun static int rk3036_codec_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
177*4882a593Smuzhiyun {
178*4882a593Smuzhiyun struct snd_soc_component *component = dai->component;
179*4882a593Smuzhiyun unsigned int reg01_val = 0, reg02_val = 0, reg03_val = 0;
180*4882a593Smuzhiyun
181*4882a593Smuzhiyun dev_dbg(component->dev, "rk3036_codec dai set fmt : %08x\n", fmt);
182*4882a593Smuzhiyun
183*4882a593Smuzhiyun switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
184*4882a593Smuzhiyun case SND_SOC_DAIFMT_CBS_CFS:
185*4882a593Smuzhiyun reg01_val |= INNO_R01_PINDIR_IN_SLAVE |
186*4882a593Smuzhiyun INNO_R01_I2SMODE_SLAVE;
187*4882a593Smuzhiyun break;
188*4882a593Smuzhiyun case SND_SOC_DAIFMT_CBM_CFM:
189*4882a593Smuzhiyun reg01_val |= INNO_R01_PINDIR_OUT_MASTER |
190*4882a593Smuzhiyun INNO_R01_I2SMODE_MASTER;
191*4882a593Smuzhiyun break;
192*4882a593Smuzhiyun default:
193*4882a593Smuzhiyun dev_err(component->dev, "invalid fmt\n");
194*4882a593Smuzhiyun return -EINVAL;
195*4882a593Smuzhiyun }
196*4882a593Smuzhiyun
197*4882a593Smuzhiyun switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
198*4882a593Smuzhiyun case SND_SOC_DAIFMT_DSP_A:
199*4882a593Smuzhiyun reg02_val |= INNO_R02_DACM_PCM;
200*4882a593Smuzhiyun break;
201*4882a593Smuzhiyun case SND_SOC_DAIFMT_I2S:
202*4882a593Smuzhiyun reg02_val |= INNO_R02_DACM_I2S;
203*4882a593Smuzhiyun break;
204*4882a593Smuzhiyun case SND_SOC_DAIFMT_RIGHT_J:
205*4882a593Smuzhiyun reg02_val |= INNO_R02_DACM_RJM;
206*4882a593Smuzhiyun break;
207*4882a593Smuzhiyun case SND_SOC_DAIFMT_LEFT_J:
208*4882a593Smuzhiyun reg02_val |= INNO_R02_DACM_LJM;
209*4882a593Smuzhiyun break;
210*4882a593Smuzhiyun default:
211*4882a593Smuzhiyun dev_err(component->dev, "set dai format failed\n");
212*4882a593Smuzhiyun return -EINVAL;
213*4882a593Smuzhiyun }
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
216*4882a593Smuzhiyun case SND_SOC_DAIFMT_NB_NF:
217*4882a593Smuzhiyun reg02_val |= INNO_R02_LRCP_NORMAL;
218*4882a593Smuzhiyun reg03_val |= INNO_R03_BCP_NORMAL;
219*4882a593Smuzhiyun break;
220*4882a593Smuzhiyun case SND_SOC_DAIFMT_IB_IF:
221*4882a593Smuzhiyun reg02_val |= INNO_R02_LRCP_REVERSAL;
222*4882a593Smuzhiyun reg03_val |= INNO_R03_BCP_REVERSAL;
223*4882a593Smuzhiyun break;
224*4882a593Smuzhiyun case SND_SOC_DAIFMT_IB_NF:
225*4882a593Smuzhiyun reg02_val |= INNO_R02_LRCP_REVERSAL;
226*4882a593Smuzhiyun reg03_val |= INNO_R03_BCP_NORMAL;
227*4882a593Smuzhiyun break;
228*4882a593Smuzhiyun case SND_SOC_DAIFMT_NB_IF:
229*4882a593Smuzhiyun reg02_val |= INNO_R02_LRCP_NORMAL;
230*4882a593Smuzhiyun reg03_val |= INNO_R03_BCP_REVERSAL;
231*4882a593Smuzhiyun break;
232*4882a593Smuzhiyun default:
233*4882a593Smuzhiyun dev_err(component->dev, "set dai format failed\n");
234*4882a593Smuzhiyun return -EINVAL;
235*4882a593Smuzhiyun }
236*4882a593Smuzhiyun
237*4882a593Smuzhiyun snd_soc_component_update_bits(component, INNO_R01, INNO_R01_I2SMODE_MSK |
238*4882a593Smuzhiyun INNO_R01_PINDIR_MSK, reg01_val);
239*4882a593Smuzhiyun snd_soc_component_update_bits(component, INNO_R02, INNO_R02_LRCP_MSK |
240*4882a593Smuzhiyun INNO_R02_DACM_MSK, reg02_val);
241*4882a593Smuzhiyun snd_soc_component_update_bits(component, INNO_R03, INNO_R03_BCP_MSK, reg03_val);
242*4882a593Smuzhiyun
243*4882a593Smuzhiyun return 0;
244*4882a593Smuzhiyun }
245*4882a593Smuzhiyun
rk3036_codec_dai_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * hw_params,struct snd_soc_dai * dai)246*4882a593Smuzhiyun static int rk3036_codec_dai_hw_params(struct snd_pcm_substream *substream,
247*4882a593Smuzhiyun struct snd_pcm_hw_params *hw_params,
248*4882a593Smuzhiyun struct snd_soc_dai *dai)
249*4882a593Smuzhiyun {
250*4882a593Smuzhiyun struct snd_soc_component *component = dai->component;
251*4882a593Smuzhiyun unsigned int reg02_val = 0, reg03_val = 0;
252*4882a593Smuzhiyun
253*4882a593Smuzhiyun switch (params_format(hw_params)) {
254*4882a593Smuzhiyun case SNDRV_PCM_FORMAT_S16_LE:
255*4882a593Smuzhiyun reg02_val |= INNO_R02_VWL_16BIT;
256*4882a593Smuzhiyun break;
257*4882a593Smuzhiyun case SNDRV_PCM_FORMAT_S20_3LE:
258*4882a593Smuzhiyun reg02_val |= INNO_R02_VWL_20BIT;
259*4882a593Smuzhiyun break;
260*4882a593Smuzhiyun case SNDRV_PCM_FORMAT_S24_LE:
261*4882a593Smuzhiyun reg02_val |= INNO_R02_VWL_24BIT;
262*4882a593Smuzhiyun break;
263*4882a593Smuzhiyun case SNDRV_PCM_FORMAT_S32_LE:
264*4882a593Smuzhiyun reg02_val |= INNO_R02_VWL_32BIT;
265*4882a593Smuzhiyun break;
266*4882a593Smuzhiyun default:
267*4882a593Smuzhiyun return -EINVAL;
268*4882a593Smuzhiyun }
269*4882a593Smuzhiyun
270*4882a593Smuzhiyun reg02_val |= INNO_R02_LRCP_NORMAL;
271*4882a593Smuzhiyun reg03_val |= INNO_R03_FWL_32BIT | INNO_R03_DACR_WORK;
272*4882a593Smuzhiyun
273*4882a593Smuzhiyun snd_soc_component_update_bits(component, INNO_R02, INNO_R02_LRCP_MSK |
274*4882a593Smuzhiyun INNO_R02_VWL_MSK, reg02_val);
275*4882a593Smuzhiyun snd_soc_component_update_bits(component, INNO_R03, INNO_R03_DACR_MSK |
276*4882a593Smuzhiyun INNO_R03_FWL_MSK, reg03_val);
277*4882a593Smuzhiyun return 0;
278*4882a593Smuzhiyun }
279*4882a593Smuzhiyun
rk3308_mute_stream(struct snd_soc_dai * dai,int mute,int stream)280*4882a593Smuzhiyun static int rk3308_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
281*4882a593Smuzhiyun {
282*4882a593Smuzhiyun struct snd_soc_component *component = dai->component;
283*4882a593Smuzhiyun struct rk3036_codec_priv *priv = snd_soc_component_get_drvdata(component);
284*4882a593Smuzhiyun
285*4882a593Smuzhiyun if (stream == SNDRV_PCM_STREAM_CAPTURE)
286*4882a593Smuzhiyun return 0;
287*4882a593Smuzhiyun
288*4882a593Smuzhiyun if (priv->pa_ctl)
289*4882a593Smuzhiyun gpiod_direction_output(priv->pa_ctl, !mute);
290*4882a593Smuzhiyun
291*4882a593Smuzhiyun return 0;
292*4882a593Smuzhiyun }
293*4882a593Smuzhiyun
294*4882a593Smuzhiyun #define RK3036_CODEC_RATES (SNDRV_PCM_RATE_8000 | \
295*4882a593Smuzhiyun SNDRV_PCM_RATE_16000 | \
296*4882a593Smuzhiyun SNDRV_PCM_RATE_32000 | \
297*4882a593Smuzhiyun SNDRV_PCM_RATE_44100 | \
298*4882a593Smuzhiyun SNDRV_PCM_RATE_48000 | \
299*4882a593Smuzhiyun SNDRV_PCM_RATE_96000)
300*4882a593Smuzhiyun
301*4882a593Smuzhiyun #define RK3036_CODEC_FMTS (SNDRV_PCM_FMTBIT_S16_LE | \
302*4882a593Smuzhiyun SNDRV_PCM_FMTBIT_S20_3LE | \
303*4882a593Smuzhiyun SNDRV_PCM_FMTBIT_S24_LE | \
304*4882a593Smuzhiyun SNDRV_PCM_FMTBIT_S32_LE)
305*4882a593Smuzhiyun
306*4882a593Smuzhiyun static const struct snd_soc_dai_ops rk3036_codec_dai_ops = {
307*4882a593Smuzhiyun .set_fmt = rk3036_codec_dai_set_fmt,
308*4882a593Smuzhiyun .hw_params = rk3036_codec_dai_hw_params,
309*4882a593Smuzhiyun .mute_stream = rk3308_mute_stream,
310*4882a593Smuzhiyun };
311*4882a593Smuzhiyun
312*4882a593Smuzhiyun static struct snd_soc_dai_driver rk3036_codec_dai_driver[] = {
313*4882a593Smuzhiyun {
314*4882a593Smuzhiyun .name = "rk3036-codec-dai",
315*4882a593Smuzhiyun .playback = {
316*4882a593Smuzhiyun .stream_name = "Playback",
317*4882a593Smuzhiyun .channels_min = 1,
318*4882a593Smuzhiyun .channels_max = 2,
319*4882a593Smuzhiyun .rates = RK3036_CODEC_RATES,
320*4882a593Smuzhiyun .formats = RK3036_CODEC_FMTS,
321*4882a593Smuzhiyun },
322*4882a593Smuzhiyun .ops = &rk3036_codec_dai_ops,
323*4882a593Smuzhiyun .symmetric_rates = 1,
324*4882a593Smuzhiyun },
325*4882a593Smuzhiyun };
326*4882a593Smuzhiyun
rk3036_codec_reset(struct snd_soc_component * component)327*4882a593Smuzhiyun static void rk3036_codec_reset(struct snd_soc_component *component)
328*4882a593Smuzhiyun {
329*4882a593Smuzhiyun snd_soc_component_write(component, INNO_R00,
330*4882a593Smuzhiyun INNO_R00_CSR_RESET | INNO_R00_CDCR_RESET);
331*4882a593Smuzhiyun snd_soc_component_write(component, INNO_R00,
332*4882a593Smuzhiyun INNO_R00_CSR_WORK | INNO_R00_CDCR_WORK);
333*4882a593Smuzhiyun }
334*4882a593Smuzhiyun
rk3036_codec_probe(struct snd_soc_component * component)335*4882a593Smuzhiyun static int rk3036_codec_probe(struct snd_soc_component *component)
336*4882a593Smuzhiyun {
337*4882a593Smuzhiyun rk3036_codec_reset(component);
338*4882a593Smuzhiyun return 0;
339*4882a593Smuzhiyun }
340*4882a593Smuzhiyun
rk3036_codec_remove(struct snd_soc_component * component)341*4882a593Smuzhiyun static void rk3036_codec_remove(struct snd_soc_component *component)
342*4882a593Smuzhiyun {
343*4882a593Smuzhiyun rk3036_codec_reset(component);
344*4882a593Smuzhiyun }
345*4882a593Smuzhiyun
rk3036_codec_set_bias_level(struct snd_soc_component * component,enum snd_soc_bias_level level)346*4882a593Smuzhiyun static int rk3036_codec_set_bias_level(struct snd_soc_component *component,
347*4882a593Smuzhiyun enum snd_soc_bias_level level)
348*4882a593Smuzhiyun {
349*4882a593Smuzhiyun switch (level) {
350*4882a593Smuzhiyun case SND_SOC_BIAS_PREPARE:
351*4882a593Smuzhiyun /* start precharge and waiting finish. */
352*4882a593Smuzhiyun snd_soc_component_write(component, INNO_R06, INNO_R06_DAC_PRECHARGE);
353*4882a593Smuzhiyun msleep(20);
354*4882a593Smuzhiyun
355*4882a593Smuzhiyun break;
356*4882a593Smuzhiyun
357*4882a593Smuzhiyun case SND_SOC_BIAS_STANDBY:
358*4882a593Smuzhiyun if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
359*4882a593Smuzhiyun /* set a big current for capacitor charging. */
360*4882a593Smuzhiyun snd_soc_component_write(component, INNO_R10, INNO_R10_MAX_CUR);
361*4882a593Smuzhiyun }
362*4882a593Smuzhiyun /* start discharge. */
363*4882a593Smuzhiyun snd_soc_component_write(component, INNO_R06, INNO_R06_DAC_DISCHARGE);
364*4882a593Smuzhiyun
365*4882a593Smuzhiyun break;
366*4882a593Smuzhiyun default:
367*4882a593Smuzhiyun break;
368*4882a593Smuzhiyun }
369*4882a593Smuzhiyun
370*4882a593Smuzhiyun return 0;
371*4882a593Smuzhiyun }
372*4882a593Smuzhiyun
373*4882a593Smuzhiyun static const struct snd_soc_component_driver rk3036_codec_driver = {
374*4882a593Smuzhiyun .probe = rk3036_codec_probe,
375*4882a593Smuzhiyun .remove = rk3036_codec_remove,
376*4882a593Smuzhiyun .set_bias_level = rk3036_codec_set_bias_level,
377*4882a593Smuzhiyun .controls = rk3036_codec_dapm_controls,
378*4882a593Smuzhiyun .num_controls = ARRAY_SIZE(rk3036_codec_dapm_controls),
379*4882a593Smuzhiyun .dapm_routes = rk3036_codec_dapm_routes,
380*4882a593Smuzhiyun .num_dapm_routes = ARRAY_SIZE(rk3036_codec_dapm_routes),
381*4882a593Smuzhiyun .dapm_widgets = rk3036_codec_dapm_widgets,
382*4882a593Smuzhiyun .num_dapm_widgets = ARRAY_SIZE(rk3036_codec_dapm_widgets),
383*4882a593Smuzhiyun .idle_bias_on = 1,
384*4882a593Smuzhiyun .use_pmdown_time = 1,
385*4882a593Smuzhiyun .endianness = 1,
386*4882a593Smuzhiyun .non_legacy_dai_naming = 1,
387*4882a593Smuzhiyun };
388*4882a593Smuzhiyun
389*4882a593Smuzhiyun static const struct regmap_config rk3036_codec_regmap_config = {
390*4882a593Smuzhiyun .reg_bits = 32,
391*4882a593Smuzhiyun .reg_stride = 4,
392*4882a593Smuzhiyun .val_bits = 32,
393*4882a593Smuzhiyun };
394*4882a593Smuzhiyun
395*4882a593Smuzhiyun #define GRF_SOC_CON0 0x00140
396*4882a593Smuzhiyun #define GRF_ACODEC_SEL (BIT(10) | BIT(16 + 10))
397*4882a593Smuzhiyun
rk3036_codec_platform_probe(struct platform_device * pdev)398*4882a593Smuzhiyun static int rk3036_codec_platform_probe(struct platform_device *pdev)
399*4882a593Smuzhiyun {
400*4882a593Smuzhiyun struct rk3036_codec_priv *priv;
401*4882a593Smuzhiyun struct device_node *of_node = pdev->dev.of_node;
402*4882a593Smuzhiyun void __iomem *base;
403*4882a593Smuzhiyun struct regmap *grf;
404*4882a593Smuzhiyun int ret;
405*4882a593Smuzhiyun
406*4882a593Smuzhiyun priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
407*4882a593Smuzhiyun if (!priv)
408*4882a593Smuzhiyun return -ENOMEM;
409*4882a593Smuzhiyun
410*4882a593Smuzhiyun base = devm_platform_ioremap_resource(pdev, 0);
411*4882a593Smuzhiyun if (IS_ERR(base))
412*4882a593Smuzhiyun return PTR_ERR(base);
413*4882a593Smuzhiyun
414*4882a593Smuzhiyun priv->base = base;
415*4882a593Smuzhiyun priv->regmap = devm_regmap_init_mmio(&pdev->dev, priv->base,
416*4882a593Smuzhiyun &rk3036_codec_regmap_config);
417*4882a593Smuzhiyun if (IS_ERR(priv->regmap)) {
418*4882a593Smuzhiyun dev_err(&pdev->dev, "init regmap failed\n");
419*4882a593Smuzhiyun return PTR_ERR(priv->regmap);
420*4882a593Smuzhiyun }
421*4882a593Smuzhiyun
422*4882a593Smuzhiyun grf = syscon_regmap_lookup_by_phandle(of_node, "rockchip,grf");
423*4882a593Smuzhiyun if (IS_ERR(grf)) {
424*4882a593Smuzhiyun dev_err(&pdev->dev, "needs 'rockchip,grf' property\n");
425*4882a593Smuzhiyun return PTR_ERR(grf);
426*4882a593Smuzhiyun }
427*4882a593Smuzhiyun ret = regmap_write(grf, GRF_SOC_CON0, GRF_ACODEC_SEL);
428*4882a593Smuzhiyun if (ret) {
429*4882a593Smuzhiyun dev_err(&pdev->dev, "Could not write to GRF: %d\n", ret);
430*4882a593Smuzhiyun return ret;
431*4882a593Smuzhiyun }
432*4882a593Smuzhiyun
433*4882a593Smuzhiyun priv->pa_ctl = devm_gpiod_get_optional(&pdev->dev, "pa-ctl",
434*4882a593Smuzhiyun GPIOD_OUT_LOW);
435*4882a593Smuzhiyun if (!priv->pa_ctl) {
436*4882a593Smuzhiyun dev_info(&pdev->dev, "Don't need pa-ctl gpio\n");
437*4882a593Smuzhiyun } else if (IS_ERR(priv->pa_ctl)) {
438*4882a593Smuzhiyun dev_err(&pdev->dev, "Unable to claim gpio pa-ctl\n");
439*4882a593Smuzhiyun return PTR_ERR(priv->pa_ctl);
440*4882a593Smuzhiyun }
441*4882a593Smuzhiyun
442*4882a593Smuzhiyun priv->pclk = devm_clk_get(&pdev->dev, "acodec_pclk");
443*4882a593Smuzhiyun if (IS_ERR(priv->pclk))
444*4882a593Smuzhiyun return PTR_ERR(priv->pclk);
445*4882a593Smuzhiyun
446*4882a593Smuzhiyun ret = clk_prepare_enable(priv->pclk);
447*4882a593Smuzhiyun if (ret < 0) {
448*4882a593Smuzhiyun dev_err(&pdev->dev, "failed to enable clk\n");
449*4882a593Smuzhiyun return ret;
450*4882a593Smuzhiyun }
451*4882a593Smuzhiyun
452*4882a593Smuzhiyun priv->dev = &pdev->dev;
453*4882a593Smuzhiyun dev_set_drvdata(&pdev->dev, priv);
454*4882a593Smuzhiyun
455*4882a593Smuzhiyun ret = devm_snd_soc_register_component(&pdev->dev, &rk3036_codec_driver,
456*4882a593Smuzhiyun rk3036_codec_dai_driver,
457*4882a593Smuzhiyun ARRAY_SIZE(rk3036_codec_dai_driver));
458*4882a593Smuzhiyun if (ret) {
459*4882a593Smuzhiyun clk_disable_unprepare(priv->pclk);
460*4882a593Smuzhiyun dev_set_drvdata(&pdev->dev, NULL);
461*4882a593Smuzhiyun }
462*4882a593Smuzhiyun
463*4882a593Smuzhiyun return ret;
464*4882a593Smuzhiyun }
465*4882a593Smuzhiyun
rk3036_codec_platform_remove(struct platform_device * pdev)466*4882a593Smuzhiyun static int rk3036_codec_platform_remove(struct platform_device *pdev)
467*4882a593Smuzhiyun {
468*4882a593Smuzhiyun struct rk3036_codec_priv *priv = dev_get_drvdata(&pdev->dev);
469*4882a593Smuzhiyun
470*4882a593Smuzhiyun clk_disable_unprepare(priv->pclk);
471*4882a593Smuzhiyun
472*4882a593Smuzhiyun return 0;
473*4882a593Smuzhiyun }
474*4882a593Smuzhiyun
475*4882a593Smuzhiyun static const struct of_device_id rk3036_codec_of_match[] = {
476*4882a593Smuzhiyun { .compatible = "rockchip,rk3036-codec", },
477*4882a593Smuzhiyun {}
478*4882a593Smuzhiyun };
479*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, rk3036_codec_of_match);
480*4882a593Smuzhiyun
481*4882a593Smuzhiyun static struct platform_driver rk3036_codec_platform_driver = {
482*4882a593Smuzhiyun .driver = {
483*4882a593Smuzhiyun .name = "rk3036-codec-platform",
484*4882a593Smuzhiyun .of_match_table = of_match_ptr(rk3036_codec_of_match),
485*4882a593Smuzhiyun },
486*4882a593Smuzhiyun .probe = rk3036_codec_platform_probe,
487*4882a593Smuzhiyun .remove = rk3036_codec_platform_remove,
488*4882a593Smuzhiyun };
489*4882a593Smuzhiyun
490*4882a593Smuzhiyun module_platform_driver(rk3036_codec_platform_driver);
491*4882a593Smuzhiyun
492*4882a593Smuzhiyun MODULE_AUTHOR("Rockchip Inc.");
493*4882a593Smuzhiyun MODULE_DESCRIPTION("Rockchip rk3036 codec driver");
494*4882a593Smuzhiyun MODULE_LICENSE("GPL");
495