162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Driver of Inno codec for rk3036 by Rockchip Inc.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Author: Rockchip Inc.
662306a36Sopenharmony_ci * Author: Zheng ShunQian<zhengsq@rock-chips.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <sound/soc.h>
1062306a36Sopenharmony_ci#include <sound/tlv.h>
1162306a36Sopenharmony_ci#include <sound/soc-dapm.h>
1262306a36Sopenharmony_ci#include <sound/soc-dai.h>
1362306a36Sopenharmony_ci#include <sound/pcm.h>
1462306a36Sopenharmony_ci#include <sound/pcm_params.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <linux/platform_device.h>
1762306a36Sopenharmony_ci#include <linux/of.h>
1862306a36Sopenharmony_ci#include <linux/clk.h>
1962306a36Sopenharmony_ci#include <linux/regmap.h>
2062306a36Sopenharmony_ci#include <linux/device.h>
2162306a36Sopenharmony_ci#include <linux/mfd/syscon.h>
2262306a36Sopenharmony_ci#include <linux/module.h>
2362306a36Sopenharmony_ci#include <linux/io.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include "inno_rk3036.h"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistruct rk3036_codec_priv {
2862306a36Sopenharmony_ci	void __iomem *base;
2962306a36Sopenharmony_ci	struct clk *pclk;
3062306a36Sopenharmony_ci	struct regmap *regmap;
3162306a36Sopenharmony_ci	struct device *dev;
3262306a36Sopenharmony_ci};
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic const DECLARE_TLV_DB_MINMAX(rk3036_codec_hp_tlv, -39, 0);
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic int rk3036_codec_antipop_info(struct snd_kcontrol *kcontrol,
3762306a36Sopenharmony_ci				     struct snd_ctl_elem_info *uinfo)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
4062306a36Sopenharmony_ci	uinfo->count = 2;
4162306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
4262306a36Sopenharmony_ci	uinfo->value.integer.max = 1;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	return 0;
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic int rk3036_codec_antipop_get(struct snd_kcontrol *kcontrol,
4862306a36Sopenharmony_ci				    struct snd_ctl_elem_value *ucontrol)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
5162306a36Sopenharmony_ci	int val, regval;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	regval = snd_soc_component_read(component, INNO_R09);
5462306a36Sopenharmony_ci	val = ((regval >> INNO_R09_HPL_ANITPOP_SHIFT) &
5562306a36Sopenharmony_ci	       INNO_R09_HP_ANTIPOP_MSK) == INNO_R09_HP_ANTIPOP_ON;
5662306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = val;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	val = ((regval >> INNO_R09_HPR_ANITPOP_SHIFT) &
5962306a36Sopenharmony_ci	       INNO_R09_HP_ANTIPOP_MSK) == INNO_R09_HP_ANTIPOP_ON;
6062306a36Sopenharmony_ci	ucontrol->value.integer.value[1] = val;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	return 0;
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic int rk3036_codec_antipop_put(struct snd_kcontrol *kcontrol,
6662306a36Sopenharmony_ci				    struct snd_ctl_elem_value *ucontrol)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
6962306a36Sopenharmony_ci	int val, ret, regmsk;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	val = (ucontrol->value.integer.value[0] ?
7262306a36Sopenharmony_ci	       INNO_R09_HP_ANTIPOP_ON : INNO_R09_HP_ANTIPOP_OFF) <<
7362306a36Sopenharmony_ci	      INNO_R09_HPL_ANITPOP_SHIFT;
7462306a36Sopenharmony_ci	val |= (ucontrol->value.integer.value[1] ?
7562306a36Sopenharmony_ci		INNO_R09_HP_ANTIPOP_ON : INNO_R09_HP_ANTIPOP_OFF) <<
7662306a36Sopenharmony_ci	       INNO_R09_HPR_ANITPOP_SHIFT;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	regmsk = INNO_R09_HP_ANTIPOP_MSK << INNO_R09_HPL_ANITPOP_SHIFT |
7962306a36Sopenharmony_ci		 INNO_R09_HP_ANTIPOP_MSK << INNO_R09_HPR_ANITPOP_SHIFT;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	ret = snd_soc_component_update_bits(component, INNO_R09,
8262306a36Sopenharmony_ci					    regmsk, val);
8362306a36Sopenharmony_ci	if (ret < 0)
8462306a36Sopenharmony_ci		return ret;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	return 0;
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci#define SOC_RK3036_CODEC_ANTIPOP_DECL(xname) \
9062306a36Sopenharmony_ci{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
9162306a36Sopenharmony_ci	.info = rk3036_codec_antipop_info, .get = rk3036_codec_antipop_get, \
9262306a36Sopenharmony_ci	.put = rk3036_codec_antipop_put, }
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic const struct snd_kcontrol_new rk3036_codec_dapm_controls[] = {
9562306a36Sopenharmony_ci	SOC_DOUBLE_R_RANGE_TLV("Headphone Volume", INNO_R07, INNO_R08,
9662306a36Sopenharmony_ci		INNO_HP_GAIN_SHIFT, INNO_HP_GAIN_N39DB,
9762306a36Sopenharmony_ci		INNO_HP_GAIN_0DB, 0, rk3036_codec_hp_tlv),
9862306a36Sopenharmony_ci	SOC_DOUBLE("Zero Cross Switch", INNO_R06, INNO_R06_VOUTL_CZ_SHIFT,
9962306a36Sopenharmony_ci		INNO_R06_VOUTR_CZ_SHIFT, 1, 0),
10062306a36Sopenharmony_ci	SOC_DOUBLE("Headphone Switch", INNO_R09, INNO_R09_HPL_MUTE_SHIFT,
10162306a36Sopenharmony_ci		INNO_R09_HPR_MUTE_SHIFT, 1, 0),
10262306a36Sopenharmony_ci	SOC_RK3036_CODEC_ANTIPOP_DECL("Anti-pop Switch"),
10362306a36Sopenharmony_ci};
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic const struct snd_kcontrol_new rk3036_codec_hpl_mixer_controls[] = {
10662306a36Sopenharmony_ci	SOC_DAPM_SINGLE("DAC Left Out Switch", INNO_R09,
10762306a36Sopenharmony_ci			INNO_R09_DACL_SWITCH_SHIFT, 1, 0),
10862306a36Sopenharmony_ci};
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistatic const struct snd_kcontrol_new rk3036_codec_hpr_mixer_controls[] = {
11162306a36Sopenharmony_ci	SOC_DAPM_SINGLE("DAC Right Out Switch", INNO_R09,
11262306a36Sopenharmony_ci			INNO_R09_DACR_SWITCH_SHIFT, 1, 0),
11362306a36Sopenharmony_ci};
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cistatic const struct snd_kcontrol_new rk3036_codec_hpl_switch_controls[] = {
11662306a36Sopenharmony_ci	SOC_DAPM_SINGLE("HP Left Out Switch", INNO_R05,
11762306a36Sopenharmony_ci			INNO_R05_HPL_WORK_SHIFT, 1, 0),
11862306a36Sopenharmony_ci};
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic const struct snd_kcontrol_new rk3036_codec_hpr_switch_controls[] = {
12162306a36Sopenharmony_ci	SOC_DAPM_SINGLE("HP Right Out Switch", INNO_R05,
12262306a36Sopenharmony_ci			INNO_R05_HPR_WORK_SHIFT, 1, 0),
12362306a36Sopenharmony_ci};
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget rk3036_codec_dapm_widgets[] = {
12662306a36Sopenharmony_ci	SND_SOC_DAPM_SUPPLY_S("DAC PWR", 1, INNO_R06,
12762306a36Sopenharmony_ci			      INNO_R06_DAC_EN_SHIFT, 0, NULL, 0),
12862306a36Sopenharmony_ci	SND_SOC_DAPM_SUPPLY_S("DACL VREF", 2, INNO_R04,
12962306a36Sopenharmony_ci			      INNO_R04_DACL_VREF_SHIFT, 0, NULL, 0),
13062306a36Sopenharmony_ci	SND_SOC_DAPM_SUPPLY_S("DACR VREF", 2, INNO_R04,
13162306a36Sopenharmony_ci			      INNO_R04_DACR_VREF_SHIFT, 0, NULL, 0),
13262306a36Sopenharmony_ci	SND_SOC_DAPM_SUPPLY_S("DACL HiLo VREF", 3, INNO_R06,
13362306a36Sopenharmony_ci			      INNO_R06_DACL_HILO_VREF_SHIFT, 0, NULL, 0),
13462306a36Sopenharmony_ci	SND_SOC_DAPM_SUPPLY_S("DACR HiLo VREF", 3, INNO_R06,
13562306a36Sopenharmony_ci			      INNO_R06_DACR_HILO_VREF_SHIFT, 0, NULL, 0),
13662306a36Sopenharmony_ci	SND_SOC_DAPM_SUPPLY_S("DACR CLK", 3, INNO_R04,
13762306a36Sopenharmony_ci			      INNO_R04_DACR_CLK_SHIFT, 0, NULL, 0),
13862306a36Sopenharmony_ci	SND_SOC_DAPM_SUPPLY_S("DACL CLK", 3, INNO_R04,
13962306a36Sopenharmony_ci			      INNO_R04_DACL_CLK_SHIFT, 0, NULL, 0),
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	SND_SOC_DAPM_DAC("DACL", "Left Playback", INNO_R04,
14262306a36Sopenharmony_ci			 INNO_R04_DACL_SW_SHIFT, 0),
14362306a36Sopenharmony_ci	SND_SOC_DAPM_DAC("DACR", "Right Playback", INNO_R04,
14462306a36Sopenharmony_ci			 INNO_R04_DACR_SW_SHIFT, 0),
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	SND_SOC_DAPM_MIXER("Left Headphone Mixer", SND_SOC_NOPM, 0, 0,
14762306a36Sopenharmony_ci		rk3036_codec_hpl_mixer_controls,
14862306a36Sopenharmony_ci		ARRAY_SIZE(rk3036_codec_hpl_mixer_controls)),
14962306a36Sopenharmony_ci	SND_SOC_DAPM_MIXER("Right Headphone Mixer", SND_SOC_NOPM, 0, 0,
15062306a36Sopenharmony_ci		rk3036_codec_hpr_mixer_controls,
15162306a36Sopenharmony_ci		ARRAY_SIZE(rk3036_codec_hpr_mixer_controls)),
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	SND_SOC_DAPM_PGA("HP Left Out", INNO_R05,
15462306a36Sopenharmony_ci			 INNO_R05_HPL_EN_SHIFT, 0, NULL, 0),
15562306a36Sopenharmony_ci	SND_SOC_DAPM_PGA("HP Right Out", INNO_R05,
15662306a36Sopenharmony_ci			 INNO_R05_HPR_EN_SHIFT, 0, NULL, 0),
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	SND_SOC_DAPM_MIXER("HP Left Switch",  SND_SOC_NOPM, 0, 0,
15962306a36Sopenharmony_ci			   rk3036_codec_hpl_switch_controls,
16062306a36Sopenharmony_ci			   ARRAY_SIZE(rk3036_codec_hpl_switch_controls)),
16162306a36Sopenharmony_ci	SND_SOC_DAPM_MIXER("HP Right Switch",  SND_SOC_NOPM, 0, 0,
16262306a36Sopenharmony_ci			   rk3036_codec_hpr_switch_controls,
16362306a36Sopenharmony_ci			   ARRAY_SIZE(rk3036_codec_hpr_switch_controls)),
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	SND_SOC_DAPM_OUTPUT("HPL"),
16662306a36Sopenharmony_ci	SND_SOC_DAPM_OUTPUT("HPR"),
16762306a36Sopenharmony_ci};
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic const struct snd_soc_dapm_route rk3036_codec_dapm_routes[] = {
17062306a36Sopenharmony_ci	{"DACL VREF", NULL, "DAC PWR"},
17162306a36Sopenharmony_ci	{"DACR VREF", NULL, "DAC PWR"},
17262306a36Sopenharmony_ci	{"DACL HiLo VREF", NULL, "DAC PWR"},
17362306a36Sopenharmony_ci	{"DACR HiLo VREF", NULL, "DAC PWR"},
17462306a36Sopenharmony_ci	{"DACL CLK", NULL, "DAC PWR"},
17562306a36Sopenharmony_ci	{"DACR CLK", NULL, "DAC PWR"},
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	{"DACL", NULL, "DACL VREF"},
17862306a36Sopenharmony_ci	{"DACL", NULL, "DACL HiLo VREF"},
17962306a36Sopenharmony_ci	{"DACL", NULL, "DACL CLK"},
18062306a36Sopenharmony_ci	{"DACR", NULL, "DACR VREF"},
18162306a36Sopenharmony_ci	{"DACR", NULL, "DACR HiLo VREF"},
18262306a36Sopenharmony_ci	{"DACR", NULL, "DACR CLK"},
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	{"Left Headphone Mixer", "DAC Left Out Switch", "DACL"},
18562306a36Sopenharmony_ci	{"Right Headphone Mixer", "DAC Right Out Switch", "DACR"},
18662306a36Sopenharmony_ci	{"HP Left Out", NULL, "Left Headphone Mixer"},
18762306a36Sopenharmony_ci	{"HP Right Out", NULL, "Right Headphone Mixer"},
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	{"HP Left Switch", "HP Left Out Switch", "HP Left Out"},
19062306a36Sopenharmony_ci	{"HP Right Switch", "HP Right Out Switch", "HP Right Out"},
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	{"HPL", NULL, "HP Left Switch"},
19362306a36Sopenharmony_ci	{"HPR", NULL, "HP Right Switch"},
19462306a36Sopenharmony_ci};
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cistatic int rk3036_codec_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	struct snd_soc_component *component = dai->component;
19962306a36Sopenharmony_ci	unsigned int reg01_val = 0,  reg02_val = 0, reg03_val = 0;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	dev_dbg(component->dev, "rk3036_codec dai set fmt : %08x\n", fmt);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
20462306a36Sopenharmony_ci	case SND_SOC_DAIFMT_CBC_CFC:
20562306a36Sopenharmony_ci		reg01_val |= INNO_R01_PINDIR_IN_SLAVE |
20662306a36Sopenharmony_ci			     INNO_R01_I2SMODE_SLAVE;
20762306a36Sopenharmony_ci		break;
20862306a36Sopenharmony_ci	case SND_SOC_DAIFMT_CBP_CFP:
20962306a36Sopenharmony_ci		reg01_val |= INNO_R01_PINDIR_OUT_MASTER |
21062306a36Sopenharmony_ci			     INNO_R01_I2SMODE_MASTER;
21162306a36Sopenharmony_ci		break;
21262306a36Sopenharmony_ci	default:
21362306a36Sopenharmony_ci		dev_err(component->dev, "invalid fmt\n");
21462306a36Sopenharmony_ci		return -EINVAL;
21562306a36Sopenharmony_ci	}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
21862306a36Sopenharmony_ci	case SND_SOC_DAIFMT_DSP_A:
21962306a36Sopenharmony_ci		reg02_val |= INNO_R02_DACM_PCM;
22062306a36Sopenharmony_ci		break;
22162306a36Sopenharmony_ci	case SND_SOC_DAIFMT_I2S:
22262306a36Sopenharmony_ci		reg02_val |= INNO_R02_DACM_I2S;
22362306a36Sopenharmony_ci		break;
22462306a36Sopenharmony_ci	case SND_SOC_DAIFMT_RIGHT_J:
22562306a36Sopenharmony_ci		reg02_val |= INNO_R02_DACM_RJM;
22662306a36Sopenharmony_ci		break;
22762306a36Sopenharmony_ci	case SND_SOC_DAIFMT_LEFT_J:
22862306a36Sopenharmony_ci		reg02_val |= INNO_R02_DACM_LJM;
22962306a36Sopenharmony_ci		break;
23062306a36Sopenharmony_ci	default:
23162306a36Sopenharmony_ci		dev_err(component->dev, "set dai format failed\n");
23262306a36Sopenharmony_ci		return -EINVAL;
23362306a36Sopenharmony_ci	}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
23662306a36Sopenharmony_ci	case SND_SOC_DAIFMT_NB_NF:
23762306a36Sopenharmony_ci		reg02_val |= INNO_R02_LRCP_NORMAL;
23862306a36Sopenharmony_ci		reg03_val |= INNO_R03_BCP_NORMAL;
23962306a36Sopenharmony_ci		break;
24062306a36Sopenharmony_ci	case SND_SOC_DAIFMT_IB_IF:
24162306a36Sopenharmony_ci		reg02_val |= INNO_R02_LRCP_REVERSAL;
24262306a36Sopenharmony_ci		reg03_val |= INNO_R03_BCP_REVERSAL;
24362306a36Sopenharmony_ci		break;
24462306a36Sopenharmony_ci	case SND_SOC_DAIFMT_IB_NF:
24562306a36Sopenharmony_ci		reg02_val |= INNO_R02_LRCP_REVERSAL;
24662306a36Sopenharmony_ci		reg03_val |= INNO_R03_BCP_NORMAL;
24762306a36Sopenharmony_ci		break;
24862306a36Sopenharmony_ci	case SND_SOC_DAIFMT_NB_IF:
24962306a36Sopenharmony_ci		reg02_val |= INNO_R02_LRCP_NORMAL;
25062306a36Sopenharmony_ci		reg03_val |= INNO_R03_BCP_REVERSAL;
25162306a36Sopenharmony_ci		break;
25262306a36Sopenharmony_ci	default:
25362306a36Sopenharmony_ci		dev_err(component->dev, "set dai format failed\n");
25462306a36Sopenharmony_ci		return -EINVAL;
25562306a36Sopenharmony_ci	}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	snd_soc_component_update_bits(component, INNO_R01, INNO_R01_I2SMODE_MSK |
25862306a36Sopenharmony_ci			    INNO_R01_PINDIR_MSK, reg01_val);
25962306a36Sopenharmony_ci	snd_soc_component_update_bits(component, INNO_R02, INNO_R02_LRCP_MSK |
26062306a36Sopenharmony_ci			    INNO_R02_DACM_MSK, reg02_val);
26162306a36Sopenharmony_ci	snd_soc_component_update_bits(component, INNO_R03, INNO_R03_BCP_MSK, reg03_val);
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	return 0;
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_cistatic int rk3036_codec_dai_hw_params(struct snd_pcm_substream *substream,
26762306a36Sopenharmony_ci				      struct snd_pcm_hw_params *hw_params,
26862306a36Sopenharmony_ci				      struct snd_soc_dai *dai)
26962306a36Sopenharmony_ci{
27062306a36Sopenharmony_ci	struct snd_soc_component *component = dai->component;
27162306a36Sopenharmony_ci	unsigned int reg02_val = 0, reg03_val = 0;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	switch (params_format(hw_params)) {
27462306a36Sopenharmony_ci	case SNDRV_PCM_FORMAT_S16_LE:
27562306a36Sopenharmony_ci		reg02_val |= INNO_R02_VWL_16BIT;
27662306a36Sopenharmony_ci		break;
27762306a36Sopenharmony_ci	case SNDRV_PCM_FORMAT_S20_3LE:
27862306a36Sopenharmony_ci		reg02_val |= INNO_R02_VWL_20BIT;
27962306a36Sopenharmony_ci		break;
28062306a36Sopenharmony_ci	case SNDRV_PCM_FORMAT_S24_LE:
28162306a36Sopenharmony_ci		reg02_val |= INNO_R02_VWL_24BIT;
28262306a36Sopenharmony_ci		break;
28362306a36Sopenharmony_ci	case SNDRV_PCM_FORMAT_S32_LE:
28462306a36Sopenharmony_ci		reg02_val |= INNO_R02_VWL_32BIT;
28562306a36Sopenharmony_ci		break;
28662306a36Sopenharmony_ci	default:
28762306a36Sopenharmony_ci		return -EINVAL;
28862306a36Sopenharmony_ci	}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	reg02_val |= INNO_R02_LRCP_NORMAL;
29162306a36Sopenharmony_ci	reg03_val |= INNO_R03_FWL_32BIT | INNO_R03_DACR_WORK;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	snd_soc_component_update_bits(component, INNO_R02, INNO_R02_LRCP_MSK |
29462306a36Sopenharmony_ci			    INNO_R02_VWL_MSK, reg02_val);
29562306a36Sopenharmony_ci	snd_soc_component_update_bits(component, INNO_R03, INNO_R03_DACR_MSK |
29662306a36Sopenharmony_ci			    INNO_R03_FWL_MSK, reg03_val);
29762306a36Sopenharmony_ci	return 0;
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci#define RK3036_CODEC_RATES (SNDRV_PCM_RATE_8000  | \
30162306a36Sopenharmony_ci			    SNDRV_PCM_RATE_16000 | \
30262306a36Sopenharmony_ci			    SNDRV_PCM_RATE_32000 | \
30362306a36Sopenharmony_ci			    SNDRV_PCM_RATE_44100 | \
30462306a36Sopenharmony_ci			    SNDRV_PCM_RATE_48000 | \
30562306a36Sopenharmony_ci			    SNDRV_PCM_RATE_96000)
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci#define RK3036_CODEC_FMTS (SNDRV_PCM_FMTBIT_S16_LE  | \
30862306a36Sopenharmony_ci			   SNDRV_PCM_FMTBIT_S20_3LE | \
30962306a36Sopenharmony_ci			   SNDRV_PCM_FMTBIT_S24_LE  | \
31062306a36Sopenharmony_ci			   SNDRV_PCM_FMTBIT_S32_LE)
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_cistatic const struct snd_soc_dai_ops rk3036_codec_dai_ops = {
31362306a36Sopenharmony_ci	.set_fmt	= rk3036_codec_dai_set_fmt,
31462306a36Sopenharmony_ci	.hw_params	= rk3036_codec_dai_hw_params,
31562306a36Sopenharmony_ci};
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_cistatic struct snd_soc_dai_driver rk3036_codec_dai_driver[] = {
31862306a36Sopenharmony_ci	{
31962306a36Sopenharmony_ci		.name = "rk3036-codec-dai",
32062306a36Sopenharmony_ci		.playback = {
32162306a36Sopenharmony_ci			.stream_name = "Playback",
32262306a36Sopenharmony_ci			.channels_min = 1,
32362306a36Sopenharmony_ci			.channels_max = 2,
32462306a36Sopenharmony_ci			.rates = RK3036_CODEC_RATES,
32562306a36Sopenharmony_ci			.formats = RK3036_CODEC_FMTS,
32662306a36Sopenharmony_ci		},
32762306a36Sopenharmony_ci		.ops = &rk3036_codec_dai_ops,
32862306a36Sopenharmony_ci		.symmetric_rate = 1,
32962306a36Sopenharmony_ci	},
33062306a36Sopenharmony_ci};
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_cistatic void rk3036_codec_reset(struct snd_soc_component *component)
33362306a36Sopenharmony_ci{
33462306a36Sopenharmony_ci	snd_soc_component_write(component, INNO_R00,
33562306a36Sopenharmony_ci		      INNO_R00_CSR_RESET | INNO_R00_CDCR_RESET);
33662306a36Sopenharmony_ci	snd_soc_component_write(component, INNO_R00,
33762306a36Sopenharmony_ci		      INNO_R00_CSR_WORK | INNO_R00_CDCR_WORK);
33862306a36Sopenharmony_ci}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_cistatic int rk3036_codec_probe(struct snd_soc_component *component)
34162306a36Sopenharmony_ci{
34262306a36Sopenharmony_ci	rk3036_codec_reset(component);
34362306a36Sopenharmony_ci	return 0;
34462306a36Sopenharmony_ci}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_cistatic void rk3036_codec_remove(struct snd_soc_component *component)
34762306a36Sopenharmony_ci{
34862306a36Sopenharmony_ci	rk3036_codec_reset(component);
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_cistatic int rk3036_codec_set_bias_level(struct snd_soc_component *component,
35262306a36Sopenharmony_ci				       enum snd_soc_bias_level level)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	switch (level) {
35562306a36Sopenharmony_ci	case SND_SOC_BIAS_STANDBY:
35662306a36Sopenharmony_ci		/* set a big current for capacitor charging. */
35762306a36Sopenharmony_ci		snd_soc_component_write(component, INNO_R10, INNO_R10_MAX_CUR);
35862306a36Sopenharmony_ci		/* start precharge */
35962306a36Sopenharmony_ci		snd_soc_component_write(component, INNO_R06, INNO_R06_DAC_PRECHARGE);
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci		break;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	case SND_SOC_BIAS_OFF:
36462306a36Sopenharmony_ci		/* set a big current for capacitor discharging. */
36562306a36Sopenharmony_ci		snd_soc_component_write(component, INNO_R10, INNO_R10_MAX_CUR);
36662306a36Sopenharmony_ci		/* start discharge. */
36762306a36Sopenharmony_ci		snd_soc_component_write(component, INNO_R06, INNO_R06_DAC_DISCHARGE);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci		break;
37062306a36Sopenharmony_ci	default:
37162306a36Sopenharmony_ci		break;
37262306a36Sopenharmony_ci	}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	return 0;
37562306a36Sopenharmony_ci}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_cistatic const struct snd_soc_component_driver rk3036_codec_driver = {
37862306a36Sopenharmony_ci	.probe			= rk3036_codec_probe,
37962306a36Sopenharmony_ci	.remove			= rk3036_codec_remove,
38062306a36Sopenharmony_ci	.set_bias_level		= rk3036_codec_set_bias_level,
38162306a36Sopenharmony_ci	.controls		= rk3036_codec_dapm_controls,
38262306a36Sopenharmony_ci	.num_controls		= ARRAY_SIZE(rk3036_codec_dapm_controls),
38362306a36Sopenharmony_ci	.dapm_routes		= rk3036_codec_dapm_routes,
38462306a36Sopenharmony_ci	.num_dapm_routes	= ARRAY_SIZE(rk3036_codec_dapm_routes),
38562306a36Sopenharmony_ci	.dapm_widgets		= rk3036_codec_dapm_widgets,
38662306a36Sopenharmony_ci	.num_dapm_widgets	= ARRAY_SIZE(rk3036_codec_dapm_widgets),
38762306a36Sopenharmony_ci	.idle_bias_on		= 1,
38862306a36Sopenharmony_ci	.use_pmdown_time	= 1,
38962306a36Sopenharmony_ci	.endianness		= 1,
39062306a36Sopenharmony_ci};
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_cistatic const struct regmap_config rk3036_codec_regmap_config = {
39362306a36Sopenharmony_ci	.reg_bits = 32,
39462306a36Sopenharmony_ci	.reg_stride = 4,
39562306a36Sopenharmony_ci	.val_bits = 32,
39662306a36Sopenharmony_ci};
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci#define GRF_SOC_CON0		0x00140
39962306a36Sopenharmony_ci#define GRF_ACODEC_SEL		(BIT(10) | BIT(16 + 10))
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_cistatic int rk3036_codec_platform_probe(struct platform_device *pdev)
40262306a36Sopenharmony_ci{
40362306a36Sopenharmony_ci	struct rk3036_codec_priv *priv;
40462306a36Sopenharmony_ci	struct device_node *of_node = pdev->dev.of_node;
40562306a36Sopenharmony_ci	void __iomem *base;
40662306a36Sopenharmony_ci	struct regmap *grf;
40762306a36Sopenharmony_ci	int ret;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
41062306a36Sopenharmony_ci	if (!priv)
41162306a36Sopenharmony_ci		return -ENOMEM;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	base = devm_platform_ioremap_resource(pdev, 0);
41462306a36Sopenharmony_ci	if (IS_ERR(base))
41562306a36Sopenharmony_ci		return PTR_ERR(base);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	priv->base = base;
41862306a36Sopenharmony_ci	priv->regmap = devm_regmap_init_mmio(&pdev->dev, priv->base,
41962306a36Sopenharmony_ci					     &rk3036_codec_regmap_config);
42062306a36Sopenharmony_ci	if (IS_ERR(priv->regmap)) {
42162306a36Sopenharmony_ci		dev_err(&pdev->dev, "init regmap failed\n");
42262306a36Sopenharmony_ci		return PTR_ERR(priv->regmap);
42362306a36Sopenharmony_ci	}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	grf = syscon_regmap_lookup_by_phandle(of_node, "rockchip,grf");
42662306a36Sopenharmony_ci	if (IS_ERR(grf)) {
42762306a36Sopenharmony_ci		dev_err(&pdev->dev, "needs 'rockchip,grf' property\n");
42862306a36Sopenharmony_ci		return PTR_ERR(grf);
42962306a36Sopenharmony_ci	}
43062306a36Sopenharmony_ci	ret = regmap_write(grf, GRF_SOC_CON0, GRF_ACODEC_SEL);
43162306a36Sopenharmony_ci	if (ret) {
43262306a36Sopenharmony_ci		dev_err(&pdev->dev, "Could not write to GRF: %d\n", ret);
43362306a36Sopenharmony_ci		return ret;
43462306a36Sopenharmony_ci	}
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	priv->pclk = devm_clk_get(&pdev->dev, "acodec_pclk");
43762306a36Sopenharmony_ci	if (IS_ERR(priv->pclk))
43862306a36Sopenharmony_ci		return PTR_ERR(priv->pclk);
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	ret = clk_prepare_enable(priv->pclk);
44162306a36Sopenharmony_ci	if (ret < 0) {
44262306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to enable clk\n");
44362306a36Sopenharmony_ci		return ret;
44462306a36Sopenharmony_ci	}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	priv->dev = &pdev->dev;
44762306a36Sopenharmony_ci	dev_set_drvdata(&pdev->dev, priv);
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	ret = devm_snd_soc_register_component(&pdev->dev, &rk3036_codec_driver,
45062306a36Sopenharmony_ci				     rk3036_codec_dai_driver,
45162306a36Sopenharmony_ci				     ARRAY_SIZE(rk3036_codec_dai_driver));
45262306a36Sopenharmony_ci	if (ret) {
45362306a36Sopenharmony_ci		clk_disable_unprepare(priv->pclk);
45462306a36Sopenharmony_ci		dev_set_drvdata(&pdev->dev, NULL);
45562306a36Sopenharmony_ci	}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	return ret;
45862306a36Sopenharmony_ci}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_cistatic void rk3036_codec_platform_remove(struct platform_device *pdev)
46162306a36Sopenharmony_ci{
46262306a36Sopenharmony_ci	struct rk3036_codec_priv *priv = dev_get_drvdata(&pdev->dev);
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	clk_disable_unprepare(priv->pclk);
46562306a36Sopenharmony_ci}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_cistatic const struct of_device_id rk3036_codec_of_match[] __maybe_unused = {
46862306a36Sopenharmony_ci	{ .compatible = "rockchip,rk3036-codec", },
46962306a36Sopenharmony_ci	{}
47062306a36Sopenharmony_ci};
47162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, rk3036_codec_of_match);
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_cistatic struct platform_driver rk3036_codec_platform_driver = {
47462306a36Sopenharmony_ci	.driver = {
47562306a36Sopenharmony_ci		.name = "rk3036-codec-platform",
47662306a36Sopenharmony_ci		.of_match_table = of_match_ptr(rk3036_codec_of_match),
47762306a36Sopenharmony_ci	},
47862306a36Sopenharmony_ci	.probe = rk3036_codec_platform_probe,
47962306a36Sopenharmony_ci	.remove_new = rk3036_codec_platform_remove,
48062306a36Sopenharmony_ci};
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_cimodule_platform_driver(rk3036_codec_platform_driver);
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ciMODULE_AUTHOR("Rockchip Inc.");
48562306a36Sopenharmony_ciMODULE_DESCRIPTION("Rockchip rk3036 codec driver");
48662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
487