Example #1
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);
	u16 reg, reg_l, reg_r, dcs_cfg;

	/* 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);

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

		/* 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_l = (reg & WM8993_DCS_DAC_WR_VAL_1_MASK)
				>> WM8993_DCS_DAC_WR_VAL_1_SHIFT;
			reg_r = reg & WM8993_DCS_DAC_WR_VAL_0_MASK;
			break;
		default:
			WARN(1, "Unknown DCS readback method");
			break;
		}

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

		/* HPOUT1L */
		if (reg_l + hubs->dcs_codes > 0 &&
		    reg_l + hubs->dcs_codes < 0xff)
			reg_l += hubs->dcs_codes;
		dcs_cfg = reg_l << WM8993_DCS_DAC_WR_VAL_1_SHIFT;

		/* HPOUT1R */
		if (reg_r + hubs->dcs_codes > 0 &&
		    reg_r + hubs->dcs_codes < 0xff)
			reg_r += hubs->dcs_codes;
		dcs_cfg |= reg_r;

		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);
	}
Example #2
0
/*
 * Startup calibration of the DC servo
 */
static void calibrate_dc_servo(struct snd_soc_codec *codec)
{
	struct wm_hubs_data *hubs = codec->private_data;
	u16 reg, dcs_cfg;

	/* 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);

	/* Enable the DC servo.  Write all bits to avoid triggering startup
	 * or write calibration.
	 */
	snd_soc_update_bits(codec, WM8993_DC_SERVO_0,
			    0xFFFF,
			    WM8993_DCS_ENA_CHAN_0 |
			    WM8993_DCS_ENA_CHAN_1 |
			    WM8993_DCS_TRIG_SERIES_1 |
			    WM8993_DCS_TRIG_SERIES_0);

	wait_for_dc_servo(codec);

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

		/* HPOUT1L */
		reg = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_1) &
			WM8993_DCS_INTEG_CHAN_0_MASK;;
		reg += hubs->dcs_codes;
		dcs_cfg = reg << WM8993_DCS_DAC_WR_VAL_1_SHIFT;

		/* HPOUT1R */
		reg = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_2) &
			WM8993_DCS_INTEG_CHAN_1_MASK;
		reg += hubs->dcs_codes;
		dcs_cfg |= reg;

		/* Do it */
		snd_soc_write(codec, WM8993_DC_SERVO_3, dcs_cfg);
		snd_soc_update_bits(codec, WM8993_DC_SERVO_0,
				    WM8993_DCS_TRIG_DAC_WR_0 |
				    WM8993_DCS_TRIG_DAC_WR_1,
				    WM8993_DCS_TRIG_DAC_WR_0 |
				    WM8993_DCS_TRIG_DAC_WR_1);

		wait_for_dc_servo(codec);
	}
}
Example #3
0
static int hp_event(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, WM8993_ANALOGUE_HP_0);

	switch (event) {
	case SND_SOC_DAPM_POST_PMU:
		snd_soc_update_bits(codec, WM8993_CHARGE_PUMP_1,
				    WM8993_CP_ENA, WM8993_CP_ENA);

		msleep(5);

		snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
				    WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA,
				    WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA);

		reg |= WM8993_HPOUT1L_DLY | WM8993_HPOUT1R_DLY;
		snd_soc_write(codec, WM8993_ANALOGUE_HP_0, reg);

		/* Start the DC servo */
		snd_soc_update_bits(codec, WM8993_DC_SERVO_0,
				    0xFFFF,
				    WM8993_DCS_ENA_CHAN_0 |
				    WM8993_DCS_ENA_CHAN_1 |
				    WM8993_DCS_TRIG_STARTUP_1 |
				    WM8993_DCS_TRIG_STARTUP_0);
		wait_for_dc_servo(codec);

		reg |= WM8993_HPOUT1R_OUTP | WM8993_HPOUT1R_RMV_SHORT |
			WM8993_HPOUT1L_OUTP | WM8993_HPOUT1L_RMV_SHORT;
		snd_soc_write(codec, WM8993_ANALOGUE_HP_0, reg);
		break;

	case SND_SOC_DAPM_PRE_PMD:
		reg &= ~(WM8993_HPOUT1L_RMV_SHORT |
			 WM8993_HPOUT1L_DLY |
			 WM8993_HPOUT1L_OUTP |
			 WM8993_HPOUT1R_RMV_SHORT |
			 WM8993_HPOUT1R_DLY |
			 WM8993_HPOUT1R_OUTP);

		snd_soc_update_bits(codec, WM8993_DC_SERVO_0,
				    0xffff, 0);

		snd_soc_write(codec, WM8993_ANALOGUE_HP_0, reg);
		snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
				    WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA,
				    0);

		snd_soc_update_bits(codec, WM8993_CHARGE_PUMP_1,
				    WM8993_CP_ENA, 0);
		break;
	}

	return 0;
}
Example #4
0
File: wm9090.c Project: 3null/linux
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;
}
Example #5
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 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, dcs_reg;

	switch (hubs->dcs_readback_mode) {
	case 2:
		dcs_reg = WM8994_DC_SERVO_4E;
		break;
	default:
		dcs_reg = WM8993_DC_SERVO_3;
		break;
	}

	/*                                                         
                                                      */
	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, dcs_reg, 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) {
		/*                           */
		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);
	}

	/*                                                         
            
  */
	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 2:
	case 1:
		reg = snd_soc_read(codec, dcs_reg);
		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");
		return;
	}

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

	/*                                     */
	if (hubs->dcs_codes_l || hubs->dcs_codes_r) {
		dev_dbg(codec->dev,
			"Applying %d/%d code DC servo correction\n",
			hubs->dcs_codes_l, hubs->dcs_codes_r);

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

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

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

		/*       */
		snd_soc_write(codec, dcs_reg, 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;
	}

	/*                                                         
                                                       */
	if (hubs->class_w && !hubs->no_cache_class_w)
		hubs->class_w_dcs = dcs_cfg;
}