📄 wm9712.c
字号:
{"Phone Mixer", "Mic 1 Sidetone Switch", "Mic PGA"}, {"Phone Mixer", "Mic 2 Sidetone Switch", "Mic PGA"}, /* inputs */ {"Line PGA", NULL, "LINEINL"}, {"Line PGA", NULL, "LINEINR"}, {"Phone PGA", NULL, "PHONE"}, {"Mic PGA", NULL, "MIC1"}, {"Mic PGA", NULL, "MIC2"}, /* left capture selector */ {"Left Capture Select", "Mic", "MIC1"}, {"Left Capture Select", "Speaker Mixer", "Speaker Mixer"}, {"Left Capture Select", "Line", "LINEINL"}, {"Left Capture Select", "Headphone Mixer", "Left HP Mixer"}, {"Left Capture Select", "Phone Mixer", "Phone Mixer"}, {"Left Capture Select", "Phone", "PHONE"}, /* right capture selector */ {"Right Capture Select", "Mic", "MIC2"}, {"Right Capture Select", "Speaker Mixer", "Speaker Mixer"}, {"Right Capture Select", "Line", "LINEINR"}, {"Right Capture Select", "Headphone Mixer", "Right HP Mixer"}, {"Right Capture Select", "Phone Mixer", "Phone Mixer"}, {"Right Capture Select", "Phone", "PHONE"}, /* ALC Sidetone */ {"ALC Sidetone Mux", "Stereo", "Left Capture Select"}, {"ALC Sidetone Mux", "Stereo", "Right Capture Select"}, {"ALC Sidetone Mux", "Left", "Left Capture Select"}, {"ALC Sidetone Mux", "Right", "Right Capture Select"}, /* ADC's */ {"Left ADC", NULL, "Left Capture Select"}, {"Right ADC", NULL, "Right Capture Select"}, /* outputs */ {"MONOOUT", NULL, "Phone Mixer"}, {"HPOUTL", NULL, "Headphone PGA"}, {"Headphone PGA", NULL, "Left HP Mixer"}, {"HPOUTR", NULL, "Headphone PGA"}, {"Headphone PGA", NULL, "Right HP Mixer"}, /* mono hp mixer */ {"Mono HP Mixer", NULL, "Left HP Mixer"}, {"Mono HP Mixer", NULL, "Right HP Mixer"}, /* Out3 Mux */ {"Out3 Mux", "Left", "Left HP Mixer"}, {"Out3 Mux", "Mono", "Phone Mixer"}, {"Out3 Mux", "Left + Right", "Mono HP Mixer"}, {"Out 3 PGA", NULL, "Out3 Mux"}, {"OUT3", NULL, "Out 3 PGA"}, /* speaker Mux */ {"Speaker Mux", "Speaker Mix", "Speaker Mixer"}, {"Speaker Mux", "Headphone Mix", "Mono HP Mixer"}, {"Speaker PGA", NULL, "Speaker Mux"}, {"LOUT2", NULL, "Speaker PGA"}, {"ROUT2", NULL, "Speaker PGA"}, {NULL, NULL, NULL},};static int wm9712_add_widgets(struct snd_soc_codec *codec){ int i; for(i = 0; i < ARRAY_SIZE(wm9712_dapm_widgets); i++) { snd_soc_dapm_new_control(codec, &wm9712_dapm_widgets[i]); } /* set up audio path audio_mapnects */ for(i = 0; audio_map[i][0] != NULL; i++) { snd_soc_dapm_connect_input(codec, audio_map[i][0], audio_map[i][1], audio_map[i][2]); } snd_soc_dapm_new_widgets(codec); return 0;}static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg){ u16 *cache = codec->reg_cache; if (reg == AC97_RESET || reg == AC97_GPIO_STATUS || reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 || reg == AC97_REC_GAIN) return soc_ac97_ops.read(codec->ac97, reg); else { reg = reg >> 1; if (reg > (ARRAY_SIZE(wm9712_reg))) return -EIO; return cache[reg]; }}static int ac97_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int val){ u16 *cache = codec->reg_cache; soc_ac97_ops.write(codec->ac97, reg, val); reg = reg >> 1; if (reg <= (ARRAY_SIZE(wm9712_reg))) cache[reg] = val; return 0;}static int ac97_prepare(struct snd_pcm_substream *substream){ struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_device *socdev = rtd->socdev; struct snd_soc_codec *codec = socdev->codec; int reg; u16 vra; vra = ac97_read(codec, AC97_EXTENDED_STATUS); ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) reg = AC97_PCM_FRONT_DAC_RATE; else reg = AC97_PCM_LR_ADC_RATE; return ac97_write(codec, reg, runtime->rate);}static int ac97_aux_prepare(struct snd_pcm_substream *substream){ struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_device *socdev = rtd->socdev; struct snd_soc_codec *codec = socdev->codec; u16 vra, xsle; vra = ac97_read(codec, AC97_EXTENDED_STATUS); ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1); xsle = ac97_read(codec, AC97_PCI_SID); ac97_write(codec, AC97_PCI_SID, xsle | 0x8000); if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) return -ENODEV; return ac97_write(codec, AC97_PCM_SURR_DAC_RATE, runtime->rate);}#define WM9712_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)struct snd_soc_codec_dai wm9712_dai[] = {{ .name = "AC97 HiFi", .type = SND_SOC_DAI_AC97_BUS, .playback = { .stream_name = "HiFi Playback", .channels_min = 1, .channels_max = 2, .rates = WM9712_AC97_RATES, .formats = SNDRV_PCM_FMTBIT_S16_LE,}, .capture = { .stream_name = "HiFi Capture", .channels_min = 1, .channels_max = 2, .rates = WM9712_AC97_RATES, .formats = SNDRV_PCM_FMTBIT_S16_LE,}, .ops = { .prepare = ac97_prepare,},},{ .name = "AC97 Aux", .playback = { .stream_name = "Aux Playback", .channels_min = 1, .channels_max = 1, .rates = WM9712_AC97_RATES, .formats = SNDRV_PCM_FMTBIT_S16_LE,}, .ops = { .prepare = ac97_aux_prepare,},}};EXPORT_SYMBOL_GPL(wm9712_dai);static int wm9712_dapm_event(struct snd_soc_codec *codec, int event){ u16 reg; switch (event) { case SNDRV_CTL_POWER_D0: /* full On */ /* liam - maybe enable thermal shutdown */ reg = ac97_read(codec, AC97_EXTENDED_MID) & 0xdfff; ac97_write(codec, AC97_EXTENDED_MID, reg); break; case SNDRV_CTL_POWER_D1: /* partial On */ case SNDRV_CTL_POWER_D2: /* partial On */ break; case SNDRV_CTL_POWER_D3hot: /* Off, with power */ /* enable master bias and vmid */ reg = ac97_read(codec, AC97_EXTENDED_MID) & 0xbbff; ac97_write(codec, AC97_EXTENDED_MID, reg); ac97_write(codec, AC97_POWERDOWN, 0x0000); break; case SNDRV_CTL_POWER_D3cold: /* Off, without power */ /* disable everything including AC link */ ac97_write(codec, AC97_EXTENDED_MID, 0xffff); ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff); ac97_write(codec, AC97_POWERDOWN, 0xffff); break; } codec->dapm_state = event; return 0;}static int wm9712_reset(struct snd_soc_codec *codec, int try_warm){ if (try_warm && soc_ac97_ops.warm_reset) { soc_ac97_ops.warm_reset(codec->ac97); if (!(ac97_read(codec, 0) & 0x8000)) return 1; } soc_ac97_ops.reset(codec->ac97); if (ac97_read(codec, 0) & 0x8000) goto err; return 0;err: printk(KERN_ERR "WM9712 AC97 reset failed\n"); return -EIO;}static int wm9712_soc_suspend(struct platform_device *pdev, pm_message_t state){ struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct snd_soc_codec *codec = socdev->codec; wm9712_dapm_event(codec, SNDRV_CTL_POWER_D3cold); return 0;}static int wm9712_soc_resume(struct platform_device *pdev){ struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct snd_soc_codec *codec = socdev->codec; int i, ret; u16 *cache = codec->reg_cache; ret = wm9712_reset(codec, 1); if (ret < 0){ printk(KERN_ERR "could not reset AC97 codec\n"); return ret; } wm9712_dapm_event(codec, SNDRV_CTL_POWER_D3hot); if (ret == 0) { /* Sync reg_cache with the hardware after cold reset */ for (i = 2; i < ARRAY_SIZE(wm9712_reg) << 1; i+=2) { if (i == AC97_INT_PAGING || i == AC97_POWERDOWN || (i > 0x58 && i != 0x5c)) continue; soc_ac97_ops.write(codec->ac97, i, cache[i>>1]); } } if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0) wm9712_dapm_event(codec, SNDRV_CTL_POWER_D0); return ret;}static int wm9712_soc_probe(struct platform_device *pdev){ struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct snd_soc_codec *codec; int ret = 0; printk(KERN_INFO "WM9711/WM9712 SoC Audio Codec %s\n", WM9712_VERSION); socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); if (socdev->codec == NULL) return -ENOMEM; codec = socdev->codec; mutex_init(&codec->mutex); codec->reg_cache = kmemdup(wm9712_reg, sizeof(wm9712_reg), GFP_KERNEL); if (codec->reg_cache == NULL) { ret = -ENOMEM; goto cache_err; } codec->reg_cache_size = sizeof(wm9712_reg); codec->reg_cache_step = 2; codec->name = "WM9712"; codec->owner = THIS_MODULE; codec->dai = wm9712_dai; codec->num_dai = ARRAY_SIZE(wm9712_dai); codec->write = ac97_write; codec->read = ac97_read; codec->dapm_event = wm9712_dapm_event; INIT_LIST_HEAD(&codec->dapm_widgets); INIT_LIST_HEAD(&codec->dapm_paths); ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0); if (ret < 0) { printk(KERN_ERR "wm9712: failed to register AC97 codec\n"); goto codec_err; } /* register pcms */ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); if (ret < 0) goto pcm_err; ret = wm9712_reset(codec, 0); if (ret < 0) { printk(KERN_ERR "AC97 link error\n"); goto reset_err; } /* set alc mux to none */ ac97_write(codec, AC97_VIDEO, ac97_read(codec, AC97_VIDEO) | 0x3000); wm9712_dapm_event(codec, SNDRV_CTL_POWER_D3hot); wm9712_add_controls(codec); wm9712_add_widgets(codec); ret = snd_soc_register_card(socdev); if (ret < 0) { printk(KERN_ERR "wm9712: failed to register card\n"); goto reset_err; } return 0;reset_err: snd_soc_free_pcms(socdev);pcm_err: snd_soc_free_ac97_codec(codec);codec_err: kfree(codec->reg_cache);cache_err: kfree(socdev->codec); socdev->codec = NULL; return ret;}static int wm9712_soc_remove(struct platform_device *pdev){ struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct snd_soc_codec *codec = socdev->codec; if (codec == NULL) return 0; snd_soc_dapm_free(socdev); snd_soc_free_pcms(socdev); snd_soc_free_ac97_codec(codec); kfree(codec->reg_cache); kfree(codec); return 0;}struct snd_soc_codec_device soc_codec_dev_wm9712 = { .probe = wm9712_soc_probe, .remove = wm9712_soc_remove, .suspend = wm9712_soc_suspend, .resume = wm9712_soc_resume,};EXPORT_SYMBOL_GPL(soc_codec_dev_wm9712);MODULE_DESCRIPTION("ASoC WM9711/WM9712 driver");MODULE_AUTHOR("Liam Girdwood");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -