?? sound_soc_codecs_wm9712.c
字號:
{"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 mixer */ {"Mono Mixer", NULL, "Left HP Mixer"}, {"Mono Mixer", NULL, "Right HP Mixer"}, /* Out3 Mux */ {"Out3 Mux", "Left", "Left HP Mixer"}, {"Out3 Mux", "Mono", "Phone Mixer"}, {"Out3 Mux", "Left + Right", "Mono 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 Mixer"}, {"Speaker PGA", NULL, "Speaker Mux"}, {"LOUT2", NULL, "Speaker PGA"}, {"ROUT2", NULL, "Speaker PGA"},};static int wm9712_add_widgets(struct snd_soc_codec *codec){ snd_soc_dapm_new_controls(codec, wm9712_dapm_widgets, ARRAY_SIZE(wm9712_dapm_widgets)); snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); 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_soc_dai *dai){ 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->card->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_soc_dai *dai){ 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->card->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)static struct snd_soc_dai_ops wm9712_dai_ops_hifi = { .prepare = ac97_prepare,};static struct snd_soc_dai_ops wm9712_dai_ops_aux = { .prepare = ac97_aux_prepare,};struct snd_soc_dai wm9712_dai[] = {{ .name = "AC97 HiFi", .ac97_control = 1, .playback = { .stream_name = "HiFi Playback", .channels_min = 1, .channels_max = 2, .rates = WM9712_AC97_RATES, .formats = SND_SOC_STD_AC97_FMTS,}, .capture = { .stream_name = "HiFi Capture", .channels_min = 1, .channels_max = 2, .rates = WM9712_AC97_RATES, .formats = SND_SOC_STD_AC97_FMTS,}, .ops = &wm9712_dai_ops_hifi,},{ .name = "AC97 Aux", .playback = { .stream_name = "Aux Playback", .channels_min = 1, .channels_max = 1, .rates = WM9712_AC97_RATES, .formats = SND_SOC_STD_AC97_FMTS,}, .ops = &wm9712_dai_ops_aux,}};EXPORT_SYMBOL_GPL(wm9712_dai);static int wm9712_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level){ switch (level) { case SND_SOC_BIAS_ON: case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: ac97_write(codec, AC97_POWERDOWN, 0x0000); break; case SND_SOC_BIAS_OFF: /* disable everything including AC link */ ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff); ac97_write(codec, AC97_POWERDOWN, 0xffff); break; } codec->bias_level = level; 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) == wm9712_reg[0]) return 1; } soc_ac97_ops.reset(codec->ac97); if (soc_ac97_ops.warm_reset) soc_ac97_ops.warm_reset(codec->ac97); if (ac97_read(codec, 0) != wm9712_reg[0]) 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->card->codec; wm9712_set_bias_level(codec, SND_SOC_BIAS_OFF); 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->card->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_set_bias_level(codec, SND_SOC_BIAS_STANDBY); 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_bias_level == SND_SOC_BIAS_ON) wm9712_set_bias_level(codec, SND_SOC_BIAS_ON); 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->card->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); if (socdev->card->codec == NULL) return -ENOMEM; codec = socdev->card->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->set_bias_level = wm9712_set_bias_level; 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 "Failed to reset WM9712: AC97 link error\n"); goto reset_err; } /* set alc mux to none */ ac97_write(codec, AC97_VIDEO, ac97_read(codec, AC97_VIDEO) | 0x3000); wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY); snd_soc_add_controls(codec, wm9712_snd_ac97_controls, ARRAY_SIZE(wm9712_snd_ac97_controls)); wm9712_add_widgets(codec); ret = snd_soc_init_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->card->codec); socdev->card->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->card->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 + -