772 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			772 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0+
 | |
| /*
 | |
|  * rk3528_codec.c - Rockchip RK3528 SoC Codec Driver
 | |
|  *
 | |
|  * Copyright (C) 2022 Rockchip Electronics Co., Ltd.
 | |
|  */
 | |
| 
 | |
| #include <linux/clk.h>
 | |
| #include <linux/device.h>
 | |
| #include <linux/delay.h>
 | |
| #include <linux/module.h>
 | |
| #include <linux/of_gpio.h>
 | |
| #include <linux/of_platform.h>
 | |
| #include <linux/platform_device.h>
 | |
| #include <linux/pm_runtime.h>
 | |
| #include <linux/regmap.h>
 | |
| #include <linux/reset.h>
 | |
| #include <sound/pcm_params.h>
 | |
| #include <sound/soc.h>
 | |
| #include <sound/tlv.h>
 | |
| 
 | |
| #include "rk3528_codec.h"
 | |
| 
 | |
| #define CODEC_DRV_NAME			"rk3528-acodec"
 | |
| 
 | |
| struct rk3528_codec_priv {
 | |
| 	const struct device *plat_dev;
 | |
| 	struct reset_control *reset;
 | |
| 	struct regmap *regmap;
 | |
| 	struct clk *pclk;
 | |
| 	struct clk *mclk;
 | |
| 	struct gpio_desc *pa_ctl_gpio;
 | |
| 	struct snd_soc_component *component;
 | |
| 	u32 pa_ctl_delay_ms;
 | |
| };
 | |
| 
 | |
| static void rk3528_codec_pa_ctrl(struct rk3528_codec_priv *rk3528, bool on)
 | |
| {
 | |
| 	if (!rk3528->pa_ctl_gpio)
 | |
| 		return;
 | |
| 
 | |
| 	if (on) {
 | |
| 		gpiod_direction_output(rk3528->pa_ctl_gpio, on);
 | |
| 		msleep(rk3528->pa_ctl_delay_ms);
 | |
| 	} else {
 | |
| 		gpiod_direction_output(rk3528->pa_ctl_gpio, on);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static int rk3528_codec_reset(struct snd_soc_component *component)
 | |
| {
 | |
| 	struct rk3528_codec_priv *rk3528 = snd_soc_component_get_drvdata(component);
 | |
| 
 | |
| 	reset_control_assert(rk3528->reset);
 | |
| 	usleep_range(10000, 11000);		/* estimated value */
 | |
| 	reset_control_deassert(rk3528->reset);
 | |
| 
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_DIG00,
 | |
| 			   ACODEC_DAC_RST_MASK |
 | |
| 			   ACODEC_SYS_RST_MASK,
 | |
| 			   ACODEC_DAC_RST_N |
 | |
| 			   ACODEC_SYS_RST_N);
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_DIG02,
 | |
| 			   ACODEC_DAC_I2S_RST_MASK,
 | |
| 			   ACODEC_DAC_I2S_RST_N);
 | |
| 	usleep_range(10000, 11000);		/* estimated value */
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_DIG00,
 | |
| 			   ACODEC_DAC_RST_MASK |
 | |
| 			   ACODEC_SYS_RST_MASK,
 | |
| 			   ACODEC_DAC_RST_P |
 | |
| 			   ACODEC_SYS_RST_P);
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_DIG02,
 | |
| 			   ACODEC_DAC_I2S_RST_MASK,
 | |
| 			   ACODEC_DAC_I2S_RST_P);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int rk3528_codec_power_on(struct rk3528_codec_priv *rk3528)
 | |
| {
 | |
| 	/* vendor step 0, Supply the power of the digital part and reset the audio codec. */
 | |
| 	/* vendor step 1 */
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA08,
 | |
| 			   ACODEC_DAC_L_POP_CTRL_MASK,
 | |
| 			   ACODEC_DAC_L_POP_CTRL_ON);
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA0C,
 | |
| 			   ACODEC_DAC_R_POP_CTRL_MASK,
 | |
| 			   ACODEC_DAC_R_POP_CTRL_ON);
 | |
| 	/* vendor step 2 */
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA01,
 | |
| 			   ACODEC_VREF_SEL_MASK, ACODEC_VREF_SEL(0xff));
 | |
| 	/* vendor step 3, supply the power of the analog part */
 | |
| 	/* vendor step 4 */
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA00,
 | |
| 			   ACODEC_VREF_MASK, ACODEC_VREF_EN);
 | |
| 
 | |
| 	/* vendor step 5, Wait until the voltage of VCM keeps stable at the AVDD/2. */
 | |
| 	usleep_range(20000, 22000);
 | |
| 	/* vendor step 6 */
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA01,
 | |
| 			   ACODEC_VREF_SEL_MASK, ACODEC_VREF_SEL(2));
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int rk3528_codec_power_off(struct rk3528_codec_priv *rk3528)
 | |
| {
 | |
| 	/*
 | |
| 	 * vendor step 0. Keep the power on and disable the DAC and ADC path.
 | |
| 	 */
 | |
| 	/* vendor step 1 */
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA01,
 | |
| 			   ACODEC_VREF_SEL_MASK, ACODEC_VREF_SEL(0xff));
 | |
| 	/* vendor step 2 */
 | |
| 	/* vendor step 3 */
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA00,
 | |
| 			   ACODEC_VREF_MASK, ACODEC_VREF_DIS);
 | |
| 	/* vendor step 4. Wait until the voltage of VCM keep stable at AGND. */
 | |
| 	usleep_range(20000, 22000);
 | |
| 	/* vendor step 5, power off the analog power supply */
 | |
| 	/* vendor step 6, power off the digital power supply */
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int rk3528_codec_dac_enable(struct rk3528_codec_priv *rk3528)
 | |
| {
 | |
| 	/* vendor step 0, power up the codec and input the mute signal */
 | |
| 	/* vendor step 1 */
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA00,
 | |
| 			   ACODEC_IBIAS_DAC_MASK,
 | |
| 			   ACODEC_IBIAS_DAC_EN);
 | |
| 	/* vendor step 2 */
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA08,
 | |
| 			   ACODEC_DAC_L_BUF_MASK,
 | |
| 			   ACODEC_DAC_L_BUF_EN);
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA0C,
 | |
| 			   ACODEC_DAC_R_BUF_MASK,
 | |
| 			   ACODEC_DAC_R_BUF_EN);
 | |
| 	/* vendor step 3 */
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA08,
 | |
| 			   ACODEC_DAC_L_POP_CTRL_MASK,
 | |
| 			   ACODEC_DAC_L_POP_CTRL_ON);
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA0C,
 | |
| 			   ACODEC_DAC_R_POP_CTRL_MASK,
 | |
| 			   ACODEC_DAC_R_POP_CTRL_ON);
 | |
| 	/* vendor step 4 */
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA09,
 | |
| 			   ACODEC_LINEOUT_L_MASK,
 | |
| 			   ACODEC_LINEOUT_L_EN);
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA0D,
 | |
| 			   ACODEC_LINEOUT_R_MASK,
 | |
| 			   ACODEC_LINEOUT_R_EN);
 | |
| 	/* vendor step 5 */
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA09,
 | |
| 			   ACODEC_LINEOUT_L_INIT_MASK,
 | |
| 			   ACODEC_LINEOUT_L_INIT_WORK);
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA0D,
 | |
| 			   ACODEC_LINEOUT_R_INIT_MASK,
 | |
| 			   ACODEC_LINEOUT_R_INIT_WORK);
 | |
| 	/* vendor step 6 */
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA08,
 | |
| 			   ACODEC_DAC_L_VREF_MASK,
 | |
| 			   ACODEC_DAC_L_VREF_EN);
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA0C,
 | |
| 			   ACODEC_DAC_R_VREF_MASK,
 | |
| 			   ACODEC_DAC_R_VREF_EN);
 | |
| 	/* vendor step 7 */
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA08,
 | |
| 			   ACODEC_DAC_L_CLK_MASK,
 | |
| 			   ACODEC_DAC_L_CLK_EN);
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA0C,
 | |
| 			   ACODEC_DAC_R_CLK_MASK,
 | |
| 			   ACODEC_DAC_R_CLK_EN);
 | |
| 	/* vendor step 8 */
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA08,
 | |
| 			   ACODEC_DAC_L_MASK,
 | |
| 			   ACODEC_DAC_L_EN);
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA0C,
 | |
| 			   ACODEC_DAC_R_MASK,
 | |
| 			   ACODEC_DAC_R_EN);
 | |
| 	/* vendor step 9 */
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA08,
 | |
| 			   ACODEC_DAC_L_INIT_MASK,
 | |
| 			   ACODEC_DAC_L_WORK);
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA0C,
 | |
| 			   ACODEC_DAC_R_INIT_MASK,
 | |
| 			   ACODEC_DAC_R_WORK);
 | |
| 	/* vendor step 10 */
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA09,
 | |
| 			   ACODEC_LINEOUT_L_MUTE_MASK,
 | |
| 			   ACODEC_LINEOUT_L_WORK);
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA0D,
 | |
| 			   ACODEC_LINEOUT_R_MUTE_MASK,
 | |
| 			   ACODEC_LINEOUT_R_WORK);
 | |
| 	/* vendor step 11, select the gain */
 | |
| 	/* vendor step 12, play music */
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int rk3528_codec_dac_disable(struct rk3528_codec_priv *rk3528)
 | |
| {
 | |
| 	/* vendor step 0, keep the dac channel work and input the mute signal */
 | |
| 	/* vendor step 1, select the gain */
 | |
| 	/* vendor step 2 */
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA09,
 | |
| 			   ACODEC_LINEOUT_L_MUTE_MASK,
 | |
| 			   ACODEC_LINEOUT_L_MUTE);
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA0D,
 | |
| 			   ACODEC_LINEOUT_R_MUTE_MASK,
 | |
| 			   ACODEC_LINEOUT_R_MUTE);
 | |
| 	/* vendor step 3 */
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA09,
 | |
| 			   ACODEC_LINEOUT_L_INIT_MASK,
 | |
| 			   ACODEC_LINEOUT_L_INIT);
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA0D,
 | |
| 			   ACODEC_LINEOUT_R_INIT_MASK,
 | |
| 			   ACODEC_LINEOUT_R_INIT);
 | |
| 	/* vendor step 4 */
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA09,
 | |
| 			   ACODEC_LINEOUT_L_MASK,
 | |
| 			   ACODEC_LINEOUT_L_DIS);
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA0D,
 | |
| 			   ACODEC_LINEOUT_R_MASK,
 | |
| 			   ACODEC_LINEOUT_R_DIS);
 | |
| 	/* vendor step 5 */
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA08,
 | |
| 			   ACODEC_DAC_L_MASK,
 | |
| 			   ACODEC_DAC_L_DIS);
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA0C,
 | |
| 			   ACODEC_DAC_R_MASK,
 | |
| 			   ACODEC_DAC_R_DIS);
 | |
| 	/* vendor step 6 */
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA08,
 | |
| 			   ACODEC_DAC_L_CLK_MASK,
 | |
| 			   ACODEC_DAC_L_CLK_DIS);
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA0C,
 | |
| 			   ACODEC_DAC_R_CLK_MASK,
 | |
| 			   ACODEC_DAC_R_CLK_DIS);
 | |
| 	/* vendor step 7 */
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA08,
 | |
| 			   ACODEC_DAC_L_VREF_MASK,
 | |
| 			   ACODEC_DAC_L_VREF_DIS);
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA0C,
 | |
| 			   ACODEC_DAC_R_VREF_MASK,
 | |
| 			   ACODEC_DAC_R_VREF_DIS);
 | |
| 	/* vendor step 8 */
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA08,
 | |
| 			   ACODEC_DAC_L_POP_CTRL_MASK,
 | |
| 			   ACODEC_DAC_L_POP_CTRL_OFF);
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA0C,
 | |
| 			   ACODEC_DAC_R_POP_CTRL_MASK,
 | |
| 			   ACODEC_DAC_R_POP_CTRL_OFF);
 | |
| 	/* vendor step 9 */
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA08,
 | |
| 			   ACODEC_DAC_L_BUF_MASK,
 | |
| 			   ACODEC_DAC_L_BUF_DIS);
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA0C,
 | |
| 			   ACODEC_DAC_R_BUF_MASK,
 | |
| 			   ACODEC_DAC_R_BUF_DIS);
 | |
| 	/* vendor step 10 */
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA00,
 | |
| 			   ACODEC_IBIAS_DAC_MASK,
 | |
| 			   ACODEC_IBIAS_DAC_DIS);
 | |
| 	/* vendor step 9 */
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA08,
 | |
| 			   ACODEC_DAC_L_INIT_MASK,
 | |
| 			   ACODEC_DAC_L_INIT);
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA0C,
 | |
| 			   ACODEC_DAC_R_INIT_MASK,
 | |
| 			   ACODEC_DAC_R_INIT);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int rk3528_codec_dac_dig_config(struct rk3528_codec_priv *rk3528,
 | |
| 				       struct snd_pcm_hw_params *params)
 | |
| {
 | |
| 	unsigned int dac_aif1 = 0;
 | |
| 
 | |
| 	switch (params_format(params)) {
 | |
| 	case SNDRV_PCM_FORMAT_S16_LE:
 | |
| 		dac_aif1 |= ACODEC_DAC_I2S_16B;
 | |
| 		break;
 | |
| 	case SNDRV_PCM_FORMAT_S20_3LE:
 | |
| 		dac_aif1 |= ACODEC_DAC_I2S_20B;
 | |
| 		break;
 | |
| 	case SNDRV_PCM_FORMAT_S24_LE:
 | |
| 		dac_aif1 |= ACODEC_DAC_I2S_24B;
 | |
| 		break;
 | |
| 	case SNDRV_PCM_FORMAT_S32_LE:
 | |
| 		dac_aif1 |= ACODEC_DAC_I2S_32B;
 | |
| 		break;
 | |
| 	default:
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	dac_aif1 |= ACODEC_DAC_I2S_I2S;
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_DIG01,
 | |
| 			   ACODEC_DAC_I2S_WL_MASK |
 | |
| 			   ACODEC_DAC_I2S_FMT_MASK,
 | |
| 			   dac_aif1);
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_DIG02,
 | |
| 			   ACODEC_DAC_I2S_RST_MASK,
 | |
| 			   ACODEC_DAC_I2S_RST_P);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int rk3528_set_dai_fmt(struct snd_soc_dai *codec_dai,
 | |
| 			      unsigned int fmt)
 | |
| {
 | |
| 	struct snd_soc_component *component = codec_dai->component;
 | |
| 	struct rk3528_codec_priv *rk3528 = snd_soc_component_get_drvdata(component);
 | |
| 	unsigned int dac_aif1 = 0, dac_aif2 = 0;
 | |
| 
 | |
| 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
 | |
| 	case SND_SOC_DAIFMT_CBS_CFS:
 | |
| 		dac_aif2 |= ACODEC_DAC_I2S_MST_FUNC_SLAVE;
 | |
| 		dac_aif2 |= ACODEC_DAC_I2S_MST_IO_SLAVE;
 | |
| 		break;
 | |
| 	case SND_SOC_DAIFMT_CBM_CFM:
 | |
| 		dac_aif2 |= ACODEC_DAC_I2S_MST_FUNC_MASTER;
 | |
| 		dac_aif2 |= ACODEC_DAC_I2S_MST_IO_MASTER;
 | |
| 		break;
 | |
| 	default:
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
 | |
| 	case SND_SOC_DAIFMT_I2S:
 | |
| 		dac_aif1 |= ACODEC_DAC_I2S_I2S;
 | |
| 		break;
 | |
| 	case SND_SOC_DAIFMT_LEFT_J:
 | |
| 		dac_aif1 |= ACODEC_DAC_I2S_LJM;
 | |
| 		break;
 | |
| 	default:
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_DIG01,
 | |
| 			   ACODEC_DAC_I2S_FMT_MASK,
 | |
| 			   dac_aif1);
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_DIG02,
 | |
| 			   ACODEC_DAC_I2S_MST_FUNC_MASK |
 | |
| 			   ACODEC_DAC_I2S_MST_IO_MASK,
 | |
| 			   dac_aif2);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int rk3528_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
 | |
| {
 | |
| 	struct snd_soc_component *component = dai->component;
 | |
| 	struct rk3528_codec_priv *rk3528 = snd_soc_component_get_drvdata(component);
 | |
| 
 | |
| 	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
 | |
| 		if (mute) {
 | |
| 			/* Mute DAC LINEOUT */
 | |
| 			regmap_update_bits(rk3528->regmap,
 | |
| 					   ACODEC_ANA09,
 | |
| 					   ACODEC_LINEOUT_L_MUTE_MASK,
 | |
| 					   ACODEC_LINEOUT_L_MUTE);
 | |
| 			regmap_update_bits(rk3528->regmap,
 | |
| 					   ACODEC_ANA0D,
 | |
| 					   ACODEC_LINEOUT_R_MUTE_MASK,
 | |
| 					   ACODEC_LINEOUT_R_MUTE);
 | |
| 			rk3528_codec_pa_ctrl(rk3528, false);
 | |
| 		} else {
 | |
| 			/* Unmute DAC LINEOUT */
 | |
| 			regmap_update_bits(rk3528->regmap,
 | |
| 					   ACODEC_ANA09,
 | |
| 					   ACODEC_LINEOUT_L_MUTE_MASK,
 | |
| 					   ACODEC_LINEOUT_L_WORK);
 | |
| 			regmap_update_bits(rk3528->regmap,
 | |
| 					   ACODEC_ANA0D,
 | |
| 					   ACODEC_LINEOUT_R_MUTE_MASK,
 | |
| 					   ACODEC_LINEOUT_R_WORK);
 | |
| 			rk3528_codec_pa_ctrl(rk3528, true);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int rk3528_codec_default_gains(struct rk3528_codec_priv *rk3528)
 | |
| {
 | |
| 	/* Prepare DAC gains */
 | |
| 	/* set LINEOUT default gains */
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_DIG06,
 | |
| 			   ACODEC_DAC_DIG_GAIN_MASK,
 | |
| 			   ACODEC_DAC_DIG_GAIN(ACODEC_DAC_DIG_0DB));
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA0B,
 | |
| 			   ACODEC_LINEOUT_L_GAIN_MASK,
 | |
| 			   ACODEC_DAC_LINEOUT_GAIN_0DB);
 | |
| 	regmap_update_bits(rk3528->regmap, ACODEC_ANA0F,
 | |
| 			   ACODEC_LINEOUT_R_GAIN_MASK,
 | |
| 			   ACODEC_DAC_LINEOUT_GAIN_0DB);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int rk3528_codec_open_playback(struct rk3528_codec_priv *rk3528)
 | |
| {
 | |
| 	rk3528_codec_dac_enable(rk3528);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int rk3528_codec_close_playback(struct rk3528_codec_priv *rk3528)
 | |
| {
 | |
| 	rk3528_codec_dac_disable(rk3528);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int rk3528_codec_dlp_down(struct rk3528_codec_priv *rk3528)
 | |
| {
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int rk3528_codec_dlp_up(struct rk3528_codec_priv *rk3528)
 | |
| {
 | |
| 	rk3528_codec_power_on(rk3528);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int rk3528_hw_params(struct snd_pcm_substream *substream,
 | |
| 			    struct snd_pcm_hw_params *params,
 | |
| 			    struct snd_soc_dai *dai)
 | |
| {
 | |
| 	struct snd_soc_component *component = dai->component;
 | |
| 	struct rk3528_codec_priv *rk3528 = snd_soc_component_get_drvdata(component);
 | |
| 
 | |
| 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 | |
| 		rk3528_codec_open_playback(rk3528);
 | |
| 		rk3528_codec_dac_dig_config(rk3528, params);
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void rk3528_pcm_shutdown(struct snd_pcm_substream *substream,
 | |
| 				struct snd_soc_dai *dai)
 | |
| {
 | |
| 	struct snd_soc_component *component = dai->component;
 | |
| 	struct rk3528_codec_priv *rk3528 = snd_soc_component_get_drvdata(component);
 | |
| 
 | |
| 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 | |
| 		rk3528_codec_close_playback(rk3528);
 | |
| 
 | |
| 	regcache_cache_only(rk3528->regmap, false);
 | |
| 	regcache_sync(rk3528->regmap);
 | |
| }
 | |
| 
 | |
| static int rk3528_codec_prepare(struct rk3528_codec_priv *rk3528)
 | |
| {
 | |
| 	/* Clear registers for ADC and DAC */
 | |
| 	rk3528_codec_close_playback(rk3528);
 | |
| 	rk3528_codec_default_gains(rk3528);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int rk3528_set_sysclk(struct snd_soc_dai *dai, int clk_id,
 | |
| 			     unsigned int freq, int dir)
 | |
| {
 | |
| 	struct snd_soc_component *component = dai->component;
 | |
| 	struct rk3528_codec_priv *rk3528 = snd_soc_component_get_drvdata(component);
 | |
| 	int ret;
 | |
| 
 | |
| 	if (!freq)
 | |
| 		return 0;
 | |
| 
 | |
| 	ret = clk_set_rate(rk3528->mclk, freq);
 | |
| 	if (ret)
 | |
| 		dev_err(rk3528->plat_dev, "Failed to set mclk %d\n", ret);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static const struct snd_soc_dai_ops rk3528_dai_ops = {
 | |
| 	.hw_params = rk3528_hw_params,
 | |
| 	.set_fmt = rk3528_set_dai_fmt,
 | |
| 	.mute_stream = rk3528_mute_stream,
 | |
| 	.shutdown = rk3528_pcm_shutdown,
 | |
| 	.set_sysclk = rk3528_set_sysclk,
 | |
| };
 | |
| 
 | |
| static struct snd_soc_dai_driver rk3528_dai[] = {
 | |
| 	{
 | |
| 		.name = "rk3528-hifi",
 | |
| 		.id = ACODEC_HIFI,
 | |
| 		.playback = {
 | |
| 			.stream_name = "HiFi Playback",
 | |
| 			.channels_min = 1,
 | |
| 			.channels_max = 2,
 | |
| 			.rates = SNDRV_PCM_RATE_8000_192000,
 | |
| 			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
 | |
| 				    SNDRV_PCM_FMTBIT_S24_LE |
 | |
| 				    SNDRV_PCM_FMTBIT_S32_LE),
 | |
| 		},
 | |
| 		.ops = &rk3528_dai_ops,
 | |
| 	},
 | |
| };
 | |
| 
 | |
| static int rk3528_codec_probe(struct snd_soc_component *component)
 | |
| {
 | |
| 	struct rk3528_codec_priv *rk3528 = snd_soc_component_get_drvdata(component);
 | |
| 
 | |
| 	rk3528->component = component;
 | |
| 	rk3528_codec_reset(component);
 | |
| 	rk3528_codec_dlp_up(rk3528);
 | |
| 	rk3528_codec_prepare(rk3528);
 | |
| 	regcache_cache_only(rk3528->regmap, false);
 | |
| 	regcache_sync(rk3528->regmap);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void rk3528_codec_remove(struct snd_soc_component *component)
 | |
| {
 | |
| 	struct rk3528_codec_priv *rk3528 = snd_soc_component_get_drvdata(component);
 | |
| 
 | |
| 	rk3528_codec_pa_ctrl(rk3528, false);
 | |
| 	rk3528_codec_power_off(rk3528);
 | |
| 	regcache_cache_only(rk3528->regmap, false);
 | |
| 	regcache_sync(rk3528->regmap);
 | |
| }
 | |
| 
 | |
| static int rk3528_codec_suspend(struct snd_soc_component *component)
 | |
| {
 | |
| 	struct rk3528_codec_priv *rk3528 = snd_soc_component_get_drvdata(component);
 | |
| 
 | |
| 	rk3528_codec_dlp_down(rk3528);
 | |
| 	regcache_cache_only(rk3528->regmap, true);
 | |
| 	clk_disable_unprepare(rk3528->mclk);
 | |
| 	clk_disable_unprepare(rk3528->pclk);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int rk3528_codec_resume(struct snd_soc_component *component)
 | |
| {
 | |
| 	struct rk3528_codec_priv *rk3528 = snd_soc_component_get_drvdata(component);
 | |
| 	int ret = 0;
 | |
| 
 | |
| 	ret = clk_prepare_enable(rk3528->pclk);
 | |
| 	if (ret < 0) {
 | |
| 		dev_err(rk3528->plat_dev,
 | |
| 			"Failed to enable acodec pclk: %d\n", ret);
 | |
| 		goto pclk_error;
 | |
| 	}
 | |
| 
 | |
| 	ret = clk_prepare_enable(rk3528->mclk);
 | |
| 	if (ret < 0) {
 | |
| 		dev_err(rk3528->plat_dev,
 | |
| 			"Failed to enable acodec mclk: %d\n", ret);
 | |
| 		goto mclk_error;
 | |
| 	}
 | |
| 
 | |
| 	regcache_cache_only(rk3528->regmap, false);
 | |
| 	ret = regcache_sync(rk3528->regmap);
 | |
| 	if (ret)
 | |
| 		goto reg_error;
 | |
| 
 | |
| 	rk3528_codec_dlp_up(rk3528);
 | |
| 
 | |
| 	return 0;
 | |
| reg_error:
 | |
| 	clk_disable_unprepare(rk3528->mclk);
 | |
| mclk_error:
 | |
| 	clk_disable_unprepare(rk3528->pclk);
 | |
| pclk_error:
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static const DECLARE_TLV_DB_SCALE(rk3528_codec_dac_lineout_gain_tlv,
 | |
| 				  -3900, 150, 600);
 | |
| 
 | |
| static const struct snd_kcontrol_new rk3528_codec_dapm_controls[] = {
 | |
| 	/* DAC LINEOUT */
 | |
| 	SOC_SINGLE_RANGE_TLV("DAC LEFT LINEOUT Volume",
 | |
| 			     ACODEC_ANA0B,
 | |
| 			     ACODEC_LINEOUT_L_GAIN_SHIFT,
 | |
| 			     ACODEC_DAC_LINEOUT_GAIN_MIN,
 | |
| 			     ACODEC_DAC_LINEOUT_GAIN_MAX,
 | |
| 			     0, rk3528_codec_dac_lineout_gain_tlv),
 | |
| 	SOC_SINGLE_RANGE_TLV("DAC RIGHT LINEOUT Volume",
 | |
| 			     ACODEC_ANA0F,
 | |
| 			     ACODEC_LINEOUT_R_GAIN_SHIFT,
 | |
| 			     ACODEC_DAC_LINEOUT_GAIN_MIN,
 | |
| 			     ACODEC_DAC_LINEOUT_GAIN_MAX,
 | |
| 			     0, rk3528_codec_dac_lineout_gain_tlv),
 | |
| };
 | |
| 
 | |
| static const struct snd_soc_component_driver soc_codec_dev_rk3528 = {
 | |
| 	.probe = rk3528_codec_probe,
 | |
| 	.remove = rk3528_codec_remove,
 | |
| 	.suspend = rk3528_codec_suspend,
 | |
| 	.resume = rk3528_codec_resume,
 | |
| 	.controls = rk3528_codec_dapm_controls,
 | |
| 	.num_controls = ARRAY_SIZE(rk3528_codec_dapm_controls),
 | |
| };
 | |
| 
 | |
| /* Set the default value or reset value */
 | |
| static const struct reg_default rk3528_codec_reg_defaults[] = {
 | |
| 	{ ACODEC_DIG00, 0x71 },
 | |
| 	{ ACODEC_DIG03, 0x53 },
 | |
| 	{ ACODEC_DIG07, 0x03 },
 | |
| 	{ ACODEC_DIG08, 0xc3 },
 | |
| 	{ ACODEC_DIG09, 0x28 },
 | |
| 	{ ACODEC_DIG0A, 0x1 },
 | |
| 	{ ACODEC_DIG0B, 0x80 },
 | |
| 	{ ACODEC_DIG0D, 0xc3 },
 | |
| 	{ ACODEC_DIG0E, 0xc3 },
 | |
| 	{ ACODEC_DIG10, 0xf1 },
 | |
| 	{ ACODEC_DIG11, 0xf1 },
 | |
| 	{ ACODEC_ANA02, 0x77 },
 | |
| 	{ ACODEC_ANA08, 0x20 },
 | |
| 	{ ACODEC_ANA0A, 0x8 },
 | |
| 	{ ACODEC_ANA0C, 0x20 },
 | |
| 	{ ACODEC_ANA0E, 0x8 },
 | |
| };
 | |
| 
 | |
| static bool rk3528_codec_volatile_reg(struct device *dev, unsigned int reg)
 | |
| {
 | |
| 	switch (reg) {
 | |
| 	case ACODEC_DIG00:
 | |
| 		return true;
 | |
| 	default:
 | |
| 		return false;
 | |
| 	}
 | |
| 	return true;
 | |
| }
 | |
| 
 | |
| static const struct regmap_config rk3528_codec_regmap_config = {
 | |
| 	.reg_bits = 32,
 | |
| 	.reg_stride = 4,
 | |
| 	.val_bits = 32,
 | |
| 	.max_register = ACODEC_REG_MAX,
 | |
| 	.volatile_reg = rk3528_codec_volatile_reg,
 | |
| 	.reg_defaults = rk3528_codec_reg_defaults,
 | |
| 	.num_reg_defaults = ARRAY_SIZE(rk3528_codec_reg_defaults),
 | |
| 	.cache_type = REGCACHE_FLAT,
 | |
| };
 | |
| 
 | |
| static const struct of_device_id rk3528_codec_of_match[] = {
 | |
| 	{ .compatible = "rockchip,rk3528-codec", },
 | |
| 	{},
 | |
| };
 | |
| 
 | |
| MODULE_DEVICE_TABLE(of, rk3528_codec_of_match);
 | |
| 
 | |
| static int rk3528_platform_probe(struct platform_device *pdev)
 | |
| {
 | |
| 	struct device_node *np = pdev->dev.of_node;
 | |
| 	struct rk3528_codec_priv *rk3528;
 | |
| 	struct resource *res;
 | |
| 	void __iomem *base;
 | |
| 	int ret;
 | |
| 
 | |
| 	rk3528 = devm_kzalloc(&pdev->dev, sizeof(*rk3528), GFP_KERNEL);
 | |
| 	if (!rk3528)
 | |
| 		return -ENOMEM;
 | |
| 
 | |
| 	rk3528->plat_dev = &pdev->dev;
 | |
| 	rk3528->reset = devm_reset_control_get_optional_exclusive(&pdev->dev, "acodec");
 | |
| 	if (IS_ERR(rk3528->reset))
 | |
| 		return PTR_ERR(rk3528->reset);
 | |
| 
 | |
| 	rk3528->pa_ctl_gpio = devm_gpiod_get_optional(&pdev->dev, "pa-ctl",
 | |
| 						       GPIOD_OUT_LOW);
 | |
| 	if (IS_ERR(rk3528->pa_ctl_gpio)) {
 | |
| 		dev_err(&pdev->dev, "Unable to claim gpio pa-ctl\n");
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	if (rk3528->pa_ctl_gpio)
 | |
| 		of_property_read_u32(np, "pa-ctl-delay-ms",
 | |
| 				     &rk3528->pa_ctl_delay_ms);
 | |
| 
 | |
| 	dev_info(&pdev->dev, "%s pa_ctl_gpio and pa_ctl_delay_ms: %d\n",
 | |
| 		rk3528->pa_ctl_gpio ? "Use" : "No use",
 | |
| 		rk3528->pa_ctl_delay_ms);
 | |
| 
 | |
| 	/* Close external PA during startup. */
 | |
| 	rk3528_codec_pa_ctrl(rk3528, false);
 | |
| 
 | |
| 	rk3528->pclk = devm_clk_get(&pdev->dev, "pclk");
 | |
| 	if (IS_ERR(rk3528->pclk)) {
 | |
| 		dev_err(&pdev->dev, "Can't get acodec pclk\n");
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	rk3528->mclk = devm_clk_get(&pdev->dev, "mclk");
 | |
| 	if (IS_ERR(rk3528->mclk)) {
 | |
| 		dev_err(&pdev->dev, "Can't get acodec mclk\n");
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	ret = clk_prepare_enable(rk3528->pclk);
 | |
| 	if (ret < 0) {
 | |
| 		dev_err(&pdev->dev, "Failed to enable acodec pclk: %d\n", ret);
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	ret = clk_prepare_enable(rk3528->mclk);
 | |
| 	if (ret < 0) {
 | |
| 		dev_err(&pdev->dev, "Failed to enable acodec mclk: %d\n", ret);
 | |
| 		goto failed_1;
 | |
| 	}
 | |
| 
 | |
| 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 | |
| 	base = devm_ioremap_resource(&pdev->dev, res);
 | |
| 	if (IS_ERR(base)) {
 | |
| 		ret = PTR_ERR(base);
 | |
| 		dev_err(&pdev->dev, "Failed to ioremap resource\n");
 | |
| 		goto failed;
 | |
| 	}
 | |
| 
 | |
| 	rk3528->regmap = devm_regmap_init_mmio(&pdev->dev, base,
 | |
| 					       &rk3528_codec_regmap_config);
 | |
| 	if (IS_ERR(rk3528->regmap)) {
 | |
| 		ret = PTR_ERR(rk3528->regmap);
 | |
| 		dev_err(&pdev->dev, "Failed to regmap mmio\n");
 | |
| 		goto failed;
 | |
| 	}
 | |
| 
 | |
| 	platform_set_drvdata(pdev, rk3528);
 | |
| 	ret = devm_snd_soc_register_component(&pdev->dev, &soc_codec_dev_rk3528,
 | |
| 					      rk3528_dai, ARRAY_SIZE(rk3528_dai));
 | |
| 	if (ret < 0) {
 | |
| 		dev_err(&pdev->dev, "Failed to register codec: %d\n", ret);
 | |
| 		goto failed;
 | |
| 	}
 | |
| 
 | |
| 	return ret;
 | |
| 
 | |
| failed:
 | |
| 	clk_disable_unprepare(rk3528->mclk);
 | |
| failed_1:
 | |
| 	clk_disable_unprepare(rk3528->pclk);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int rk3528_platform_remove(struct platform_device *pdev)
 | |
| {
 | |
| 	struct rk3528_codec_priv *rk3528 =
 | |
| 		(struct rk3528_codec_priv *)platform_get_drvdata(pdev);
 | |
| 
 | |
| 	clk_disable_unprepare(rk3528->mclk);
 | |
| 	clk_disable_unprepare(rk3528->pclk);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static struct platform_driver rk3528_codec_driver = {
 | |
| 	.driver = {
 | |
| 		   .name = CODEC_DRV_NAME,
 | |
| 		   .of_match_table = of_match_ptr(rk3528_codec_of_match),
 | |
| 	},
 | |
| 	.probe = rk3528_platform_probe,
 | |
| 	.remove = rk3528_platform_remove,
 | |
| };
 | |
| module_platform_driver(rk3528_codec_driver);
 | |
| 
 | |
| MODULE_DESCRIPTION("ASoC rk3528 Codec Driver");
 | |
| MODULE_AUTHOR("Jason Zhu <jason.zhu@rock-chips.com>");
 | |
| MODULE_LICENSE("GPL");
 |