579 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			579 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0
 | |
| //
 | |
| // rxk1000_codec.c  --  rk1000 ALSA Soc Audio driver
 | |
| //
 | |
| // Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd All rights reserved.
 | |
| 
 | |
| #include <linux/delay.h>
 | |
| #include <linux/gpio/consumer.h>
 | |
| #include <linux/i2c.h>
 | |
| #include <linux/module.h>
 | |
| #include <sound/core.h>
 | |
| #include <sound/pcm.h>
 | |
| #include <sound/pcm_params.h>
 | |
| #include <sound/soc.h>
 | |
| #include <sound/soc-dapm.h>
 | |
| #include "rk1000_codec.h"
 | |
| 
 | |
| #define FREQ441KHZ	(0x11 << 1)
 | |
| /* rk1000 output volume, DAC Digital Gain */
 | |
| /* 0x0000 ~ 0xF42 */
 | |
| #define VOLUME_OUTPUT	0xF42
 | |
| /* 0x0 ~ 0x3f(bit0-bit5)  max=0x0(+6DB) min=0x3f(-60DB)	 Analog Gain */
 | |
| #define VOLUME_CODEC_PA	0x0
 | |
| 
 | |
| /* rk1000 input volume, rk610 can not adjust the recording volume */
 | |
| #define VOLUME_INPUT	0x07
 | |
| 
 | |
| #define OUT_CAPLESS	(1)
 | |
| 
 | |
| static const struct reg_default rk1000_codec_reg[] = {
 | |
| 	{ 0x00, 0x05 },
 | |
| 	{ 0x01, 0x04 },
 | |
| 	{ 0x02, 0xfd },
 | |
| 	{ 0x03, 0xf3 },
 | |
| 	{ 0x04, 0x03 },
 | |
| 	{ 0x05, 0x00 },
 | |
| 	{ 0x06, 0x00 },
 | |
| 	{ 0x07, 0x00 },
 | |
| 	{ 0x08, 0x00 },
 | |
| 	{ 0x09, 0x05 },
 | |
| 	{ 0x0a, 0x00 },
 | |
| 	{ 0x0b, 0x00 },
 | |
| 	{ 0x0c, 0x97 },
 | |
| 	{ 0x0d, 0x97 },
 | |
| 	{ 0x0e, 0x97 },
 | |
| 	{ 0x0f, 0x97 },
 | |
| 	{ 0x10, 0x97 },
 | |
| 	{ 0x11, 0x97 },
 | |
| 	{ 0x12, 0xcc },
 | |
| 	{ 0x13, 0x00 },
 | |
| 	{ 0x14, 0x00 },
 | |
| 	{ 0x15, 0xf1 },
 | |
| 	{ 0x16, 0x90 },
 | |
| 	{ 0x17, 0xff },
 | |
| 	{ 0x18, 0xff },
 | |
| 	{ 0x19, 0xff },
 | |
| 	{ 0x1a, 0x9c },
 | |
| 	{ 0x1b, 0x00 },
 | |
| 	{ 0x1c, 0x00 },
 | |
| 	{ 0x1d, 0xff },
 | |
| 	{ 0x1e, 0xff },
 | |
| 	{ 0x1f, 0xff },
 | |
| };
 | |
| 
 | |
| struct rk1000_codec_priv {
 | |
| 	struct regmap *regmap;
 | |
| 	struct regmap *ctlmap;
 | |
| 	struct snd_soc_component *component;
 | |
| 	struct delayed_work pa_delayed_work;
 | |
| 	struct gpio_desc *spk_en_gpio;
 | |
| 	/*
 | |
| 	 * Some amplifiers enable a longer time.
 | |
| 	 * config after pa_enable_io delay pa_enable_time(ms)
 | |
| 	 * so value range is 0 - 8000.
 | |
| 	 */
 | |
| 	unsigned int pa_enable_time;
 | |
| };
 | |
| 
 | |
| static void spk_ctrl_fun(struct snd_soc_component *component, int status)
 | |
| {
 | |
| 	struct rk1000_codec_priv *rk1000_codec;
 | |
| 
 | |
| 	rk1000_codec = snd_soc_component_get_drvdata(component);
 | |
| 
 | |
| 	if (rk1000_codec->spk_en_gpio)
 | |
| 		gpiod_set_value(rk1000_codec->spk_en_gpio, status);
 | |
| }
 | |
| 
 | |
| static int rk1000_codec_set_bias_level(struct snd_soc_component *component,
 | |
| 				       enum snd_soc_bias_level level)
 | |
| {
 | |
| 	switch (level) {
 | |
| 	case SND_SOC_BIAS_ON:
 | |
| 		break;
 | |
| 
 | |
| 	case SND_SOC_BIAS_PREPARE:
 | |
| 		snd_soc_component_write(component, ACCELCODEC_R1D, 0x2a);
 | |
| 		snd_soc_component_write(component, ACCELCODEC_R1E, 0x40);
 | |
| 		snd_soc_component_write(component, ACCELCODEC_R1F, 0x49);
 | |
| 		break;
 | |
| 
 | |
| 	case SND_SOC_BIAS_STANDBY:
 | |
| 		snd_soc_component_write(component, ACCELCODEC_R1D, 0x2a);
 | |
| 		snd_soc_component_write(component, ACCELCODEC_R1E, 0x40);
 | |
| 		snd_soc_component_write(component, ACCELCODEC_R1F, 0x49);
 | |
| 		break;
 | |
| 
 | |
| 	case SND_SOC_BIAS_OFF:
 | |
| 		spk_ctrl_fun(component, GPIO_LOW);
 | |
| 		snd_soc_component_write(component, ACCELCODEC_R1D, 0xFF);
 | |
| 		snd_soc_component_write(component, ACCELCODEC_R1E, 0xFF);
 | |
| 		snd_soc_component_write(component, ACCELCODEC_R1F, 0xFF);
 | |
| 		break;
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int rk1000_codec_set_dai_fmt(struct snd_soc_dai *codec_dai,
 | |
| 				    unsigned int fmt)
 | |
| {
 | |
| 	struct snd_soc_component *component = codec_dai->component;
 | |
| 	struct rk1000_codec_priv *rk1000_codec;
 | |
| 	u16 iface = 0;
 | |
| 
 | |
| 	rk1000_codec = snd_soc_component_get_drvdata(component);
 | |
| 	/* setup Vmid and Vref, other module power down */
 | |
| 	snd_soc_component_write(component, ACCELCODEC_R1D, 0x2a);
 | |
| 	snd_soc_component_write(component, ACCELCODEC_R1E, 0x40);
 | |
| 	/* set master/slave audio interface */
 | |
| 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
 | |
| 	case SND_SOC_DAIFMT_CBM_CFM:
 | |
| 		iface = 0x0040;
 | |
| 		break;
 | |
| 
 | |
| 	case SND_SOC_DAIFMT_CBS_CFS:
 | |
| 		iface = 0x0000;
 | |
| 		break;
 | |
| 
 | |
| 	default:
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 	/* interface format */
 | |
| 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
 | |
| 	case SND_SOC_DAIFMT_I2S:
 | |
| 		iface |= 0x0002;
 | |
| 		break;
 | |
| 
 | |
| 	case SND_SOC_DAIFMT_RIGHT_J:
 | |
| 		break;
 | |
| 
 | |
| 	case SND_SOC_DAIFMT_LEFT_J:
 | |
| 		iface |= 0x0001;
 | |
| 		break;
 | |
| 
 | |
| 	case SND_SOC_DAIFMT_DSP_A:
 | |
| 		iface |= 0x0003;
 | |
| 		break;
 | |
| 
 | |
| 	case SND_SOC_DAIFMT_DSP_B:
 | |
| 		iface |= 0x0013;
 | |
| 		break;
 | |
| 
 | |
| 	default:
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	/* clock inversion */
 | |
| 	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
 | |
| 	case SND_SOC_DAIFMT_NB_NF:
 | |
| 		break;
 | |
| 
 | |
| 	case SND_SOC_DAIFMT_IB_IF:
 | |
| 		iface |= 0x0090;
 | |
| 		break;
 | |
| 
 | |
| 	case SND_SOC_DAIFMT_IB_NF:
 | |
| 		iface |= 0x0080;
 | |
| 		break;
 | |
| 
 | |
| 	case SND_SOC_DAIFMT_NB_IF:
 | |
| 		iface |= 0x0010;
 | |
| 		break;
 | |
| 
 | |
| 	default:
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	snd_soc_component_write(component, ACCELCODEC_R09, iface);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int rk1000_codec_pcm_hw_params(struct snd_pcm_substream *substream,
 | |
| 				      struct snd_pcm_hw_params *params,
 | |
| 				      struct snd_soc_dai *dai)
 | |
| {
 | |
| 	u32 iface;
 | |
| 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 | |
| 	struct snd_soc_component *component = dai->component;
 | |
| 	unsigned int dai_fmt;
 | |
| 
 | |
| 	dai_fmt = rtd->card->dai_link[0].dai_fmt;
 | |
| 	iface = snd_soc_component_read(component, ACCELCODEC_R09) & 0x1f3;
 | |
| 	snd_soc_component_write(component, ACCELCODEC_R0C, 0x17);
 | |
| 	snd_soc_component_write(component, ACCELCODEC_R04,
 | |
| 				ASC_INT_MUTE_L | ASC_INT_MUTE_R |
 | |
| 		      ASC_SIDETONE_L_OFF | ASC_SIDETONE_R_OFF);
 | |
| 	snd_soc_component_write(component, ACCELCODEC_R0B,
 | |
| 				ASC_DEC_DISABLE | ASC_INT_DISABLE);
 | |
| 
 | |
| 	if ((dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBM_CFM)
 | |
| 		iface |= ASC_INVERT_BCLK;
 | |
| 	snd_soc_component_write(component, ACCELCODEC_R09, iface);
 | |
| 	snd_soc_component_write(component, ACCELCODEC_R0A, 0xa0);
 | |
| 	snd_soc_component_write(component, ACCELCODEC_R0B, ASC_DEC_ENABLE | ASC_INT_ENABLE);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int rk1000_codec_mute(struct snd_soc_dai *dai, int mute, int stream)
 | |
| {
 | |
| 	struct snd_soc_component *component = dai->component;
 | |
| 	struct rk1000_codec_priv *rk1000_codec;
 | |
| 
 | |
| 	rk1000_codec = snd_soc_component_get_drvdata(component);
 | |
| 	if (mute) {
 | |
| 		/* AOL */
 | |
| 		snd_soc_component_write(component, ACCELCODEC_R17, 0xFF);
 | |
| 		/* AOR */
 | |
| 		snd_soc_component_write(component, ACCELCODEC_R18, 0xFF);
 | |
| 		/* AOM */
 | |
| 		snd_soc_component_write(component, ACCELCODEC_R19, 0xFF);
 | |
| 		/* soft mute */
 | |
| 		snd_soc_component_write(component, ACCELCODEC_R04,
 | |
| 					ASC_INT_MUTE_L | ASC_INT_MUTE_R |
 | |
| 			      ASC_SIDETONE_L_OFF | ASC_SIDETONE_R_OFF);
 | |
| 	} else {
 | |
| 		/* setup Vmid and Vref, other module power down */
 | |
| 		snd_soc_component_write(component, ACCELCODEC_R1D, 0x2a);
 | |
| 		snd_soc_component_write(component, ACCELCODEC_R1E, 0x40);
 | |
| 		/* AOL */
 | |
| 		snd_soc_component_write(component, ACCELCODEC_R17,
 | |
| 					VOLUME_CODEC_PA | ASC_OUTPUT_ACTIVE |
 | |
| 			      ASC_CROSSZERO_EN);
 | |
| 		/* AOR */
 | |
| 		snd_soc_component_write(component, ACCELCODEC_R18,
 | |
| 					VOLUME_CODEC_PA | ASC_OUTPUT_ACTIVE |
 | |
| 			      ASC_CROSSZERO_EN);
 | |
| 		snd_soc_component_write(component, ACCELCODEC_R04,
 | |
| 					ASC_INT_ACTIVE_L | ASC_INT_ACTIVE_R |
 | |
| 			      ASC_SIDETONE_L_OFF | ASC_SIDETONE_R_OFF);
 | |
| 		/* AOM */
 | |
| 		snd_soc_component_write(component, ACCELCODEC_R19, 0x7F);
 | |
| 		#if OUT_CAPLESS
 | |
| 		snd_soc_component_write(component, ACCELCODEC_R1F,
 | |
| 					0x09 | ASC_PDMIXM_ENABLE);
 | |
| 		#else
 | |
| 		snd_soc_component_write(component, ACCELCODEC_R1F,
 | |
| 					0x09 | ASC_PDMIXM_ENABLE | ASC_PDPAM_ENABLE);
 | |
| 		#endif
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void pa_delayedwork(struct work_struct *work)
 | |
| {
 | |
| 	struct rk1000_codec_priv *priv = container_of(work,
 | |
| 						      struct rk1000_codec_priv,
 | |
| 						      pa_delayed_work.work);
 | |
| 	struct snd_soc_component *component = priv->component;
 | |
| 
 | |
| 	spk_ctrl_fun(component, GPIO_HIGH);
 | |
| }
 | |
| 
 | |
| static struct snd_soc_dai_ops rk1000_codec_ops = {
 | |
| 	.hw_params = rk1000_codec_pcm_hw_params,
 | |
| 	.set_fmt = rk1000_codec_set_dai_fmt,
 | |
| 	.mute_stream = rk1000_codec_mute,
 | |
| 	.no_capture_mute = 1,
 | |
| };
 | |
| 
 | |
| #define RK1000_CODEC_RATES SNDRV_PCM_RATE_8000_192000
 | |
| #define RK1000_CODEC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |  \
 | |
| 			      SNDRV_PCM_FMTBIT_S20_3LE | \
 | |
| 			      SNDRV_PCM_FMTBIT_S24_LE)
 | |
| 
 | |
| static struct snd_soc_dai_driver rk1000_codec_dai[] = {
 | |
| 	{
 | |
| 		.name = "rk1000_codec",
 | |
| 		.playback = {
 | |
| 			.stream_name = "Playback",
 | |
| 			.channels_min = 2,
 | |
| 			.channels_max = 8,
 | |
| 			.rates = RK1000_CODEC_RATES,
 | |
| 			.formats = RK1000_CODEC_FORMATS,
 | |
| 		},
 | |
| 		.capture = {
 | |
| 			.stream_name = "Capture",
 | |
| 			.channels_min = 2,
 | |
| 			.channels_max = 2,
 | |
| 			.rates = RK1000_CODEC_RATES,
 | |
| 			.formats = RK1000_CODEC_FORMATS,
 | |
| 		 },
 | |
| 		.ops = &rk1000_codec_ops,
 | |
| 		.symmetric_rate = 1,
 | |
| 	}
 | |
| };
 | |
| 
 | |
| static void rk1000_codec_reg_init(struct snd_soc_component *component)
 | |
| {
 | |
| 	struct rk1000_codec_priv *rk1000_codec;
 | |
| 	unsigned int digital_gain;
 | |
| 	unsigned int mic_vol;
 | |
| 	int ret;
 | |
| 
 | |
| 	mic_vol = VOLUME_INPUT;
 | |
| 	rk1000_codec = snd_soc_component_get_drvdata(component);
 | |
| 	ret = snd_soc_component_write(component, ACCELCODEC_R1D, 0x30);
 | |
| 	snd_soc_component_write(component, ACCELCODEC_R1E, 0x40);
 | |
| 	/*Route R-LPF->R-Mixer, L-LPF->L-Mixer*/
 | |
| 	snd_soc_component_write(component, ACCELCODEC_R15, 0xC1);
 | |
| 	/*With Cap Output, VMID ramp up slow*/
 | |
| 	snd_soc_component_write(component, ACCELCODEC_R1A, 0x14);
 | |
| 	mdelay(10);
 | |
| 	snd_soc_component_write(component, ACCELCODEC_R0C, 0x10 | ASC_INPUT_VOL_0DB);
 | |
| 	snd_soc_component_write(component, ACCELCODEC_R0D, 0x10 | ASC_INPUT_VOL_0DB);
 | |
| 	if (mic_vol > 0x07) {
 | |
| 		/*Select MIC input*/
 | |
| 		snd_soc_component_write(component, ACCELCODEC_R12,
 | |
| 					0x4c | ASC_MIC_INPUT | ASC_MIC_BOOST_20DB);
 | |
| 		mic_vol -= 0x07;
 | |
| 	} else {
 | |
| 		/*Select MIC input*/
 | |
| 		snd_soc_component_write(component, ACCELCODEC_R12, 0x4c | ASC_MIC_INPUT);
 | |
| 	}
 | |
| 	/*use default value*/
 | |
| 	snd_soc_component_write(component, ACCELCODEC_R1C, ASC_DEM_ENABLE);
 | |
| 	snd_soc_component_write(component, ACCELCODEC_R0E, 0x10 | mic_vol);
 | |
| 	/* disable route PGA->R/L Mixer, PGA gain 0db. */
 | |
| 	snd_soc_component_write(component, ACCELCODEC_R13, 0x05 | 0 << 3);
 | |
| 	snd_soc_component_write(component, ACCELCODEC_R14, 0x05 | 0 << 3);
 | |
| 	/*2soft mute*/
 | |
| 	snd_soc_component_write(component, ACCELCODEC_R04,
 | |
| 				ASC_INT_MUTE_L | ASC_INT_MUTE_R |
 | |
| 				ASC_SIDETONE_L_OFF | ASC_SIDETONE_R_OFF);
 | |
| 	/*2set default SR and clk*/
 | |
| 	snd_soc_component_write(component, ACCELCODEC_R0A, FREQ441KHZ | ASC_NORMAL_MODE |
 | |
| 				(0x10 << 1) | ASC_CLKNODIV | ASC_CLK_ENABLE);
 | |
| 	/*2Config audio  interface*/
 | |
| 	snd_soc_component_write(component, ACCELCODEC_R09, ASC_I2S_MODE |
 | |
| 				ASC_16BIT_MODE | ASC_NORMAL_LRCLK |
 | |
| 				ASC_LRSWAP_DISABLE | ASC_NORMAL_BCLK);
 | |
| 	snd_soc_component_write(component, ACCELCODEC_R00, ASC_HPF_ENABLE |
 | |
| 				ASC_DSM_MODE_ENABLE | ASC_SCRAMBLE_ENABLE |
 | |
| 				ASC_DITHER_ENABLE | ASC_BCLKDIV_4);
 | |
| 	/*2volume,input,output*/
 | |
| 	digital_gain = VOLUME_OUTPUT;
 | |
| 	if (snd_soc_component_read(component, ACCELCODEC_R05) != 0x0f) {
 | |
| 		snd_soc_component_write(component, ACCELCODEC_R05,
 | |
| 					(digital_gain >> 8) & 0xFF);
 | |
| 		snd_soc_component_write(component, ACCELCODEC_R06, digital_gain & 0xFF);
 | |
| 	}
 | |
| 
 | |
| 	if (snd_soc_component_read(component, ACCELCODEC_R07) != 0x0f) {
 | |
| 		snd_soc_component_write(component, ACCELCODEC_R07,
 | |
| 					(digital_gain >> 8) & 0xFF);
 | |
| 		snd_soc_component_write(component, ACCELCODEC_R08, digital_gain & 0xFF);
 | |
| 	}
 | |
| 
 | |
| 	snd_soc_component_write(component, ACCELCODEC_R0B,
 | |
| 				ASC_DEC_ENABLE | ASC_INT_ENABLE);
 | |
| 
 | |
| 	#if OUT_CAPLESS
 | |
| 	snd_soc_component_write(component, ACCELCODEC_R1F,
 | |
| 				0x09 | ASC_PDMIXM_ENABLE);
 | |
| 	#else
 | |
| 	snd_soc_component_write(component, ACCELCODEC_R1F, 0x09 |
 | |
| 				ASC_PDMIXM_ENABLE | ASC_PDPAM_ENABLE);
 | |
| 	#endif
 | |
| 
 | |
| 	snd_soc_component_write(component, ACCELCODEC_R17, VOLUME_CODEC_PA |
 | |
| 				ASC_OUTPUT_ACTIVE | ASC_CROSSZERO_EN);
 | |
| 	snd_soc_component_write(component, ACCELCODEC_R18, VOLUME_CODEC_PA |
 | |
| 				ASC_OUTPUT_ACTIVE | ASC_CROSSZERO_EN);
 | |
| 	snd_soc_component_write(component, ACCELCODEC_R04, ASC_INT_ACTIVE_L |
 | |
| 				ASC_INT_ACTIVE_R | ASC_SIDETONE_L_OFF |
 | |
| 				ASC_SIDETONE_R_OFF);
 | |
| 	snd_soc_component_write(component, ACCELCODEC_R19, 0x7F);
 | |
| }
 | |
| 
 | |
| static int rk1000_codec_suspend(struct snd_soc_component *component)
 | |
| {
 | |
| 	spk_ctrl_fun(component, GPIO_LOW);
 | |
| 	rk1000_codec_set_bias_level(component, SND_SOC_BIAS_OFF);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int rk1000_codec_resume(struct snd_soc_component *component)
 | |
| {
 | |
| 	rk1000_codec_set_bias_level(component, SND_SOC_BIAS_PREPARE);
 | |
| 	spk_ctrl_fun(component, GPIO_HIGH);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int rk1000_codec_probe(struct snd_soc_component *component)
 | |
| {
 | |
| 	struct rk1000_codec_priv *rk1000_codec;
 | |
| 
 | |
| 	rk1000_codec = snd_soc_component_get_drvdata(component);
 | |
| 	rk1000_codec->component = component;
 | |
| 
 | |
| 	INIT_DELAYED_WORK(&rk1000_codec->pa_delayed_work,
 | |
| 			  pa_delayedwork);
 | |
| 
 | |
| 	rk1000_codec_set_bias_level(component, SND_SOC_BIAS_PREPARE);
 | |
| 	schedule_delayed_work(&rk1000_codec->pa_delayed_work,
 | |
| 			      msecs_to_jiffies(rk1000_codec->pa_enable_time));
 | |
| 	rk1000_codec_reg_init(component);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void rk1000_codec_remove(struct snd_soc_component *component)
 | |
| {
 | |
| 	rk1000_codec_set_bias_level(component, SND_SOC_BIAS_OFF);
 | |
| }
 | |
| 
 | |
| static const struct snd_soc_component_driver soc_codec_dev_rk1000_codec = {
 | |
| 	.probe = rk1000_codec_probe,
 | |
| 	.remove = rk1000_codec_remove,
 | |
| 	.suspend = rk1000_codec_suspend,
 | |
| 	.resume = rk1000_codec_resume,
 | |
| 	.set_bias_level = rk1000_codec_set_bias_level,
 | |
| };
 | |
| 
 | |
| static int rk1000_reg_write(void *context, unsigned int reg,
 | |
| 			    unsigned int value)
 | |
| {
 | |
| 	struct i2c_client *i2c = context;
 | |
| 	struct i2c_msg msg;
 | |
| 	u8 buf;
 | |
| 	int ret;
 | |
| 
 | |
| 	buf = value;
 | |
| 	msg.addr = i2c->addr | reg;
 | |
| 	msg.flags = i2c->flags & I2C_M_TEN;
 | |
| 	msg.len = 1;
 | |
| 	msg.buf = &buf;
 | |
| 
 | |
| 	ret = i2c_transfer(i2c->adapter, &msg, 1);
 | |
| 
 | |
| 	return (ret == 1) ? 0 : ret;
 | |
| }
 | |
| 
 | |
| static int rk1000_reg_read(void *context, unsigned int reg,
 | |
| 			   unsigned int *value)
 | |
| {
 | |
| 	struct i2c_client *i2c = context;
 | |
| 	struct i2c_msg msg;
 | |
| 	u8 buf;
 | |
| 	int ret;
 | |
| 
 | |
| 	msg.addr = i2c->addr | reg;
 | |
| 	msg.flags = I2C_M_RD;
 | |
| 	msg.len = 1;
 | |
| 	msg.buf = &buf;
 | |
| 
 | |
| 	ret = i2c_transfer(i2c->adapter, &msg, 1);
 | |
| 	if (ret != 1)
 | |
| 		return ret;
 | |
| 
 | |
| 	*value = buf;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static const struct regmap_config rk1000_codec_regmap = {
 | |
| 	.reg_bits = 8,
 | |
| 	.val_bits = 8,
 | |
| 	.reg_write = rk1000_reg_write,
 | |
| 	.reg_read = rk1000_reg_read,
 | |
| 	.max_register = ACCELCODEC_R1F,
 | |
| 	.cache_type = REGCACHE_FLAT,
 | |
| 	.reg_defaults = rk1000_codec_reg,
 | |
| 	.num_reg_defaults = ARRAY_SIZE(rk1000_codec_reg),
 | |
| };
 | |
| 
 | |
| static const struct regmap_config rk1000_ctl_regmap = {
 | |
| 	.reg_bits = 8,
 | |
| 	.val_bits = 8,
 | |
| 	.max_register = CODEC_CON,
 | |
| 	.cache_type = REGCACHE_FLAT,
 | |
| };
 | |
| 
 | |
| static int rk1000_codec_i2c_probe(struct i2c_client *i2c,
 | |
| 				  const struct i2c_device_id *id)
 | |
| {
 | |
| 	struct rk1000_codec_priv *rk1000;
 | |
| 	struct device_node *np = i2c->dev.of_node;
 | |
| 	struct device_node *ctl;
 | |
| 	struct i2c_client *ctl_client;
 | |
| 
 | |
| 	rk1000 = devm_kzalloc(&i2c->dev, sizeof(*rk1000), GFP_KERNEL);
 | |
| 	if (!rk1000)
 | |
| 		return -ENOMEM;
 | |
| 
 | |
| 	i2c_set_clientdata(i2c, rk1000);
 | |
| 
 | |
| 	of_property_read_u32(np, "rockchip,pa-en-time-ms",
 | |
| 			     &rk1000->pa_enable_time);
 | |
| 
 | |
| 	rk1000->spk_en_gpio = devm_gpiod_get_optional(&i2c->dev, "rockchip,spk-en",
 | |
| 						      GPIOD_OUT_LOW);
 | |
| 	if (IS_ERR(rk1000->spk_en_gpio))
 | |
| 		return PTR_ERR(rk1000->spk_en_gpio);
 | |
| 
 | |
| 	ctl = of_parse_phandle(np, "rockchip,ctl", 0);
 | |
| 	if (!ctl)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	ctl_client = of_find_i2c_device_by_node(ctl);
 | |
| 	if (!ctl_client) {
 | |
| 		dev_err(&i2c->dev, "can't find control client\n");
 | |
| 		return -EPROBE_DEFER;
 | |
| 	}
 | |
| 
 | |
| 	rk1000->regmap = devm_regmap_init(&i2c->dev, NULL,
 | |
| 					  i2c, &rk1000_codec_regmap);
 | |
| 	if (IS_ERR(rk1000->regmap))
 | |
| 		return PTR_ERR(rk1000->regmap);
 | |
| 
 | |
| 	rk1000->ctlmap = devm_regmap_init_i2c(ctl_client,
 | |
| 					      &rk1000_ctl_regmap);
 | |
| 	if (IS_ERR(rk1000->ctlmap))
 | |
| 		return PTR_ERR(rk1000->ctlmap);
 | |
| 
 | |
| 	regmap_write(rk1000->ctlmap, CODEC_CON, CODEC_ON);
 | |
| 
 | |
| 	return devm_snd_soc_register_component(&i2c->dev, &soc_codec_dev_rk1000_codec,
 | |
| 					       rk1000_codec_dai,
 | |
| 					       ARRAY_SIZE(rk1000_codec_dai));
 | |
| }
 | |
| 
 | |
| static void rk1000_codec_i2c_remove(struct i2c_client *i2c)
 | |
| {
 | |
| 	struct rk1000_codec_priv *rk1000 = i2c_get_clientdata(i2c);
 | |
| 
 | |
| 	regmap_write(rk1000->ctlmap, CODEC_CON, CODEC_OFF);
 | |
| }
 | |
| 
 | |
| static const struct i2c_device_id rk1000_codec_i2c_id[] = {
 | |
| 	{ "rk1000_codec", 0 },
 | |
| 	{ }
 | |
| };
 | |
| MODULE_DEVICE_TABLE(i2c, rk1000_codec_i2c_id);
 | |
| 
 | |
| static const struct of_device_id rk1000_codec_of_match[] = {
 | |
| 	{ .compatible = "rockchip,rk1000-codec", },
 | |
| 	{},
 | |
| };
 | |
| 
 | |
| static struct i2c_driver rk1000_codec_i2c_driver = {
 | |
| 	.driver = {
 | |
| 		.name = "rk1000_codec",
 | |
| 		.of_match_table = of_match_ptr(rk1000_codec_of_match),
 | |
| 	},
 | |
| 	.probe = rk1000_codec_i2c_probe,
 | |
| 	.remove   = rk1000_codec_i2c_remove,
 | |
| 	.id_table = rk1000_codec_i2c_id,
 | |
| };
 | |
| module_i2c_driver(rk1000_codec_i2c_driver);
 | |
| 
 | |
| MODULE_DESCRIPTION("Rockchip RK1000 CODEC driver");
 | |
| MODULE_AUTHOR("Sugar Zhang <sugar.zhang@rock-chips.com>");
 | |
| MODULE_LICENSE("GPL v2");
 |