static int wm8350_set_fll(struct snd_soc_dai *codec_dai, int pll_id, int source, unsigned int freq_in, unsigned int freq_out) { struct snd_soc_codec *codec = codec_dai->codec; struct wm8350 *wm8350 = codec->control_data; struct wm8350_data *priv = codec->private_data; struct _fll_div fll_div; int ret = 0; u16 fll_1, fll_4; if (freq_in == priv->fll_freq_in && freq_out == priv->fll_freq_out) return 0; /* power down FLL - we need to do this for reconfiguration */ wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_FLL_ENA | WM8350_FLL_OSC_ENA); if (freq_out == 0 || freq_in == 0) return ret; ret = fll_factors(&fll_div, freq_in, freq_out); if (ret < 0) return ret; dev_dbg(wm8350->dev, "FLL in %u FLL out %u N 0x%x K 0x%x div %d ratio %d", freq_in, freq_out, fll_div.n, fll_div.k, fll_div.div, fll_div.ratio); /* set up N.K & dividers */ fll_1 = wm8350_codec_read(codec, WM8350_FLL_CONTROL_1) & ~(WM8350_FLL_OUTDIV_MASK | WM8350_FLL_RSP_RATE_MASK | 0xc000); wm8350_codec_write(codec, WM8350_FLL_CONTROL_1, fll_1 | (fll_div.div << 8) | 0x50); wm8350_codec_write(codec, WM8350_FLL_CONTROL_2, (fll_div.ratio << 11) | (fll_div. n & WM8350_FLL_N_MASK)); wm8350_codec_write(codec, WM8350_FLL_CONTROL_3, fll_div.k); fll_4 = wm8350_codec_read(codec, WM8350_FLL_CONTROL_4) & ~(WM8350_FLL_FRAC | WM8350_FLL_SLOW_LOCK_REF); wm8350_codec_write(codec, WM8350_FLL_CONTROL_4, fll_4 | (fll_div.k ? WM8350_FLL_FRAC : 0) | (fll_div.ratio == 8 ? WM8350_FLL_SLOW_LOCK_REF : 0)); /* power FLL on */ wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_FLL_OSC_ENA); wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_FLL_ENA); priv->fll_freq_out = freq_out; priv->fll_freq_in = freq_in; return 0; }
static int wm8350_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_link *pcm_link = substream->private_data; struct snd_soc_codec *codec = pcm_link->codec; u16 iface = wm8350_codec_read(codec, WM8350_AI_FORMATING) & ~WM8350_AIF_WL_MASK; /* bit size */ switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: break; case SNDRV_PCM_FORMAT_S20_3LE: iface |= 0x1 << 10; break; case SNDRV_PCM_FORMAT_S24_LE: iface |= 0x2 << 10; break; case SNDRV_PCM_FORMAT_S32_LE: iface |= 0x3 << 10; break; } wm8350_codec_write(codec, WM8350_AI_FORMATING, iface); return 0; }
static int wm8350_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, unsigned int freq, int dir) { struct snd_soc_codec *codec = codec_dai->codec; struct wm8350* wm8350 = codec->control_data; u16 fll_4; switch(clk_id) { case WM8350_MCLK_SEL_MCLK: wm8350_clear_bits(wm8350, WM8350_CLOCK_CONTROL_1, WM8350_MCLK_SEL); break; case WM8350_MCLK_SEL_PLL_MCLK: case WM8350_MCLK_SEL_PLL_DAC: case WM8350_MCLK_SEL_PLL_ADC: case WM8350_MCLK_SEL_PLL_32K: wm8350_set_bits(wm8350, WM8350_CLOCK_CONTROL_1, WM8350_MCLK_SEL); fll_4 = wm8350_codec_read(codec, WM8350_FLL_CONTROL_4) & ~WM8350_FLL_CLK_SRC_MASK; wm8350_codec_write(codec, WM8350_FLL_CONTROL_4, fll_4 | clk_id); break; } /* MCLK direction */ if (dir == WM8350_MCLK_DIR_OUT) wm8350_set_bits(wm8350, WM8350_CLOCK_CONTROL_2, WM8350_MCLK_DIR); else wm8350_clear_bits(wm8350, WM8350_CLOCK_CONTROL_2, WM8350_MCLK_DIR); return 0; }
static int wm8350_put_volsw_2r_vu(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct wm8350_out_ramp *or = codec->private_data; struct wm8350_output *out1 = &or->out1, *out2 = &or->out2; int ret, reg = kcontrol->private_value & 0xff; u16 val; /* we cache the OUT1/2 volumes when the codec is inactive to keep * pops to a low during ramping */ if (reg == WM8350_LOUT1_VOLUME) { out1->left_vol = ucontrol->value.integer.value[0]; out1->right_vol = ucontrol->value.integer.value[1]; if (!codec->active) return 1; } else if (reg == WM8350_LOUT2_VOLUME) { out2->left_vol = ucontrol->value.integer.value[0]; out2->right_vol = ucontrol->value.integer.value[1]; if (!codec->active) return 1; } ret = snd_soc_put_volsw_2r(kcontrol, ucontrol); if (ret < 0) return ret; /* now hit the volume update bits (always bit 8) */ val = wm8350_codec_read(codec, reg); wm8350_codec_write(codec, reg, val | WM8350_OUT1_VU); return 1; }
static int wm8350_set_fll(struct snd_soc_dai *codec_dai, int pll_id, unsigned int freq_in, unsigned int freq_out) { struct snd_soc_codec *codec = codec_dai->codec; struct wm8350* wm8350 = codec->control_data; struct _fll_div fll_div; int ret = 0; u16 fll_1, fll_4; if (freq_out == 0 || freq_in == 0) { /* power down FLL */ wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_FLL_ENA | WM8350_FLL_OSC_ENA); return ret; } ret = fll_factors(&fll_div, freq_in, freq_out); if (ret < 0) return ret; /* set up N.K & dividers */ fll_1 = wm8350_codec_read(codec, WM8350_FLL_CONTROL_1) & ~(WM8350_FLL_OUTDIV_MASK | 0xc000); wm8350_codec_write(codec, WM8350_FLL_CONTROL_1, fll_1 | (fll_div.div << 8)); wm8350_codec_write(codec, WM8350_FLL_CONTROL_2, (fll_div.ratio << 11) | (fll_div.n & WM8350_FLL_N_MASK)); wm8350_codec_write(codec, WM8350_FLL_CONTROL_3, fll_div.k); fll_4 = wm8350_codec_read(codec, WM8350_FLL_CONTROL_4) & ~(WM8350_FLL_FRAC | WM8350_FLL_SLOW_LOCK_REF); wm8350_codec_write(codec, WM8350_FLL_CONTROL_4, fll_4 | (fll_div.k ? WM8350_FLL_FRAC : 0) | (fll_div.ratio == 8 ? WM8350_FLL_SLOW_LOCK_REF : 0)); /* power FLL on */ wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_FLL_OSC_ENA); /* do we need to wait here ? */ wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_FLL_ENA); return 0; }
static int wm8350_set_clkdiv(struct snd_soc_dai *codec_dai, int div_id, int div) { struct snd_soc_codec *codec = codec_dai->codec; u16 val; switch (div_id) { case WM8350_ADC_CLKDIV: val = wm8350_codec_read(codec, WM8350_ADC_DIVIDER) & ~WM8350_ADC_CLKDIV_MASK; wm8350_codec_write(codec, WM8350_ADC_DIVIDER, val | div); break; case WM8350_DAC_CLKDIV: val = wm8350_codec_read(codec, WM8350_DAC_CLOCK_CONTROL) & ~WM8350_DAC_CLKDIV_MASK; wm8350_codec_write(codec, WM8350_DAC_CLOCK_CONTROL, val | div); break; case WM8350_BCLK_CLKDIV: val = wm8350_codec_read(codec, WM8350_CLOCK_CONTROL_1) & ~WM8350_BCLK_DIV_MASK; wm8350_codec_write(codec, WM8350_CLOCK_CONTROL_1, val | div); break; case WM8350_OPCLK_CLKDIV: val = wm8350_codec_read(codec, WM8350_CLOCK_CONTROL_1) & ~WM8350_OPCLK_DIV_MASK; wm8350_codec_write(codec, WM8350_CLOCK_CONTROL_1, val | div); break; case WM8350_SYS_CLKDIV: val = wm8350_codec_read(codec, WM8350_CLOCK_CONTROL_1) & ~WM8350_MCLK_DIV_MASK; wm8350_codec_write(codec, WM8350_CLOCK_CONTROL_1, val | div); break; case WM8350_DACLR_CLKDIV: val = wm8350_codec_read(codec, WM8350_DAC_LR_RATE) & ~WM8350_DACLRC_RATE_MASK; wm8350_codec_write(codec, WM8350_DAC_LR_RATE, val | div); break; case WM8350_ADCLR_CLKDIV: val = wm8350_codec_read(codec, WM8350_ADC_LR_RATE) & ~WM8350_ADCLRC_RATE_MASK; wm8350_codec_write(codec, WM8350_ADC_LR_RATE, val | div); break; default: return -EINVAL; } return 0; }
static int wm8350_put_volsw_2r_vu(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct wm8350_data *wm8350_priv = codec->private_data; struct wm8350_output *out = NULL; struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; int ret; unsigned int reg = mc->reg; u16 val; /* For OUT1 and OUT2 we shadow the values and only actually write * them out when active in order to ensure the amplifier comes on * as quietly as possible. */ switch (reg) { case WM8350_LOUT1_VOLUME: out = &wm8350_priv->out1; break; case WM8350_LOUT2_VOLUME: out = &wm8350_priv->out2; break; default: break; } if (out) { out->left_vol = ucontrol->value.integer.value[0]; out->right_vol = ucontrol->value.integer.value[1]; if (!out->active) return 1; } ret = snd_soc_put_volsw_2r(kcontrol, ucontrol); if (ret < 0) return ret; /* now hit the volume update bits (always bit 8) */ val = wm8350_codec_read(codec, reg); wm8350_codec_write(codec, reg, val | WM8350_OUT1_VU); return 1; }
static int wm8350_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *codec_dai) { struct snd_soc_codec *codec = codec_dai->codec; struct wm8350 *wm8350 = codec->control_data; u16 iface = wm8350_codec_read(codec, WM8350_AI_FORMATING) & ~WM8350_AIF_WL_MASK; /* bit size */ switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: break; case SNDRV_PCM_FORMAT_S20_3LE: iface |= 0x1 << 10; break; case SNDRV_PCM_FORMAT_S24_LE: iface |= 0x2 << 10; break; case SNDRV_PCM_FORMAT_S32_LE: iface |= 0x3 << 10; break; } wm8350_codec_write(codec, WM8350_AI_FORMATING, iface); /* The sloping stopband filter is recommended for use with * lower sample rates to improve performance. */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { if (params_rate(params) < 24000) wm8350_set_bits(wm8350, WM8350_DAC_MUTE_VOLUME, WM8350_DAC_SB_FILT); else wm8350_clear_bits(wm8350, WM8350_DAC_MUTE_VOLUME, WM8350_DAC_SB_FILT); } return 0; }
static int wm8350_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { struct snd_soc_codec *codec = codec_dai->codec; u16 iface = wm8350_codec_read(codec, WM8350_AI_FORMATING) & ~(WM8350_AIF_BCLK_INV | WM8350_AIF_LRCLK_INV | WM8350_AIF_FMT_MASK); u16 master = wm8350_codec_read(codec, WM8350_AI_DAC_CONTROL) & ~WM8350_BCLK_MSTR; u16 dac_lrc = wm8350_codec_read(codec, WM8350_DAC_LR_RATE) & ~WM8350_DACLRC_ENA; u16 adc_lrc = wm8350_codec_read(codec, WM8350_ADC_LR_RATE) & ~WM8350_ADCLRC_ENA; /* set master/slave audio interface */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: master |= WM8350_BCLK_MSTR; dac_lrc |= WM8350_DACLRC_ENA; adc_lrc |= WM8350_ADCLRC_ENA; break; case SND_SOC_DAIFMT_CBS_CFS: break; default: return -EINVAL; } /* interface format */ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: iface |= 0x2 << 8; break; case SND_SOC_DAIFMT_RIGHT_J: break; case SND_SOC_DAIFMT_LEFT_J: iface |= 0x1 << 8; break; case SND_SOC_DAIFMT_DSP_A: iface |= 0x3 << 8; break; case SND_SOC_DAIFMT_DSP_B: iface |= 0x3 << 8; // lg not sure which mode break; default: return -EINVAL; } /* clock inversion */ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: break; case SND_SOC_DAIFMT_IB_IF: iface |= WM8350_AIF_LRCLK_INV | WM8350_AIF_BCLK_INV; break; case SND_SOC_DAIFMT_IB_NF: iface |= WM8350_AIF_BCLK_INV; break; case SND_SOC_DAIFMT_NB_IF: iface |= WM8350_AIF_LRCLK_INV; break; default: return -EINVAL; } wm8350_codec_write(codec, WM8350_AI_FORMATING, iface); wm8350_codec_write(codec, WM8350_AI_DAC_CONTROL, master); wm8350_codec_write(codec, WM8350_DAC_LR_RATE, dac_lrc); wm8350_codec_write(codec, WM8350_ADC_LR_RATE, adc_lrc); return 0; }
static int wm8350_probe(struct platform_device *pdev) { struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct snd_soc_codec *codec; struct wm8350 *wm8350; struct wm8350_data *priv; int ret; struct wm8350_output *out1; struct wm8350_output *out2; BUG_ON(!wm8350_codec); socdev->card->codec = wm8350_codec; codec = socdev->card->codec; wm8350 = codec->control_data; priv = codec->private_data; /* Enable the codec */ wm8350_set_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA); /* Enable robust clocking mode in ADC */ wm8350_codec_write(codec, WM8350_SECURITY, 0xa7); wm8350_codec_write(codec, 0xde, 0x13); wm8350_codec_write(codec, WM8350_SECURITY, 0); /* read OUT1 & OUT2 volumes */ out1 = &priv->out1; out2 = &priv->out2; out1->left_vol = (wm8350_reg_read(wm8350, WM8350_LOUT1_VOLUME) & WM8350_OUT1L_VOL_MASK) >> WM8350_OUT1L_VOL_SHIFT; out1->right_vol = (wm8350_reg_read(wm8350, WM8350_ROUT1_VOLUME) & WM8350_OUT1R_VOL_MASK) >> WM8350_OUT1R_VOL_SHIFT; out2->left_vol = (wm8350_reg_read(wm8350, WM8350_LOUT2_VOLUME) & WM8350_OUT2L_VOL_MASK) >> WM8350_OUT1L_VOL_SHIFT; out2->right_vol = (wm8350_reg_read(wm8350, WM8350_ROUT2_VOLUME) & WM8350_OUT2R_VOL_MASK) >> WM8350_OUT1R_VOL_SHIFT; wm8350_reg_write(wm8350, WM8350_LOUT1_VOLUME, 0); wm8350_reg_write(wm8350, WM8350_ROUT1_VOLUME, 0); wm8350_reg_write(wm8350, WM8350_LOUT2_VOLUME, 0); wm8350_reg_write(wm8350, WM8350_ROUT2_VOLUME, 0); /* Latch VU bits & mute */ wm8350_set_bits(wm8350, WM8350_LOUT1_VOLUME, WM8350_OUT1_VU | WM8350_OUT1L_MUTE); wm8350_set_bits(wm8350, WM8350_LOUT2_VOLUME, WM8350_OUT2_VU | WM8350_OUT2L_MUTE); wm8350_set_bits(wm8350, WM8350_ROUT1_VOLUME, WM8350_OUT1_VU | WM8350_OUT1R_MUTE); wm8350_set_bits(wm8350, WM8350_ROUT2_VOLUME, WM8350_OUT2_VU | WM8350_OUT2R_MUTE); wm8350_mask_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L); wm8350_mask_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R); wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L, wm8350_hp_jack_handler, priv); wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R, wm8350_hp_jack_handler, priv); ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); if (ret < 0) { dev_err(&pdev->dev, "failed to create pcms\n"); return ret; } snd_soc_add_controls(codec, wm8350_snd_controls, ARRAY_SIZE(wm8350_snd_controls)); wm8350_add_widgets(codec); wm8350_set_bias_level(codec, SND_SOC_BIAS_STANDBY); return 0; }
static int wm8350_codec_probe(struct snd_soc_codec *codec) { struct wm8350 *wm8350 = dev_get_platdata(codec->dev); struct wm8350_data *priv; struct wm8350_output *out1; struct wm8350_output *out2; int ret, i; if (wm8350->codec.platform_data == NULL) { dev_err(codec->dev, "No audio platform data supplied\n"); return -EINVAL; } priv = kzalloc(sizeof(struct wm8350_data), GFP_KERNEL); if (priv == NULL) return -ENOMEM; snd_soc_codec_set_drvdata(codec, priv); for (i = 0; i < ARRAY_SIZE(supply_names); i++) priv->supplies[i].supply = supply_names[i]; ret = regulator_bulk_get(wm8350->dev, ARRAY_SIZE(priv->supplies), priv->supplies); if (ret != 0) goto err_priv; wm8350->codec.codec = codec; codec->control_data = wm8350; /* Put the codec into reset if it wasn't already */ wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA); INIT_DELAYED_WORK(&codec->delayed_work, wm8350_pga_work); /* Enable the codec */ wm8350_set_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA); /* Enable robust clocking mode in ADC */ wm8350_codec_write(codec, WM8350_SECURITY, 0xa7); wm8350_codec_write(codec, 0xde, 0x13); wm8350_codec_write(codec, WM8350_SECURITY, 0); /* read OUT1 & OUT2 volumes */ out1 = &priv->out1; out2 = &priv->out2; out1->left_vol = (wm8350_reg_read(wm8350, WM8350_LOUT1_VOLUME) & WM8350_OUT1L_VOL_MASK) >> WM8350_OUT1L_VOL_SHIFT; out1->right_vol = (wm8350_reg_read(wm8350, WM8350_ROUT1_VOLUME) & WM8350_OUT1R_VOL_MASK) >> WM8350_OUT1R_VOL_SHIFT; out2->left_vol = (wm8350_reg_read(wm8350, WM8350_LOUT2_VOLUME) & WM8350_OUT2L_VOL_MASK) >> WM8350_OUT1L_VOL_SHIFT; out2->right_vol = (wm8350_reg_read(wm8350, WM8350_ROUT2_VOLUME) & WM8350_OUT2R_VOL_MASK) >> WM8350_OUT1R_VOL_SHIFT; wm8350_reg_write(wm8350, WM8350_LOUT1_VOLUME, 0); wm8350_reg_write(wm8350, WM8350_ROUT1_VOLUME, 0); wm8350_reg_write(wm8350, WM8350_LOUT2_VOLUME, 0); wm8350_reg_write(wm8350, WM8350_ROUT2_VOLUME, 0); /* Latch VU bits & mute */ wm8350_set_bits(wm8350, WM8350_LOUT1_VOLUME, WM8350_OUT1_VU | WM8350_OUT1L_MUTE); wm8350_set_bits(wm8350, WM8350_LOUT2_VOLUME, WM8350_OUT2_VU | WM8350_OUT2L_MUTE); wm8350_set_bits(wm8350, WM8350_ROUT1_VOLUME, WM8350_OUT1_VU | WM8350_OUT1R_MUTE); wm8350_set_bits(wm8350, WM8350_ROUT2_VOLUME, WM8350_OUT2_VU | WM8350_OUT2R_MUTE); /* Make sure jack detect is disabled to start off with */ wm8350_clear_bits(wm8350, WM8350_JACK_DETECT, WM8350_JDL_ENA | WM8350_JDR_ENA); wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L, wm8350_hp_jack_handler, 0, "Left jack detect", priv); wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R, wm8350_hp_jack_handler, 0, "Right jack detect", priv); wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_MICSCD, wm8350_mic_handler, 0, "Microphone short", priv); wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_MICD, wm8350_mic_handler, 0, "Microphone detect", priv); snd_soc_add_controls(codec, wm8350_snd_controls, ARRAY_SIZE(wm8350_snd_controls)); wm8350_add_widgets(codec); wm8350_set_bias_level(codec, SND_SOC_BIAS_STANDBY); return 0; err_priv: kfree(priv); return ret; }