Example #1
0
static int wm8350_set_fll(struct snd_soc_dai *codec_dai,
			  int pll_id, int source, unsigned int freq_in,
			  unsigned int freq_out)
{
	struct snd_soc_codec *codec = codec_dai->codec;
	struct wm8350 *wm8350 = codec->control_data;
	struct wm8350_data *priv = codec->private_data;
	struct _fll_div fll_div;
	int ret = 0;
	u16 fll_1, fll_4;

	if (freq_in == priv->fll_freq_in && freq_out == priv->fll_freq_out)
		return 0;

	/* power down FLL - we need to do this for reconfiguration */
	wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_4,
			  WM8350_FLL_ENA | WM8350_FLL_OSC_ENA);

	if (freq_out == 0 || freq_in == 0)
		return ret;

	ret = fll_factors(&fll_div, freq_in, freq_out);
	if (ret < 0)
		return ret;
	dev_dbg(wm8350->dev,
		"FLL in %u FLL out %u N 0x%x K 0x%x div %d ratio %d",
		freq_in, freq_out, fll_div.n, fll_div.k, fll_div.div,
		fll_div.ratio);

	/* set up N.K & dividers */
	fll_1 = wm8350_codec_read(codec, WM8350_FLL_CONTROL_1) &
	    ~(WM8350_FLL_OUTDIV_MASK | WM8350_FLL_RSP_RATE_MASK | 0xc000);
	wm8350_codec_write(codec, WM8350_FLL_CONTROL_1,
			   fll_1 | (fll_div.div << 8) | 0x50);
	wm8350_codec_write(codec, WM8350_FLL_CONTROL_2,
			   (fll_div.ratio << 11) | (fll_div.
						    n & WM8350_FLL_N_MASK));
	wm8350_codec_write(codec, WM8350_FLL_CONTROL_3, fll_div.k);
	fll_4 = wm8350_codec_read(codec, WM8350_FLL_CONTROL_4) &
	    ~(WM8350_FLL_FRAC | WM8350_FLL_SLOW_LOCK_REF);
	wm8350_codec_write(codec, WM8350_FLL_CONTROL_4,
			   fll_4 | (fll_div.k ? WM8350_FLL_FRAC : 0) |
			   (fll_div.ratio == 8 ? WM8350_FLL_SLOW_LOCK_REF : 0));

	/* power FLL on */
	wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_FLL_OSC_ENA);
	wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_FLL_ENA);

	priv->fll_freq_out = freq_out;
	priv->fll_freq_in = freq_in;

	return 0;
}
Example #2
0
static int wm8350_pcm_hw_params(struct snd_pcm_substream *substream,
	struct snd_pcm_hw_params *params)
{
	struct snd_soc_pcm_link *pcm_link = substream->private_data;
	struct snd_soc_codec *codec = pcm_link->codec;
	u16 iface = wm8350_codec_read(codec, WM8350_AI_FORMATING) &
		~WM8350_AIF_WL_MASK; 

	/* bit size */
	switch (params_format(params)) {
	case SNDRV_PCM_FORMAT_S16_LE:
		break;
	case SNDRV_PCM_FORMAT_S20_3LE:
		iface |= 0x1 << 10;
		break;
	case SNDRV_PCM_FORMAT_S24_LE:
		iface |= 0x2 << 10;
		break;
	case SNDRV_PCM_FORMAT_S32_LE:
		iface |= 0x3 << 10;
		break;
	}

	wm8350_codec_write(codec, WM8350_AI_FORMATING, iface);
	return 0;
}
Example #3
0
static int wm8350_set_dai_sysclk(struct snd_soc_dai *codec_dai,
		int clk_id, unsigned int freq, int dir)
{
	struct snd_soc_codec *codec = codec_dai->codec;
	struct wm8350* wm8350 = codec->control_data;
	u16 fll_4;
	
	switch(clk_id) {
	case WM8350_MCLK_SEL_MCLK:
		wm8350_clear_bits(wm8350, WM8350_CLOCK_CONTROL_1, 
			WM8350_MCLK_SEL);
		break;
	case WM8350_MCLK_SEL_PLL_MCLK:
	case WM8350_MCLK_SEL_PLL_DAC:
	case WM8350_MCLK_SEL_PLL_ADC:
	case WM8350_MCLK_SEL_PLL_32K:
		wm8350_set_bits(wm8350, WM8350_CLOCK_CONTROL_1, 
			WM8350_MCLK_SEL);
		fll_4 = wm8350_codec_read(codec, WM8350_FLL_CONTROL_4) & 
			~WM8350_FLL_CLK_SRC_MASK;
		wm8350_codec_write(codec, WM8350_FLL_CONTROL_4, 
			fll_4 | clk_id);
		break;
	}
		
	/* MCLK direction */
	if (dir == WM8350_MCLK_DIR_OUT)
		wm8350_set_bits(wm8350, WM8350_CLOCK_CONTROL_2, 
			WM8350_MCLK_DIR);
	else 
		wm8350_clear_bits(wm8350, WM8350_CLOCK_CONTROL_2, 
			WM8350_MCLK_DIR);
				
	return 0;
}
Example #4
0
static int wm8350_put_volsw_2r_vu(struct snd_kcontrol *kcontrol,
	struct snd_ctl_elem_value *ucontrol)
{
	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
	struct wm8350_out_ramp *or = codec->private_data;
	struct wm8350_output *out1 = &or->out1, *out2 = &or->out2;
	int ret, reg = kcontrol->private_value & 0xff;
	u16 val;
	
	/* we cache the OUT1/2 volumes when the codec is inactive to keep 
	 * pops to a low during ramping */
	if (reg == WM8350_LOUT1_VOLUME) {
		out1->left_vol = ucontrol->value.integer.value[0];
		out1->right_vol = ucontrol->value.integer.value[1];
		if (!codec->active)
			return 1;
	} else if (reg == WM8350_LOUT2_VOLUME) {
		out2->left_vol = ucontrol->value.integer.value[0];
		out2->right_vol = ucontrol->value.integer.value[1];
		if (!codec->active)
			return 1;
	}
	
	ret = snd_soc_put_volsw_2r(kcontrol, ucontrol);
	if (ret < 0)
		return ret;
		
	/* now hit the volume update bits (always bit 8) */
	val = wm8350_codec_read(codec, reg);
	wm8350_codec_write(codec, reg, val | WM8350_OUT1_VU);
	return 1;
}
Example #5
0
static int wm8350_set_fll(struct snd_soc_dai *codec_dai,
		int pll_id, unsigned int freq_in, unsigned int freq_out)
{
	struct snd_soc_codec *codec = codec_dai->codec;
	struct wm8350* wm8350 = codec->control_data;
	struct _fll_div fll_div;
	int ret = 0;
	u16 fll_1, fll_4;
	
	if (freq_out == 0 || freq_in == 0) {
		/* power down FLL */
		wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_4, 
			WM8350_FLL_ENA | WM8350_FLL_OSC_ENA);
		return ret;
	}
	
	ret = fll_factors(&fll_div, freq_in, freq_out);
	if (ret < 0)
		return ret;
		
	/* set up N.K & dividers */
	fll_1 = wm8350_codec_read(codec, WM8350_FLL_CONTROL_1) & 
		~(WM8350_FLL_OUTDIV_MASK | 0xc000);
	wm8350_codec_write(codec, WM8350_FLL_CONTROL_1, 
		fll_1 | (fll_div.div << 8));
	wm8350_codec_write(codec, WM8350_FLL_CONTROL_2, 
		 (fll_div.ratio << 11) | (fll_div.n & WM8350_FLL_N_MASK));
	wm8350_codec_write(codec, WM8350_FLL_CONTROL_3, fll_div.k);
	fll_4 = wm8350_codec_read(codec, WM8350_FLL_CONTROL_4) & 
		~(WM8350_FLL_FRAC | WM8350_FLL_SLOW_LOCK_REF);
	wm8350_codec_write(codec, WM8350_FLL_CONTROL_4, 
		fll_4 | (fll_div.k ? WM8350_FLL_FRAC : 0) | 
		(fll_div.ratio == 8 ? WM8350_FLL_SLOW_LOCK_REF : 0));
		
	/* power FLL on */
	wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_FLL_OSC_ENA);
	/* do we need to wait here ? */
	wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_FLL_ENA);
	return 0;	
}
Example #6
0
static int wm8350_set_clkdiv(struct snd_soc_dai *codec_dai, 
	int div_id, int div)
{
	struct snd_soc_codec *codec = codec_dai->codec;
	u16 val;
	
	switch (div_id) {
	case  WM8350_ADC_CLKDIV:
		val = wm8350_codec_read(codec, WM8350_ADC_DIVIDER) &
			~WM8350_ADC_CLKDIV_MASK;    
		wm8350_codec_write(codec, WM8350_ADC_DIVIDER, val | div);
	break;
	case WM8350_DAC_CLKDIV:
		val = wm8350_codec_read(codec, WM8350_DAC_CLOCK_CONTROL) &
			~WM8350_DAC_CLKDIV_MASK;    
		wm8350_codec_write(codec, WM8350_DAC_CLOCK_CONTROL, val | div);
	break;
	case WM8350_BCLK_CLKDIV:
		val = wm8350_codec_read(codec, WM8350_CLOCK_CONTROL_1) &
			~WM8350_BCLK_DIV_MASK;    
		wm8350_codec_write(codec, WM8350_CLOCK_CONTROL_1, val | div);
	break;
	case WM8350_OPCLK_CLKDIV:
		val = wm8350_codec_read(codec, WM8350_CLOCK_CONTROL_1) &
			~WM8350_OPCLK_DIV_MASK;    
		wm8350_codec_write(codec, WM8350_CLOCK_CONTROL_1, val | div);
	break;
	case WM8350_SYS_CLKDIV:
		val = wm8350_codec_read(codec, WM8350_CLOCK_CONTROL_1) &
			~WM8350_MCLK_DIV_MASK;    
		wm8350_codec_write(codec, WM8350_CLOCK_CONTROL_1, val | div);
	break;
	case WM8350_DACLR_CLKDIV:
		val = wm8350_codec_read(codec, WM8350_DAC_LR_RATE) &
			~WM8350_DACLRC_RATE_MASK;    
		wm8350_codec_write(codec, WM8350_DAC_LR_RATE, val | div);
	break;
	case WM8350_ADCLR_CLKDIV:
		val = wm8350_codec_read(codec, WM8350_ADC_LR_RATE) &
			~WM8350_ADCLRC_RATE_MASK;    
		wm8350_codec_write(codec, WM8350_ADC_LR_RATE, val | div);
	break;
	default:
		return -EINVAL;
	}
	
	return 0;
}
Example #7
0
static int wm8350_put_volsw_2r_vu(struct snd_kcontrol *kcontrol,
				  struct snd_ctl_elem_value *ucontrol)
{
	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
	struct wm8350_data *wm8350_priv = codec->private_data;
	struct wm8350_output *out = NULL;
	struct soc_mixer_control *mc =
		(struct soc_mixer_control *)kcontrol->private_value;
	int ret;
	unsigned int reg = mc->reg;
	u16 val;

	/* For OUT1 and OUT2 we shadow the values and only actually write
	 * them out when active in order to ensure the amplifier comes on
	 * as quietly as possible. */
	switch (reg) {
	case WM8350_LOUT1_VOLUME:
		out = &wm8350_priv->out1;
		break;
	case WM8350_LOUT2_VOLUME:
		out = &wm8350_priv->out2;
		break;
	default:
		break;
	}

	if (out) {
		out->left_vol = ucontrol->value.integer.value[0];
		out->right_vol = ucontrol->value.integer.value[1];
		if (!out->active)
			return 1;
	}

	ret = snd_soc_put_volsw_2r(kcontrol, ucontrol);
	if (ret < 0)
		return ret;

	/* now hit the volume update bits (always bit 8) */
	val = wm8350_codec_read(codec, reg);
	wm8350_codec_write(codec, reg, val | WM8350_OUT1_VU);
	return 1;
}
Example #8
0
static int wm8350_pcm_hw_params(struct snd_pcm_substream *substream,
				struct snd_pcm_hw_params *params,
				struct snd_soc_dai *codec_dai)
{
	struct snd_soc_codec *codec = codec_dai->codec;
	struct wm8350 *wm8350 = codec->control_data;
	u16 iface = wm8350_codec_read(codec, WM8350_AI_FORMATING) &
	    ~WM8350_AIF_WL_MASK;

	/* bit size */
	switch (params_format(params)) {
	case SNDRV_PCM_FORMAT_S16_LE:
		break;
	case SNDRV_PCM_FORMAT_S20_3LE:
		iface |= 0x1 << 10;
		break;
	case SNDRV_PCM_FORMAT_S24_LE:
		iface |= 0x2 << 10;
		break;
	case SNDRV_PCM_FORMAT_S32_LE:
		iface |= 0x3 << 10;
		break;
	}

	wm8350_codec_write(codec, WM8350_AI_FORMATING, iface);

	/* The sloping stopband filter is recommended for use with
	 * lower sample rates to improve performance.
	 */
	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
		if (params_rate(params) < 24000)
			wm8350_set_bits(wm8350, WM8350_DAC_MUTE_VOLUME,
					WM8350_DAC_SB_FILT);
		else
			wm8350_clear_bits(wm8350, WM8350_DAC_MUTE_VOLUME,
					  WM8350_DAC_SB_FILT);
	}

	return 0;
}
Example #9
0
static int wm8350_set_dai_fmt(struct snd_soc_dai *codec_dai,
		unsigned int fmt)
{
	struct snd_soc_codec *codec = codec_dai->codec;
	u16 iface = wm8350_codec_read(codec, WM8350_AI_FORMATING) &
		~(WM8350_AIF_BCLK_INV | WM8350_AIF_LRCLK_INV | 
		WM8350_AIF_FMT_MASK);
	u16 master = wm8350_codec_read(codec, WM8350_AI_DAC_CONTROL) &
		~WM8350_BCLK_MSTR;
	u16 dac_lrc = wm8350_codec_read(codec, WM8350_DAC_LR_RATE) &
		~WM8350_DACLRC_ENA;
	u16 adc_lrc = wm8350_codec_read(codec, WM8350_ADC_LR_RATE) &
		~WM8350_ADCLRC_ENA;  


	/* set master/slave audio interface */
	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
	case SND_SOC_DAIFMT_CBM_CFM:
		master |= WM8350_BCLK_MSTR;
		dac_lrc |= WM8350_DACLRC_ENA;
		adc_lrc |= WM8350_ADCLRC_ENA;
		break;
	case SND_SOC_DAIFMT_CBS_CFS:
		break;
	default:
		return -EINVAL;
	}

	/* interface format */
	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
	case SND_SOC_DAIFMT_I2S:
		iface |= 0x2 << 8;
		break;
	case SND_SOC_DAIFMT_RIGHT_J:
		break;
	case SND_SOC_DAIFMT_LEFT_J:
		iface |= 0x1 << 8;
		break;
	case SND_SOC_DAIFMT_DSP_A:
		iface |= 0x3 << 8;
		break;
	case SND_SOC_DAIFMT_DSP_B:
		iface |= 0x3 << 8; // lg not sure which mode
		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 |= WM8350_AIF_LRCLK_INV | WM8350_AIF_BCLK_INV;
		break;
	case SND_SOC_DAIFMT_IB_NF:
		iface |= WM8350_AIF_BCLK_INV;
		break;
	case SND_SOC_DAIFMT_NB_IF:
		iface |= WM8350_AIF_LRCLK_INV;
		break;
	default:
		return -EINVAL;
	}

	wm8350_codec_write(codec, WM8350_AI_FORMATING, iface);
	wm8350_codec_write(codec, WM8350_AI_DAC_CONTROL, master);
	wm8350_codec_write(codec, WM8350_DAC_LR_RATE, dac_lrc);
	wm8350_codec_write(codec, WM8350_ADC_LR_RATE, adc_lrc);
	return 0;
}