brintos

brintos / linux-shallow public Read only

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