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