📄 wm8753.c
字号:
voice |= 0x0003; break; case SND_SOC_DAIFMT_DSP_B: voice |= 0x0013; break; default: return -EINVAL; } wm8753_write(codec, WM8753_PCM, voice); return 0;}/* * Set PCM DAI bit size and sample rate. */static int wm8753_pcm_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 wm8753_priv *wm8753 = codec->private_data; u16 voice = wm8753_read_reg_cache(codec, WM8753_PCM) & 0x01f3; u16 srate = wm8753_read_reg_cache(codec, WM8753_SRATE1) & 0x017f; /* bit size */ switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: break; case SNDRV_PCM_FORMAT_S20_3LE: voice |= 0x0004; break; case SNDRV_PCM_FORMAT_S24_LE: voice |= 0x0008; break; case SNDRV_PCM_FORMAT_S32_LE: voice |= 0x000c; break; } /* sample rate */ if (params_rate(params) * 384 == wm8753->pcmclk) srate |= 0x80; wm8753_write(codec, WM8753_SRATE1, srate); wm8753_write(codec, WM8753_PCM, voice); return 0;}/* * Set's PCM dai fmt and BCLK. */static int wm8753_pcm_set_dai_fmt(struct snd_soc_codec_dai *codec_dai, unsigned int fmt){ struct snd_soc_codec *codec = codec_dai->codec; u16 voice, ioctl; voice = wm8753_read_reg_cache(codec, WM8753_PCM) & 0x011f; ioctl = wm8753_read_reg_cache(codec, WM8753_IOCTL) & 0x015d; /* set master/slave audio interface */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBS_CFS: break; case SND_SOC_DAIFMT_CBM_CFM: ioctl |= 0x2; case SND_SOC_DAIFMT_CBM_CFS: voice |= 0x0040; break; default: return -EINVAL; } /* clock inversion */ 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: voice |= 0x0080; break; default: return -EINVAL; } break; case SND_SOC_DAIFMT_I2S: case SND_SOC_DAIFMT_RIGHT_J: case SND_SOC_DAIFMT_LEFT_J: voice &= ~0x0010; switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: break; case SND_SOC_DAIFMT_IB_IF: voice |= 0x0090; break; case SND_SOC_DAIFMT_IB_NF: voice |= 0x0080; break; case SND_SOC_DAIFMT_NB_IF: voice |= 0x0010; break; default: return -EINVAL; } break; default: return -EINVAL; } wm8753_write(codec, WM8753_PCM, voice); wm8753_write(codec, WM8753_IOCTL, ioctl); return 0;}static int wm8753_set_dai_clkdiv(struct snd_soc_codec_dai *codec_dai, int div_id, int div){ struct snd_soc_codec *codec = codec_dai->codec; u16 reg; switch (div_id) { case WM8753_PCMDIV: reg = wm8753_read_reg_cache(codec, WM8753_CLOCK) & 0x003f; wm8753_write(codec, WM8753_CLOCK, reg | div); break; case WM8753_BCLKDIV: reg = wm8753_read_reg_cache(codec, WM8753_SRATE2) & 0x01c7; wm8753_write(codec, WM8753_SRATE2, reg | div); break; case WM8753_VXCLKDIV: reg = wm8753_read_reg_cache(codec, WM8753_SRATE2) & 0x003f; wm8753_write(codec, WM8753_SRATE2, reg | div); break; default: return -EINVAL; } return 0;}/* * Set's HiFi DAC format. */static int wm8753_hdac_set_dai_fmt(struct snd_soc_codec_dai *codec_dai, unsigned int fmt){ struct snd_soc_codec *codec = codec_dai->codec; u16 hifi = wm8753_read_reg_cache(codec, WM8753_HIFI) & 0x01e0; /* interface format */ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: hifi |= 0x0002; break; case SND_SOC_DAIFMT_RIGHT_J: break; case SND_SOC_DAIFMT_LEFT_J: hifi |= 0x0001; break; case SND_SOC_DAIFMT_DSP_A: hifi |= 0x0003; break; case SND_SOC_DAIFMT_DSP_B: hifi |= 0x0013; break; default: return -EINVAL; } wm8753_write(codec, WM8753_HIFI, hifi); return 0;}/* * Set's I2S DAI format. */static int wm8753_i2s_set_dai_fmt(struct snd_soc_codec_dai *codec_dai, unsigned int fmt){ struct snd_soc_codec *codec = codec_dai->codec; u16 ioctl, hifi; hifi = wm8753_read_reg_cache(codec, WM8753_HIFI) & 0x011f; ioctl = wm8753_read_reg_cache(codec, WM8753_IOCTL) & 0x00ae; /* set master/slave audio interface */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBS_CFS: break; case SND_SOC_DAIFMT_CBM_CFM: ioctl |= 0x1; case SND_SOC_DAIFMT_CBM_CFS: hifi |= 0x0040; break; default: return -EINVAL; } /* clock inversion */ 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: hifi |= 0x0080; break; default: return -EINVAL; } break; case SND_SOC_DAIFMT_I2S: case SND_SOC_DAIFMT_RIGHT_J: case SND_SOC_DAIFMT_LEFT_J: hifi &= ~0x0010; switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: break; case SND_SOC_DAIFMT_IB_IF: hifi |= 0x0090; break; case SND_SOC_DAIFMT_IB_NF: hifi |= 0x0080; break; case SND_SOC_DAIFMT_NB_IF: hifi |= 0x0010; break; default: return -EINVAL; } break; default: return -EINVAL; } wm8753_write(codec, WM8753_HIFI, hifi); wm8753_write(codec, WM8753_IOCTL, ioctl); return 0;}/* * Set PCM DAI bit size and sample rate. */static int wm8753_i2s_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 wm8753_priv *wm8753 = codec->private_data; u16 srate = wm8753_read_reg_cache(codec, WM8753_SRATE1) & 0x01c0; u16 hifi = wm8753_read_reg_cache(codec, WM8753_HIFI) & 0x01f3; int coeff; /* is digital filter coefficient valid ? */ coeff = get_coeff(wm8753->sysclk, params_rate(params)); if (coeff < 0) { printk(KERN_ERR "wm8753 invalid MCLK or rate\n"); return coeff; } wm8753_write(codec, WM8753_SRATE1, srate | (coeff_div[coeff].sr << 1) | coeff_div[coeff].usb); /* bit size */ switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: break; case SNDRV_PCM_FORMAT_S20_3LE: hifi |= 0x0004; break; case SNDRV_PCM_FORMAT_S24_LE: hifi |= 0x0008; break; case SNDRV_PCM_FORMAT_S32_LE: hifi |= 0x000c; break; } wm8753_write(codec, WM8753_HIFI, hifi); return 0;}static int wm8753_mode1v_set_dai_fmt(struct snd_soc_codec_dai *codec_dai, unsigned int fmt){ struct snd_soc_codec *codec = codec_dai->codec; u16 clock; /* set clk source as pcmclk */ clock = wm8753_read_reg_cache(codec, WM8753_CLOCK) & 0xfffb; wm8753_write(codec, WM8753_CLOCK, clock); if (wm8753_vdac_adc_set_dai_fmt(codec_dai, fmt) < 0) return -EINVAL; return wm8753_pcm_set_dai_fmt(codec_dai, fmt);}static int wm8753_mode1h_set_dai_fmt(struct snd_soc_codec_dai *codec_dai, unsigned int fmt){ if (wm8753_hdac_set_dai_fmt(codec_dai, fmt) < 0) return -EINVAL; return wm8753_i2s_set_dai_fmt(codec_dai, fmt);}static int wm8753_mode2_set_dai_fmt(struct snd_soc_codec_dai *codec_dai, unsigned int fmt){ struct snd_soc_codec *codec = codec_dai->codec; u16 clock; /* set clk source as pcmclk */ clock = wm8753_read_reg_cache(codec, WM8753_CLOCK) & 0xfffb; wm8753_write(codec, WM8753_CLOCK, clock); if (wm8753_vdac_adc_set_dai_fmt(codec_dai, fmt) < 0) return -EINVAL; return wm8753_i2s_set_dai_fmt(codec_dai, fmt);}static int wm8753_mode3_4_set_dai_fmt(struct snd_soc_codec_dai *codec_dai, unsigned int fmt){ struct snd_soc_codec *codec = codec_dai->codec; u16 clock; /* set clk source as mclk */ clock = wm8753_read_reg_cache(codec, WM8753_CLOCK) & 0xfffb; wm8753_write(codec, WM8753_CLOCK, clock | 0x4); if (wm8753_hdac_set_dai_fmt(codec_dai, fmt) < 0) return -EINVAL; if (wm8753_vdac_adc_set_dai_fmt(codec_dai, fmt) < 0) return -EINVAL; return wm8753_i2s_set_dai_fmt(codec_dai, fmt);}static int wm8753_mute(struct snd_soc_codec_dai *dai, int mute){ struct snd_soc_codec *codec = dai->codec; u16 mute_reg = wm8753_read_reg_cache(codec, WM8753_DAC) & 0xfff7; /* the digital mute covers the HiFi and Voice DAC's on the WM8753. * make sure we check if they are not both active when we mute */ if (mute && dai->id == 1) { if (!wm8753_dai[WM8753_DAI_VOICE].playback.active || !wm8753_dai[WM8753_DAI_HIFI].playback.active) wm8753_write(codec, WM8753_DAC, mute_reg | 0x8); } else { if (mute) wm8753_write(codec, WM8753_DAC, mute_reg | 0x8); else wm8753_write(codec, WM8753_DAC, mute_reg); } return 0;}static int wm8753_dapm_event(struct snd_soc_codec *codec, int event){ u16 pwr_reg = wm8753_read_reg_cache(codec, WM8753_PWR1) & 0xfe3e; switch (event) { case SNDRV_CTL_POWER_D0: /* full On */ /* set vmid to 50k and unmute dac */ wm8753_write(codec, WM8753_PWR1, pwr_reg | 0x00c0); break; case SNDRV_CTL_POWER_D1: /* partial On */ case SNDRV_CTL_POWER_D2: /* partial On */ /* set vmid to 5k for quick power up */ wm8753_write(codec, WM8753_PWR1, pwr_reg | 0x01c1); break; case SNDRV_CTL_POWER_D3hot: /* Off, with power */ /* mute dac and set vmid to 500k, enable VREF */ wm8753_write(codec, WM8753_PWR1, pwr_reg | 0x0141); break; case SNDRV_CTL_POWER_D3cold: /* Off, without power */ wm8753_write(codec, WM8753_PWR1, 0x0001); break; } codec->dapm_state = event; return 0;}#define WM8753_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)#define WM8753_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE)/* * The WM8753 supports upto 4 different and mutually exclusive DAI * configurations. This gives 2 PCM's available for use, hifi and voice. * NOTE: The Voice PCM cannot play or capture audio to the CPU as it's DAI * is connected between the wm8753 and a BT codec or GSM modem. * * 1. Voice over PCM DAI - HIFI DAC over HIFI DAI * 2. Voice over HIFI DAI - HIFI disabled * 3. Voice disabled - HIFI over HIFI * 4. Voice disabled - HIFI over HIFI, uses voice DAI LRC for capture */static const struct snd_soc_codec_dai wm8753_all_dai[] = {/* DAI HiFi mode 1 */{ .name = "WM8753 HiFi", .id = 1, .playback = { .stream_name = "HiFi Playback", .channels_min = 1, .channels_max = 2, .rates = WM8753_RATES, .formats = WM8753_FORMATS,}, .capture = { /* dummy for fast DAI switching */ .stream_name = "Capture", .channels_min = 1, .channels_max = 2, .rates = WM8753_RATES, .formats = WM8753_FORMATS,}, .ops = { .hw_params = wm8753_i2s_hw_params,}, .dai_ops = { .digital_mute = wm8753_mute, .set_fmt = wm8753_mode1h_set_dai_fmt, .set_clkdiv = wm8753_set_dai_clkdiv, .set_pll = wm8753_set_dai_pll, .set_sysclk = wm8753_set_dai_sysclk, },},/* DAI Voice mode 1 */{ .name = "WM8753 Voice", .id = 1, .playback = { .stream_name = "Voice Playback", .channels_min = 1, .channels_max = 1, .rates = WM8753_RATES, .formats = WM8753_FORMATS,}, .capture = { .stream_name = "Capture", .channels_min = 1, .channels_max = 2, .rates = WM8753_RATES, .formats = WM8753_FORMATS,}, .ops = { .hw_params = wm8753_pcm_hw_params,}, .dai_ops = { .digital_mute = wm8753_mute,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -