18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Driver of Inno codec for rk3036 by Rockchip Inc.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Author: Rockchip Inc.
68c2ecf20Sopenharmony_ci * Author: Zheng ShunQian<zhengsq@rock-chips.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <sound/soc.h>
108c2ecf20Sopenharmony_ci#include <sound/tlv.h>
118c2ecf20Sopenharmony_ci#include <sound/soc-dapm.h>
128c2ecf20Sopenharmony_ci#include <sound/soc-dai.h>
138c2ecf20Sopenharmony_ci#include <sound/pcm.h>
148c2ecf20Sopenharmony_ci#include <sound/pcm_params.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
178c2ecf20Sopenharmony_ci#include <linux/of.h>
188c2ecf20Sopenharmony_ci#include <linux/clk.h>
198c2ecf20Sopenharmony_ci#include <linux/regmap.h>
208c2ecf20Sopenharmony_ci#include <linux/device.h>
218c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h>
228c2ecf20Sopenharmony_ci#include <linux/module.h>
238c2ecf20Sopenharmony_ci#include <linux/io.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include "inno_rk3036.h"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistruct rk3036_codec_priv {
288c2ecf20Sopenharmony_ci	void __iomem *base;
298c2ecf20Sopenharmony_ci	struct clk *pclk;
308c2ecf20Sopenharmony_ci	struct regmap *regmap;
318c2ecf20Sopenharmony_ci	struct device *dev;
328c2ecf20Sopenharmony_ci};
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_MINMAX(rk3036_codec_hp_tlv, -39, 0);
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic int rk3036_codec_antipop_info(struct snd_kcontrol *kcontrol,
378c2ecf20Sopenharmony_ci				     struct snd_ctl_elem_info *uinfo)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
408c2ecf20Sopenharmony_ci	uinfo->count = 2;
418c2ecf20Sopenharmony_ci	uinfo->value.integer.min = 0;
428c2ecf20Sopenharmony_ci	uinfo->value.integer.max = 1;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	return 0;
458c2ecf20Sopenharmony_ci}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic int rk3036_codec_antipop_get(struct snd_kcontrol *kcontrol,
488c2ecf20Sopenharmony_ci				    struct snd_ctl_elem_value *ucontrol)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
518c2ecf20Sopenharmony_ci	int val, regval;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	regval = snd_soc_component_read(component, INNO_R09);
548c2ecf20Sopenharmony_ci	val = ((regval >> INNO_R09_HPL_ANITPOP_SHIFT) &
558c2ecf20Sopenharmony_ci	       INNO_R09_HP_ANTIPOP_MSK) == INNO_R09_HP_ANTIPOP_ON;
568c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = val;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	val = ((regval >> INNO_R09_HPR_ANITPOP_SHIFT) &
598c2ecf20Sopenharmony_ci	       INNO_R09_HP_ANTIPOP_MSK) == INNO_R09_HP_ANTIPOP_ON;
608c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[1] = val;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	return 0;
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic int rk3036_codec_antipop_put(struct snd_kcontrol *kcontrol,
668c2ecf20Sopenharmony_ci				    struct snd_ctl_elem_value *ucontrol)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
698c2ecf20Sopenharmony_ci	int val, ret, regmsk;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	val = (ucontrol->value.integer.value[0] ?
728c2ecf20Sopenharmony_ci	       INNO_R09_HP_ANTIPOP_ON : INNO_R09_HP_ANTIPOP_OFF) <<
738c2ecf20Sopenharmony_ci	      INNO_R09_HPL_ANITPOP_SHIFT;
748c2ecf20Sopenharmony_ci	val |= (ucontrol->value.integer.value[1] ?
758c2ecf20Sopenharmony_ci		INNO_R09_HP_ANTIPOP_ON : INNO_R09_HP_ANTIPOP_OFF) <<
768c2ecf20Sopenharmony_ci	       INNO_R09_HPR_ANITPOP_SHIFT;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	regmsk = INNO_R09_HP_ANTIPOP_MSK << INNO_R09_HPL_ANITPOP_SHIFT |
798c2ecf20Sopenharmony_ci		 INNO_R09_HP_ANTIPOP_MSK << INNO_R09_HPR_ANITPOP_SHIFT;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	ret = snd_soc_component_update_bits(component, INNO_R09,
828c2ecf20Sopenharmony_ci					    regmsk, val);
838c2ecf20Sopenharmony_ci	if (ret < 0)
848c2ecf20Sopenharmony_ci		return ret;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	return 0;
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci#define SOC_RK3036_CODEC_ANTIPOP_DECL(xname) \
908c2ecf20Sopenharmony_ci{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
918c2ecf20Sopenharmony_ci	.info = rk3036_codec_antipop_info, .get = rk3036_codec_antipop_get, \
928c2ecf20Sopenharmony_ci	.put = rk3036_codec_antipop_put, }
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new rk3036_codec_dapm_controls[] = {
958c2ecf20Sopenharmony_ci	SOC_DOUBLE_R_RANGE_TLV("Headphone Volume", INNO_R07, INNO_R08,
968c2ecf20Sopenharmony_ci		INNO_HP_GAIN_SHIFT, INNO_HP_GAIN_N39DB,
978c2ecf20Sopenharmony_ci		INNO_HP_GAIN_0DB, 0, rk3036_codec_hp_tlv),
988c2ecf20Sopenharmony_ci	SOC_DOUBLE("Zero Cross Switch", INNO_R06, INNO_R06_VOUTL_CZ_SHIFT,
998c2ecf20Sopenharmony_ci		INNO_R06_VOUTR_CZ_SHIFT, 1, 0),
1008c2ecf20Sopenharmony_ci	SOC_DOUBLE("Headphone Switch", INNO_R09, INNO_R09_HPL_MUTE_SHIFT,
1018c2ecf20Sopenharmony_ci		INNO_R09_HPR_MUTE_SHIFT, 1, 0),
1028c2ecf20Sopenharmony_ci	SOC_RK3036_CODEC_ANTIPOP_DECL("Anti-pop Switch"),
1038c2ecf20Sopenharmony_ci};
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new rk3036_codec_hpl_mixer_controls[] = {
1068c2ecf20Sopenharmony_ci	SOC_DAPM_SINGLE("DAC Left Out Switch", INNO_R09,
1078c2ecf20Sopenharmony_ci			INNO_R09_DACL_SWITCH_SHIFT, 1, 0),
1088c2ecf20Sopenharmony_ci};
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new rk3036_codec_hpr_mixer_controls[] = {
1118c2ecf20Sopenharmony_ci	SOC_DAPM_SINGLE("DAC Right Out Switch", INNO_R09,
1128c2ecf20Sopenharmony_ci			INNO_R09_DACR_SWITCH_SHIFT, 1, 0),
1138c2ecf20Sopenharmony_ci};
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new rk3036_codec_hpl_switch_controls[] = {
1168c2ecf20Sopenharmony_ci	SOC_DAPM_SINGLE("HP Left Out Switch", INNO_R05,
1178c2ecf20Sopenharmony_ci			INNO_R05_HPL_WORK_SHIFT, 1, 0),
1188c2ecf20Sopenharmony_ci};
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new rk3036_codec_hpr_switch_controls[] = {
1218c2ecf20Sopenharmony_ci	SOC_DAPM_SINGLE("HP Right Out Switch", INNO_R05,
1228c2ecf20Sopenharmony_ci			INNO_R05_HPR_WORK_SHIFT, 1, 0),
1238c2ecf20Sopenharmony_ci};
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_widget rk3036_codec_dapm_widgets[] = {
1268c2ecf20Sopenharmony_ci	SND_SOC_DAPM_SUPPLY_S("DAC PWR", 1, INNO_R06,
1278c2ecf20Sopenharmony_ci			      INNO_R06_DAC_EN_SHIFT, 0, NULL, 0),
1288c2ecf20Sopenharmony_ci	SND_SOC_DAPM_SUPPLY_S("DACL VREF", 2, INNO_R04,
1298c2ecf20Sopenharmony_ci			      INNO_R04_DACL_VREF_SHIFT, 0, NULL, 0),
1308c2ecf20Sopenharmony_ci	SND_SOC_DAPM_SUPPLY_S("DACR VREF", 2, INNO_R04,
1318c2ecf20Sopenharmony_ci			      INNO_R04_DACR_VREF_SHIFT, 0, NULL, 0),
1328c2ecf20Sopenharmony_ci	SND_SOC_DAPM_SUPPLY_S("DACL HiLo VREF", 3, INNO_R06,
1338c2ecf20Sopenharmony_ci			      INNO_R06_DACL_HILO_VREF_SHIFT, 0, NULL, 0),
1348c2ecf20Sopenharmony_ci	SND_SOC_DAPM_SUPPLY_S("DACR HiLo VREF", 3, INNO_R06,
1358c2ecf20Sopenharmony_ci			      INNO_R06_DACR_HILO_VREF_SHIFT, 0, NULL, 0),
1368c2ecf20Sopenharmony_ci	SND_SOC_DAPM_SUPPLY_S("DACR CLK", 3, INNO_R04,
1378c2ecf20Sopenharmony_ci			      INNO_R04_DACR_CLK_SHIFT, 0, NULL, 0),
1388c2ecf20Sopenharmony_ci	SND_SOC_DAPM_SUPPLY_S("DACL CLK", 3, INNO_R04,
1398c2ecf20Sopenharmony_ci			      INNO_R04_DACL_CLK_SHIFT, 0, NULL, 0),
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	SND_SOC_DAPM_DAC("DACL", "Left Playback", INNO_R04,
1428c2ecf20Sopenharmony_ci			 INNO_R04_DACL_SW_SHIFT, 0),
1438c2ecf20Sopenharmony_ci	SND_SOC_DAPM_DAC("DACR", "Right Playback", INNO_R04,
1448c2ecf20Sopenharmony_ci			 INNO_R04_DACR_SW_SHIFT, 0),
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	SND_SOC_DAPM_MIXER("Left Headphone Mixer", SND_SOC_NOPM, 0, 0,
1478c2ecf20Sopenharmony_ci		rk3036_codec_hpl_mixer_controls,
1488c2ecf20Sopenharmony_ci		ARRAY_SIZE(rk3036_codec_hpl_mixer_controls)),
1498c2ecf20Sopenharmony_ci	SND_SOC_DAPM_MIXER("Right Headphone Mixer", SND_SOC_NOPM, 0, 0,
1508c2ecf20Sopenharmony_ci		rk3036_codec_hpr_mixer_controls,
1518c2ecf20Sopenharmony_ci		ARRAY_SIZE(rk3036_codec_hpr_mixer_controls)),
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	SND_SOC_DAPM_PGA("HP Left Out", INNO_R05,
1548c2ecf20Sopenharmony_ci			 INNO_R05_HPL_EN_SHIFT, 0, NULL, 0),
1558c2ecf20Sopenharmony_ci	SND_SOC_DAPM_PGA("HP Right Out", INNO_R05,
1568c2ecf20Sopenharmony_ci			 INNO_R05_HPR_EN_SHIFT, 0, NULL, 0),
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	SND_SOC_DAPM_MIXER("HP Left Switch",  SND_SOC_NOPM, 0, 0,
1598c2ecf20Sopenharmony_ci			   rk3036_codec_hpl_switch_controls,
1608c2ecf20Sopenharmony_ci			   ARRAY_SIZE(rk3036_codec_hpl_switch_controls)),
1618c2ecf20Sopenharmony_ci	SND_SOC_DAPM_MIXER("HP Right Switch",  SND_SOC_NOPM, 0, 0,
1628c2ecf20Sopenharmony_ci			   rk3036_codec_hpr_switch_controls,
1638c2ecf20Sopenharmony_ci			   ARRAY_SIZE(rk3036_codec_hpr_switch_controls)),
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	SND_SOC_DAPM_OUTPUT("HPL"),
1668c2ecf20Sopenharmony_ci	SND_SOC_DAPM_OUTPUT("HPR"),
1678c2ecf20Sopenharmony_ci};
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_route rk3036_codec_dapm_routes[] = {
1708c2ecf20Sopenharmony_ci	{"DACL VREF", NULL, "DAC PWR"},
1718c2ecf20Sopenharmony_ci	{"DACR VREF", NULL, "DAC PWR"},
1728c2ecf20Sopenharmony_ci	{"DACL HiLo VREF", NULL, "DAC PWR"},
1738c2ecf20Sopenharmony_ci	{"DACR HiLo VREF", NULL, "DAC PWR"},
1748c2ecf20Sopenharmony_ci	{"DACL CLK", NULL, "DAC PWR"},
1758c2ecf20Sopenharmony_ci	{"DACR CLK", NULL, "DAC PWR"},
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	{"DACL", NULL, "DACL VREF"},
1788c2ecf20Sopenharmony_ci	{"DACL", NULL, "DACL HiLo VREF"},
1798c2ecf20Sopenharmony_ci	{"DACL", NULL, "DACL CLK"},
1808c2ecf20Sopenharmony_ci	{"DACR", NULL, "DACR VREF"},
1818c2ecf20Sopenharmony_ci	{"DACR", NULL, "DACR HiLo VREF"},
1828c2ecf20Sopenharmony_ci	{"DACR", NULL, "DACR CLK"},
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	{"Left Headphone Mixer", "DAC Left Out Switch", "DACL"},
1858c2ecf20Sopenharmony_ci	{"Right Headphone Mixer", "DAC Right Out Switch", "DACR"},
1868c2ecf20Sopenharmony_ci	{"HP Left Out", NULL, "Left Headphone Mixer"},
1878c2ecf20Sopenharmony_ci	{"HP Right Out", NULL, "Right Headphone Mixer"},
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	{"HP Left Switch", "HP Left Out Switch", "HP Left Out"},
1908c2ecf20Sopenharmony_ci	{"HP Right Switch", "HP Right Out Switch", "HP Right Out"},
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	{"HPL", NULL, "HP Left Switch"},
1938c2ecf20Sopenharmony_ci	{"HPR", NULL, "HP Right Switch"},
1948c2ecf20Sopenharmony_ci};
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_cistatic int rk3036_codec_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
1978c2ecf20Sopenharmony_ci{
1988c2ecf20Sopenharmony_ci	struct snd_soc_component *component = dai->component;
1998c2ecf20Sopenharmony_ci	unsigned int reg01_val = 0,  reg02_val = 0, reg03_val = 0;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	dev_dbg(component->dev, "rk3036_codec dai set fmt : %08x\n", fmt);
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
2048c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_CBS_CFS:
2058c2ecf20Sopenharmony_ci		reg01_val |= INNO_R01_PINDIR_IN_SLAVE |
2068c2ecf20Sopenharmony_ci			     INNO_R01_I2SMODE_SLAVE;
2078c2ecf20Sopenharmony_ci		break;
2088c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_CBM_CFM:
2098c2ecf20Sopenharmony_ci		reg01_val |= INNO_R01_PINDIR_OUT_MASTER |
2108c2ecf20Sopenharmony_ci			     INNO_R01_I2SMODE_MASTER;
2118c2ecf20Sopenharmony_ci		break;
2128c2ecf20Sopenharmony_ci	default:
2138c2ecf20Sopenharmony_ci		dev_err(component->dev, "invalid fmt\n");
2148c2ecf20Sopenharmony_ci		return -EINVAL;
2158c2ecf20Sopenharmony_ci	}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
2188c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_DSP_A:
2198c2ecf20Sopenharmony_ci		reg02_val |= INNO_R02_DACM_PCM;
2208c2ecf20Sopenharmony_ci		break;
2218c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_I2S:
2228c2ecf20Sopenharmony_ci		reg02_val |= INNO_R02_DACM_I2S;
2238c2ecf20Sopenharmony_ci		break;
2248c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_RIGHT_J:
2258c2ecf20Sopenharmony_ci		reg02_val |= INNO_R02_DACM_RJM;
2268c2ecf20Sopenharmony_ci		break;
2278c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_LEFT_J:
2288c2ecf20Sopenharmony_ci		reg02_val |= INNO_R02_DACM_LJM;
2298c2ecf20Sopenharmony_ci		break;
2308c2ecf20Sopenharmony_ci	default:
2318c2ecf20Sopenharmony_ci		dev_err(component->dev, "set dai format failed\n");
2328c2ecf20Sopenharmony_ci		return -EINVAL;
2338c2ecf20Sopenharmony_ci	}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
2368c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_NB_NF:
2378c2ecf20Sopenharmony_ci		reg02_val |= INNO_R02_LRCP_NORMAL;
2388c2ecf20Sopenharmony_ci		reg03_val |= INNO_R03_BCP_NORMAL;
2398c2ecf20Sopenharmony_ci		break;
2408c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_IB_IF:
2418c2ecf20Sopenharmony_ci		reg02_val |= INNO_R02_LRCP_REVERSAL;
2428c2ecf20Sopenharmony_ci		reg03_val |= INNO_R03_BCP_REVERSAL;
2438c2ecf20Sopenharmony_ci		break;
2448c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_IB_NF:
2458c2ecf20Sopenharmony_ci		reg02_val |= INNO_R02_LRCP_REVERSAL;
2468c2ecf20Sopenharmony_ci		reg03_val |= INNO_R03_BCP_NORMAL;
2478c2ecf20Sopenharmony_ci		break;
2488c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_NB_IF:
2498c2ecf20Sopenharmony_ci		reg02_val |= INNO_R02_LRCP_NORMAL;
2508c2ecf20Sopenharmony_ci		reg03_val |= INNO_R03_BCP_REVERSAL;
2518c2ecf20Sopenharmony_ci		break;
2528c2ecf20Sopenharmony_ci	default:
2538c2ecf20Sopenharmony_ci		dev_err(component->dev, "set dai format failed\n");
2548c2ecf20Sopenharmony_ci		return -EINVAL;
2558c2ecf20Sopenharmony_ci	}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	snd_soc_component_update_bits(component, INNO_R01, INNO_R01_I2SMODE_MSK |
2588c2ecf20Sopenharmony_ci			    INNO_R01_PINDIR_MSK, reg01_val);
2598c2ecf20Sopenharmony_ci	snd_soc_component_update_bits(component, INNO_R02, INNO_R02_LRCP_MSK |
2608c2ecf20Sopenharmony_ci			    INNO_R02_DACM_MSK, reg02_val);
2618c2ecf20Sopenharmony_ci	snd_soc_component_update_bits(component, INNO_R03, INNO_R03_BCP_MSK, reg03_val);
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	return 0;
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_cistatic int rk3036_codec_dai_hw_params(struct snd_pcm_substream *substream,
2678c2ecf20Sopenharmony_ci				      struct snd_pcm_hw_params *hw_params,
2688c2ecf20Sopenharmony_ci				      struct snd_soc_dai *dai)
2698c2ecf20Sopenharmony_ci{
2708c2ecf20Sopenharmony_ci	struct snd_soc_component *component = dai->component;
2718c2ecf20Sopenharmony_ci	unsigned int reg02_val = 0, reg03_val = 0;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	switch (params_format(hw_params)) {
2748c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_S16_LE:
2758c2ecf20Sopenharmony_ci		reg02_val |= INNO_R02_VWL_16BIT;
2768c2ecf20Sopenharmony_ci		break;
2778c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_S20_3LE:
2788c2ecf20Sopenharmony_ci		reg02_val |= INNO_R02_VWL_20BIT;
2798c2ecf20Sopenharmony_ci		break;
2808c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_S24_LE:
2818c2ecf20Sopenharmony_ci		reg02_val |= INNO_R02_VWL_24BIT;
2828c2ecf20Sopenharmony_ci		break;
2838c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_S32_LE:
2848c2ecf20Sopenharmony_ci		reg02_val |= INNO_R02_VWL_32BIT;
2858c2ecf20Sopenharmony_ci		break;
2868c2ecf20Sopenharmony_ci	default:
2878c2ecf20Sopenharmony_ci		return -EINVAL;
2888c2ecf20Sopenharmony_ci	}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	reg02_val |= INNO_R02_LRCP_NORMAL;
2918c2ecf20Sopenharmony_ci	reg03_val |= INNO_R03_FWL_32BIT | INNO_R03_DACR_WORK;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	snd_soc_component_update_bits(component, INNO_R02, INNO_R02_LRCP_MSK |
2948c2ecf20Sopenharmony_ci			    INNO_R02_VWL_MSK, reg02_val);
2958c2ecf20Sopenharmony_ci	snd_soc_component_update_bits(component, INNO_R03, INNO_R03_DACR_MSK |
2968c2ecf20Sopenharmony_ci			    INNO_R03_FWL_MSK, reg03_val);
2978c2ecf20Sopenharmony_ci	return 0;
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci#define RK3036_CODEC_RATES (SNDRV_PCM_RATE_8000  | \
3018c2ecf20Sopenharmony_ci			    SNDRV_PCM_RATE_16000 | \
3028c2ecf20Sopenharmony_ci			    SNDRV_PCM_RATE_32000 | \
3038c2ecf20Sopenharmony_ci			    SNDRV_PCM_RATE_44100 | \
3048c2ecf20Sopenharmony_ci			    SNDRV_PCM_RATE_48000 | \
3058c2ecf20Sopenharmony_ci			    SNDRV_PCM_RATE_96000)
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci#define RK3036_CODEC_FMTS (SNDRV_PCM_FMTBIT_S16_LE  | \
3088c2ecf20Sopenharmony_ci			   SNDRV_PCM_FMTBIT_S20_3LE | \
3098c2ecf20Sopenharmony_ci			   SNDRV_PCM_FMTBIT_S24_LE  | \
3108c2ecf20Sopenharmony_ci			   SNDRV_PCM_FMTBIT_S32_LE)
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops rk3036_codec_dai_ops = {
3138c2ecf20Sopenharmony_ci	.set_fmt	= rk3036_codec_dai_set_fmt,
3148c2ecf20Sopenharmony_ci	.hw_params	= rk3036_codec_dai_hw_params,
3158c2ecf20Sopenharmony_ci};
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver rk3036_codec_dai_driver[] = {
3188c2ecf20Sopenharmony_ci	{
3198c2ecf20Sopenharmony_ci		.name = "rk3036-codec-dai",
3208c2ecf20Sopenharmony_ci		.playback = {
3218c2ecf20Sopenharmony_ci			.stream_name = "Playback",
3228c2ecf20Sopenharmony_ci			.channels_min = 1,
3238c2ecf20Sopenharmony_ci			.channels_max = 2,
3248c2ecf20Sopenharmony_ci			.rates = RK3036_CODEC_RATES,
3258c2ecf20Sopenharmony_ci			.formats = RK3036_CODEC_FMTS,
3268c2ecf20Sopenharmony_ci		},
3278c2ecf20Sopenharmony_ci		.ops = &rk3036_codec_dai_ops,
3288c2ecf20Sopenharmony_ci		.symmetric_rates = 1,
3298c2ecf20Sopenharmony_ci	},
3308c2ecf20Sopenharmony_ci};
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_cistatic void rk3036_codec_reset(struct snd_soc_component *component)
3338c2ecf20Sopenharmony_ci{
3348c2ecf20Sopenharmony_ci	snd_soc_component_write(component, INNO_R00,
3358c2ecf20Sopenharmony_ci		      INNO_R00_CSR_RESET | INNO_R00_CDCR_RESET);
3368c2ecf20Sopenharmony_ci	snd_soc_component_write(component, INNO_R00,
3378c2ecf20Sopenharmony_ci		      INNO_R00_CSR_WORK | INNO_R00_CDCR_WORK);
3388c2ecf20Sopenharmony_ci}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_cistatic int rk3036_codec_probe(struct snd_soc_component *component)
3418c2ecf20Sopenharmony_ci{
3428c2ecf20Sopenharmony_ci	rk3036_codec_reset(component);
3438c2ecf20Sopenharmony_ci	return 0;
3448c2ecf20Sopenharmony_ci}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_cistatic void rk3036_codec_remove(struct snd_soc_component *component)
3478c2ecf20Sopenharmony_ci{
3488c2ecf20Sopenharmony_ci	rk3036_codec_reset(component);
3498c2ecf20Sopenharmony_ci}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_cistatic int rk3036_codec_set_bias_level(struct snd_soc_component *component,
3528c2ecf20Sopenharmony_ci				       enum snd_soc_bias_level level)
3538c2ecf20Sopenharmony_ci{
3548c2ecf20Sopenharmony_ci	switch (level) {
3558c2ecf20Sopenharmony_ci	case SND_SOC_BIAS_STANDBY:
3568c2ecf20Sopenharmony_ci		/* set a big current for capacitor charging. */
3578c2ecf20Sopenharmony_ci		snd_soc_component_write(component, INNO_R10, INNO_R10_MAX_CUR);
3588c2ecf20Sopenharmony_ci		/* start precharge */
3598c2ecf20Sopenharmony_ci		snd_soc_component_write(component, INNO_R06, INNO_R06_DAC_PRECHARGE);
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci		break;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	case SND_SOC_BIAS_OFF:
3648c2ecf20Sopenharmony_ci		/* set a big current for capacitor discharging. */
3658c2ecf20Sopenharmony_ci		snd_soc_component_write(component, INNO_R10, INNO_R10_MAX_CUR);
3668c2ecf20Sopenharmony_ci		/* start discharge. */
3678c2ecf20Sopenharmony_ci		snd_soc_component_write(component, INNO_R06, INNO_R06_DAC_DISCHARGE);
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci		break;
3708c2ecf20Sopenharmony_ci	default:
3718c2ecf20Sopenharmony_ci		break;
3728c2ecf20Sopenharmony_ci	}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	return 0;
3758c2ecf20Sopenharmony_ci}
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver rk3036_codec_driver = {
3788c2ecf20Sopenharmony_ci	.probe			= rk3036_codec_probe,
3798c2ecf20Sopenharmony_ci	.remove			= rk3036_codec_remove,
3808c2ecf20Sopenharmony_ci	.set_bias_level		= rk3036_codec_set_bias_level,
3818c2ecf20Sopenharmony_ci	.controls		= rk3036_codec_dapm_controls,
3828c2ecf20Sopenharmony_ci	.num_controls		= ARRAY_SIZE(rk3036_codec_dapm_controls),
3838c2ecf20Sopenharmony_ci	.dapm_routes		= rk3036_codec_dapm_routes,
3848c2ecf20Sopenharmony_ci	.num_dapm_routes	= ARRAY_SIZE(rk3036_codec_dapm_routes),
3858c2ecf20Sopenharmony_ci	.dapm_widgets		= rk3036_codec_dapm_widgets,
3868c2ecf20Sopenharmony_ci	.num_dapm_widgets	= ARRAY_SIZE(rk3036_codec_dapm_widgets),
3878c2ecf20Sopenharmony_ci	.idle_bias_on		= 1,
3888c2ecf20Sopenharmony_ci	.use_pmdown_time	= 1,
3898c2ecf20Sopenharmony_ci	.endianness		= 1,
3908c2ecf20Sopenharmony_ci	.non_legacy_dai_naming	= 1,
3918c2ecf20Sopenharmony_ci};
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_cistatic const struct regmap_config rk3036_codec_regmap_config = {
3948c2ecf20Sopenharmony_ci	.reg_bits = 32,
3958c2ecf20Sopenharmony_ci	.reg_stride = 4,
3968c2ecf20Sopenharmony_ci	.val_bits = 32,
3978c2ecf20Sopenharmony_ci};
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci#define GRF_SOC_CON0		0x00140
4008c2ecf20Sopenharmony_ci#define GRF_ACODEC_SEL		(BIT(10) | BIT(16 + 10))
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_cistatic int rk3036_codec_platform_probe(struct platform_device *pdev)
4038c2ecf20Sopenharmony_ci{
4048c2ecf20Sopenharmony_ci	struct rk3036_codec_priv *priv;
4058c2ecf20Sopenharmony_ci	struct device_node *of_node = pdev->dev.of_node;
4068c2ecf20Sopenharmony_ci	void __iomem *base;
4078c2ecf20Sopenharmony_ci	struct regmap *grf;
4088c2ecf20Sopenharmony_ci	int ret;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
4118c2ecf20Sopenharmony_ci	if (!priv)
4128c2ecf20Sopenharmony_ci		return -ENOMEM;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	base = devm_platform_ioremap_resource(pdev, 0);
4158c2ecf20Sopenharmony_ci	if (IS_ERR(base))
4168c2ecf20Sopenharmony_ci		return PTR_ERR(base);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	priv->base = base;
4198c2ecf20Sopenharmony_ci	priv->regmap = devm_regmap_init_mmio(&pdev->dev, priv->base,
4208c2ecf20Sopenharmony_ci					     &rk3036_codec_regmap_config);
4218c2ecf20Sopenharmony_ci	if (IS_ERR(priv->regmap)) {
4228c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "init regmap failed\n");
4238c2ecf20Sopenharmony_ci		return PTR_ERR(priv->regmap);
4248c2ecf20Sopenharmony_ci	}
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	grf = syscon_regmap_lookup_by_phandle(of_node, "rockchip,grf");
4278c2ecf20Sopenharmony_ci	if (IS_ERR(grf)) {
4288c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "needs 'rockchip,grf' property\n");
4298c2ecf20Sopenharmony_ci		return PTR_ERR(grf);
4308c2ecf20Sopenharmony_ci	}
4318c2ecf20Sopenharmony_ci	ret = regmap_write(grf, GRF_SOC_CON0, GRF_ACODEC_SEL);
4328c2ecf20Sopenharmony_ci	if (ret) {
4338c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Could not write to GRF: %d\n", ret);
4348c2ecf20Sopenharmony_ci		return ret;
4358c2ecf20Sopenharmony_ci	}
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	priv->pclk = devm_clk_get(&pdev->dev, "acodec_pclk");
4388c2ecf20Sopenharmony_ci	if (IS_ERR(priv->pclk))
4398c2ecf20Sopenharmony_ci		return PTR_ERR(priv->pclk);
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(priv->pclk);
4428c2ecf20Sopenharmony_ci	if (ret < 0) {
4438c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to enable clk\n");
4448c2ecf20Sopenharmony_ci		return ret;
4458c2ecf20Sopenharmony_ci	}
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	priv->dev = &pdev->dev;
4488c2ecf20Sopenharmony_ci	dev_set_drvdata(&pdev->dev, priv);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	ret = devm_snd_soc_register_component(&pdev->dev, &rk3036_codec_driver,
4518c2ecf20Sopenharmony_ci				     rk3036_codec_dai_driver,
4528c2ecf20Sopenharmony_ci				     ARRAY_SIZE(rk3036_codec_dai_driver));
4538c2ecf20Sopenharmony_ci	if (ret) {
4548c2ecf20Sopenharmony_ci		clk_disable_unprepare(priv->pclk);
4558c2ecf20Sopenharmony_ci		dev_set_drvdata(&pdev->dev, NULL);
4568c2ecf20Sopenharmony_ci	}
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	return ret;
4598c2ecf20Sopenharmony_ci}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_cistatic int rk3036_codec_platform_remove(struct platform_device *pdev)
4628c2ecf20Sopenharmony_ci{
4638c2ecf20Sopenharmony_ci	struct rk3036_codec_priv *priv = dev_get_drvdata(&pdev->dev);
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	clk_disable_unprepare(priv->pclk);
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	return 0;
4688c2ecf20Sopenharmony_ci}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_cistatic const struct of_device_id rk3036_codec_of_match[] = {
4718c2ecf20Sopenharmony_ci	{ .compatible = "rockchip,rk3036-codec", },
4728c2ecf20Sopenharmony_ci	{}
4738c2ecf20Sopenharmony_ci};
4748c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, rk3036_codec_of_match);
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_cistatic struct platform_driver rk3036_codec_platform_driver = {
4778c2ecf20Sopenharmony_ci	.driver = {
4788c2ecf20Sopenharmony_ci		.name = "rk3036-codec-platform",
4798c2ecf20Sopenharmony_ci		.of_match_table = of_match_ptr(rk3036_codec_of_match),
4808c2ecf20Sopenharmony_ci	},
4818c2ecf20Sopenharmony_ci	.probe = rk3036_codec_platform_probe,
4828c2ecf20Sopenharmony_ci	.remove = rk3036_codec_platform_remove,
4838c2ecf20Sopenharmony_ci};
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_cimodule_platform_driver(rk3036_codec_platform_driver);
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ciMODULE_AUTHOR("Rockchip Inc.");
4888c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Rockchip rk3036 codec driver");
4898c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
490