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