Beispiel #1
0
/*
 * Program the CS4270 with the given hardware parameters.
 *
 * The .dai_ops functions are used to provide board-specific data, like
 * input frequencies, to this driver.  This function takes that information,
 * combines it with the hardware parameters provided, and programs the
 * hardware accordingly.
 */
static int cs4270_hw_params(struct snd_pcm_substream *substream,
			    struct snd_pcm_hw_params *params)
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	struct snd_soc_device *socdev = rtd->socdev;
	struct snd_soc_codec *codec = socdev->codec;
	struct cs4270_private *cs4270 = codec->private_data;
	unsigned int ret = 0;
	unsigned int i;
	unsigned int rate;
	unsigned int ratio;
	int reg;

	/* Figure out which MCLK/LRCK ratio to use */

	rate = params_rate(params);	/* Sampling rate, in Hz */
	ratio = cs4270->mclk / rate;	/* MCLK/LRCK ratio */

	for (i = 0; i < NUM_MCLK_RATIOS; i++) {
		if (mclk_ratios[i] == ratio)
			break;
	}

	if (i == NUM_MCLK_RATIOS) {
		/* We did not find a matching ratio */
		printk(KERN_ERR "cs4270: could not find matching ratio\n");
		return -EINVAL;
	}

	/* Freeze and power-down the codec */

	ret = snd_soc_write(codec, CS4270_PWRCTL, CS4270_PWRCTL_FREEZE |
			    CS4270_PWRCTL_PDN_ADC | CS4270_PWRCTL_PDN_DAC |
			    CS4270_PWRCTL_PDN);
	if (ret < 0) {
		printk(KERN_ERR "cs4270: I2C write failed\n");
		return ret;
	}

	/* Program the mode control register */

	reg = snd_soc_read(codec, CS4270_MODE);
	reg &= ~(CS4270_MODE_SPEED_MASK | CS4270_MODE_DIV_MASK);
	reg |= cs4270_mode_ratios[i].speed_mode | cs4270_mode_ratios[i].mclk;

	ret = snd_soc_write(codec, CS4270_MODE, reg);
	if (ret < 0) {
		printk(KERN_ERR "cs4270: I2C write failed\n");
		return ret;
	}

	/* Program the format register */

	reg = snd_soc_read(codec, CS4270_FORMAT);
	reg &= ~(CS4270_FORMAT_DAC_MASK | CS4270_FORMAT_ADC_MASK);

	switch (cs4270->mode) {
	case SND_SOC_DAIFMT_I2S:
		reg |= CS4270_FORMAT_DAC_I2S | CS4270_FORMAT_ADC_I2S;
		break;
	case SND_SOC_DAIFMT_LEFT_J:
		reg |= CS4270_FORMAT_DAC_LJ | CS4270_FORMAT_ADC_LJ;
		break;
	default:
		printk(KERN_ERR "cs4270: unknown format\n");
		return -EINVAL;
	}

	ret = snd_soc_write(codec, CS4270_FORMAT, reg);
	if (ret < 0) {
		printk(KERN_ERR "cs4270: I2C write failed\n");
		return ret;
	}

	/* Disable auto-mute.  This feature appears to be buggy, because in
	   some situations, auto-mute will not deactivate when it should. */

	reg = snd_soc_read(codec, CS4270_MUTE);
	reg &= ~CS4270_MUTE_AUTO;
	ret = snd_soc_write(codec, CS4270_MUTE, reg);
	if (ret < 0) {
		printk(KERN_ERR "cs4270: I2C write failed\n");
		return ret;
	}

	/* Thaw and power-up the codec */

	ret = snd_soc_write(codec, CS4270_PWRCTL, 0);
	if (ret < 0) {
		printk(KERN_ERR "cs4270: I2C write failed\n");
		return ret;
	}

	return ret;
}
Beispiel #2
0
static int ad1980_soc_probe(struct snd_soc_codec *codec)
{
    struct snd_ac97 *ac97;
    struct regmap *regmap;
    int ret;
    u16 vendor_id2;
    u16 ext_status;

    ac97 = snd_soc_new_ac97_codec(codec);
    if (IS_ERR(ac97)) {
        ret = PTR_ERR(ac97);
        dev_err(codec->dev, "Failed to register AC97 codec: %d\n", ret);
        return ret;
    }

    regmap = regmap_init_ac97(ac97, &ad1980_regmap_config);
    if (IS_ERR(regmap)) {
        ret = PTR_ERR(regmap);
        goto err_free_ac97;
    }

    snd_soc_codec_init_regmap(codec, regmap);
    snd_soc_codec_set_drvdata(codec, ac97);

    ret = ad1980_reset(codec, 0);
    if (ret < 0)
        goto reset_err;

    /* Read out vendor ID to make sure it is ad1980 */
    if (snd_soc_read(codec, AC97_VENDOR_ID1) != 0x4144) {
        ret = -ENODEV;
        goto reset_err;
    }

    vendor_id2 = snd_soc_read(codec, AC97_VENDOR_ID2);

    if (vendor_id2 != 0x5370) {
        if (vendor_id2 != 0x5374) {
            ret = -ENODEV;
            goto reset_err;
        } else {
            dev_warn(codec->dev,
                     "Found AD1981 - only 2/2 IN/OUT Channels supported\n");
        }
    }

    /* unmute captures and playbacks volume */
    snd_soc_write(codec, AC97_MASTER, 0x0000);
    snd_soc_write(codec, AC97_PCM, 0x0000);
    snd_soc_write(codec, AC97_REC_GAIN, 0x0000);
    snd_soc_write(codec, AC97_CENTER_LFE_MASTER, 0x0000);
    snd_soc_write(codec, AC97_SURROUND_MASTER, 0x0000);

    /*power on LFE/CENTER/Surround DACs*/
    ext_status = snd_soc_read(codec, AC97_EXTENDED_STATUS);
    snd_soc_write(codec, AC97_EXTENDED_STATUS, ext_status&~0x3800);

    return 0;

reset_err:
    snd_soc_codec_exit_regmap(codec);
err_free_ac97:
    snd_soc_free_ac97_codec(ac97);
    return ret;
}
static int wm8961_hw_params(struct snd_pcm_substream *substream,
			    struct snd_pcm_hw_params *params,
			    struct snd_soc_dai *dai)
{
	struct snd_soc_codec *codec = dai->codec;
	struct wm8961_priv *wm8961 = snd_soc_codec_get_drvdata(codec);
	int i, best, target, fs;
	u16 reg;

	fs = params_rate(params);

	if (!wm8961->sysclk) {
		dev_err(codec->dev, "MCLK has not been specified\n");
		return -EINVAL;
	}

	
	best = 0;
	for (i = 0; i < ARRAY_SIZE(wm8961_srate); i++) {
		if (abs(wm8961_srate[i].rate - fs) <
		    abs(wm8961_srate[best].rate - fs))
			best = i;
	}
	reg = snd_soc_read(codec, WM8961_ADDITIONAL_CONTROL_3);
	reg &= ~WM8961_SAMPLE_RATE_MASK;
	reg |= wm8961_srate[best].val;
	snd_soc_write(codec, WM8961_ADDITIONAL_CONTROL_3, reg);
	dev_dbg(codec->dev, "Selected SRATE %dHz for %dHz\n",
		wm8961_srate[best].rate, fs);

	
	target = wm8961->sysclk / fs;

	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && target < 64) {
		dev_err(codec->dev,
			"SYSCLK must be at least 64*fs for DAC\n");
		return -EINVAL;
	}
	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE && target < 256) {
		dev_err(codec->dev,
			"SYSCLK must be at least 256*fs for ADC\n");
		return -EINVAL;
	}

	for (i = 0; i < ARRAY_SIZE(wm8961_clk_sys_ratio); i++) {
		if (wm8961_clk_sys_ratio[i].ratio >= target)
			break;
	}
	if (i == ARRAY_SIZE(wm8961_clk_sys_ratio)) {
		dev_err(codec->dev, "Unable to generate CLK_SYS_RATE\n");
		return -EINVAL;
	}
	dev_dbg(codec->dev, "Selected CLK_SYS_RATE of %d for %d/%d=%d\n",
		wm8961_clk_sys_ratio[i].ratio, wm8961->sysclk, fs,
		wm8961->sysclk / fs);

	reg = snd_soc_read(codec, WM8961_CLOCKING_4);
	reg &= ~WM8961_CLK_SYS_RATE_MASK;
	reg |= wm8961_clk_sys_ratio[i].val << WM8961_CLK_SYS_RATE_SHIFT;
	snd_soc_write(codec, WM8961_CLOCKING_4, reg);

	reg = snd_soc_read(codec, WM8961_AUDIO_INTERFACE_0);
	reg &= ~WM8961_WL_MASK;
	switch (params_format(params)) {
	case SNDRV_PCM_FORMAT_S16_LE:
		break;
	case SNDRV_PCM_FORMAT_S20_3LE:
		reg |= 1 << WM8961_WL_SHIFT;
		break;
	case SNDRV_PCM_FORMAT_S24_LE:
		reg |= 2 << WM8961_WL_SHIFT;
		break;
	case SNDRV_PCM_FORMAT_S32_LE:
		reg |= 3 << WM8961_WL_SHIFT;
		break;
	default:
		return -EINVAL;
	}
	snd_soc_write(codec, WM8961_AUDIO_INTERFACE_0, reg);

	
	reg = snd_soc_read(codec, WM8961_ADC_DAC_CONTROL_2);
	if (fs <= 24000)
		reg |= WM8961_DACSLOPE;
	else
		reg &= ~WM8961_DACSLOPE;
	snd_soc_write(codec, WM8961_ADC_DAC_CONTROL_2, reg);

	return 0;
}
static int wm9081_set_dai_fmt(struct snd_soc_dai *dai,
			      unsigned int fmt)
{
	struct snd_soc_codec *codec = dai->codec;
	struct wm9081_priv *wm9081 = codec->private_data;
	unsigned int aif2 = snd_soc_read(codec, WM9081_AUDIO_INTERFACE_2);

	aif2 &= ~(WM9081_AIF_BCLK_INV | WM9081_AIF_LRCLK_INV |
		  WM9081_BCLK_DIR | WM9081_LRCLK_DIR | WM9081_AIF_FMT_MASK);

	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
	case SND_SOC_DAIFMT_CBS_CFS:
		wm9081->master = 0;
		break;
	case SND_SOC_DAIFMT_CBS_CFM:
		aif2 |= WM9081_LRCLK_DIR;
		wm9081->master = 1;
		break;
	case SND_SOC_DAIFMT_CBM_CFS:
		aif2 |= WM9081_BCLK_DIR;
		wm9081->master = 1;
		break;
	case SND_SOC_DAIFMT_CBM_CFM:
		aif2 |= WM9081_LRCLK_DIR | WM9081_BCLK_DIR;
		wm9081->master = 1;
		break;
	default:
		return -EINVAL;
	}

	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
	case SND_SOC_DAIFMT_DSP_B:
		aif2 |= WM9081_AIF_LRCLK_INV;
	case SND_SOC_DAIFMT_DSP_A:
		aif2 |= 0x3;
		break;
	case SND_SOC_DAIFMT_I2S:
		aif2 |= 0x2;
		break;
	case SND_SOC_DAIFMT_RIGHT_J:
		break;
	case SND_SOC_DAIFMT_LEFT_J:
		aif2 |= 0x1;
		break;
	default:
		return -EINVAL;
	}

	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
	case SND_SOC_DAIFMT_DSP_A:
	case SND_SOC_DAIFMT_DSP_B:
		/* frame inversion not valid for DSP modes */
		switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
		case SND_SOC_DAIFMT_NB_NF:
			break;
		case SND_SOC_DAIFMT_IB_NF:
			aif2 |= WM9081_AIF_BCLK_INV;
			break;
		default:
			return -EINVAL;
		}
		break;

	case SND_SOC_DAIFMT_I2S:
	case SND_SOC_DAIFMT_RIGHT_J:
	case SND_SOC_DAIFMT_LEFT_J:
		switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
		case SND_SOC_DAIFMT_NB_NF:
			break;
		case SND_SOC_DAIFMT_IB_IF:
			aif2 |= WM9081_AIF_BCLK_INV | WM9081_AIF_LRCLK_INV;
			break;
		case SND_SOC_DAIFMT_IB_NF:
			aif2 |= WM9081_AIF_BCLK_INV;
			break;
		case SND_SOC_DAIFMT_NB_IF:
			aif2 |= WM9081_AIF_LRCLK_INV;
			break;
		default:
			return -EINVAL;
		}
		break;
	default:
		return -EINVAL;
	}

	snd_soc_write(codec, WM9081_AUDIO_INTERFACE_2, aif2);

	return 0;
}
Beispiel #5
0
static int sdp4430_twl6040_init(struct snd_soc_pcm_runtime *rtd)
{
	struct snd_soc_codec *codec = rtd->codec;
	struct twl6040 *twl6040 = codec->control_data;
	struct snd_soc_dapm_context *dapm = &codec->dapm;
	int hsotrim, left_offset, right_offset, mode, ret;


	/* Add SDP4430 specific controls */
	ret = snd_soc_add_controls(codec, sdp4430_controls,
				ARRAY_SIZE(sdp4430_controls));
	if (ret)
		return ret;

	/* Add SDP4430 specific widgets */
	ret = snd_soc_dapm_new_controls(dapm, sdp4430_twl6040_dapm_widgets,
				ARRAY_SIZE(sdp4430_twl6040_dapm_widgets));
	if (ret)
		return ret;

	/* Set up SDP4430 specific audio path audio_map */
	snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));

	/* SDP4430 connected pins */
	snd_soc_dapm_enable_pin(dapm, "Ext Mic");
	snd_soc_dapm_enable_pin(dapm, "Ext Spk");
	snd_soc_dapm_enable_pin(dapm, "AFML");
	snd_soc_dapm_enable_pin(dapm, "AFMR");
	snd_soc_dapm_enable_pin(dapm, "Headset Mic");
	snd_soc_dapm_enable_pin(dapm, "Headset Stereophone");

	/* allow audio paths from the audio modem to run during suspend */
	snd_soc_dapm_ignore_suspend(dapm, "Ext Mic");
	snd_soc_dapm_ignore_suspend(dapm, "Ext Spk");
	snd_soc_dapm_ignore_suspend(dapm, "AFML");
	snd_soc_dapm_ignore_suspend(dapm, "AFMR");
	snd_soc_dapm_ignore_suspend(dapm, "Headset Mic");
	snd_soc_dapm_ignore_suspend(dapm, "Headset Stereophone");
	snd_soc_dapm_ignore_suspend(dapm, "Digital Mic 0");
	snd_soc_dapm_ignore_suspend(dapm, "Digital Mic 1");
	snd_soc_dapm_ignore_suspend(dapm, "Digital Mic 2");

	ret = snd_soc_dapm_sync(dapm);
	if (ret)
		return ret;

	/* Headset jack detection */
	ret = snd_soc_jack_new(codec, "Headset Jack",
				SND_JACK_HEADSET, &hs_jack);
	if (ret)
		return ret;

	ret = snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins),
				hs_jack_pins);

	if (machine_is_omap_4430sdp() || machine_is_omap_tabletblaze()
		|| machine_is_omap4_panda())
		twl6040_hs_jack_detect(codec, &hs_jack, SND_JACK_HEADSET);
	else
		snd_soc_jack_report(&hs_jack, SND_JACK_HEADSET, SND_JACK_HEADSET);

	/* DC offset cancellation computation */
	hsotrim = snd_soc_read(codec, TWL6040_REG_HSOTRIM);
	right_offset = (hsotrim & TWL6040_HSRO) >> TWL6040_HSRO_OFFSET;
	left_offset = hsotrim & TWL6040_HSLO;

	if (twl6040_get_icrev(twl6040) < TWL6040_REV_1_3)
		/* For ES under ES_1.3 HS step is 2 mV */
		mode = 2;
	else
		/* For ES_1.3 HS step is 1 mV */
		mode = 1;

	abe_dsp_set_hs_offset(left_offset, right_offset, mode);

	/* don't wait before switching of HS power */
	rtd->pmdown_time = 0;

	return ret;
}
Beispiel #6
0
/**
 * cs4270_hw_params - program the CS4270 with the given hardware parameters.
 * @substream: the audio stream
 * @params: the hardware parameters to set
 * @dai: the SOC DAI (ignored)
 *
 * This function programs the hardware with the values provided.
 * Specifically, the sample rate and the data format.
 *
 * The .ops functions are used to provide board-specific data, like input
 * frequencies, to this driver.  This function takes that information,
 * combines it with the hardware parameters provided, and programs the
 * hardware accordingly.
 */
static int cs4270_hw_params(struct snd_pcm_substream *substream,
			    struct snd_pcm_hw_params *params,
			    struct snd_soc_dai *dai)
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	struct snd_soc_device *socdev = rtd->socdev;
	struct snd_soc_codec *codec = socdev->card->codec;
	struct cs4270_private *cs4270 = codec->private_data;
	int ret;
	unsigned int i;
	unsigned int rate;
	unsigned int ratio;
	int reg;

	/* Figure out which MCLK/LRCK ratio to use */

	rate = params_rate(params);	/* Sampling rate, in Hz */
	ratio = cs4270->mclk / rate;	/* MCLK/LRCK ratio */

	for (i = 0; i < NUM_MCLK_RATIOS; i++) {
		if (cs4270_mode_ratios[i].ratio == ratio)
			break;
	}

	if (i == NUM_MCLK_RATIOS) {
		/* We did not find a matching ratio */
		dev_err(codec->dev, "could not find matching ratio\n");
		return -EINVAL;
	}

	/* Set the sample rate */

	reg = snd_soc_read(codec, CS4270_MODE);
	reg &= ~(CS4270_MODE_SPEED_MASK | CS4270_MODE_DIV_MASK);
	reg |= cs4270_mode_ratios[i].mclk;

	if (cs4270->slave_mode)
		reg |= CS4270_MODE_SLAVE;
	else
		reg |= cs4270_mode_ratios[i].speed_mode;

	ret = snd_soc_write(codec, CS4270_MODE, reg);
	if (ret < 0) {
		dev_err(codec->dev, "i2c write failed\n");
		return ret;
	}

	/* Set the DAI format */

	reg = snd_soc_read(codec, CS4270_FORMAT);
	reg &= ~(CS4270_FORMAT_DAC_MASK | CS4270_FORMAT_ADC_MASK);

	switch (cs4270->mode) {
	case SND_SOC_DAIFMT_I2S:
		reg |= CS4270_FORMAT_DAC_I2S | CS4270_FORMAT_ADC_I2S;
		break;
	case SND_SOC_DAIFMT_LEFT_J:
		reg |= CS4270_FORMAT_DAC_LJ | CS4270_FORMAT_ADC_LJ;
		break;
	default:
		dev_err(codec->dev, "unknown dai format\n");
		return -EINVAL;
	}

	ret = snd_soc_write(codec, CS4270_FORMAT, reg);
	if (ret < 0) {
		dev_err(codec->dev, "i2c write failed\n");
		return ret;
	}

	return ret;
}
static int configure_clock(struct snd_soc_codec *codec)
{
	struct wm9081_priv *wm9081 = codec->private_data;
	int new_sysclk, i, target;
	unsigned int reg;
	int ret = 0;
	int mclkdiv = 0;
	int fll = 0;

	switch (wm9081->sysclk_source) {
	case WM9081_SYSCLK_MCLK:
		if (wm9081->mclk_rate > 12225000) {
			mclkdiv = 1;
			wm9081->sysclk_rate = wm9081->mclk_rate / 2;
		} else {
			wm9081->sysclk_rate = wm9081->mclk_rate;
		}
		wm9081_set_fll(codec, WM9081_SYSCLK_FLL_MCLK, 0, 0);
		break;

	case WM9081_SYSCLK_FLL_MCLK:
		/* If we have a sample rate calculate a CLK_SYS that
		 * gives us a suitable DAC configuration, plus BCLK.
		 * Ideally we would check to see if we can clock
		 * directly from MCLK and only use the FLL if this is
		 * not the case, though care must be taken with free
		 * running mode.
		 */
		if (wm9081->master && wm9081->bclk) {
			/* Make sure we can generate CLK_SYS and BCLK
			 * and that we've got 3MHz for optimal
			 * performance. */
			for (i = 0; i < ARRAY_SIZE(clk_sys_rates); i++) {
				target = wm9081->fs * clk_sys_rates[i].ratio;
				new_sysclk = target;
				if (target >= wm9081->bclk &&
				    target > 3000000)
					break;
			}

			if (i == ARRAY_SIZE(clk_sys_rates))
				return -EINVAL;

		} else if (wm9081->fs) {
			for (i = 0; i < ARRAY_SIZE(clk_sys_rates); i++) {
				new_sysclk = clk_sys_rates[i].ratio
					* wm9081->fs;
				if (new_sysclk > 3000000)
					break;
			}

			if (i == ARRAY_SIZE(clk_sys_rates))
				return -EINVAL;

		} else {
			new_sysclk = 12288000;
		}

		ret = wm9081_set_fll(codec, WM9081_SYSCLK_FLL_MCLK,
				     wm9081->mclk_rate, new_sysclk);
		if (ret == 0) {
			wm9081->sysclk_rate = new_sysclk;

			/* Switch SYSCLK over to FLL */
			fll = 1;
		} else {
			wm9081->sysclk_rate = wm9081->mclk_rate;
		}
		break;

	default:
		return -EINVAL;
	}

	reg = snd_soc_read(codec, WM9081_CLOCK_CONTROL_1);
	if (mclkdiv)
		reg |= WM9081_MCLKDIV2;
	else
		reg &= ~WM9081_MCLKDIV2;
	snd_soc_write(codec, WM9081_CLOCK_CONTROL_1, reg);

	reg = snd_soc_read(codec, WM9081_CLOCK_CONTROL_3);
	if (fll)
		reg |= WM9081_CLK_SRC_SEL;
	else
		reg &= ~WM9081_CLK_SRC_SEL;
	snd_soc_write(codec, WM9081_CLOCK_CONTROL_3, reg);

	dev_dbg(codec->dev, "CLK_SYS is %dHz\n", wm9081->sysclk_rate);

	return ret;
}
Beispiel #8
0
static int out_pga_event(struct snd_soc_dapm_widget *w,
			 struct snd_kcontrol *kcontrol, int event)
{
	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
	struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
	int reg, val;
	int dcs_mask;
	int dcs_l, dcs_r;
	int dcs_l_reg, dcs_r_reg;
	int timeout;
	int pwr_reg;

	/* This code is shared between HP and LINEOUT; we do all our
	 * power management in stereo pairs to avoid latency issues so
	 * we reuse shift to identify which rather than strcmp() the
	 * name. */
	reg = w->shift;

	switch (reg) {
	case WM8904_ANALOGUE_HP_0:
		pwr_reg = WM8904_POWER_MANAGEMENT_2;
		dcs_mask = WM8904_DCS_ENA_CHAN_0 | WM8904_DCS_ENA_CHAN_1;
		dcs_r_reg = WM8904_DC_SERVO_8;
		dcs_l_reg = WM8904_DC_SERVO_9;
		dcs_l = 0;
		dcs_r = 1;
		break;
	case WM8904_ANALOGUE_LINEOUT_0:
		pwr_reg = WM8904_POWER_MANAGEMENT_3;
		dcs_mask = WM8904_DCS_ENA_CHAN_2 | WM8904_DCS_ENA_CHAN_3;
		dcs_r_reg = WM8904_DC_SERVO_6;
		dcs_l_reg = WM8904_DC_SERVO_7;
		dcs_l = 2;
		dcs_r = 3;
		break;
	default:
		WARN(1, "Invalid reg %d\n", reg);
		return -EINVAL;
	}

	switch (event) {
	case SND_SOC_DAPM_PRE_PMU:
		/* Power on the PGAs */
		snd_soc_update_bits(codec, pwr_reg,
				    WM8904_HPL_PGA_ENA | WM8904_HPR_PGA_ENA,
				    WM8904_HPL_PGA_ENA | WM8904_HPR_PGA_ENA);

		/* Power on the amplifier */
		snd_soc_update_bits(codec, reg,
				    WM8904_HPL_ENA | WM8904_HPR_ENA,
				    WM8904_HPL_ENA | WM8904_HPR_ENA);


		/* Enable the first stage */
		snd_soc_update_bits(codec, reg,
				    WM8904_HPL_ENA_DLY | WM8904_HPR_ENA_DLY,
				    WM8904_HPL_ENA_DLY | WM8904_HPR_ENA_DLY);

		/* Power up the DC servo */
		snd_soc_update_bits(codec, WM8904_DC_SERVO_0,
				    dcs_mask, dcs_mask);

		/* Either calibrate the DC servo or restore cached state
		 * if we have that.
		 */
		if (wm8904->dcs_state[dcs_l] || wm8904->dcs_state[dcs_r]) {
			dev_dbg(codec->dev, "Restoring DC servo state\n");

			snd_soc_write(codec, dcs_l_reg,
				      wm8904->dcs_state[dcs_l]);
			snd_soc_write(codec, dcs_r_reg,
				      wm8904->dcs_state[dcs_r]);

			snd_soc_write(codec, WM8904_DC_SERVO_1, dcs_mask);

			timeout = 20;
		} else {
			dev_dbg(codec->dev, "Calibrating DC servo\n");

			snd_soc_write(codec, WM8904_DC_SERVO_1,
				dcs_mask << WM8904_DCS_TRIG_STARTUP_0_SHIFT);

			timeout = 500;
		}

		/* Wait for DC servo to complete */
		dcs_mask <<= WM8904_DCS_CAL_COMPLETE_SHIFT;
		do {
			val = snd_soc_read(codec, WM8904_DC_SERVO_READBACK_0);
			if ((val & dcs_mask) == dcs_mask)
				break;

			msleep(1);
		} while (--timeout);

		if ((val & dcs_mask) != dcs_mask)
			dev_warn(codec->dev, "DC servo timed out\n");
		else
			dev_dbg(codec->dev, "DC servo ready\n");

		/* Enable the output stage */
		snd_soc_update_bits(codec, reg,
				    WM8904_HPL_ENA_OUTP | WM8904_HPR_ENA_OUTP,
				    WM8904_HPL_ENA_OUTP | WM8904_HPR_ENA_OUTP);
		break;

	case SND_SOC_DAPM_POST_PMU:
		/* Unshort the output itself */
		snd_soc_update_bits(codec, reg,
				    WM8904_HPL_RMV_SHORT |
				    WM8904_HPR_RMV_SHORT,
				    WM8904_HPL_RMV_SHORT |
				    WM8904_HPR_RMV_SHORT);

		break;

	case SND_SOC_DAPM_PRE_PMD:
		/* Short the output */
		snd_soc_update_bits(codec, reg,
				    WM8904_HPL_RMV_SHORT |
				    WM8904_HPR_RMV_SHORT, 0);
		break;

	case SND_SOC_DAPM_POST_PMD:
		/* Cache the DC servo configuration; this will be
		 * invalidated if we change the configuration. */
		wm8904->dcs_state[dcs_l] = snd_soc_read(codec, dcs_l_reg);
		wm8904->dcs_state[dcs_r] = snd_soc_read(codec, dcs_r_reg);

		snd_soc_update_bits(codec, WM8904_DC_SERVO_0,
				    dcs_mask, 0);

		/* Disable the amplifier input and output stages */
		snd_soc_update_bits(codec, reg,
				    WM8904_HPL_ENA | WM8904_HPR_ENA |
				    WM8904_HPL_ENA_DLY | WM8904_HPR_ENA_DLY |
				    WM8904_HPL_ENA_OUTP | WM8904_HPR_ENA_OUTP,
				    0);

		/* PGAs too */
		snd_soc_update_bits(codec, pwr_reg,
				    WM8904_HPL_PGA_ENA | WM8904_HPR_PGA_ENA,
				    0);
		break;
	}

	return 0;
}
Beispiel #9
0
static int wm8960_set_bias_level(struct snd_soc_codec *codec,
				 enum snd_soc_bias_level level)
{
	struct wm8960_data *pdata = codec->dev->platform_data;
	u16 reg;

	switch (level) {
	case SND_SOC_BIAS_ON:
		break;

	case SND_SOC_BIAS_PREPARE:
		/* Set VMID to 2x50k */
		reg = snd_soc_read(codec, WM8960_POWER1);
		reg &= ~0x180;
		reg |= 0x80;
		snd_soc_write(codec, WM8960_POWER1, reg);
		break;

	case SND_SOC_BIAS_STANDBY:
		if (codec->bias_level == SND_SOC_BIAS_OFF) {
			/* Enable anti-pop features */
			snd_soc_write(codec, WM8960_APOP1,
				     WM8960_POBCTRL | WM8960_SOFT_ST |
				     WM8960_BUFDCOPEN | WM8960_BUFIOEN);

			/* Discharge HP output */
			reg = WM8960_DISOP;
			if (pdata)
				reg |= pdata->dres << 4;
			snd_soc_write(codec, WM8960_APOP2, reg);

			msleep(400);

			snd_soc_write(codec, WM8960_APOP2, 0);

			/* Enable & ramp VMID at 2x50k */
			reg = snd_soc_read(codec, WM8960_POWER1);
			reg |= 0x80;
			snd_soc_write(codec, WM8960_POWER1, reg);
			msleep(100);

			/* Enable VREF */
			snd_soc_write(codec, WM8960_POWER1, reg | WM8960_VREF);

			/* Disable anti-pop features */
			snd_soc_write(codec, WM8960_APOP1, WM8960_BUFIOEN);
		}

		/* Set VMID to 2x250k */
		reg = snd_soc_read(codec, WM8960_POWER1);
		reg &= ~0x180;
		reg |= 0x100;
		snd_soc_write(codec, WM8960_POWER1, reg);
		break;

	case SND_SOC_BIAS_OFF:
		/* Enable anti-pop features */
		snd_soc_write(codec, WM8960_APOP1,
			     WM8960_POBCTRL | WM8960_SOFT_ST |
			     WM8960_BUFDCOPEN | WM8960_BUFIOEN);

		/* Disable VMID and VREF, let them discharge */
		snd_soc_write(codec, WM8960_POWER1, 0);
		msleep(600);

		snd_soc_write(codec, WM8960_APOP1, 0);
		break;
	}

	codec->bias_level = level;

	return 0;
}
static int alc5632_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
		int source, unsigned int freq_in, unsigned int freq_out)
{
	int i;
	struct snd_soc_codec *codec = codec_dai->codec;
	int gbl_clk = 0, pll_div = 0;
	u16 reg;

	if (pll_id < ALC5632_PLL_FR_MCLK || pll_id > ALC5632_PLL_FR_VBCLK)
		return -EINVAL;

	/*                   */
	snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD2,
				ALC5632_PWR_ADD2_PLL1,
				0);
	snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD2,
				ALC5632_PWR_ADD2_PLL2,
				0);

	/*                               */
	reg = snd_soc_read(codec, ALC5632_DAI_CONTROL);
	if (reg & ALC5632_DAI_SDP_SLAVE_MODE)
		return 0;

	if (!freq_in || !freq_out)
		return 0;

	switch (pll_id) {
	case ALC5632_PLL_FR_MCLK:
		for (i = 0; i < ARRAY_SIZE(codec_master_pll_div); i++) {
			if (codec_master_pll_div[i].pll_in == freq_in
			   && codec_master_pll_div[i].pll_out == freq_out) {
				/*                      */
				pll_div  = codec_master_pll_div[i].regvalue;
				break;
			}
		}
		break;
	case ALC5632_PLL_FR_BCLK:
		for (i = 0; i < ARRAY_SIZE(codec_slave_pll_div); i++) {
			if (codec_slave_pll_div[i].pll_in == freq_in
			   && codec_slave_pll_div[i].pll_out == freq_out) {
				/*                        */
				gbl_clk = ALC5632_PLL_FR_BCLK;
				pll_div = codec_slave_pll_div[i].regvalue;
				break;
			}
		}
		break;
	case ALC5632_PLL_FR_VBCLK:
		for (i = 0; i < ARRAY_SIZE(codec_slave_pll_div); i++) {
			if (codec_slave_pll_div[i].pll_in == freq_in
			   && codec_slave_pll_div[i].pll_out == freq_out) {
				/*                             */
				gbl_clk = ALC5632_PLL_FR_VBCLK;
				pll_div = codec_slave_pll_div[i].regvalue;
				break;
			}
		}
		break;
	default:
		return -EINVAL;
	}

	if (!pll_div)
		return -EINVAL;

	/*                        */
	snd_soc_write(codec, ALC5632_GPCR2, gbl_clk);
	/*                        */
	snd_soc_write(codec, ALC5632_PLL1_CTRL, pll_div);
	/*             */
	snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD2,
				ALC5632_PWR_ADD2_PLL1,
				ALC5632_PWR_ADD2_PLL1);
	/*             */
	snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD2,
				ALC5632_PWR_ADD2_PLL2,
				ALC5632_PWR_ADD2_PLL2);
	/*                         */
	snd_soc_update_bits(codec, ALC5632_GPCR1,
			ALC5632_GPCR1_CLK_SYS_SRC_SEL_PLL1,
			ALC5632_GPCR1_CLK_SYS_SRC_SEL_PLL1);

	return 0;
}
static int rt_codec_hwdep_ioctl_common(struct snd_hwdep *hw,
		struct file *file, unsigned int cmd, unsigned long arg)
{
	struct snd_soc_codec *codec = hw->private_data;
	struct rt_codec_cmd __user *_rt_codec = (struct rt_codec_cmd *)arg;
	struct rt_codec_cmd rt_codec;
	int *buf, *p;

	if (copy_from_user(&rt_codec, _rt_codec, sizeof(rt_codec))) {
		dev_err(codec->dev, "copy_from_user faild\n");
		return -EFAULT;
	}
	dev_dbg(codec->dev, "%s(): rt_codec.number=%d, cmd=%d\n",
			__func__, rt_codec.number, cmd);
	buf = kmalloc(sizeof(*buf) * rt_codec.number, GFP_KERNEL);
	if (buf == NULL)
		return -ENOMEM;
	if (copy_from_user(buf, rt_codec.buf, sizeof(*buf) * rt_codec.number))
		goto err;

	switch (cmd) {
	case RT_READ_CODEC_REG_IOCTL:
		for (p = buf; p < buf + rt_codec.number / 2; p++)
			*(p + rt_codec.number / 2) = snd_soc_read(codec, *p);
		if (copy_to_user(rt_codec.buf, buf, sizeof(*buf) * rt_codec.number))
			goto err;
		break;
	case RT_WRITE_CODEC_REG_IOCTL:
		for (p = buf; p < buf + rt_codec.number / 2; p++)
			snd_soc_write(codec, *p, *(p + rt_codec.number / 2));
		break;
	case RT_READ_CODEC_INDEX_IOCTL:
		if (NULL == rt_codec_ioctl_ops.index_read)
			goto err;

		for (p = buf; p < buf + rt_codec.number / 2; p++)
			*(p+rt_codec.number / 2) = rt_codec_ioctl_ops.index_read(
							codec, *p);
		if (copy_to_user(rt_codec.buf, buf,
			sizeof(*buf) * rt_codec.number))
			goto err;
		break;

	case RT_WRITE_CODEC_INDEX_IOCTL:
		if (NULL == rt_codec_ioctl_ops.index_write)
			goto err;

		for (p = buf; p < buf + rt_codec.number / 2; p++) {
			dev_dbg(codec->dev, "%x , %x\n",
				*p, *(p+rt_codec.number / 2));
			rt_codec_ioctl_ops.index_write(codec, *p,
				*(p+rt_codec.number / 2));
		}
		break;

	default:
		if (NULL == rt_codec_ioctl_ops.ioctl_common)
			goto err;

		rt_codec_ioctl_ops.ioctl_common(hw, file, cmd, arg);
		break;
	}

	kfree(buf);
	return 0;

err:
	kfree(buf);
	return -EFAULT;
}
/**
 * sta32x_hw_params - program the STA32X with the given hardware parameters.
 * @substream: the audio stream
 * @params: the hardware parameters to set
 * @dai: the SOC DAI (ignored)
 *
 * This function programs the hardware with the values provided.
 * Specifically, the sample rate and the data format.
 */
static int sta32x_hw_params(struct snd_pcm_substream *substream,
			    struct snd_pcm_hw_params *params,
			    struct snd_soc_dai *dai)
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	struct snd_soc_codec *codec = rtd->codec;
	struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
	unsigned int rate;
	int i, mcs = -1, ir = -1;
	u8 confa, confb;

	rate = params_rate(params);
	pr_debug("rate: %u\n", rate);
	for (i = 0; i < ARRAY_SIZE(interpolation_ratios); i++)
		if (interpolation_ratios[i].fs == rate) {
			ir = interpolation_ratios[i].ir;
			break;
		}
	if (ir < 0)
		return -EINVAL;
	for (i = 0; mclk_ratios[ir][i].ratio; i++)
		if (mclk_ratios[ir][i].ratio * rate == sta32x->mclk) {
			mcs = mclk_ratios[ir][i].mcs;
			break;
		}
	if (mcs < 0)
		return -EINVAL;

	confa = snd_soc_read(codec, STA32X_CONFA);
	confa &= ~(STA32X_CONFA_MCS_MASK | STA32X_CONFA_IR_MASK);
	confa |= (ir << STA32X_CONFA_IR_SHIFT) | (mcs << STA32X_CONFA_MCS_SHIFT);

	confb = snd_soc_read(codec, STA32X_CONFB);
	confb &= ~(STA32X_CONFB_SAI_MASK | STA32X_CONFB_SAIFB);
	switch (params_format(params)) {
	case SNDRV_PCM_FORMAT_S24_LE:
	case SNDRV_PCM_FORMAT_S24_BE:
	case SNDRV_PCM_FORMAT_S24_3LE:
	case SNDRV_PCM_FORMAT_S24_3BE:
		pr_debug("24bit\n");
		/* fall through */
	case SNDRV_PCM_FORMAT_S32_LE:
	case SNDRV_PCM_FORMAT_S32_BE:
		pr_debug("24bit or 32bit\n");
		switch (sta32x->format) {
		case SND_SOC_DAIFMT_I2S:
			confb |= 0x0;
			break;
		case SND_SOC_DAIFMT_LEFT_J:
			confb |= 0x1;
			break;
		case SND_SOC_DAIFMT_RIGHT_J:
			confb |= 0x2;
			break;
		}

		break;
	case SNDRV_PCM_FORMAT_S20_3LE:
	case SNDRV_PCM_FORMAT_S20_3BE:
		pr_debug("20bit\n");
		switch (sta32x->format) {
		case SND_SOC_DAIFMT_I2S:
			confb |= 0x4;
			break;
		case SND_SOC_DAIFMT_LEFT_J:
			confb |= 0x5;
			break;
		case SND_SOC_DAIFMT_RIGHT_J:
			confb |= 0x6;
			break;
		}

		break;
	case SNDRV_PCM_FORMAT_S18_3LE:
	case SNDRV_PCM_FORMAT_S18_3BE:
		pr_debug("18bit\n");
		switch (sta32x->format) {
		case SND_SOC_DAIFMT_I2S:
			confb |= 0x8;
			break;
		case SND_SOC_DAIFMT_LEFT_J:
			confb |= 0x9;
			break;
		case SND_SOC_DAIFMT_RIGHT_J:
			confb |= 0xa;
			break;
		}

		break;
	case SNDRV_PCM_FORMAT_S16_LE:
	case SNDRV_PCM_FORMAT_S16_BE:
		pr_debug("16bit\n");
		switch (sta32x->format) {
		case SND_SOC_DAIFMT_I2S:
			confb |= 0x0;
			break;
		case SND_SOC_DAIFMT_LEFT_J:
			confb |= 0xd;
			break;
		case SND_SOC_DAIFMT_RIGHT_J:
			confb |= 0xe;
			break;
		}

		break;
	default:
		return -EINVAL;
	}

	snd_soc_write(codec, STA32X_CONFA, confa);
	snd_soc_write(codec, STA32X_CONFB, confb);
	return 0;
}
Beispiel #13
0
static int cs42l73_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
	struct snd_soc_codec *codec = codec_dai->codec;
	struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
	u8 id = codec_dai->id;
	unsigned int inv, format;
	u8 spc, mmcc;

	spc = snd_soc_read(codec, CS42L73_SPC(id));
	mmcc = snd_soc_read(codec, CS42L73_MMCC(id));

	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
	case SND_SOC_DAIFMT_CBM_CFM:
		mmcc |= CS42L73_MS_MASTER;
		break;

	case SND_SOC_DAIFMT_CBS_CFS:
		mmcc &= ~CS42L73_MS_MASTER;
		break;

	default:
		return -EINVAL;
	}

	format = (fmt & SND_SOC_DAIFMT_FORMAT_MASK);
	inv = (fmt & SND_SOC_DAIFMT_INV_MASK);

	switch (format) {
	case SND_SOC_DAIFMT_I2S:
		spc &= ~CS42L73_SPDIF_PCM;
		break;
	case SND_SOC_DAIFMT_DSP_A:
	case SND_SOC_DAIFMT_DSP_B:
		if (mmcc & CS42L73_MS_MASTER) {
			dev_err(codec->dev,
				"PCM format in slave mode only\n");
			return -EINVAL;
		}
		if (id == CS42L73_ASP) {
			dev_err(codec->dev,
				"PCM format is not supported on ASP port\n");
			return -EINVAL;
		}
		spc |= CS42L73_SPDIF_PCM;
		break;
	default:
		return -EINVAL;
	}

	if (spc & CS42L73_SPDIF_PCM) {
		/* Clear PCM mode, clear PCM_BIT_ORDER bit for MSB->LSB */
		spc &= ~(CS42L73_PCM_MODE_MASK | CS42L73_PCM_BIT_ORDER);
		switch (format) {
		case SND_SOC_DAIFMT_DSP_B:
			if (inv == SND_SOC_DAIFMT_IB_IF)
				spc |= CS42L73_PCM_MODE0;
			if (inv == SND_SOC_DAIFMT_IB_NF)
				spc |= CS42L73_PCM_MODE1;
		break;
		case SND_SOC_DAIFMT_DSP_A:
			if (inv == SND_SOC_DAIFMT_IB_IF)
				spc |= CS42L73_PCM_MODE1;
			break;
		default:
			return -EINVAL;
		}
	}

	priv->config[id].spc = spc;
	priv->config[id].mmcc = mmcc;

	return 0;
}
Beispiel #14
0
static int most2120_codec_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params)
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
    struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
    struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
    unsigned int pll_out = 0, ddiv = 0, adiv = 0, bdiv;
    int ret = 0;
    int iis_clk;
#if MOST2120_WM8960_DEBUG
    int i;
#endif

    DBGOUT("Entered %s\n", __func__);

    iis_clk = cpu_get_clock_hz(1); /* Get PLL1 freq */
    
    //printk("iis_clk = %d params_rate = %d\n", iis_clk, params_rate(params));
    switch (params_rate(params)) {
      case 8000:
        ddiv = WM8960_DAC_DIV_6;
        adiv = WM8960_ADC_DIV_6;
        bdiv = WM8960_BCLK_DIV_24;
        pll_out = 12288000;
        break;
      case 16000:
        ddiv = WM8960_DAC_DIV_3;
        adiv = WM8960_ADC_DIV_3;
        bdiv = WM8960_BCLK_DIV_12;
        pll_out = 12288000;
        break;
      case 32000:
        ddiv = WM8960_DAC_DIV_1_5;
        adiv = WM8960_ADC_DIV_1_5;
        bdiv = WM8960_BCLK_DIV_6;
        pll_out = 12288000;
        break;
      case 48000:
        ddiv = WM8960_DAC_DIV_1;
        adiv = WM8960_ADC_DIV_1;
        bdiv = WM8960_BCLK_DIV_4;
        pll_out = 12288000;
        break;
      case 11025:
        ddiv = WM8960_DAC_DIV_4;
        adiv = WM8960_ADC_DIV_4;
        bdiv = WM8960_BCLK_DIV_16;
        pll_out = 11289600;
        break;
      case 22050:
        ddiv = WM8960_DAC_DIV_2;
        adiv = WM8960_ADC_DIV_2;
        bdiv = WM8960_BCLK_DIV_8;
        pll_out = 11289600;
        break;
      case 44100:
        ddiv = WM8960_DAC_DIV_1;
        adiv = WM8960_ADC_DIV_1;
        bdiv = WM8960_BCLK_DIV_4;
        pll_out = 11289600;
        break;
      default :
        ddiv = WM8960_DAC_DIV_1;
        adiv = WM8960_ADC_DIV_1;
        bdiv = WM8960_BCLK_DIV_4;
        pll_out = 11289600;
    }

    /* NEXELL's nxp2120 has limited clock generation function, so it would be better
       Nxp2120 only generate reference clock from PLL1, 192MHz / 16 = 12 MHz...
       And then let WM8960 generate required clock for itself. */

    /* set codec DAI configuration */
    ret = snd_soc_dai_set_fmt(codec_dai,
        SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
        SND_SOC_DAIFMT_CBM_CFM); /* Set master mode for generate bitclk and fs(lrclk) */
    if (ret < 0)
        return ret;

    /* set cpu DAI configuration */
    ret = snd_soc_dai_set_fmt(cpu_dai,
        SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
        SND_SOC_DAIFMT_CBS_CFS); /* Set slave mode for bitclk, and fs(lrclk) */
    if (ret < 0)
        return ret;

    /* set CPU generate reference clock for Codec's PLL. So Set PLL1 as clock source.  */
    ret = snd_soc_dai_set_sysclk(cpu_dai, NX_I2S_CLKSRC_0, NX_I2S_CLKSRC0_PLL1, SND_SOC_CLOCK_OUT);
    if (ret < 0)
        return ret;
    
    /* Our I2S is Slave, So Set to EXT_BIT.  */
    ret = snd_soc_dai_set_sysclk(cpu_dai, NX_I2S_CLKSRC_1, NX_I2S_CLKSRC1_EXT_BIT, SND_SOC_CLOCK_IN);
    if (ret < 0)
        return ret;
    
    /* Set prescaler set to 16 to generate 12MHz around clock */
    ret = snd_soc_dai_set_clkdiv(cpu_dai, NX_I2S_CLKDIV_0, 16 - 1); /* PLL1 192MHz -> 12MHz with 1/16 */
    if (ret < 0)
        return ret;
    
    /* Use Bitclk as Clock Source and no division. */
    ret = snd_soc_dai_set_clkdiv(cpu_dai, NX_I2S_CLKDIV_1, 1 - 1); /* No div as BITCLK */
    if (ret < 0)
        return ret;

    /* Set fs as 64fs(256fs). */
    ret = snd_soc_dai_set_clkdiv(cpu_dai, NX_I2S_CLKDIV_SYNC_PERIOD, NX_I2S_PERDIV_64);
    if (ret < 0)
        return ret;
   
    /* set codec sysclock from PLL source */
    ret = snd_soc_dai_set_clkdiv(codec_dai, WM8960_SYSCLKSEL, WM8960_SYSCLK_PLL);
    if (ret < 0)
        return ret;

    /* Now All the bit rate and rootclock should be set. */
    /* set codec sysclock from PLL source */
    ret = snd_soc_dai_set_pll(codec_dai, 0, 0, iis_clk / 16, pll_out);
    if (ret < 0)
        return ret;
   
    /* set codec speaker DCLK for SYSCLK/2. -> SYSCLK/16 */
    ret = snd_soc_dai_set_clkdiv(codec_dai, WM8960_DCLKDIV, WM8960_DCLK_DIV_16);
    if (ret < 0)
        return ret;

    /* set codec DAC div factor to ddiv. */
    ret = snd_soc_dai_set_clkdiv(codec_dai, WM8960_DACDIV, ddiv);
    if (ret < 0)
        return ret;
    
    /* set codec ADC div factor to adiv. */
    ret = snd_soc_dai_set_clkdiv(codec_dai, WM8960_ADCDIV, adiv);
    if (ret < 0)
        return ret;
    
    /* Bit Clock should be devided for NEXELL 256fs machine  */
    ret = snd_soc_dai_set_clkdiv(codec_dai, WM8960_BCLKDIV, bdiv);
    if (ret < 0)
        return ret;

    
#if MOST2120_WM8960_DEBUG
    for (i=0; i<56;i++) {
        printk("codec addr = 0x%x(%d) val= %x\n", i, i, snd_soc_read(codec_dai->codec, i));
    }
#endif
	return 0;
}
Beispiel #15
0
static int wm8961_register(struct wm8961_priv *wm8961)
{
	struct snd_soc_codec *codec = &wm8961->codec;
	int ret;
	u16 reg;

	if (wm8961_codec) {
		dev_err(codec->dev, "Another WM8961 is registered\n");
		ret = -EINVAL;
		goto err;
	}

	mutex_init(&codec->mutex);
	INIT_LIST_HEAD(&codec->dapm_widgets);
	INIT_LIST_HEAD(&codec->dapm_paths);

	snd_soc_codec_set_drvdata(codec, wm8961);
	codec->name = "WM8961";
	codec->owner = THIS_MODULE;
	codec->dai = &wm8961_dai;
	codec->num_dai = 1;
	codec->reg_cache_size = ARRAY_SIZE(wm8961->reg_cache);
	codec->reg_cache = &wm8961->reg_cache;
	codec->bias_level = SND_SOC_BIAS_OFF;
	codec->set_bias_level = wm8961_set_bias_level;
	codec->volatile_register = wm8961_volatile_register;

	memcpy(codec->reg_cache, wm8961_reg_defaults,
	       sizeof(wm8961_reg_defaults));

	ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C);
	if (ret != 0) {
		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
		goto err;
	}

	reg = snd_soc_read(codec, WM8961_SOFTWARE_RESET);
	if (reg != 0x1801) {
		dev_err(codec->dev, "Device is not a WM8961: ID=0x%x\n", reg);
		ret = -EINVAL;
		goto err;
	}

	/* This isn't volatile - readback doesn't correspond to write */
	reg = codec->hw_read(codec, WM8961_RIGHT_INPUT_VOLUME);
	dev_info(codec->dev, "WM8961 family %d revision %c\n",
		 (reg & WM8961_DEVICE_ID_MASK) >> WM8961_DEVICE_ID_SHIFT,
		 ((reg & WM8961_CHIP_REV_MASK) >> WM8961_CHIP_REV_SHIFT)
		 + 'A');

	ret = wm8961_reset(codec);
	if (ret < 0) {
		dev_err(codec->dev, "Failed to issue reset\n");
		return ret;
	}

	/* Enable class W */
	reg = snd_soc_read(codec, WM8961_CHARGE_PUMP_B);
	reg |= WM8961_CP_DYN_PWR_MASK;
	snd_soc_write(codec, WM8961_CHARGE_PUMP_B, reg);

	/* Latch volume update bits (right channel only, we always
	 * write both out) and default ZC on. */
	reg = snd_soc_read(codec, WM8961_ROUT1_VOLUME);
	snd_soc_write(codec, WM8961_ROUT1_VOLUME,
		     reg | WM8961_LO1ZC | WM8961_OUT1VU);
	snd_soc_write(codec, WM8961_LOUT1_VOLUME, reg | WM8961_LO1ZC);
	reg = snd_soc_read(codec, WM8961_ROUT2_VOLUME);
	snd_soc_write(codec, WM8961_ROUT2_VOLUME,
		     reg | WM8961_SPKRZC | WM8961_SPKVU);
	snd_soc_write(codec, WM8961_LOUT2_VOLUME, reg | WM8961_SPKLZC);

	reg = snd_soc_read(codec, WM8961_RIGHT_ADC_VOLUME);
	snd_soc_write(codec, WM8961_RIGHT_ADC_VOLUME, reg | WM8961_ADCVU);
	reg = snd_soc_read(codec, WM8961_RIGHT_INPUT_VOLUME);
	snd_soc_write(codec, WM8961_RIGHT_INPUT_VOLUME, reg | WM8961_IPVU);

	/* Use soft mute by default */
	reg = snd_soc_read(codec, WM8961_ADC_DAC_CONTROL_2);
	reg |= WM8961_DACSMM;
	snd_soc_write(codec, WM8961_ADC_DAC_CONTROL_2, reg);

	/* Use automatic clocking mode by default; for now this is all
	 * we support.
	 */
	reg = snd_soc_read(codec, WM8961_CLOCKING_3);
	reg &= ~WM8961_MANUAL_MODE;
	snd_soc_write(codec, WM8961_CLOCKING_3, reg);

	wm8961_set_bias_level(codec, SND_SOC_BIAS_STANDBY);

	wm8961_dai.dev = codec->dev;

	wm8961_codec = codec;

	ret = snd_soc_register_codec(codec);
	if (ret != 0) {
		dev_err(codec->dev, "Failed to register codec: %d\n", ret);
		return ret;
	}

	ret = snd_soc_register_dai(&wm8961_dai);
	if (ret != 0) {
		dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
		snd_soc_unregister_codec(codec);
		return ret;
	}

	return 0;

err:
	kfree(wm8961);
	return ret;
}
Beispiel #16
0
static int wm8523_register(struct wm8523_priv *wm8523,
			   enum snd_soc_control_type control)
{
	int ret;
	struct snd_soc_codec *codec = &wm8523->codec;
	int i;

	if (wm8523_codec) {
		dev_err(codec->dev, "Another WM8523 is registered\n");
		return -EINVAL;
	}

	mutex_init(&codec->mutex);
	INIT_LIST_HEAD(&codec->dapm_widgets);
	INIT_LIST_HEAD(&codec->dapm_paths);

	snd_soc_codec_set_drvdata(codec, wm8523);
	codec->name = "WM8523";
	codec->owner = THIS_MODULE;
	codec->bias_level = SND_SOC_BIAS_OFF;
	codec->set_bias_level = wm8523_set_bias_level;
	codec->dai = &wm8523_dai;
	codec->num_dai = 1;
	codec->reg_cache_size = WM8523_REGISTER_COUNT;
	codec->reg_cache = &wm8523->reg_cache;
	codec->volatile_register = wm8523_volatile_register;

	wm8523->rate_constraint.list = &wm8523->rate_constraint_list[0];
	wm8523->rate_constraint.count =
		ARRAY_SIZE(wm8523->rate_constraint_list);

	memcpy(codec->reg_cache, wm8523_reg, sizeof(wm8523_reg));

	ret = snd_soc_codec_set_cache_io(codec, 8, 16, control);
	if (ret != 0) {
		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
		goto err;
	}

	for (i = 0; i < ARRAY_SIZE(wm8523->supplies); i++)
		wm8523->supplies[i].supply = wm8523_supply_names[i];

	ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8523->supplies),
				 wm8523->supplies);
	if (ret != 0) {
		dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
		goto err;
	}

	ret = regulator_bulk_enable(ARRAY_SIZE(wm8523->supplies),
				    wm8523->supplies);
	if (ret != 0) {
		dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
		goto err_get;
	}

	ret = snd_soc_read(codec, WM8523_DEVICE_ID);
	if (ret < 0) {
		dev_err(codec->dev, "Failed to read ID register\n");
		goto err_enable;
	}
	if (ret != wm8523_reg[WM8523_DEVICE_ID]) {
		dev_err(codec->dev, "Device is not a WM8523, ID is %x\n", ret);
		ret = -EINVAL;
		goto err_enable;
	}

	ret = snd_soc_read(codec, WM8523_REVISION);
	if (ret < 0) {
		dev_err(codec->dev, "Failed to read revision register\n");
		goto err_enable;
	}
	dev_info(codec->dev, "revision %c\n",
		 (ret & WM8523_CHIP_REV_MASK) + 'A');

	ret = wm8523_reset(codec);
	if (ret < 0) {
		dev_err(codec->dev, "Failed to issue reset\n");
		goto err_enable;
	}

	wm8523_dai.dev = codec->dev;

	/* Change some default settings - latch VU and enable ZC */
	wm8523->reg_cache[WM8523_DAC_GAINR] |= WM8523_DACR_VU;
	wm8523->reg_cache[WM8523_DAC_CTRL3] |= WM8523_ZC;

	wm8523_set_bias_level(codec, SND_SOC_BIAS_STANDBY);

	/* Bias level configuration will have done an extra enable */
	regulator_bulk_disable(ARRAY_SIZE(wm8523->supplies), wm8523->supplies);

	wm8523_codec = codec;

	ret = snd_soc_register_codec(codec);
	if (ret != 0) {
		dev_err(codec->dev, "Failed to register codec: %d\n", ret);
		return ret;
	}

	ret = snd_soc_register_dai(&wm8523_dai);
	if (ret != 0) {
		dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
		snd_soc_unregister_codec(codec);
		return ret;
	}

	return 0;

err_enable:
	regulator_bulk_disable(ARRAY_SIZE(wm8523->supplies), wm8523->supplies);
err_get:
	regulator_bulk_free(ARRAY_SIZE(wm8523->supplies), wm8523->supplies);
err:
	kfree(wm8523);
	return ret;
}
Beispiel #17
0
/*
 * The headphone output supports special anti-pop sequences giving
 * silent power up and power down.
 */
static int wm8961_hp_event(struct snd_soc_dapm_widget *w,
			   struct snd_kcontrol *kcontrol, int event)
{
	struct snd_soc_codec *codec = w->codec;
	u16 hp_reg = snd_soc_read(codec, WM8961_ANALOGUE_HP_0);
	u16 cp_reg = snd_soc_read(codec, WM8961_CHARGE_PUMP_1);
	u16 pwr_reg = snd_soc_read(codec, WM8961_PWR_MGMT_2);
	u16 dcs_reg = snd_soc_read(codec, WM8961_DC_SERVO_1);
	int timeout = 500;

	if (event & SND_SOC_DAPM_POST_PMU) {
		/* Make sure the output is shorted */
		hp_reg &= ~(WM8961_HPR_RMV_SHORT | WM8961_HPL_RMV_SHORT);
		snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);

		/* Enable the charge pump */
		cp_reg |= WM8961_CP_ENA;
		snd_soc_write(codec, WM8961_CHARGE_PUMP_1, cp_reg);
		mdelay(5);

		/* Enable the PGA */
		pwr_reg |= WM8961_LOUT1_PGA | WM8961_ROUT1_PGA;
		snd_soc_write(codec, WM8961_PWR_MGMT_2, pwr_reg);

		/* Enable the amplifier */
		hp_reg |= WM8961_HPR_ENA | WM8961_HPL_ENA;
		snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);

		/* Second stage enable */
		hp_reg |= WM8961_HPR_ENA_DLY | WM8961_HPL_ENA_DLY;
		snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);

		/* Enable the DC servo & trigger startup */
		dcs_reg |=
			WM8961_DCS_ENA_CHAN_HPR | WM8961_DCS_TRIG_STARTUP_HPR |
			WM8961_DCS_ENA_CHAN_HPL | WM8961_DCS_TRIG_STARTUP_HPL;
		dev_dbg(codec->dev, "Enabling DC servo\n");

		snd_soc_write(codec, WM8961_DC_SERVO_1, dcs_reg);
		do {
			msleep(1);
			dcs_reg = snd_soc_read(codec, WM8961_DC_SERVO_1);
		} while (--timeout &&
			 dcs_reg & (WM8961_DCS_TRIG_STARTUP_HPR |
				WM8961_DCS_TRIG_STARTUP_HPL));
		if (dcs_reg & (WM8961_DCS_TRIG_STARTUP_HPR |
			       WM8961_DCS_TRIG_STARTUP_HPL))
			dev_err(codec->dev, "DC servo timed out\n");
		else
			dev_dbg(codec->dev, "DC servo startup complete\n");

		/* Enable the output stage */
		hp_reg |= WM8961_HPR_ENA_OUTP | WM8961_HPL_ENA_OUTP;
		snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);

		/* Remove the short on the output stage */
		hp_reg |= WM8961_HPR_RMV_SHORT | WM8961_HPL_RMV_SHORT;
		snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
	}

	if (event & SND_SOC_DAPM_PRE_PMD) {
		/* Short the output */
		hp_reg &= ~(WM8961_HPR_RMV_SHORT | WM8961_HPL_RMV_SHORT);
		snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);

		/* Disable the output stage */
		hp_reg &= ~(WM8961_HPR_ENA_OUTP | WM8961_HPL_ENA_OUTP);
		snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);

		/* Disable DC offset cancellation */
		dcs_reg &= ~(WM8961_DCS_ENA_CHAN_HPR |
			     WM8961_DCS_ENA_CHAN_HPL);
		snd_soc_write(codec, WM8961_DC_SERVO_1, dcs_reg);

		/* Finish up */
		hp_reg &= ~(WM8961_HPR_ENA_DLY | WM8961_HPR_ENA |
			    WM8961_HPL_ENA_DLY | WM8961_HPL_ENA);
		snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);

		/* Disable the PGA */
		pwr_reg &= ~(WM8961_LOUT1_PGA | WM8961_ROUT1_PGA);
		snd_soc_write(codec, WM8961_PWR_MGMT_2, pwr_reg);

		/* Disable the charge pump */
		dev_dbg(codec->dev, "Disabling charge pump\n");
		snd_soc_write(codec, WM8961_CHARGE_PUMP_1,
			     cp_reg & ~WM8961_CP_ENA);
	}

	return 0;
}
Beispiel #18
0
static int wm8900_hp_event(struct snd_soc_dapm_widget *w,
			   struct snd_kcontrol *kcontrol, int event)
{
	struct snd_soc_codec *codec = w->codec;
	u16 hpctl1 = snd_soc_read(codec, WM8900_REG_HPCTL1);

	switch (event) {
	case SND_SOC_DAPM_PRE_PMU:
		/* Clamp headphone outputs */
		hpctl1 = WM8900_REG_HPCTL1_HP_CLAMP_IP |
			WM8900_REG_HPCTL1_HP_CLAMP_OP;
		snd_soc_write(codec, WM8900_REG_HPCTL1, hpctl1);
		break;

	case SND_SOC_DAPM_POST_PMU:
		/* Enable the input stage */
		hpctl1 &= ~WM8900_REG_HPCTL1_HP_CLAMP_IP;
		hpctl1 |= WM8900_REG_HPCTL1_HP_SHORT |
			WM8900_REG_HPCTL1_HP_SHORT2 |
			WM8900_REG_HPCTL1_HP_IPSTAGE_ENA;
		snd_soc_write(codec, WM8900_REG_HPCTL1, hpctl1);

		msleep(400);

		/* Enable the output stage */
		hpctl1 &= ~WM8900_REG_HPCTL1_HP_CLAMP_OP;
		hpctl1 |= WM8900_REG_HPCTL1_HP_OPSTAGE_ENA;
		snd_soc_write(codec, WM8900_REG_HPCTL1, hpctl1);

		/* Remove the shorts */
		hpctl1 &= ~WM8900_REG_HPCTL1_HP_SHORT2;
		snd_soc_write(codec, WM8900_REG_HPCTL1, hpctl1);
		hpctl1 &= ~WM8900_REG_HPCTL1_HP_SHORT;
		snd_soc_write(codec, WM8900_REG_HPCTL1, hpctl1);
		break;

	case SND_SOC_DAPM_PRE_PMD:
		/* Short the output */
		hpctl1 |= WM8900_REG_HPCTL1_HP_SHORT;
		snd_soc_write(codec, WM8900_REG_HPCTL1, hpctl1);

		/* Disable the output stage */
		hpctl1 &= ~WM8900_REG_HPCTL1_HP_OPSTAGE_ENA;
		snd_soc_write(codec, WM8900_REG_HPCTL1, hpctl1);

		/* Clamp the outputs and power down input */
		hpctl1 |= WM8900_REG_HPCTL1_HP_CLAMP_IP |
			WM8900_REG_HPCTL1_HP_CLAMP_OP;
		hpctl1 &= ~WM8900_REG_HPCTL1_HP_IPSTAGE_ENA;
		snd_soc_write(codec, WM8900_REG_HPCTL1, hpctl1);
		break;

	case SND_SOC_DAPM_POST_PMD:
		/* Disable everything */
		snd_soc_write(codec, WM8900_REG_HPCTL1, 0);
		break;

	default:
		BUG();
	}

	return 0;
}
Beispiel #19
0
static int wm9081_set_fll(struct snd_soc_codec *codec, int fll_id,
			  unsigned int Fref, unsigned int Fout)
{
	struct wm9081_priv *wm9081 = codec->private_data;
	u16 reg1, reg4, reg5;
	struct _fll_div fll_div;
	int ret;
	int clk_sys_reg;

	/* Any change? */
	if (Fref == wm9081->fll_fref && Fout == wm9081->fll_fout)
		return 0;

	/* Disable the FLL */
	if (Fout == 0) {
		dev_dbg(codec->dev, "FLL disabled\n");
		wm9081->fll_fref = 0;
		wm9081->fll_fout = 0;

		return 0;
	}

	ret = fll_factors(&fll_div, Fref, Fout);
	if (ret != 0)
		return ret;

	reg5 = snd_soc_read(codec, WM9081_FLL_CONTROL_5);
	reg5 &= ~WM9081_FLL_CLK_SRC_MASK;

	switch (fll_id) {
	case WM9081_SYSCLK_FLL_MCLK:
		reg5 |= 0x1;
		break;

	default:
		dev_err(codec->dev, "Unknown FLL ID %d\n", fll_id);
		return -EINVAL;
	}

	/* Disable CLK_SYS while we reconfigure */
	clk_sys_reg = snd_soc_read(codec, WM9081_CLOCK_CONTROL_3);
	if (clk_sys_reg & WM9081_CLK_SYS_ENA)
		snd_soc_write(codec, WM9081_CLOCK_CONTROL_3,
			     clk_sys_reg & ~WM9081_CLK_SYS_ENA);

	/* Any FLL configuration change requires that the FLL be
	 * disabled first. */
	reg1 = snd_soc_read(codec, WM9081_FLL_CONTROL_1);
	reg1 &= ~WM9081_FLL_ENA;
	snd_soc_write(codec, WM9081_FLL_CONTROL_1, reg1);

	/* Apply the configuration */
	if (fll_div.k)
		reg1 |= WM9081_FLL_FRAC_MASK;
	else
		reg1 &= ~WM9081_FLL_FRAC_MASK;
	snd_soc_write(codec, WM9081_FLL_CONTROL_1, reg1);

	snd_soc_write(codec, WM9081_FLL_CONTROL_2,
		     (fll_div.fll_outdiv << WM9081_FLL_OUTDIV_SHIFT) |
		     (fll_div.fll_fratio << WM9081_FLL_FRATIO_SHIFT));
	snd_soc_write(codec, WM9081_FLL_CONTROL_3, fll_div.k);

	reg4 = snd_soc_read(codec, WM9081_FLL_CONTROL_4);
	reg4 &= ~WM9081_FLL_N_MASK;
	reg4 |= fll_div.n << WM9081_FLL_N_SHIFT;
	snd_soc_write(codec, WM9081_FLL_CONTROL_4, reg4);

	reg5 &= ~WM9081_FLL_CLK_REF_DIV_MASK;
	reg5 |= fll_div.fll_clk_ref_div << WM9081_FLL_CLK_REF_DIV_SHIFT;
	snd_soc_write(codec, WM9081_FLL_CONTROL_5, reg5);

	/* Enable the FLL */
	snd_soc_write(codec, WM9081_FLL_CONTROL_1, reg1 | WM9081_FLL_ENA);

	/* Then bring CLK_SYS up again if it was disabled */
	if (clk_sys_reg & WM9081_CLK_SYS_ENA)
		snd_soc_write(codec, WM9081_CLOCK_CONTROL_3, clk_sys_reg);

	dev_dbg(codec->dev, "FLL enabled at %dHz->%dHz\n", Fref, Fout);

	wm9081->fll_fref = Fref;
	wm9081->fll_fout = Fout;

	return 0;
}
static int tegra_hifi_hw_params(struct snd_pcm_substream *substream,
					struct snd_pcm_hw_params *params)
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
	struct snd_soc_codec *codec = codec_dai->codec;
	struct tegra_audio_data* audio_data = rtd->socdev->codec_data;
	enum dac_dap_data_format data_fmt;
	int dai_flag = 0, sys_clk;
	int err;

	ASOC_FUNCTION("");

	if (tegra_das_is_port_master(tegra_audio_codec_type_hifi))
		dai_flag |= SND_SOC_DAIFMT_CBM_CFM;
	else
		dai_flag |= SND_SOC_DAIFMT_CBS_CFS;

	data_fmt = tegra_das_get_codec_data_fmt(tegra_audio_codec_type_hifi);

	/* We are supporting DSP and I2s format for now */
	if (data_fmt & dac_dap_data_format_i2s)
		dai_flag |= SND_SOC_DAIFMT_I2S;
	else
		dai_flag |= SND_SOC_DAIFMT_DSP_A;

	err = snd_soc_dai_set_fmt(codec_dai, dai_flag);
	if (err < 0) {
		pr_err("codec_dai fmt not set \n");
		return err;
	}

	err = snd_soc_dai_set_fmt(cpu_dai, dai_flag);
	if (err < 0) {
		pr_err("cpu_dai fmt not set \n");
		return err;
	}

	sys_clk = clk_get_rate(audio_data->dap_mclk);
	err = snd_soc_dai_set_sysclk(codec_dai, 0, sys_clk, SND_SOC_CLOCK_IN);
	if (err < 0) {
		pr_err("codec_dai clock not set\n");
		return err;
	}

	err = snd_soc_dai_set_sysclk(cpu_dai, 0, sys_clk, SND_SOC_CLOCK_IN);
	if (err < 0) {
		pr_err("cpu_dai clock not set\n");
		return err;
	}

	if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) {
		int CtrlReg = 0;
		int VolumeCtrlReg = 0;
		int SidetoneCtrlReg = 0;
		int SideToneAtenuation = 0;

		snd_soc_write(codec, WM8903_ANALOGUE_LEFT_INPUT_0, 0X7);
		snd_soc_write(codec, WM8903_ANALOGUE_RIGHT_INPUT_0, 0X7);

		/* Mic Bias enable */
		CtrlReg = (0x1<<B00_MICBIAS_ENA) | (0x1<<B01_MICDET_ENA);
		snd_soc_write(codec, WM8903_MIC_BIAS_CONTROL_0, CtrlReg);

		/* Enable DRC */
		CtrlReg = snd_soc_read(codec, WM8903_DRC_0);
		CtrlReg |= (1<<B15_DRC_ENA);
		snd_soc_write(codec, WM8903_DRC_0, CtrlReg);

		
		CtrlReg = LUNA_INTERNAL_MIC_SETTING_R1R2;
		snd_soc_write(codec, WM8903_ANALOGUE_RIGHT_INPUT_1, CtrlReg);
		CtrlReg = LUNA_EXTERNAL_MIC_SETTING_L2L1;
		snd_soc_write(codec, WM8903_ANALOGUE_LEFT_INPUT_1, CtrlReg);
		VolumeCtrlReg = (0x1C << B00_IN_VOL);

		/* Mic Setting */
		snd_soc_write(codec, WM8903_ANALOGUE_LEFT_INPUT_0,
				VolumeCtrlReg);
		snd_soc_write(codec, WM8903_ANALOGUE_RIGHT_INPUT_0,
				VolumeCtrlReg);

		
		CtrlReg = snd_soc_read(codec, WM8903_AUDIO_INTERFACE_0);
		CtrlReg  = SET_REG_VAL(CtrlReg, 0x1, B06_AIF_ADCR, 0x0);
		CtrlReg  = SET_REG_VAL(CtrlReg, 0x1, B07_AIF_ADCL, 0x0);
		snd_soc_write(codec, WM8903_AUDIO_INTERFACE_0, CtrlReg);

		/* Enable analog inputs */
		CtrlReg = (0x1<<B01_INL_ENA) | (0x1<<B00_INR_ENA);
		snd_soc_write(codec, WM8903_POWER_MANAGEMENT_0, CtrlReg);
		/* ADC Settings */
		CtrlReg = snd_soc_read(codec, WM8903_ADC_DIGITAL_0);
		CtrlReg |= (0x1<<B04_ADC_HPF_ENA);
		snd_soc_write(codec, WM8903_ADC_DIGITAL_0, CtrlReg);
#if 0
		SidetoneCtrlReg = 0;
		snd_soc_write(codec, R20_SIDETONE_CTRL, SidetoneCtrlReg);
#endif
		/* Enable ADC */
		CtrlReg = snd_soc_read(codec, WM8903_POWER_MANAGEMENT_6);
		CtrlReg |= (0x1<<B00_ADCR_ENA)|(0x1<<B01_ADCL_ENA);
		snd_soc_write(codec, WM8903_POWER_MANAGEMENT_6, CtrlReg);
#if 0
		
		SidetoneCtrlReg = (0x1<<2) | (0x2<<0);
		SideToneAtenuation = 12 ; 
		SidetoneCtrlReg |= (SideToneAtenuation<<8)
				| (SideToneAtenuation<<4);
		snd_soc_write(codec, R20_SIDETONE_CTRL, SidetoneCtrlReg);
#endif
		CtrlReg = snd_soc_read(codec, R29_DRC_1);
		CtrlReg |= 0x3; /*mic volume 18 db */
		snd_soc_write(codec, R29_DRC_1, CtrlReg);
	}

	return 0;
}
Beispiel #21
0
static int wm9081_set_bias_level(struct snd_soc_codec *codec,
				 enum snd_soc_bias_level level)
{
	u16 reg;

	switch (level) {
	case SND_SOC_BIAS_ON:
		break;

	case SND_SOC_BIAS_PREPARE:
		/* VMID=2*40k */
		reg = snd_soc_read(codec, WM9081_VMID_CONTROL);
		reg &= ~WM9081_VMID_SEL_MASK;
		reg |= 0x2;
		snd_soc_write(codec, WM9081_VMID_CONTROL, reg);

		/* Normal bias current */
		reg = snd_soc_read(codec, WM9081_BIAS_CONTROL_1);
		reg &= ~WM9081_STBY_BIAS_ENA;
		snd_soc_write(codec, WM9081_BIAS_CONTROL_1, reg);
		break;

	case SND_SOC_BIAS_STANDBY:
		/* Initial cold start */
		if (codec->bias_level == SND_SOC_BIAS_OFF) {
			/* Disable LINEOUT discharge */
			reg = snd_soc_read(codec, WM9081_ANTI_POP_CONTROL);
			reg &= ~WM9081_LINEOUT_DISCH;
			snd_soc_write(codec, WM9081_ANTI_POP_CONTROL, reg);

			/* Select startup bias source */
			reg = snd_soc_read(codec, WM9081_BIAS_CONTROL_1);
			reg |= WM9081_BIAS_SRC | WM9081_BIAS_ENA;
			snd_soc_write(codec, WM9081_BIAS_CONTROL_1, reg);

			/* VMID 2*4k; Soft VMID ramp enable */
			reg = snd_soc_read(codec, WM9081_VMID_CONTROL);
			reg |= WM9081_VMID_RAMP | 0x6;
			snd_soc_write(codec, WM9081_VMID_CONTROL, reg);

			mdelay(100);

			/* Normal bias enable & soft start off */
			reg |= WM9081_BIAS_ENA;
			reg &= ~WM9081_VMID_RAMP;
			snd_soc_write(codec, WM9081_VMID_CONTROL, reg);

			/* Standard bias source */
			reg = snd_soc_read(codec, WM9081_BIAS_CONTROL_1);
			reg &= ~WM9081_BIAS_SRC;
			snd_soc_write(codec, WM9081_BIAS_CONTROL_1, reg);
		}

		/* VMID 2*240k */
		reg = snd_soc_read(codec, WM9081_BIAS_CONTROL_1);
		reg &= ~WM9081_VMID_SEL_MASK;
		reg |= 0x40;
		snd_soc_write(codec, WM9081_VMID_CONTROL, reg);

		/* Standby bias current on */
		reg = snd_soc_read(codec, WM9081_BIAS_CONTROL_1);
		reg |= WM9081_STBY_BIAS_ENA;
		snd_soc_write(codec, WM9081_BIAS_CONTROL_1, reg);
		break;

	case SND_SOC_BIAS_OFF:
		/* Startup bias source */
		reg = snd_soc_read(codec, WM9081_BIAS_CONTROL_1);
		reg |= WM9081_BIAS_SRC;
		snd_soc_write(codec, WM9081_BIAS_CONTROL_1, reg);

		/* Disable VMID and biases with soft ramping */
		reg = snd_soc_read(codec, WM9081_VMID_CONTROL);
		reg &= ~(WM9081_VMID_SEL_MASK | WM9081_BIAS_ENA);
		reg |= WM9081_VMID_RAMP;
		snd_soc_write(codec, WM9081_VMID_CONTROL, reg);

		/* Actively discharge LINEOUT */
		reg = snd_soc_read(codec, WM9081_ANTI_POP_CONTROL);
		reg |= WM9081_LINEOUT_DISCH;
		snd_soc_write(codec, WM9081_ANTI_POP_CONTROL, reg);
		break;
	}

	codec->bias_level = level;

	return 0;
}
static int wm8993_hw_params(struct snd_pcm_substream *substream,
			    struct snd_pcm_hw_params *params,
			    struct snd_soc_dai *dai)
{
	struct snd_soc_codec *codec = dai->codec;
	struct wm8993_priv *wm8993 = codec->private_data;
	int ret, i, best, best_val, cur_val;
	unsigned int clocking1, clocking3, aif1, aif4;

	clocking1 = snd_soc_read(codec, WM8993_CLOCKING_1);
	clocking1 &= ~WM8993_BCLK_DIV_MASK;

	clocking3 = snd_soc_read(codec, WM8993_CLOCKING_3);
	clocking3 &= ~(WM8993_CLK_SYS_RATE_MASK | WM8993_SAMPLE_RATE_MASK);

	aif1 = snd_soc_read(codec, WM8993_AUDIO_INTERFACE_1);
	aif1 &= ~WM8993_AIF_WL_MASK;

	aif4 = snd_soc_read(codec, WM8993_AUDIO_INTERFACE_4);
	aif4 &= ~WM8993_LRCLK_RATE_MASK;

	/* What BCLK do we need? */
	wm8993->fs = params_rate(params);
	wm8993->bclk = 2 * wm8993->fs;
	if (wm8993->tdm_slots) {
		dev_dbg(codec->dev, "Configuring for %d %d bit TDM slots\n",
			wm8993->tdm_slots, wm8993->tdm_width);
		wm8993->bclk *= wm8993->tdm_width * wm8993->tdm_slots;
	} else {
		switch (params_format(params)) {
		case SNDRV_PCM_FORMAT_S16_LE:
			wm8993->bclk *= 16;
			break;
		case SNDRV_PCM_FORMAT_S20_3LE:
			wm8993->bclk *= 20;
			aif1 |= 0x8;
			break;
		case SNDRV_PCM_FORMAT_S24_LE:
			wm8993->bclk *= 24;
			aif1 |= 0x10;
			break;
		case SNDRV_PCM_FORMAT_S32_LE:
			wm8993->bclk *= 32;
			aif1 |= 0x18;
			break;
		default:
			return -EINVAL;
		}
	}

	dev_dbg(codec->dev, "Target BCLK is %dHz\n", wm8993->bclk);

	ret = configure_clock(codec);
	if (ret != 0)
		return ret;

	/* Select nearest CLK_SYS_RATE */
	best = 0;
	best_val = abs((wm8993->sysclk_rate / clk_sys_rates[0].ratio)
		       - wm8993->fs);
	for (i = 1; i < ARRAY_SIZE(clk_sys_rates); i++) {
		cur_val = abs((wm8993->sysclk_rate /
			       clk_sys_rates[i].ratio) - wm8993->fs);;
		if (cur_val < best_val) {
			best = i;
			best_val = cur_val;
		}
	}
	dev_dbg(codec->dev, "Selected CLK_SYS_RATIO of %d\n",
		clk_sys_rates[best].ratio);
	clocking3 |= (clk_sys_rates[best].clk_sys_rate
		      << WM8993_CLK_SYS_RATE_SHIFT);

	/* SAMPLE_RATE */
	best = 0;
	best_val = abs(wm8993->fs - sample_rates[0].rate);
	for (i = 1; i < ARRAY_SIZE(sample_rates); i++) {
		/* Closest match */
		cur_val = abs(wm8993->fs - sample_rates[i].rate);
		if (cur_val < best_val) {
			best = i;
			best_val = cur_val;
		}
	}
	dev_dbg(codec->dev, "Selected SAMPLE_RATE of %dHz\n",
		sample_rates[best].rate);
	clocking3 |= (sample_rates[best].sample_rate
		      << WM8993_SAMPLE_RATE_SHIFT);

	/* BCLK_DIV */
	best = 0;
	best_val = INT_MAX;
	for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) {
		cur_val = ((wm8993->sysclk_rate * 10) / bclk_divs[i].div)
			- wm8993->bclk;
		if (cur_val < 0) /* Table is sorted */
			break;
		if (cur_val < best_val) {
			best = i;
			best_val = cur_val;
		}
	}
	wm8993->bclk = (wm8993->sysclk_rate * 10) / bclk_divs[best].div;
	dev_dbg(codec->dev, "Selected BCLK_DIV of %d for %dHz BCLK\n",
		bclk_divs[best].div, wm8993->bclk);
	clocking1 |= bclk_divs[best].bclk_div << WM8993_BCLK_DIV_SHIFT;

	/* LRCLK is a simple fraction of BCLK */
	dev_dbg(codec->dev, "LRCLK_RATE is %d\n", wm8993->bclk / wm8993->fs);
	aif4 |= wm8993->bclk / wm8993->fs;

	snd_soc_write(codec, WM8993_CLOCKING_1, clocking1);
	snd_soc_write(codec, WM8993_CLOCKING_3, clocking3);
	snd_soc_write(codec, WM8993_AUDIO_INTERFACE_1, aif1);
	snd_soc_write(codec, WM8993_AUDIO_INTERFACE_4, aif4);

	/* ReTune Mobile? */
	if (wm8993->pdata.num_retune_configs) {
		u16 eq1 = snd_soc_read(codec, WM8993_EQ1);
		struct wm8993_retune_mobile_setting *s;

		best = 0;
		best_val = abs(wm8993->pdata.retune_configs[0].rate
			       - wm8993->fs);
		for (i = 0; i < wm8993->pdata.num_retune_configs; i++) {
			cur_val = abs(wm8993->pdata.retune_configs[i].rate
				      - wm8993->fs);
			if (cur_val < best_val) {
				best_val = cur_val;
				best = i;
			}
		}
		s = &wm8993->pdata.retune_configs[best];

		dev_dbg(codec->dev, "ReTune Mobile %s tuned for %dHz\n",
			s->name, s->rate);

		/* Disable EQ while we reconfigure */
		snd_soc_update_bits(codec, WM8993_EQ1, WM8993_EQ_ENA, 0);

		for (i = 1; i < ARRAY_SIZE(s->config); i++)
			snd_soc_write(codec, WM8993_EQ1 + i, s->config[i]);

		snd_soc_update_bits(codec, WM8993_EQ1, WM8993_EQ_ENA, eq1);
	}

	return 0;
}
Beispiel #23
0
static int wm8940_i2s_hw_params(struct snd_pcm_substream *substream,
				struct snd_pcm_hw_params *params,
				struct snd_soc_dai *dai)
{
	struct snd_soc_codec *codec = dai->codec;
	u16 iface = snd_soc_read(codec, WM8940_IFACE) & 0xFD9F;
	u16 addcntrl = snd_soc_read(codec, WM8940_ADDCNTRL) & 0xFFF1;
	u16 companding =  snd_soc_read(codec,
						WM8940_COMPANDINGCTL) & 0xFFDF;
	int ret;

	/* LoutR control */
	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE
	    && params_channels(params) == 2)
		iface |= (1 << 9);

	switch (params_rate(params)) {
	case 8000:
		addcntrl |= (0x5 << 1);
		break;
	case 11025:
		addcntrl |= (0x4 << 1);
		break;
	case 16000:
		addcntrl |= (0x3 << 1);
		break;
	case 22050:
		addcntrl |= (0x2 << 1);
		break;
	case 32000:
		addcntrl |= (0x1 << 1);
		break;
	case 44100:
	case 48000:
		break;
	}
	ret = snd_soc_write(codec, WM8940_ADDCNTRL, addcntrl);
	if (ret)
		goto error_ret;

	switch (params_width(params)) {
	case 8:
		companding = companding | (1 << 5);
		break;
	case 16:
		break;
	case 20:
		iface |= (1 << 5);
		break;
	case 24:
		iface |= (2 << 5);
		break;
	case 32:
		iface |= (3 << 5);
		break;
	}
	ret = snd_soc_write(codec, WM8940_COMPANDINGCTL, companding);
	if (ret)
		goto error_ret;
	ret = snd_soc_write(codec, WM8940_IFACE, iface);

error_ret:
	return ret;
}
static int wm8993_i2c_probe(struct i2c_client *i2c,
			    const struct i2c_device_id *id)
{
	struct wm8993_priv *wm8993;
	struct snd_soc_codec *codec;
	unsigned int val;
	int ret;
	int i;

	if (wm8993_codec) {
		dev_err(&i2c->dev, "A WM8993 is already registered\n");
		return -EINVAL;
	}

	wm8993 = kzalloc(sizeof(struct wm8993_priv), GFP_KERNEL);
	if (wm8993 == NULL)
		return -ENOMEM;

	codec = &wm8993->codec;
	if (i2c->dev.platform_data)
		memcpy(&wm8993->pdata, i2c->dev.platform_data,
		       sizeof(wm8993->pdata));

	mutex_init(&codec->mutex);
	INIT_LIST_HEAD(&codec->dapm_widgets);
	INIT_LIST_HEAD(&codec->dapm_paths);

	codec->name = "WM8993";
	codec->volatile_register = wm8993_volatile;
	codec->reg_cache = wm8993->reg_cache;
	codec->reg_cache_size = ARRAY_SIZE(wm8993->reg_cache);
	codec->bias_level = SND_SOC_BIAS_OFF;
	codec->set_bias_level = wm8993_set_bias_level;
	codec->dai = &wm8993_dai;
	codec->num_dai = 1;
	codec->private_data = wm8993;

	wm8993->hubs_data.hp_startup_mode = 1;
	wm8993->hubs_data.dcs_codes = -2;

	memcpy(wm8993->reg_cache, wm8993_reg_defaults,
	       sizeof(wm8993->reg_cache));

	ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C);
	if (ret != 0) {
		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
		goto err;
	}

	i2c_set_clientdata(i2c, wm8993);
	codec->control_data = i2c;
	wm8993_codec = codec;

	codec->dev = &i2c->dev;

	for (i = 0; i < ARRAY_SIZE(wm8993->supplies); i++)
		wm8993->supplies[i].supply = wm8993_supply_names[i];

	ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8993->supplies),
				 wm8993->supplies);
	if (ret != 0) {
		dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
		goto err;
	}

	ret = regulator_bulk_enable(ARRAY_SIZE(wm8993->supplies),
				    wm8993->supplies);
	if (ret != 0) {
		dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
		goto err_get;
	}

	val = snd_soc_read(codec, WM8993_SOFTWARE_RESET);
	if (val != wm8993_reg_defaults[WM8993_SOFTWARE_RESET]) {
		dev_err(codec->dev, "Invalid ID register value %x\n", val);
		ret = -EINVAL;
		goto err_enable;
	}

	ret = snd_soc_write(codec, WM8993_SOFTWARE_RESET, 0xffff);
	if (ret != 0)
		goto err_enable;

	codec->cache_only = 1;

	/* By default we're using the output mixers */
	wm8993->class_w_users = 2;

	/* Latch volume update bits and default ZC on */
	snd_soc_update_bits(codec, WM8993_RIGHT_DAC_DIGITAL_VOLUME,
			    WM8993_DAC_VU, WM8993_DAC_VU);
	snd_soc_update_bits(codec, WM8993_RIGHT_ADC_DIGITAL_VOLUME,
			    WM8993_ADC_VU, WM8993_ADC_VU);

	/* Manualy manage the HPOUT sequencing for independent stereo
	 * control. */
	snd_soc_update_bits(codec, WM8993_ANALOGUE_HP_0,
			    WM8993_HPOUT1_AUTO_PU, 0);

	/* Use automatic clock configuration */
	snd_soc_update_bits(codec, WM8993_CLOCKING_4, WM8993_SR_MODE, 0);

	wm_hubs_handle_analogue_pdata(codec, wm8993->pdata.lineout1_diff,
				      wm8993->pdata.lineout2_diff,
				      wm8993->pdata.lineout1fb,
				      wm8993->pdata.lineout2fb,
				      wm8993->pdata.jd_scthr,
				      wm8993->pdata.jd_thr,
				      wm8993->pdata.micbias1_lvl,
				      wm8993->pdata.micbias2_lvl);
			     
	ret = wm8993_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
	if (ret != 0)
		goto err_enable;

	wm8993_dai.dev = codec->dev;

	ret = snd_soc_register_dai(&wm8993_dai);
	if (ret != 0)
		goto err_bias;

	ret = snd_soc_register_codec(codec);

	return 0;

err_bias:
	wm8993_set_bias_level(codec, SND_SOC_BIAS_OFF);
err_enable:
	regulator_bulk_disable(ARRAY_SIZE(wm8993->supplies), wm8993->supplies);
err_get:
	regulator_bulk_free(ARRAY_SIZE(wm8993->supplies), wm8993->supplies);
err:
	wm8993_codec = NULL;
	kfree(wm8993);
	return ret;
}
Beispiel #25
0
static int hp_ev(struct snd_soc_dapm_widget *w,
		 struct snd_kcontrol *kcontrol, int event)
{
	struct snd_soc_codec *codec = w->codec;
	unsigned int reg = snd_soc_read(codec, WM9090_ANALOGUE_HP_0);

	switch (event) {
	case SND_SOC_DAPM_POST_PMU:
		snd_soc_update_bits(codec, WM9090_CHARGE_PUMP_1,
				    WM9090_CP_ENA, WM9090_CP_ENA);

		msleep(5);

		snd_soc_update_bits(codec, WM9090_POWER_MANAGEMENT_1,
				    WM9090_HPOUT1L_ENA | WM9090_HPOUT1R_ENA,
				    WM9090_HPOUT1L_ENA | WM9090_HPOUT1R_ENA);

		reg |= WM9090_HPOUT1L_DLY | WM9090_HPOUT1R_DLY;
		snd_soc_write(codec, WM9090_ANALOGUE_HP_0, reg);

		/* Start the DC servo.  We don't currently use the
		 * ability to save the state since we don't have full
		 * control of the analogue paths and they can change
		 * DC offsets; see the WM8904 driver for an example of
		 * doing so.
		 */
		snd_soc_write(codec, WM9090_DC_SERVO_0,
			      WM9090_DCS_ENA_CHAN_0 |
			      WM9090_DCS_ENA_CHAN_1 |
			      WM9090_DCS_TRIG_STARTUP_1 |
			      WM9090_DCS_TRIG_STARTUP_0);
		wait_for_dc_servo(codec);

		reg |= WM9090_HPOUT1R_OUTP | WM9090_HPOUT1R_RMV_SHORT |
			WM9090_HPOUT1L_OUTP | WM9090_HPOUT1L_RMV_SHORT;
		snd_soc_write(codec, WM9090_ANALOGUE_HP_0, reg);
		break;

	case SND_SOC_DAPM_PRE_PMD:
		reg &= ~(WM9090_HPOUT1L_RMV_SHORT |
			 WM9090_HPOUT1L_DLY |
			 WM9090_HPOUT1L_OUTP |
			 WM9090_HPOUT1R_RMV_SHORT |
			 WM9090_HPOUT1R_DLY |
			 WM9090_HPOUT1R_OUTP);

		snd_soc_write(codec, WM9090_ANALOGUE_HP_0, reg);

		snd_soc_write(codec, WM9090_DC_SERVO_0, 0);

		snd_soc_update_bits(codec, WM9090_POWER_MANAGEMENT_1,
				    WM9090_HPOUT1L_ENA | WM9090_HPOUT1R_ENA,
				    0);

		snd_soc_update_bits(codec, WM9090_CHARGE_PUMP_1,
				    WM9090_CP_ENA, 0);
		break;
	}

	return 0;
}
static int wm8993_set_fll(struct snd_soc_dai *dai, int fll_id, int source,
			  unsigned int Fref, unsigned int Fout)
{
	struct snd_soc_codec *codec = dai->codec;
	struct wm8993_priv *wm8993 = codec->private_data;
	u16 reg1, reg4, reg5;
	struct _fll_div fll_div;
	int ret;

	/* Any change? */
	if (Fref == wm8993->fll_fref && Fout == wm8993->fll_fout)
		return 0;

	/* Disable the FLL */
	if (Fout == 0) {
		dev_dbg(codec->dev, "FLL disabled\n");
		wm8993->fll_fref = 0;
		wm8993->fll_fout = 0;

		reg1 = snd_soc_read(codec, WM8993_FLL_CONTROL_1);
		reg1 &= ~WM8993_FLL_ENA;
		snd_soc_write(codec, WM8993_FLL_CONTROL_1, reg1);

		return 0;
	}

	ret = fll_factors(&fll_div, Fref, Fout);
	if (ret != 0)
		return ret;

	reg5 = snd_soc_read(codec, WM8993_FLL_CONTROL_5);
	reg5 &= ~WM8993_FLL_CLK_SRC_MASK;

	switch (fll_id) {
	case WM8993_FLL_MCLK:
		break;

	case WM8993_FLL_LRCLK:
		reg5 |= 1;
		break;

	case WM8993_FLL_BCLK:
		reg5 |= 2;
		break;

	default:
		dev_err(codec->dev, "Unknown FLL ID %d\n", fll_id);
		return -EINVAL;
	}

	/* Any FLL configuration change requires that the FLL be
	 * disabled first. */
	reg1 = snd_soc_read(codec, WM8993_FLL_CONTROL_1);
	reg1 &= ~WM8993_FLL_ENA;
	snd_soc_write(codec, WM8993_FLL_CONTROL_1, reg1);

	/* Apply the configuration */
	if (fll_div.k)
		reg1 |= WM8993_FLL_FRAC_MASK;
	else
		reg1 &= ~WM8993_FLL_FRAC_MASK;
	snd_soc_write(codec, WM8993_FLL_CONTROL_1, reg1);

	snd_soc_write(codec, WM8993_FLL_CONTROL_2,
		      (fll_div.fll_outdiv << WM8993_FLL_OUTDIV_SHIFT) |
		      (fll_div.fll_fratio << WM8993_FLL_FRATIO_SHIFT));
	snd_soc_write(codec, WM8993_FLL_CONTROL_3, fll_div.k);

	reg4 = snd_soc_read(codec, WM8993_FLL_CONTROL_4);
	reg4 &= ~WM8993_FLL_N_MASK;
	reg4 |= fll_div.n << WM8993_FLL_N_SHIFT;
	snd_soc_write(codec, WM8993_FLL_CONTROL_4, reg4);

	reg5 &= ~WM8993_FLL_CLK_REF_DIV_MASK;
	reg5 |= fll_div.fll_clk_ref_div << WM8993_FLL_CLK_REF_DIV_SHIFT;
	snd_soc_write(codec, WM8993_FLL_CONTROL_5, reg5);

	/* Enable the FLL */
	snd_soc_write(codec, WM8993_FLL_CONTROL_1, reg1 | WM8993_FLL_ENA);

	dev_dbg(codec->dev, "FLL enabled at %dHz->%dHz\n", Fref, Fout);

	wm8993->fll_fref = Fref;
	wm8993->fll_fout = Fout;
	wm8993->fll_src = source;

	return 0;
}
static int wm8961_hp_event(struct snd_soc_dapm_widget *w,
			   struct snd_kcontrol *kcontrol, int event)
{
	struct snd_soc_codec *codec = w->codec;
	u16 hp_reg = snd_soc_read(codec, WM8961_ANALOGUE_HP_0);
	u16 cp_reg = snd_soc_read(codec, WM8961_CHARGE_PUMP_1);
	u16 pwr_reg = snd_soc_read(codec, WM8961_PWR_MGMT_2);
	u16 dcs_reg = snd_soc_read(codec, WM8961_DC_SERVO_1);
	int timeout = 500;

	if (event & SND_SOC_DAPM_POST_PMU) {
		
		hp_reg &= ~(WM8961_HPR_RMV_SHORT | WM8961_HPL_RMV_SHORT);
		snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);

		
		cp_reg |= WM8961_CP_ENA;
		snd_soc_write(codec, WM8961_CHARGE_PUMP_1, cp_reg);
		mdelay(5);

		
		pwr_reg |= WM8961_LOUT1_PGA | WM8961_ROUT1_PGA;
		snd_soc_write(codec, WM8961_PWR_MGMT_2, pwr_reg);

		
		hp_reg |= WM8961_HPR_ENA | WM8961_HPL_ENA;
		snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);

		
		hp_reg |= WM8961_HPR_ENA_DLY | WM8961_HPL_ENA_DLY;
		snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);

		
		dcs_reg |=
			WM8961_DCS_ENA_CHAN_HPR | WM8961_DCS_TRIG_STARTUP_HPR |
			WM8961_DCS_ENA_CHAN_HPL | WM8961_DCS_TRIG_STARTUP_HPL;
		dev_dbg(codec->dev, "Enabling DC servo\n");

		snd_soc_write(codec, WM8961_DC_SERVO_1, dcs_reg);
		do {
			msleep(1);
			dcs_reg = snd_soc_read(codec, WM8961_DC_SERVO_1);
		} while (--timeout &&
			 dcs_reg & (WM8961_DCS_TRIG_STARTUP_HPR |
				WM8961_DCS_TRIG_STARTUP_HPL));
		if (dcs_reg & (WM8961_DCS_TRIG_STARTUP_HPR |
			       WM8961_DCS_TRIG_STARTUP_HPL))
			dev_err(codec->dev, "DC servo timed out\n");
		else
			dev_dbg(codec->dev, "DC servo startup complete\n");

		
		hp_reg |= WM8961_HPR_ENA_OUTP | WM8961_HPL_ENA_OUTP;
		snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);

		
		hp_reg |= WM8961_HPR_RMV_SHORT | WM8961_HPL_RMV_SHORT;
		snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
	}

	if (event & SND_SOC_DAPM_PRE_PMD) {
		
		hp_reg &= ~(WM8961_HPR_RMV_SHORT | WM8961_HPL_RMV_SHORT);
		snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);

		
		hp_reg &= ~(WM8961_HPR_ENA_OUTP | WM8961_HPL_ENA_OUTP);
		snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);

		
		dcs_reg &= ~(WM8961_DCS_ENA_CHAN_HPR |
			     WM8961_DCS_ENA_CHAN_HPL);
		snd_soc_write(codec, WM8961_DC_SERVO_1, dcs_reg);

		
		hp_reg &= ~(WM8961_HPR_ENA_DLY | WM8961_HPR_ENA |
			    WM8961_HPL_ENA_DLY | WM8961_HPL_ENA);
		snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);

		
		pwr_reg &= ~(WM8961_LOUT1_PGA | WM8961_ROUT1_PGA);
		snd_soc_write(codec, WM8961_PWR_MGMT_2, pwr_reg);

		
		dev_dbg(codec->dev, "Disabling charge pump\n");
		snd_soc_write(codec, WM8961_CHARGE_PUMP_1,
			     cp_reg & ~WM8961_CP_ENA);
	}

	return 0;
}
Beispiel #28
0
/*
 * Startup calibration of the DC servo
 */
static void calibrate_dc_servo(struct snd_soc_codec *codec)
{
	struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
	s8 offset;
	u16 reg, reg_l, reg_r, dcs_cfg;

	/* If we're using a digital only path and have a previously
	 * callibrated DC servo offset stored then use that. */
	if (hubs->class_w && hubs->class_w_dcs) {
		dev_dbg(codec->dev, "Using cached DC servo offset %x\n",
			hubs->class_w_dcs);
		snd_soc_write(codec, WM8993_DC_SERVO_3, hubs->class_w_dcs);
		wait_for_dc_servo(codec,
				  WM8993_DCS_TRIG_DAC_WR_0 |
				  WM8993_DCS_TRIG_DAC_WR_1);
		return;
	}

	if (hubs->series_startup) {
		/* Set for 32 series updates */
		snd_soc_update_bits(codec, WM8993_DC_SERVO_1,
				    WM8993_DCS_SERIES_NO_01_MASK,
				    32 << WM8993_DCS_SERIES_NO_01_SHIFT);
		wait_for_dc_servo(codec,
				  WM8993_DCS_TRIG_SERIES_0 |
				  WM8993_DCS_TRIG_SERIES_1);
	} else {
		wait_for_dc_servo(codec,
				  WM8993_DCS_TRIG_STARTUP_0 |
				  WM8993_DCS_TRIG_STARTUP_1);
	}

	/* Different chips in the family support different readback
	 * methods.
	 */
	switch (hubs->dcs_readback_mode) {
	case 0:
		reg_l = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_1)
			& WM8993_DCS_INTEG_CHAN_0_MASK;
		reg_r = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_2)
			& WM8993_DCS_INTEG_CHAN_1_MASK;
		break;
	case 1:
		reg = snd_soc_read(codec, WM8993_DC_SERVO_3);
		reg_r = (reg & WM8993_DCS_DAC_WR_VAL_1_MASK)
			>> WM8993_DCS_DAC_WR_VAL_1_SHIFT;
		reg_l = reg & WM8993_DCS_DAC_WR_VAL_0_MASK;
		break;
	default:
		WARN(1, "Unknown DCS readback method\n");
		break;
	}

	dev_dbg(codec->dev, "DCS input: %x %x\n", reg_l, reg_r);

	/* Apply correction to DC servo result */
	if (hubs->dcs_codes) {
		dev_dbg(codec->dev, "Applying %d code DC servo correction\n",
			hubs->dcs_codes);

		/* HPOUT1R */
		offset = reg_r;
		offset += hubs->dcs_codes;
		dcs_cfg = (u8)offset << WM8993_DCS_DAC_WR_VAL_1_SHIFT;

		/* HPOUT1L */
		offset = reg_l;
		offset += hubs->dcs_codes;
		dcs_cfg |= (u8)offset;

		dev_dbg(codec->dev, "DCS result: %x\n", dcs_cfg);

		/* Do it */
		snd_soc_write(codec, WM8993_DC_SERVO_3, dcs_cfg);
		wait_for_dc_servo(codec,
				  WM8993_DCS_TRIG_DAC_WR_0 |
				  WM8993_DCS_TRIG_DAC_WR_1);
	} else {
		dcs_cfg = reg_r << WM8993_DCS_DAC_WR_VAL_1_SHIFT;
		dcs_cfg |= reg_l;
	}

	/* Save the callibrated offset if we're in class W mode and
	 * therefore don't have any analogue signal mixed in. */
	if (hubs->class_w)
		hubs->class_w_dcs = dcs_cfg;
}
static int wm8961_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
	struct snd_soc_codec *codec = dai->codec;
	u16 aif = snd_soc_read(codec, WM8961_AUDIO_INTERFACE_0);

	aif &= ~(WM8961_BCLKINV | WM8961_LRP |
		 WM8961_MS | WM8961_FORMAT_MASK);

	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
	case SND_SOC_DAIFMT_CBM_CFM:
		aif |= WM8961_MS;
		break;
	case SND_SOC_DAIFMT_CBS_CFS:
		break;
	default:
		return -EINVAL;
	}

	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
	case SND_SOC_DAIFMT_RIGHT_J:
		break;

	case SND_SOC_DAIFMT_LEFT_J:
		aif |= 1;
		break;

	case SND_SOC_DAIFMT_I2S:
		aif |= 2;
		break;

	case SND_SOC_DAIFMT_DSP_B:
		aif |= WM8961_LRP;
	case SND_SOC_DAIFMT_DSP_A:
		aif |= 3;
		switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
		case SND_SOC_DAIFMT_NB_NF:
		case SND_SOC_DAIFMT_IB_NF:
			break;
		default:
			return -EINVAL;
		}
		break;

	default:
		return -EINVAL;
	}

	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
	case SND_SOC_DAIFMT_NB_NF:
		break;
	case SND_SOC_DAIFMT_NB_IF:
		aif |= WM8961_LRP;
		break;
	case SND_SOC_DAIFMT_IB_NF:
		aif |= WM8961_BCLKINV;
		break;
	case SND_SOC_DAIFMT_IB_IF:
		aif |= WM8961_BCLKINV | WM8961_LRP;
		break;
	default:
		return -EINVAL;
	}

	return snd_soc_write(codec, WM8961_AUDIO_INTERFACE_0, aif);
}
Beispiel #30
0
static int wm9090_i2c_probe(struct i2c_client *i2c,
			    const struct i2c_device_id *id)
{
	struct wm9090_priv *wm9090;
	struct snd_soc_codec *codec;
	int ret;

	wm9090 = kzalloc(sizeof(*wm9090), GFP_KERNEL);
	if (wm9090 == NULL) {
		dev_err(&i2c->dev, "Can not allocate memory\n");
		return -ENOMEM;
	}
	codec = &wm9090->codec;

	if (i2c->dev.platform_data)
		memcpy(&wm9090->pdata, i2c->dev.platform_data,
		       sizeof(wm9090->pdata));

	wm9090_codec = codec;

	i2c_set_clientdata(i2c, wm9090);

	mutex_init(&codec->mutex);
	INIT_LIST_HEAD(&codec->dapm_widgets);
	INIT_LIST_HEAD(&codec->dapm_paths);

	codec->control_data = i2c;
	snd_soc_codec_set_drvdata(codec, wm9090);
	codec->dev = &i2c->dev;
	codec->name = "WM9090";
	codec->owner = THIS_MODULE;
	codec->bias_level = SND_SOC_BIAS_OFF;
	codec->set_bias_level = wm9090_set_bias_level,
	codec->reg_cache_size = WM9090_MAX_REGISTER + 1;
	codec->reg_cache = &wm9090->reg_cache;
	codec->volatile_register = wm9090_volatile;

	ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C);
	if (ret != 0) {
		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
		goto err;
	}

	memcpy(&wm9090->reg_cache, wm9090_reg_defaults,
	       sizeof(wm9090->reg_cache));

	ret = snd_soc_read(codec, WM9090_SOFTWARE_RESET);
	if (ret < 0)
		goto err;
	if (ret != wm9090_reg_defaults[WM9090_SOFTWARE_RESET]) {
		dev_err(&i2c->dev, "Device is not a WM9090, ID=%x\n", ret);
		ret = -EINVAL;
		goto err;
	}

	ret = snd_soc_write(codec, WM9090_SOFTWARE_RESET, 0);
	if (ret < 0)
		goto err;

	/* Configure some defaults; they will be written out when we
	 * bring the bias up.
	 */
	wm9090->reg_cache[WM9090_IN1_LINE_INPUT_A_VOLUME] |= WM9090_IN1_VU
		| WM9090_IN1A_ZC;
	wm9090->reg_cache[WM9090_IN1_LINE_INPUT_B_VOLUME] |= WM9090_IN1_VU
		| WM9090_IN1B_ZC;
	wm9090->reg_cache[WM9090_IN2_LINE_INPUT_A_VOLUME] |= WM9090_IN2_VU
		| WM9090_IN2A_ZC;
	wm9090->reg_cache[WM9090_IN2_LINE_INPUT_B_VOLUME] |= WM9090_IN2_VU
		| WM9090_IN2B_ZC;
	wm9090->reg_cache[WM9090_SPEAKER_VOLUME_LEFT] |=
		WM9090_SPKOUT_VU | WM9090_SPKOUTL_ZC;
	wm9090->reg_cache[WM9090_LEFT_OUTPUT_VOLUME] |=
		WM9090_HPOUT1_VU | WM9090_HPOUT1L_ZC;
	wm9090->reg_cache[WM9090_RIGHT_OUTPUT_VOLUME] |=
		WM9090_HPOUT1_VU | WM9090_HPOUT1R_ZC;

	wm9090->reg_cache[WM9090_CLOCKING_1] |= WM9090_TOCLK_ENA;

	wm9090_set_bias_level(codec, SND_SOC_BIAS_STANDBY);

	ret = snd_soc_register_codec(codec);
	if (ret != 0) {
		dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret);
		goto err_bias;
	}

	return 0;

err_bias:
	wm9090_set_bias_level(codec, SND_SOC_BIAS_OFF);
err:
	kfree(wm9090);
	i2c_set_clientdata(i2c, NULL);
	wm9090_codec = NULL;

	return ret;
}