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

📄 wm8731.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * wm8731.c  --  WM8731 ALSA SoC Audio driver * * Copyright 2005 Openedhand Ltd. * * Author: Richard Purdie <richard@openedhand.com> * * Based on wm8753.c by Liam Girdwood * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/pm.h>#include <linux/i2c.h>#include <linux/platform_device.h>#include <sound/driver.h>#include <sound/core.h>#include <sound/pcm.h>#include <sound/pcm_params.h>#include <sound/soc.h>#include <sound/soc-dapm.h>#include <sound/initval.h>#include "wm8731.h"#define AUDIO_NAME "wm8731"#define WM8731_VERSION "0.13"/* * Debug */#define WM8731_DEBUG 0#ifdef WM8731_DEBUG#define dbg(format, arg...) \	printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)#else#define dbg(format, arg...) do {} while (0)#endif#define err(format, arg...) \	printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)#define info(format, arg...) \	printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)#define warn(format, arg...) \	printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)struct snd_soc_codec_device soc_codec_dev_wm8731;/* codec private data */struct wm8731_priv {	unsigned int sysclk;};/* * wm8731 register cache * We can't read the WM8731 register space when we are * using 2 wire for device control, so we cache them instead. * There is no point in caching the reset register */static const u16 wm8731_reg[WM8731_CACHEREGNUM] = {    0x0097, 0x0097, 0x0079, 0x0079,    0x000a, 0x0008, 0x009f, 0x000a,    0x0000, 0x0000};/* * read wm8731 register cache */static inline unsigned int wm8731_read_reg_cache(struct snd_soc_codec *codec,	unsigned int reg){	u16 *cache = codec->reg_cache;	if (reg == WM8731_RESET)		return 0;	if (reg >= WM8731_CACHEREGNUM)		return -1;	return cache[reg];}/* * write wm8731 register cache */static inline void wm8731_write_reg_cache(struct snd_soc_codec *codec,	u16 reg, unsigned int value){	u16 *cache = codec->reg_cache;	if (reg >= WM8731_CACHEREGNUM)		return;	cache[reg] = value;}/* * write to the WM8731 register space */static int wm8731_write(struct snd_soc_codec *codec, unsigned int reg,	unsigned int value){	u8 data[2];	/* data is	 *   D15..D9 WM8731 register offset	 *   D8...D0 register data	 */	data[0] = (reg << 1) | ((value >> 8) & 0x0001);	data[1] = value & 0x00ff;	wm8731_write_reg_cache (codec, reg, value);	if (codec->hw_write(codec->control_data, data, 2) == 2)		return 0;	else		return -EIO;}#define wm8731_reset(c)	wm8731_write(c, WM8731_RESET, 0)static const char *wm8731_input_select[] = {"Line In", "Mic"};static const char *wm8731_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};static const struct soc_enum wm8731_enum[] = {	SOC_ENUM_SINGLE(WM8731_APANA, 2, 2, wm8731_input_select),	SOC_ENUM_SINGLE(WM8731_APDIGI, 1, 4, wm8731_deemph),};static const struct snd_kcontrol_new wm8731_snd_controls[] = {SOC_DOUBLE_R("Master Playback Volume", WM8731_LOUT1V, WM8731_ROUT1V,	0, 127, 0),SOC_DOUBLE_R("Master Playback ZC Switch", WM8731_LOUT1V, WM8731_ROUT1V,	7, 1, 0),SOC_DOUBLE_R("Capture Volume", WM8731_LINVOL, WM8731_RINVOL, 0, 31, 0),SOC_DOUBLE_R("Line Capture Switch", WM8731_LINVOL, WM8731_RINVOL, 7, 1, 1),SOC_SINGLE("Mic Boost (+20dB)", WM8731_APANA, 0, 1, 0),SOC_SINGLE("Capture Mic Switch", WM8731_APANA, 1, 1, 1),SOC_SINGLE("Sidetone Playback Volume", WM8731_APANA, 6, 3, 1),SOC_SINGLE("ADC High Pass Filter Switch", WM8731_APDIGI, 0, 1, 1),SOC_SINGLE("Store DC Offset Switch", WM8731_APDIGI, 4, 1, 0),SOC_ENUM("Playback De-emphasis", wm8731_enum[1]),};/* add non dapm controls */static int wm8731_add_controls(struct snd_soc_codec *codec){	int err, i;	for (i = 0; i < ARRAY_SIZE(wm8731_snd_controls); i++) {		if ((err = snd_ctl_add(codec->card,				snd_soc_cnew(&wm8731_snd_controls[i],codec, NULL))) < 0)			return err;	}	return 0;}/* Output Mixer */static const struct snd_kcontrol_new wm8731_output_mixer_controls[] = {SOC_DAPM_SINGLE("Line Bypass Switch", WM8731_APANA, 3, 1, 0),SOC_DAPM_SINGLE("Mic Sidetone Switch", WM8731_APANA, 5, 1, 0),SOC_DAPM_SINGLE("HiFi Playback Switch", WM8731_APANA, 4, 1, 0),};/* Input mux */static const struct snd_kcontrol_new wm8731_input_mux_controls =SOC_DAPM_ENUM("Input Select", wm8731_enum[0]);static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = {SND_SOC_DAPM_MIXER("Output Mixer", WM8731_PWR, 4, 1,	&wm8731_output_mixer_controls[0],	ARRAY_SIZE(wm8731_output_mixer_controls)),SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8731_PWR, 3, 1),SND_SOC_DAPM_OUTPUT("LOUT"),SND_SOC_DAPM_OUTPUT("LHPOUT"),SND_SOC_DAPM_OUTPUT("ROUT"),SND_SOC_DAPM_OUTPUT("RHPOUT"),SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8731_PWR, 2, 1),SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, &wm8731_input_mux_controls),SND_SOC_DAPM_PGA("Line Input", WM8731_PWR, 0, 1, NULL, 0),SND_SOC_DAPM_MICBIAS("Mic Bias", WM8731_PWR, 1, 1),SND_SOC_DAPM_INPUT("MICIN"),SND_SOC_DAPM_INPUT("RLINEIN"),SND_SOC_DAPM_INPUT("LLINEIN"),};static const char *intercon[][3] = {	/* output mixer */	{"Output Mixer", "Line Bypass Switch", "Line Input"},	{"Output Mixer", "HiFi Playback Switch", "DAC"},	{"Output Mixer", "Mic Sidetone Switch", "Mic Bias"},	/* outputs */	{"RHPOUT", NULL, "Output Mixer"},	{"ROUT", NULL, "Output Mixer"},	{"LHPOUT", NULL, "Output Mixer"},	{"LOUT", NULL, "Output Mixer"},	/* input mux */	{"Input Mux", "Line In", "Line Input"},	{"Input Mux", "Mic", "Mic Bias"},	{"ADC", NULL, "Input Mux"},	/* inputs */	{"Line Input", NULL, "LLINEIN"},	{"Line Input", NULL, "RLINEIN"},	{"Mic Bias", NULL, "MICIN"},	/* terminator */	{NULL, NULL, NULL},};static int wm8731_add_widgets(struct snd_soc_codec *codec){	int i;	for(i = 0; i < ARRAY_SIZE(wm8731_dapm_widgets); i++) {		snd_soc_dapm_new_control(codec, &wm8731_dapm_widgets[i]);	}	/* set up audio path interconnects */	for(i = 0; intercon[i][0] != NULL; i++) {		snd_soc_dapm_connect_input(codec, intercon[i][0],			intercon[i][1], intercon[i][2]);	}	snd_soc_dapm_new_widgets(codec);	return 0;}struct _coeff_div {	u32 mclk;	u32 rate;	u16 fs;	u8 sr:4;	u8 bosr:1;	u8 usb:1;};/* codec mclk clock divider coefficients */static const struct _coeff_div coeff_div[] = {	/* 48k */	{12288000, 48000, 256, 0x0, 0x0, 0x0},	{18432000, 48000, 384, 0x0, 0x1, 0x0},	{12000000, 48000, 250, 0x0, 0x0, 0x1},	/* 32k */	{12288000, 32000, 384, 0x6, 0x0, 0x0},	{18432000, 32000, 576, 0x6, 0x1, 0x0},	{12000000, 32000, 375, 0x6, 0x0, 0x1},	/* 8k */	{12288000, 8000, 1536, 0x3, 0x0, 0x0},	{18432000, 8000, 2304, 0x3, 0x1, 0x0},	{11289600, 8000, 1408, 0xb, 0x0, 0x0},	{16934400, 8000, 2112, 0xb, 0x1, 0x0},	{12000000, 8000, 1500, 0x3, 0x0, 0x1},	/* 96k */	{12288000, 96000, 128, 0x7, 0x0, 0x0},	{18432000, 96000, 192, 0x7, 0x1, 0x0},	{12000000, 96000, 125, 0x7, 0x0, 0x1},	/* 44.1k */	{11289600, 44100, 256, 0x8, 0x0, 0x0},	{16934400, 44100, 384, 0x8, 0x1, 0x0},	{12000000, 44100, 272, 0x8, 0x1, 0x1},	/* 88.2k */	{11289600, 88200, 128, 0xf, 0x0, 0x0},	{16934400, 88200, 192, 0xf, 0x1, 0x0},	{12000000, 88200, 136, 0xf, 0x1, 0x1},};static inline int get_coeff(int mclk, int rate){	int i;	for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {		if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)			return i;	}	return 0;}static int wm8731_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 wm8731_priv *wm8731 = codec->private_data;	u16 iface = wm8731_read_reg_cache(codec, WM8731_IFACE) & 0xfff3;	int i = get_coeff(wm8731->sysclk, params_rate(params));	u16 srate = (coeff_div[i].sr << 2) |		(coeff_div[i].bosr << 1) | coeff_div[i].usb;	wm8731_write(codec, WM8731_SRATE, srate);	/* bit size */	switch (params_format(params)) {	case SNDRV_PCM_FORMAT_S16_LE:		break;	case SNDRV_PCM_FORMAT_S20_3LE:		iface |= 0x0004;		break;	case SNDRV_PCM_FORMAT_S24_LE:		iface |= 0x0008;		break;	}	wm8731_write(codec, WM8731_IFACE, iface);	return 0;}static int wm8731_pcm_prepare(struct snd_pcm_substream *substream){	struct snd_soc_pcm_runtime *rtd = substream->private_data;	struct snd_soc_device *socdev = rtd->socdev;	struct snd_soc_codec *codec = socdev->codec;	/* set active */	wm8731_write(codec, WM8731_ACTIVE, 0x0001);	return 0;}static void wm8731_shutdown(struct snd_pcm_substream *substream){	struct snd_soc_pcm_runtime *rtd = substream->private_data;	struct snd_soc_device *socdev = rtd->socdev;	struct snd_soc_codec *codec = socdev->codec;	/* deactivate */	if (!codec->active) {		udelay(50);		wm8731_write(codec, WM8731_ACTIVE, 0x0);	}}static int wm8731_mute(struct snd_soc_codec_dai *dai, int mute){	struct snd_soc_codec *codec = dai->codec;	u16 mute_reg = wm8731_read_reg_cache(codec, WM8731_APDIGI) & 0xfff7;	if (mute)		wm8731_write(codec, WM8731_APDIGI, mute_reg | 0x8);	else		wm8731_write(codec, WM8731_APDIGI, mute_reg);	return 0;}static int wm8731_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,		int clk_id, unsigned int freq, int dir){	struct snd_soc_codec *codec = codec_dai->codec;	struct wm8731_priv *wm8731 = codec->private_data;	switch (freq) {	case 11289600:	case 12000000:	case 12288000:	case 16934400:	case 18432000:		wm8731->sysclk = freq;		return 0;	}	return -EINVAL;}static int wm8731_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -