⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sound_soc_codecs_wm9712.c

📁 Wolfson9712和Wolfson9705的驱动
💻 C
📖 第 1 页 / 共 2 页
字号:
	{"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 + -