971 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			971 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0-or-later
 | |
| /*
 | |
|  * Rockchip Audio Delta-sigma Digital Converter Interface
 | |
|  *
 | |
|  * Copyright (C) 2023 Rockchip Electronics Co., Ltd.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| #include <linux/module.h>
 | |
| #include <linux/device.h>
 | |
| #include <linux/delay.h>
 | |
| #include <linux/of.h>
 | |
| #include <linux/of_gpio.h>
 | |
| #include <linux/of_platform.h>
 | |
| #include <linux/clk.h>
 | |
| #include <linux/mfd/syscon.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/dmaengine_pcm.h>
 | |
| #include <sound/tlv.h>
 | |
| #include "rk_dsm.h"
 | |
| 
 | |
| #define RK3506_DSM_AUDM0_EN			BIT(0)
 | |
| #define RK3506_DSM_AUDM1_EN			BIT(1)
 | |
| #define RK3506_IOC1_REG_BASE			(0xff660000)
 | |
| #define RK3506_GPIO1_IOC_GPIO1C_IOMUX_SEL_0	(0x0030) /* DSM_AUD_RP_M0 / DSM_AUD_RN_M0 */
 | |
| #define RK3506_GPIO1_IOC_GPIO1C2_SEL_MASK	GENMASK(11, 8)
 | |
| #define RK3506_GPIO1_IOC_DSM_AUD_RP_M0		(4 << 8)
 | |
| #define RK3506_GPIO1_IOC_GPIO2C2		(0 << 8)
 | |
| #define RK3506_GPIO1_IOC_GPIO1C1_SEL_MASK	GENMASK(7, 4)
 | |
| #define RK3506_GPIO1_IOC_DSM_AUD_RN_M0		(4 << 4)
 | |
| #define RK3506_GPIO1_IOC_GPIO2C1		(0 << 4)
 | |
| #define RK3506_GPIO1_IOC_GPIO1D_IOMUX_SEL_0	(0x0038) /* DSM_AUD_LP_M0 / DSM_AUD_LN_M0 */
 | |
| #define RK3506_GPIO1_IOC_GPIO1D1_SEL_MASK	GENMASK(7, 4)
 | |
| #define RK3506_GPIO1_IOC_DSM_AUD_LP_M0		(4 << 4)
 | |
| #define RK3506_GPIO1_IOC_GPIO2D1		(0 << 4)
 | |
| #define RK3506_GPIO1_IOC_GPIO1D0_SEL_MASK	GENMASK(3, 0)
 | |
| #define RK3506_GPIO1_IOC_DSM_AUD_LN_M0		(4 << 0)
 | |
| #define RK3506_GPIO1_IOC_GPIO2D0		(0 << 0)
 | |
| #define RK3506_IOC2_REG_BASE			(0xff4d8000)
 | |
| #define RK3506_GPIO2_IOC_GPIO2B_IOMUX_SEL_1	(0x004c) /* DSM_AUD_LP_M1 / DSM_AUD_LN_M1 / DSM_AUD_RP_M1 / DSM_AUD_RN_M1 */
 | |
| #define RK3506_GPIO2_IOC_GPIO2B7_SEL_MASK	GENMASK(15, 12)
 | |
| #define RK3506_GPIO2_IOC_DSM_AUD_LP_M1		(2 << 12)
 | |
| #define RK3506_GPIO2_IOC_GPIO2B7		(0 << 12)
 | |
| #define RK3506_GPIO2_IOC_GPIO2B6_SEL_MASK	GENMASK(11, 8)
 | |
| #define RK3506_GPIO2_IOC_DSM_AUD_LN_M1		(2 << 8)
 | |
| #define RK3506_GPIO2_IOC_GPIO2B6		(0 << 8)
 | |
| #define RK3506_GPIO2_IOC_GPIO2B5_SEL_MASK	GENMASK(7, 4)
 | |
| #define RK3506_GPIO2_IOC_DSM_AUD_RP_M1		(2 << 4)
 | |
| #define RK3506_GPIO2_IOC_GPIO2B5		(0 << 4)
 | |
| #define RK3506_GPIO2_IOC_GPIO2B4_SEL_MASK	GENMASK(3, 0)
 | |
| #define RK3506_GPIO2_IOC_DSM_AUD_RN_M1		(2 << 0)
 | |
| #define RK3506_GPIO2_IOC_GPIO2B4		(0 << 0)
 | |
| 
 | |
| #define RK3506_GRF_SOC_CON0		(0x0)
 | |
| #define RK3506_DSM_SEL			(9)
 | |
| #define RK3562_GRF_PERI_AUDIO_CON	(0x0070)
 | |
| #define RK3576_SYS_GRF_SOC_CON2		(0x0008)
 | |
| #define RK3576_DSM_SEL			(0x0)
 | |
| 
 | |
| #define RKDSM_VOL_VAL_MAX		(0xff)
 | |
| 
 | |
| enum {
 | |
| 	RKDSM_ON_GPIO = 0,
 | |
| 	RKDSM_ON_FUNC,
 | |
| };
 | |
| 
 | |
| struct rk_dsm_soc_data {
 | |
| 	int (*init)(struct device *dev);
 | |
| 	void (*deinit)(struct device *dev);
 | |
| 	int (*iomux_switch)(struct device *dev, int type);
 | |
| };
 | |
| 
 | |
| struct rk_dsm_vols {
 | |
| 	int vol_l;
 | |
| 	int vol_r;
 | |
| 	int polarity;
 | |
| };
 | |
| 
 | |
| struct rk_dsm_iomux_res {
 | |
| 	phys_addr_t regbase;
 | |
| 	unsigned long size;
 | |
| };
 | |
| 
 | |
| struct rk_dsm_iomux {
 | |
| 	void __iomem **ioc;
 | |
| 	unsigned int audm_en;
 | |
| 	int res_num;
 | |
| };
 | |
| 
 | |
| struct rk_dsm_priv {
 | |
| 	struct regmap *grf;
 | |
| 	struct regmap *regmap;
 | |
| 	struct clk *clk_dac;
 | |
| 	struct clk *pclk;
 | |
| 	unsigned int pa_ctl_delay_ms;
 | |
| 	struct gpio_desc *pa_ctl;
 | |
| 	struct reset_control *rc;
 | |
| 	const struct rk_dsm_soc_data *data;
 | |
| 	struct device *dev;
 | |
| 	struct rk_dsm_vols vols;
 | |
| 	struct rk_dsm_iomux iomuxes;
 | |
| 	struct pinctrl *pinctrl;
 | |
| 	struct pinctrl_state *pin_state;
 | |
| 	struct pinctrl_state *io_state;
 | |
| };
 | |
| 
 | |
| /* DAC digital gain */
 | |
| static const DECLARE_TLV_DB_SCALE(dac_tlv, -95625, 375, 0);
 | |
| 
 | |
| /* DAC Cutoff for High Pass Filter */
 | |
| static const char * const dac_hpf_cutoff_text[] = {
 | |
| 	"80Hz", "100Hz", "120Hz", "140Hz",
 | |
| };
 | |
| 
 | |
| static SOC_ENUM_SINGLE_DECL(dac_hpf_cutoff_enum, DACHPF, 4,
 | |
| 			    dac_hpf_cutoff_text);
 | |
| 
 | |
| static const char * const pa_ctl[] = {"Off", "On"};
 | |
| 
 | |
| static const struct soc_enum pa_enum =
 | |
| 	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(pa_ctl), pa_ctl);
 | |
| 
 | |
| static int rk_dsm_dac_vol_get(struct snd_kcontrol *kcontrol,
 | |
| 			      struct snd_ctl_elem_value *ucontrol)
 | |
| {
 | |
| 	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
 | |
| 	struct soc_mixer_control *mc =
 | |
| 		(struct soc_mixer_control *)kcontrol->private_value;
 | |
| 	unsigned int val = snd_soc_component_read(component, mc->reg);
 | |
| 	unsigned int sign = snd_soc_component_read(component, DACVOGP);
 | |
| 	unsigned int mask = (1 << fls(mc->max)) - 1;
 | |
| 	unsigned int shift = mc->shift;
 | |
| 	int mid = mc->max / 2;
 | |
| 	int uv;
 | |
| 
 | |
| 	uv = (val >> shift) & mask;
 | |
| 	if (sign)
 | |
| 		uv = mid + uv;
 | |
| 	else
 | |
| 		uv = mid - uv;
 | |
| 
 | |
| 	ucontrol->value.integer.value[0] = uv;
 | |
| 	ucontrol->value.integer.value[1] = uv;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int rk_dsm_dac_vol_put(struct snd_kcontrol *kcontrol,
 | |
| 			      struct snd_ctl_elem_value *ucontrol)
 | |
| {
 | |
| 	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
 | |
| 	struct soc_mixer_control *mc =
 | |
| 		(struct soc_mixer_control *)kcontrol->private_value;
 | |
| 	struct rk_dsm_priv *rd =
 | |
| 		snd_soc_component_get_drvdata(component);
 | |
| 	unsigned int reg = mc->reg;
 | |
| 	unsigned int rreg = mc->rreg;
 | |
| 	unsigned int shift = mc->shift;
 | |
| 	unsigned int mask = (1 << fls(mc->max)) - 1;
 | |
| 	unsigned int val, val_mask, sign;
 | |
| 	int uv = ucontrol->value.integer.value[0];
 | |
| 	int min = mc->min;
 | |
| 	int mid = mc->max / 2;
 | |
| 
 | |
| 	if (uv > mid) {
 | |
| 		sign = DSM_DACVOGP_VOLGPL0_POS | DSM_DACVOGP_VOLGPR0_POS;
 | |
| 		uv = uv - mid;
 | |
| 	} else {
 | |
| 		sign = DSM_DACVOGP_VOLGPL0_NEG | DSM_DACVOGP_VOLGPR0_NEG;
 | |
| 		uv = mid - uv;
 | |
| 	}
 | |
| 
 | |
| 	val = ((uv + min) & mask);
 | |
| 	val_mask = mask << shift;
 | |
| 	val = val << shift;
 | |
| 
 | |
| 	snd_soc_component_update_bits(component, reg, val_mask, val);
 | |
| 	snd_soc_component_update_bits(component, rreg, val_mask, val);
 | |
| 	snd_soc_component_write(component, DACVOGP, sign);
 | |
| 	rd->vols.vol_l = val;
 | |
| 	rd->vols.vol_r = val;
 | |
| 	rd->vols.polarity = sign;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int rk_dsm_dac_pa_get(struct snd_kcontrol *kcontrol,
 | |
| 			     struct snd_ctl_elem_value *ucontrol)
 | |
| {
 | |
| 	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
 | |
| 	struct rk_dsm_priv *rd = snd_soc_component_get_drvdata(component);
 | |
| 
 | |
| 	if (!rd->pa_ctl)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	ucontrol->value.enumerated.item[0] = gpiod_get_value(rd->pa_ctl);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int rk_dsm_dac_pa_put(struct snd_kcontrol *kcontrol,
 | |
| 			     struct snd_ctl_elem_value *ucontrol)
 | |
| {
 | |
| 	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
 | |
| 	struct rk_dsm_priv *rd = snd_soc_component_get_drvdata(component);
 | |
| 
 | |
| 	if (!rd->pa_ctl)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	gpiod_set_value(rd->pa_ctl, ucontrol->value.enumerated.item[0]);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static const struct snd_kcontrol_new rk_dsm_snd_controls[] = {
 | |
| 	SOC_DOUBLE_R_EXT_TLV("DAC Digital Volume",
 | |
| 			     DACVOLL0, DACVOLR0, 0, 0x1fe, 0,
 | |
| 			     rk_dsm_dac_vol_get,
 | |
| 			     rk_dsm_dac_vol_put,
 | |
| 			     dac_tlv),
 | |
| 
 | |
| 	SOC_ENUM("DAC HPF Cutoff", dac_hpf_cutoff_enum),
 | |
| 	SOC_SINGLE("DAC HPF Switch", DACHPF, 0, 1, 0),
 | |
| 	SOC_ENUM_EXT("Power Amplifier", pa_enum,
 | |
| 		     rk_dsm_dac_pa_get,
 | |
| 		     rk_dsm_dac_pa_put),
 | |
| };
 | |
| 
 | |
| /*
 | |
|  * ACDC_CLK  D2A_CLK   D2A_SYNC Sample rates supported
 | |
|  * 49.152MHz 49.152MHz 6.144MHz 12/24/48/96/192kHz
 | |
|  * 45.154MHz 45.154MHz 5.644MHz 11.025/22.05/44.1/88.2/176.4kHz
 | |
|  * 32.768MHz 32.768MHz 4.096MHz 8/16/32/64/128kHz
 | |
|  */
 | |
| static void rk_dsm_get_clk(unsigned int samplerate,
 | |
| 			   unsigned int *mclk,
 | |
| 			   unsigned int *sclk)
 | |
| {
 | |
| 	switch (samplerate) {
 | |
| 	case 12000:
 | |
| 	case 24000:
 | |
| 	case 48000:
 | |
| 	case 96000:
 | |
| 	case 192000:
 | |
| 		*mclk = 49152000;
 | |
| 		*sclk = 6144000;
 | |
| 		break;
 | |
| 	case 11025:
 | |
| 	case 22050:
 | |
| 	case 44100:
 | |
| 	case 88200:
 | |
| 	case 176400:
 | |
| 		*mclk = 45158400;
 | |
| 		*sclk = 5644800;
 | |
| 		break;
 | |
| 	case 8000:
 | |
| 	case 16000:
 | |
| 	case 32000:
 | |
| 	case 64000:
 | |
| 	case 128000:
 | |
| 		*mclk = 32768000;
 | |
| 		*sclk = 4096000;
 | |
| 		break;
 | |
| 	default:
 | |
| 		*mclk = 0;
 | |
| 		*sclk = 0;
 | |
| 		break;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void rk_dsm_enable_clk_dac(struct rk_dsm_priv *rd)
 | |
| {
 | |
| 	regmap_update_bits(rd->regmap, DACCLKCTRL,
 | |
| 			   DSM_DACCLKCTRL_DAC_CKE_MASK |
 | |
| 			   DSM_DACCLKCTRL_I2SRX_CKE_MASK |
 | |
| 			   DSM_DACCLKCTRL_CKE_BCLKRX_MASK |
 | |
| 			   DSM_DACCLKCTRL_DAC_SYNC_ENA_MASK |
 | |
| 			   DSM_DACCLKCTRL_DAC_MODE_ATTENU_MASK,
 | |
| 			   DSM_DACCLKCTRL_DAC_CKE_EN |
 | |
| 			   DSM_DACCLKCTRL_I2SRX_CKE_EN |
 | |
| 			   DSM_DACCLKCTRL_CKE_BCLKRX_EN |
 | |
| 			   DSM_DACCLKCTRL_DAC_SYNC_ENA_EN |
 | |
| 			   DSM_DACCLKCTRL_DAC_MODE_ATTENU_EN);
 | |
| }
 | |
| 
 | |
| static int rk_dsm_set_clk(struct rk_dsm_priv *rd,
 | |
| 			  struct snd_pcm_substream *substream,
 | |
| 			  unsigned int samplerate)
 | |
| {
 | |
| 	unsigned int mclk, sclk, bclk;
 | |
| 	unsigned int div_bclk;
 | |
| 
 | |
| 	rk_dsm_get_clk(samplerate, &mclk, &sclk);
 | |
| 	if (!mclk || !sclk)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	bclk = 64 * samplerate;
 | |
| 	div_bclk = DIV_ROUND_CLOSEST(mclk, bclk);
 | |
| 
 | |
| 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 | |
| 		clk_set_rate(rd->clk_dac, mclk);
 | |
| 
 | |
| 		rk_dsm_enable_clk_dac(rd);
 | |
| 
 | |
| 		regmap_update_bits(rd->regmap, DACSCLKRXINT_DIV,
 | |
| 				   DSM_DACSCLKRXINT_DIV_SCKRXDIV_MASK,
 | |
| 				   DSM_DACSCLKRXINT_DIV_SCKRXDIV(div_bclk));
 | |
| 		regmap_update_bits(rd->regmap, I2S_CKR0,
 | |
| 				   DSM_I2S_CKR0_RSD_MASK,
 | |
| 				   DSM_I2S_CKR0_RSD_64);
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int rk_dsm_set_dai_fmt(struct snd_soc_dai *dai,
 | |
| 			      unsigned int fmt)
 | |
| {
 | |
| 	struct rk_dsm_priv *rd =
 | |
| 		snd_soc_component_get_drvdata(dai->component);
 | |
| 	unsigned int mask = 0, val = 0;
 | |
| 
 | |
| 	/* master mode only */
 | |
| 	regmap_update_bits(rd->regmap, I2S_CKR1,
 | |
| 			   DSM_I2S_CKR1_MSS_MASK,
 | |
| 			   DSM_I2S_CKR1_MSS_MASTER);
 | |
| 
 | |
| 	mask = DSM_I2S_CKR1_CKP_MASK |
 | |
| 	       DSM_I2S_CKR1_RLP_MASK;
 | |
| 
 | |
| 	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
 | |
| 	case SND_SOC_DAIFMT_NB_NF:
 | |
| 		val = DSM_I2S_CKR1_CKP_NORMAL |
 | |
| 		      DSM_I2S_CKR1_RLP_NORMAL;
 | |
| 		break;
 | |
| 	case SND_SOC_DAIFMT_IB_IF:
 | |
| 		val = DSM_I2S_CKR1_CKP_INVERTED |
 | |
| 		      DSM_I2S_CKR1_RLP_INVERTED;
 | |
| 		break;
 | |
| 	case SND_SOC_DAIFMT_IB_NF:
 | |
| 		val = DSM_I2S_CKR1_CKP_INVERTED |
 | |
| 		      DSM_I2S_CKR1_RLP_NORMAL;
 | |
| 		break;
 | |
| 	case SND_SOC_DAIFMT_NB_IF:
 | |
| 		val = DSM_I2S_CKR1_CKP_NORMAL |
 | |
| 		      DSM_I2S_CKR1_RLP_INVERTED;
 | |
| 		break;
 | |
| 	default:
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	regmap_update_bits(rd->regmap, I2S_CKR1, mask, val);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int rk_dsm_hw_params(struct snd_pcm_substream *substream,
 | |
| 			    struct snd_pcm_hw_params *params,
 | |
| 			    struct snd_soc_dai *dai)
 | |
| {
 | |
| 	struct rk_dsm_priv *rd =
 | |
| 		snd_soc_component_get_drvdata(dai->component);
 | |
| 	unsigned int srt = 0, val = 0;
 | |
| 
 | |
| 	rk_dsm_set_clk(rd, substream, params_rate(params));
 | |
| 
 | |
| 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 | |
| 		switch (params_rate(params)) {
 | |
| 		case 8000:
 | |
| 		case 11025:
 | |
| 		case 12000:
 | |
| 			srt = 0;
 | |
| 			break;
 | |
| 		case 16000:
 | |
| 		case 22050:
 | |
| 		case 24000:
 | |
| 			srt = 1;
 | |
| 			break;
 | |
| 		case 32000:
 | |
| 		case 44100:
 | |
| 		case 48000:
 | |
| 			srt = 2;
 | |
| 			break;
 | |
| 		case 64000:
 | |
| 		case 88200:
 | |
| 		case 96000:
 | |
| 			srt = 3;
 | |
| 			break;
 | |
| 		case 128000:
 | |
| 		case 176400:
 | |
| 		case 192000:
 | |
| 			srt = 4;
 | |
| 			break;
 | |
| 		default:
 | |
| 			return -EINVAL;
 | |
| 		}
 | |
| 
 | |
| 		regmap_update_bits(rd->regmap, DACCFG1,
 | |
| 				   DSM_DACCFG1_DACSRT_MASK,
 | |
| 				   DSM_DACCFG1_DACSRT(srt));
 | |
| 
 | |
| 		switch (params_format(params)) {
 | |
| 		case SNDRV_PCM_FORMAT_S16_LE:
 | |
| 			val = 16;
 | |
| 			break;
 | |
| 		case SNDRV_PCM_FORMAT_S24_LE:
 | |
| 		case SNDRV_PCM_FORMAT_S32_LE:
 | |
| 			val = 24;
 | |
| 			break;
 | |
| 		default:
 | |
| 			return -EINVAL;
 | |
| 		}
 | |
| 
 | |
| 		regmap_update_bits(rd->regmap, I2S_RXCR0,
 | |
| 				   DSM_I2S_RXCR0_VDW_MASK,
 | |
| 				   DSM_I2S_RXCR0_VDW(val));
 | |
| 		regmap_update_bits(rd->regmap, DACDSM_CTRL,
 | |
| 				   DSM_DACDSM_CTRL_DSM_MODE_CKE_MASK |
 | |
| 				   DSM_DACDSM_CTRL_DSM_EN_MASK,
 | |
| 				   DSM_DACDSM_CTRL_DSM_MODE_CKE_EN |
 | |
| 				   DSM_DACDSM_CTRL_DSM_EN);
 | |
| 
 | |
| 		regmap_update_bits(rd->regmap, I2S_XFER,
 | |
| 				   DSM_I2S_XFER_RXS_MASK,
 | |
| 				   DSM_I2S_XFER_RXS_START);
 | |
| 		regmap_update_bits(rd->regmap, DACDIGEN,
 | |
| 				   DSM_DACDIGEN_DAC_GLBEN_MASK |
 | |
| 				   DSM_DACDIGEN_DACEN_L0R1_MASK,
 | |
| 				   DSM_DACDIGEN_DAC_GLBEN_EN |
 | |
| 				   DSM_DACDIGEN_DACEN_L0R1_EN);
 | |
| 
 | |
| 		if (rd->data && rd->data->iomux_switch)
 | |
| 			rd->data->iomux_switch(rd->dev, RKDSM_ON_FUNC);
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int rk_dsm_pcm_startup(struct snd_pcm_substream *substream,
 | |
| 			      struct snd_soc_dai *dai)
 | |
| {
 | |
| 	struct rk_dsm_priv *rd =
 | |
| 		snd_soc_component_get_drvdata(dai->component);
 | |
| 
 | |
| 	/* Recover DAC Volumes */
 | |
| 	regmap_write(rd->regmap, DACVOLL0, rd->vols.vol_l);
 | |
| 	regmap_write(rd->regmap, DACVOLR0, rd->vols.vol_r);
 | |
| 	regmap_write(rd->regmap, DACVOGP, rd->vols.polarity);
 | |
| 
 | |
| 	gpiod_set_value(rd->pa_ctl, 1);
 | |
| 	if (rd->pa_ctl_delay_ms)
 | |
| 		msleep(rd->pa_ctl_delay_ms);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void rk_dsm_reset(struct rk_dsm_priv *rd)
 | |
| {
 | |
| 	if (IS_ERR(rd->rc))
 | |
| 		return;
 | |
| 
 | |
| 	reset_control_assert(rd->rc);
 | |
| 	udelay(5);
 | |
| 	reset_control_deassert(rd->rc);
 | |
| 	udelay(5);
 | |
| }
 | |
| 
 | |
| static void rk_dsm_pcm_shutdown(struct snd_pcm_substream *substream,
 | |
| 				struct snd_soc_dai *dai)
 | |
| {
 | |
| 	struct rk_dsm_priv *rd =
 | |
| 		snd_soc_component_get_drvdata(dai->component);
 | |
| 
 | |
| 	gpiod_set_value(rd->pa_ctl, 0);
 | |
| 
 | |
| 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 | |
| 		regmap_update_bits(rd->regmap, DACDSM_CTRL,
 | |
| 				   DSM_DACDSM_CTRL_DSM_MODE_CKE_MASK |
 | |
| 				   DSM_DACDSM_CTRL_DSM_EN_MASK,
 | |
| 				   DSM_DACDSM_CTRL_DSM_MODE_CKE_DIS |
 | |
| 				   DSM_DACDSM_CTRL_DSM_DIS);
 | |
| 		regmap_update_bits(rd->regmap, I2S_XFER,
 | |
| 				   DSM_I2S_XFER_RXS_MASK,
 | |
| 				   DSM_I2S_XFER_RXS_STOP);
 | |
| 		regmap_update_bits(rd->regmap, I2S_CLR,
 | |
| 				   DSM_I2S_CLR_RXC_MASK,
 | |
| 				   DSM_I2S_CLR_RXC_CLR);
 | |
| 		regmap_update_bits(rd->regmap, DACDIGEN,
 | |
| 				   DSM_DACDIGEN_DAC_GLBEN_MASK |
 | |
| 				   DSM_DACDIGEN_DACEN_L0R1_MASK,
 | |
| 				   DSM_DACDIGEN_DAC_GLBEN_DIS |
 | |
| 				   DSM_DACDIGEN_DACEN_L0R1_DIS);
 | |
| 	}
 | |
| 
 | |
| 	rk_dsm_reset(rd);
 | |
| }
 | |
| 
 | |
| static int rk_dsm_pcm_trigger(struct snd_pcm_substream *substream,
 | |
| 				int cmd, struct snd_soc_dai *dai)
 | |
| {
 | |
| 	struct rk_dsm_priv *rd =
 | |
| 		snd_soc_component_get_drvdata(dai->component);
 | |
| 
 | |
| 	switch (cmd) {
 | |
| 	case SNDRV_PCM_TRIGGER_SUSPEND:
 | |
| 	case SNDRV_PCM_TRIGGER_STOP:
 | |
| 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 | |
| 		/**
 | |
| 		 * After receiving the stop action, adjust the volume to the
 | |
| 		 * minimum as soon as possible and switch the differential pair
 | |
| 		 * of DSM to IO state, so that the same direction change of P/N
 | |
| 		 * level can reduce the pop sound to the minimum.
 | |
| 		 */
 | |
| 		regmap_write(rd->regmap, DACVOLL0, RKDSM_VOL_VAL_MAX);
 | |
| 		regmap_write(rd->regmap, DACVOLR0, RKDSM_VOL_VAL_MAX);
 | |
| 		regmap_write(rd->regmap, DACVOGP, DSM_DACVOGP_VOLGPL0_NEG | DSM_DACVOGP_VOLGPR0_NEG);
 | |
| 		regcache_cache_only(rd->regmap, false);
 | |
| 		regcache_mark_dirty(rd->regmap);
 | |
| 		regcache_sync(rd->regmap);
 | |
| 		if (rd->data && rd->data->iomux_switch)
 | |
| 			rd->data->iomux_switch(rd->dev, RKDSM_ON_GPIO);
 | |
| 		break;
 | |
| 	default:
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static const struct snd_soc_dai_ops rd_dai_ops = {
 | |
| 	.hw_params = rk_dsm_hw_params,
 | |
| 	.set_fmt = rk_dsm_set_dai_fmt,
 | |
| 	.startup = rk_dsm_pcm_startup,
 | |
| 	.shutdown = rk_dsm_pcm_shutdown,
 | |
| 	.trigger = rk_dsm_pcm_trigger,
 | |
| };
 | |
| 
 | |
| static struct snd_soc_dai_driver rd_dai[] = {
 | |
| 	{
 | |
| 		.name = "rk_dsm",
 | |
| 		.id = 0,
 | |
| 		.playback = {
 | |
| 			.stream_name = "Playback",
 | |
| 			.channels_min = 2,
 | |
| 			.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 = &rd_dai_ops,
 | |
| 	},
 | |
| };
 | |
| 
 | |
| static const struct snd_soc_component_driver soc_codec_dev_rd = {
 | |
| 	.controls = rk_dsm_snd_controls,
 | |
| 	.num_controls = ARRAY_SIZE(rk_dsm_snd_controls),
 | |
| };
 | |
| 
 | |
| static const struct reg_default rd_reg_defaults[] = {
 | |
| 	{ DACVUCTL, 0x1 },
 | |
| 	{ DACINT_DIV, 0x7 },
 | |
| 	{ DACDSM_DIV, 0x3 },
 | |
| };
 | |
| 
 | |
| static const struct regmap_config rd_regmap_config = {
 | |
| 	.reg_bits = 32,
 | |
| 	.reg_stride = 4,
 | |
| 	.val_bits = 32,
 | |
| 	.max_register = VERSION,
 | |
| 	.reg_defaults = rd_reg_defaults,
 | |
| 	.num_reg_defaults = ARRAY_SIZE(rd_reg_defaults),
 | |
| 	.cache_type = REGCACHE_FLAT,
 | |
| };
 | |
| 
 | |
| static int rk3506_soc_init(struct device *dev)
 | |
| {
 | |
| 	struct rk_dsm_priv *rd = dev_get_drvdata(dev);
 | |
| 	struct rk_dsm_iomux_res res[] = {
 | |
| 		{ .regbase = RK3506_IOC1_REG_BASE, .size = 0x100, },
 | |
| 		{ .regbase = RK3506_IOC2_REG_BASE, .size = 0x100, },
 | |
| 	};
 | |
| 	int c;
 | |
| 
 | |
| 	rd->iomuxes.res_num = ARRAY_SIZE(res);
 | |
| 	rd->iomuxes.ioc = devm_kcalloc(dev, rd->iomuxes.res_num, sizeof(void __iomem *), GFP_KERNEL);
 | |
| 	if (!rd->iomuxes.ioc)
 | |
| 		return -ENOMEM;
 | |
| 
 | |
| 	for (c = 0; c < rd->iomuxes.res_num; c++) {
 | |
| 		rd->iomuxes.ioc[c] = ioremap(res[c].regbase, res[c].size);
 | |
| 		if (!rd->iomuxes.ioc[c]) {
 | |
| 			int n;
 | |
| 
 | |
| 			/* iounmap previous mapped address */
 | |
| 			for (n = 0; n < rd->iomuxes.res_num; n++) {
 | |
| 				if (rd->iomuxes.ioc[n]) {
 | |
| 					iounmap(rd->iomuxes.ioc[n]);
 | |
| 					rd->iomuxes.ioc[n] = NULL;
 | |
| 				}
 | |
| 			}
 | |
| 			dev_err(dev, "ioremap res[%d] start: 0x%lx failed\n",
 | |
| 				c, (unsigned long)res[c].regbase);
 | |
| 			return -ENOMEM;
 | |
| 		}
 | |
| 		dev_info(dev, "ioremap ioc res[%d] start: 0x%lx success\n", c, (unsigned long)res[c].regbase);
 | |
| 	}
 | |
| 
 | |
| 	rd->pinctrl = devm_pinctrl_get(dev);
 | |
| 	if (!IS_ERR_OR_NULL(rd->pinctrl)) {
 | |
| 		const char *io_name;
 | |
| 		const char *pin_name;
 | |
| 
 | |
| 		if (rd->iomuxes.audm_en == (RK3506_DSM_AUDM0_EN | RK3506_DSM_AUDM1_EN)) {
 | |
| 			io_name = "audm0m1-iodown";
 | |
| 			pin_name = "audm0m1-pins";
 | |
| 		} else if (rd->iomuxes.audm_en == RK3506_DSM_AUDM1_EN) {
 | |
| 			io_name = "audm1-iodown";
 | |
| 			pin_name = "audm1-pins";
 | |
| 		} else {
 | |
| 			/* default case */
 | |
| 			io_name = "audm0-iodown";
 | |
| 			pin_name = "audm0-pins";
 | |
| 			rd->iomuxes.audm_en = RK3506_DSM_AUDM0_EN;
 | |
| 		}
 | |
| 
 | |
| 		rd->io_state = pinctrl_lookup_state(rd->pinctrl, io_name);
 | |
| 		if (IS_ERR(rd->io_state)) {
 | |
| 			rd->io_state = NULL;
 | |
| 			dev_err(dev, "Have no dsm pinctrl io state by: %s\n", io_name);
 | |
| 		}
 | |
| 
 | |
| 		rd->pin_state = pinctrl_lookup_state(rd->pinctrl, pin_name);
 | |
| 		if (IS_ERR(rd->pin_state)) {
 | |
| 			rd->pin_state = NULL;
 | |
| 			dev_err(dev, "Have no dsm pinctrl pin state, by: %s\n", pin_name);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (rd->data && rd->data->iomux_switch)
 | |
| 		rd->data->iomux_switch(rd->dev, RKDSM_ON_GPIO);
 | |
| 
 | |
| 	dev_info(dev, "iomuxes audm_en: 0x%x\n", rd->iomuxes.audm_en);
 | |
| 	/* enable internal codec to sai3 */
 | |
| 	return regmap_write(rd->grf, RK3506_GRF_SOC_CON0,
 | |
| 			    BIT(RK3506_DSM_SEL) << 16 | BIT(RK3506_DSM_SEL));
 | |
| }
 | |
| 
 | |
| static void rk3506_soc_deinit(struct device *dev)
 | |
| {
 | |
| 	struct rk_dsm_priv *rd = dev_get_drvdata(dev);
 | |
| 	int c;
 | |
| 
 | |
| 	regmap_write(rd->grf, RK3506_GRF_SOC_CON0, BIT(RK3506_DSM_SEL) << 16);
 | |
| 	for (c = 0; c < rd->iomuxes.res_num; c++) {
 | |
| 		if (rd->iomuxes.ioc[c]) {
 | |
| 			iounmap(rd->iomuxes.ioc[c]);
 | |
| 			rd->iomuxes.ioc[c] = NULL;
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static int rk3506_soc_iomux_switch(struct device *dev, int type)
 | |
| {
 | |
| 	struct rk_dsm_priv *rd = dev_get_drvdata(dev);
 | |
| 	struct rk_dsm_iomux *iomuxes = &rd->iomuxes;
 | |
| 	unsigned long flags;
 | |
| 
 | |
| 	local_irq_save(flags);
 | |
| 	if (iomuxes->audm_en & RK3506_DSM_AUDM0_EN) {
 | |
| 		/**
 | |
| 		 * DSM_AUD_RN_M0 - GPIO1_C1
 | |
| 		 * DSM_AUD_RP_M0 - GPIO1_C2
 | |
| 		 * DSM_AUD_LN_M0 - GPIO1_D0
 | |
| 		 * DSM_AUD_LP_M0 - GPIO1_D1
 | |
| 		 */
 | |
| 		if (type == RKDSM_ON_FUNC) {
 | |
| 			writel(((RK3506_GPIO1_IOC_GPIO1C2_SEL_MASK |
 | |
| 			       RK3506_GPIO1_IOC_GPIO1C1_SEL_MASK) << 16) |
 | |
| 			       (RK3506_GPIO1_IOC_DSM_AUD_RP_M0 |
 | |
| 			       RK3506_GPIO1_IOC_DSM_AUD_RN_M0),
 | |
| 			       iomuxes->ioc[0] + RK3506_GPIO1_IOC_GPIO1C_IOMUX_SEL_0);
 | |
| 			writel(((RK3506_GPIO1_IOC_GPIO1D1_SEL_MASK |
 | |
| 			       RK3506_GPIO1_IOC_GPIO1D0_SEL_MASK) << 16) |
 | |
| 			       (RK3506_GPIO1_IOC_DSM_AUD_LP_M0 |
 | |
| 			       RK3506_GPIO1_IOC_DSM_AUD_LN_M0),
 | |
| 			       iomuxes->ioc[0] + RK3506_GPIO1_IOC_GPIO1D_IOMUX_SEL_0);
 | |
| 		} else {
 | |
| 			/* RKDSM_ON_GPIO */
 | |
| 			writel(((RK3506_GPIO1_IOC_GPIO1C2_SEL_MASK |
 | |
| 			       RK3506_GPIO1_IOC_GPIO1C1_SEL_MASK) << 16) |
 | |
| 			       (RK3506_GPIO1_IOC_GPIO2C2 |
 | |
| 			       RK3506_GPIO1_IOC_GPIO2C1),
 | |
| 			       iomuxes->ioc[0] + RK3506_GPIO1_IOC_GPIO1C_IOMUX_SEL_0);
 | |
| 			writel(((RK3506_GPIO1_IOC_GPIO1D1_SEL_MASK |
 | |
| 			       RK3506_GPIO1_IOC_GPIO1D0_SEL_MASK) << 16) |
 | |
| 			       (RK3506_GPIO1_IOC_GPIO2D1 |
 | |
| 			       RK3506_GPIO1_IOC_GPIO2D0),
 | |
| 			       iomuxes->ioc[0] + RK3506_GPIO1_IOC_GPIO1D_IOMUX_SEL_0);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (iomuxes->audm_en & RK3506_DSM_AUDM1_EN) {
 | |
| 		/**
 | |
| 		 * DSM_AUD_RN_M1 - GPIO2_B4
 | |
| 		 * DSM_AUD_RP_M1 - GPIO2_B5
 | |
| 		 * DSM_AUD_LN_M1 - GPIO2_B6
 | |
| 		 * DSM_AUD_LP_M1 - GPIO2_B7
 | |
| 		 */
 | |
| 		if (type == RKDSM_ON_FUNC) {
 | |
| 			writel(((RK3506_GPIO2_IOC_GPIO2B7_SEL_MASK |
 | |
| 					  RK3506_GPIO2_IOC_GPIO2B6_SEL_MASK |
 | |
| 					  RK3506_GPIO2_IOC_GPIO2B5_SEL_MASK |
 | |
| 					  RK3506_GPIO2_IOC_GPIO2B4_SEL_MASK) << 16) |
 | |
| 					 (RK3506_GPIO2_IOC_DSM_AUD_LP_M1 |
 | |
| 					  RK3506_GPIO2_IOC_DSM_AUD_LN_M1 |
 | |
| 					  RK3506_GPIO2_IOC_DSM_AUD_RP_M1 |
 | |
| 					  RK3506_GPIO2_IOC_DSM_AUD_RN_M1),
 | |
| 					 iomuxes->ioc[1] + RK3506_GPIO2_IOC_GPIO2B_IOMUX_SEL_1);
 | |
| 		} else {
 | |
| 			/* RKDSM_ON_GPIO */
 | |
| 			writel(((RK3506_GPIO2_IOC_GPIO2B7_SEL_MASK |
 | |
| 				  RK3506_GPIO2_IOC_GPIO2B6_SEL_MASK |
 | |
| 				  RK3506_GPIO2_IOC_GPIO2B5_SEL_MASK |
 | |
| 				  RK3506_GPIO2_IOC_GPIO2B4_SEL_MASK) << 16) |
 | |
| 				 (RK3506_GPIO2_IOC_GPIO2B7 |
 | |
| 				  RK3506_GPIO2_IOC_GPIO2B6 |
 | |
| 				  RK3506_GPIO2_IOC_GPIO2B5 |
 | |
| 				  RK3506_GPIO2_IOC_GPIO2B4),
 | |
| 				 iomuxes->ioc[1] + RK3506_GPIO2_IOC_GPIO2B_IOMUX_SEL_1);
 | |
| 		}
 | |
| 	}
 | |
| 	local_irq_restore(flags);
 | |
| 
 | |
| 	/* Keeping sync with pinctrl framework */
 | |
| 	if (type == RKDSM_ON_FUNC)
 | |
| 		pinctrl_select_state(rd->pinctrl, rd->pin_state);
 | |
| 	else
 | |
| 		pinctrl_select_state(rd->pinctrl, rd->io_state);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static const struct rk_dsm_soc_data rk3506_data = {
 | |
| 	.init = rk3506_soc_init,
 | |
| 	.deinit = rk3506_soc_deinit,
 | |
| 	.iomux_switch = rk3506_soc_iomux_switch,
 | |
| };
 | |
| 
 | |
| static int rk3562_soc_init(struct device *dev)
 | |
| {
 | |
| 	struct rk_dsm_priv *rd = dev_get_drvdata(dev);
 | |
| 
 | |
| 	/* enable internal codec to i2s1 */
 | |
| 	return regmap_write(rd->grf, RK3562_GRF_PERI_AUDIO_CON,
 | |
| 			    (BIT(14) << 16 | BIT(14) | 0x0a100a10));
 | |
| }
 | |
| 
 | |
| static void rk3562_soc_deinit(struct device *dev)
 | |
| {
 | |
| 	struct rk_dsm_priv *rd = dev_get_drvdata(dev);
 | |
| 
 | |
| 	regmap_write(rd->grf, RK3562_GRF_PERI_AUDIO_CON, (BIT(14) << 16) | 0x0a100a10);
 | |
| }
 | |
| 
 | |
| static const struct rk_dsm_soc_data rk3562_data = {
 | |
| 	.init = rk3562_soc_init,
 | |
| 	.deinit = rk3562_soc_deinit,
 | |
| };
 | |
| 
 | |
| static int rk3576_soc_init(struct device *dev)
 | |
| {
 | |
| 	struct rk_dsm_priv *rd = dev_get_drvdata(dev);
 | |
| 
 | |
| 	/* enable internal codec to sai4 */
 | |
| 	return regmap_write(rd->grf, RK3576_SYS_GRF_SOC_CON2,
 | |
| 			    BIT(RK3576_DSM_SEL) << 16 | BIT(RK3576_DSM_SEL));
 | |
| }
 | |
| 
 | |
| static void rk3576_soc_deinit(struct device *dev)
 | |
| {
 | |
| 	struct rk_dsm_priv *rd = dev_get_drvdata(dev);
 | |
| 
 | |
| 	regmap_write(rd->grf, RK3576_SYS_GRF_SOC_CON2, BIT(RK3576_DSM_SEL) << 16);
 | |
| }
 | |
| 
 | |
| static const struct rk_dsm_soc_data rk3576_data = {
 | |
| 	.init = rk3576_soc_init,
 | |
| 	.deinit = rk3576_soc_deinit,
 | |
| };
 | |
| 
 | |
| #ifdef CONFIG_OF
 | |
| static const struct of_device_id rd_of_match[] = {
 | |
| 	{ .compatible = "rockchip,rk3506-dsm", .data = &rk3506_data },
 | |
| 	{ .compatible = "rockchip,rk3562-dsm", .data = &rk3562_data },
 | |
| 	{ .compatible = "rockchip,rk3576-dsm", .data = &rk3576_data },
 | |
| 	{},
 | |
| };
 | |
| MODULE_DEVICE_TABLE(of, rd_of_match);
 | |
| #endif
 | |
| 
 | |
| #ifdef CONFIG_PM
 | |
| static int rk_dsm_runtime_resume(struct device *dev)
 | |
| {
 | |
| 	struct rk_dsm_priv *rd = dev_get_drvdata(dev);
 | |
| 	int ret = 0;
 | |
| 
 | |
| 	ret = clk_prepare_enable(rd->pclk);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	regcache_cache_only(rd->regmap, false);
 | |
| 	regcache_mark_dirty(rd->regmap);
 | |
| 	ret = regcache_sync(rd->regmap);
 | |
| 	if (ret)
 | |
| 		goto err;
 | |
| 
 | |
| 	ret = clk_prepare_enable(rd->clk_dac);
 | |
| 	if (ret)
 | |
| 		goto err;
 | |
| 
 | |
| 	return 0;
 | |
| err:
 | |
| 	clk_disable_unprepare(rd->pclk);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int rk_dsm_runtime_suspend(struct device *dev)
 | |
| {
 | |
| 	struct rk_dsm_priv *rd = dev_get_drvdata(dev);
 | |
| 
 | |
| 	regcache_cache_only(rd->regmap, true);
 | |
| 	clk_disable_unprepare(rd->clk_dac);
 | |
| 	clk_disable_unprepare(rd->pclk);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| static int rk_dsm_platform_probe(struct platform_device *pdev)
 | |
| {
 | |
| 	struct device_node *np = pdev->dev.of_node;
 | |
| 	struct rk_dsm_priv *rd;
 | |
| 	void __iomem *base;
 | |
| 	int ret = 0;
 | |
| 
 | |
| 	rd = devm_kzalloc(&pdev->dev, sizeof(*rd), GFP_KERNEL);
 | |
| 	if (!rd)
 | |
| 		return -ENOMEM;
 | |
| 
 | |
| 	rd->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
 | |
| 	if (IS_ERR(rd->grf))
 | |
| 		return PTR_ERR(rd->grf);
 | |
| 
 | |
| 	if (device_property_read_u32(&pdev->dev, "rockchip,pa-ctl-delay-ms",
 | |
| 				     &rd->pa_ctl_delay_ms))
 | |
| 		rd->pa_ctl_delay_ms = 0;
 | |
| 
 | |
| 	if (device_property_read_u32(&pdev->dev, "rockchip,dsm-audm-en", &rd->iomuxes.audm_en))
 | |
| 		rd->iomuxes.audm_en = 0;
 | |
| 
 | |
| 	rd->rc = devm_reset_control_get(&pdev->dev, "reset");
 | |
| 
 | |
| 	rd->clk_dac = devm_clk_get(&pdev->dev, "dac");
 | |
| 	if (IS_ERR(rd->clk_dac))
 | |
| 		return PTR_ERR(rd->clk_dac);
 | |
| 
 | |
| 	rd->pclk = devm_clk_get(&pdev->dev, "pclk");
 | |
| 	if (IS_ERR(rd->pclk))
 | |
| 		return PTR_ERR(rd->pclk);
 | |
| 
 | |
| 	base = devm_platform_ioremap_resource(pdev, 0);
 | |
| 	if (IS_ERR(base))
 | |
| 		return PTR_ERR(base);
 | |
| 
 | |
| 	rd->regmap =
 | |
| 		devm_regmap_init_mmio(&pdev->dev, base, &rd_regmap_config);
 | |
| 	if (IS_ERR(rd->regmap))
 | |
| 		return PTR_ERR(rd->regmap);
 | |
| 
 | |
| 	rd->dev = &pdev->dev;
 | |
| 	platform_set_drvdata(pdev, rd);
 | |
| 
 | |
| 	rd->data = device_get_match_data(&pdev->dev);
 | |
| 	if (rd->data && rd->data->init) {
 | |
| 		ret = rd->data->init(&pdev->dev);
 | |
| 		if (ret)
 | |
| 			return ret;
 | |
| 	}
 | |
| 
 | |
| 	pm_runtime_enable(&pdev->dev);
 | |
| 	if (!pm_runtime_enabled(&pdev->dev)) {
 | |
| 		ret = rk_dsm_runtime_resume(&pdev->dev);
 | |
| 		if (ret)
 | |
| 			goto err_pm_disable;
 | |
| 	}
 | |
| 
 | |
| 	regmap_update_bits(rd->regmap, DACDSM_CTRL,
 | |
| 			   DSM_DACDSM_CTRL_DSM_MODE_MASK,
 | |
| 			   DSM_DACDSM_CTRL_DSM_MODE_0);
 | |
| 
 | |
| 	rd->pa_ctl = devm_gpiod_get_optional(&pdev->dev, "pa-ctl",
 | |
| 					     GPIOD_OUT_LOW);
 | |
| 
 | |
| 	if (!rd->pa_ctl) {
 | |
| 		dev_info(&pdev->dev, "no need pa-ctl gpio\n");
 | |
| 	} else if (IS_ERR(rd->pa_ctl)) {
 | |
| 		ret = PTR_ERR(rd->pa_ctl);
 | |
| 		dev_err(&pdev->dev, "fail to request gpio pa-ctl\n");
 | |
| 		goto err_suspend;
 | |
| 	}
 | |
| 
 | |
| 	ret = devm_snd_soc_register_component(&pdev->dev, &soc_codec_dev_rd,
 | |
| 					      rd_dai, ARRAY_SIZE(rd_dai));
 | |
| 
 | |
| 	if (ret)
 | |
| 		goto err_suspend;
 | |
| 
 | |
| 	return 0;
 | |
| 
 | |
| err_suspend:
 | |
| 	if (!pm_runtime_status_suspended(&pdev->dev))
 | |
| 		rk_dsm_runtime_suspend(&pdev->dev);
 | |
| err_pm_disable:
 | |
| 	pm_runtime_disable(&pdev->dev);
 | |
| 
 | |
| 	if (rd->data && rd->data->deinit)
 | |
| 		rd->data->deinit(&pdev->dev);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int rk_dsm_platform_remove(struct platform_device *pdev)
 | |
| {
 | |
| 	struct rk_dsm_priv *rd = dev_get_drvdata(&pdev->dev);
 | |
| 
 | |
| 	pm_runtime_disable(&pdev->dev);
 | |
| 	if (!pm_runtime_status_suspended(&pdev->dev))
 | |
| 		rk_dsm_runtime_suspend(&pdev->dev);
 | |
| 
 | |
| 	if (rd->data && rd->data->deinit)
 | |
| 		rd->data->deinit(&pdev->dev);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static const struct dev_pm_ops rd_pm = {
 | |
| 	SET_RUNTIME_PM_OPS(rk_dsm_runtime_suspend,
 | |
| 			   rk_dsm_runtime_resume, NULL)
 | |
| };
 | |
| 
 | |
| static struct platform_driver rk_dsm_driver = {
 | |
| 	.driver = {
 | |
| 		.name = "rk_dsm",
 | |
| 		.of_match_table = of_match_ptr(rd_of_match),
 | |
| 		.pm = &rd_pm,
 | |
| 	},
 | |
| 	.probe = rk_dsm_platform_probe,
 | |
| 	.remove = rk_dsm_platform_remove,
 | |
| };
 | |
| module_platform_driver(rk_dsm_driver);
 | |
| 
 | |
| MODULE_AUTHOR("Jason Zhu <jason.zhu@rock-chips.com>");
 | |
| MODULE_DESCRIPTION("ASoC Rockchip Delta-sigma Digital Converter Driver");
 | |
| MODULE_LICENSE("GPL");
 |