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

📄 wm8753.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * wm8753.c  --  WM8753 ALSA Soc Audio driver * * Copyright 2003 Wolfson Microelectronics PLC. * Author: Liam Girdwood *         liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com * *  This program is free software; you can redistribute  it and/or modify it *  under  the terms of  the GNU General  Public License as published by the *  Free Software Foundation;  either version 2 of the  License, or (at your *  option) any later version. * * Notes: *  The WM8753 is a low power, high quality stereo codec with integrated PCM *  codec designed for portable digital telephony applications. * * Dual DAI:- * * This driver support 2 DAI PCM's. This makes the default PCM available for * HiFi audio (e.g. MP3, ogg) playback/capture and the other PCM available for * voice. * * Please note that the voice PCM can be connected directly to a Bluetooth * codec or GSM modem and thus cannot be read or written to, although it is * available to be configured with snd_hw_params(), etc and kcontrols in the * normal alsa manner. * * Fast DAI switching:- * * The driver can now fast switch between the DAI configurations via a * an alsa kcontrol. This allows the PCM to remain open. * */#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/version.h>#include <linux/kernel.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 <asm/div64.h>#include "wm8753.h"#define AUDIO_NAME "wm8753"#define WM8753_VERSION "0.16"/* * Debug */#define WM8753_DEBUG 0#ifdef WM8753_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)static int caps_charge = 2000;module_param(caps_charge, int, 0);MODULE_PARM_DESC(caps_charge, "WM8753 cap charge time (msecs)");static void wm8753_set_dai_mode(struct snd_soc_codec *codec,	unsigned int mode);/* codec private data */struct wm8753_priv {	unsigned int sysclk;	unsigned int pcmclk;};/* * wm8753 register cache * We can't read the WM8753 register space when we * are using 2 wire for device control, so we cache them instead. */static const u16 wm8753_reg[] = {	0x0008, 0x0000, 0x000a, 0x000a,	0x0033, 0x0000, 0x0007, 0x00ff,	0x00ff, 0x000f, 0x000f, 0x007b,	0x0000, 0x0032, 0x0000, 0x00c3,	0x00c3, 0x00c0, 0x0000, 0x0000,	0x0000, 0x0000, 0x0000, 0x0000,	0x0000, 0x0000, 0x0000, 0x0000,	0x0000, 0x0000, 0x0000, 0x0055,	0x0005, 0x0050, 0x0055, 0x0050,	0x0055, 0x0050, 0x0055, 0x0079,	0x0079, 0x0079, 0x0079, 0x0079,	0x0000, 0x0000, 0x0000, 0x0000,	0x0097, 0x0097, 0x0000, 0x0004,	0x0000, 0x0083, 0x0024, 0x01ba,	0x0000, 0x0083, 0x0024, 0x01ba,	0x0000, 0x0000};/* * read wm8753 register cache */static inline unsigned int wm8753_read_reg_cache(struct snd_soc_codec *codec,	unsigned int reg){	u16 *cache = codec->reg_cache;	if (reg < 1 || reg > (ARRAY_SIZE(wm8753_reg) + 1))		return -1;	return cache[reg - 1];}/* * write wm8753 register cache */static inline void wm8753_write_reg_cache(struct snd_soc_codec *codec,	unsigned int reg, unsigned int value){	u16 *cache = codec->reg_cache;	if (reg < 1 || reg > 0x3f)		return;	cache[reg - 1] = value;}/* * write to the WM8753 register space */static int wm8753_write(struct snd_soc_codec *codec, unsigned int reg,	unsigned int value){	u8 data[2];	/* data is	 *   D15..D9 WM8753 register offset	 *   D8...D0 register data	 */	data[0] = (reg << 1) | ((value >> 8) & 0x0001);	data[1] = value & 0x00ff;	wm8753_write_reg_cache (codec, reg, value);	if (codec->hw_write(codec->control_data, data, 2) == 2)		return 0;	else		return -EIO;}#define wm8753_reset(c) wm8753_write(c, WM8753_RESET, 0)/* * WM8753 Controls */static const char *wm8753_base[] = {"Linear Control", "Adaptive Boost"};static const char *wm8753_base_filter[] =	{"130Hz @ 48kHz", "200Hz @ 48kHz", "100Hz @ 16kHz", "400Hz @ 48kHz",	"100Hz @ 8kHz", "200Hz @ 8kHz"};static const char *wm8753_treble[] = {"8kHz", "4kHz"};static const char *wm8753_alc_func[] = {"Off", "Right", "Left", "Stereo"};static const char *wm8753_ng_type[] = {"Constant PGA Gain", "Mute ADC Output"};static const char *wm8753_3d_func[] = {"Capture", "Playback"};static const char *wm8753_3d_uc[] = {"2.2kHz", "1.5kHz"};static const char *wm8753_3d_lc[] = {"200Hz", "500Hz"};static const char *wm8753_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz"};static const char *wm8753_mono_mix[] = {"Stereo", "Left", "Right", "Mono"};static const char *wm8753_dac_phase[] = {"Non Inverted", "Inverted"};static const char *wm8753_line_mix[] = {"Line 1 + 2", "Line 1 - 2",	"Line 1", "Line 2"};static const char *wm8753_mono_mux[] = {"Line Mix", "Rx Mix"};static const char *wm8753_right_mux[] = {"Line 2", "Rx Mix"};static const char *wm8753_left_mux[] = {"Line 1", "Rx Mix"};static const char *wm8753_rxmsel[] = {"RXP - RXN", "RXP + RXN", "RXP", "RXN"};static const char *wm8753_sidetone_mux[] = {"Left PGA", "Mic 1", "Mic 2",	"Right PGA"};static const char *wm8753_mono2_src[] = {"Inverted Mono 1", "Left", "Right",	"Left + Right"};static const char *wm8753_out3[] = {"VREF", "ROUT2", "Left + Right"};static const char *wm8753_out4[] = {"VREF", "Capture ST", "LOUT2"};static const char *wm8753_radcsel[] = {"PGA", "Line or RXP-RXN", "Sidetone"};static const char *wm8753_ladcsel[] = {"PGA", "Line or RXP-RXN", "Line"};static const char *wm8753_mono_adc[] = {"Stereo", "Analogue Mix Left",	"Analogue Mix Right", "Digital Mono Mix"};static const char *wm8753_adc_hp[] = {"3.4Hz @ 48kHz", "82Hz @ 16k",	"82Hz @ 8kHz", "170Hz @ 8kHz"};static const char *wm8753_adc_filter[] = {"HiFi", "Voice"};static const char *wm8753_mic_sel[] = {"Mic 1", "Mic 2", "Mic 3"};static const char *wm8753_dai_mode[] = {"DAI 0", "DAI 1", "DAI 2", "DAI 3"};static const char *wm8753_dat_sel[] = {"Stereo", "Left ADC", "Right ADC",	"Channel Swap"};static const struct soc_enum wm8753_enum[] = {SOC_ENUM_SINGLE(WM8753_BASS, 7, 2, wm8753_base),SOC_ENUM_SINGLE(WM8753_BASS, 4, 6, wm8753_base_filter),SOC_ENUM_SINGLE(WM8753_TREBLE, 6, 2, wm8753_treble),SOC_ENUM_SINGLE(WM8753_ALC1, 7, 4, wm8753_alc_func),SOC_ENUM_SINGLE(WM8753_NGATE, 1, 2, wm8753_ng_type),SOC_ENUM_SINGLE(WM8753_3D, 7, 2, wm8753_3d_func),SOC_ENUM_SINGLE(WM8753_3D, 6, 2, wm8753_3d_uc),SOC_ENUM_SINGLE(WM8753_3D, 5, 2, wm8753_3d_lc),SOC_ENUM_SINGLE(WM8753_DAC, 1, 4, wm8753_deemp),SOC_ENUM_SINGLE(WM8753_DAC, 4, 4, wm8753_mono_mix),SOC_ENUM_SINGLE(WM8753_DAC, 6, 2, wm8753_dac_phase),SOC_ENUM_SINGLE(WM8753_INCTL1, 3, 4, wm8753_line_mix),SOC_ENUM_SINGLE(WM8753_INCTL1, 2, 2, wm8753_mono_mux),SOC_ENUM_SINGLE(WM8753_INCTL1, 1, 2, wm8753_right_mux),SOC_ENUM_SINGLE(WM8753_INCTL1, 0, 2, wm8753_left_mux),SOC_ENUM_SINGLE(WM8753_INCTL2, 6, 4, wm8753_rxmsel),SOC_ENUM_SINGLE(WM8753_INCTL2, 4, 4, wm8753_sidetone_mux),SOC_ENUM_SINGLE(WM8753_OUTCTL, 7, 4, wm8753_mono2_src),SOC_ENUM_SINGLE(WM8753_OUTCTL, 0, 3, wm8753_out3),SOC_ENUM_SINGLE(WM8753_ADCTL2, 7, 3, wm8753_out4),SOC_ENUM_SINGLE(WM8753_ADCIN, 2, 3, wm8753_radcsel),SOC_ENUM_SINGLE(WM8753_ADCIN, 0, 3, wm8753_ladcsel),SOC_ENUM_SINGLE(WM8753_ADCIN, 4, 4, wm8753_mono_adc),SOC_ENUM_SINGLE(WM8753_ADC, 2, 4, wm8753_adc_hp),SOC_ENUM_SINGLE(WM8753_ADC, 4, 2, wm8753_adc_filter),SOC_ENUM_SINGLE(WM8753_MICBIAS, 6, 3, wm8753_mic_sel),SOC_ENUM_SINGLE(WM8753_IOCTL, 2, 4, wm8753_dai_mode),SOC_ENUM_SINGLE(WM8753_ADC, 7, 4, wm8753_dat_sel),};static int wm8753_get_dai(struct snd_kcontrol *kcontrol,	struct snd_ctl_elem_value *ucontrol){	struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);	int mode = wm8753_read_reg_cache(codec, WM8753_IOCTL);	ucontrol->value.integer.value[0] = (mode & 0xc) >> 2;	return 0;}static int wm8753_set_dai(struct snd_kcontrol *kcontrol,	struct snd_ctl_elem_value *ucontrol){	struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);	int mode = wm8753_read_reg_cache(codec, WM8753_IOCTL);	if (((mode &0xc) >> 2) == ucontrol->value.integer.value[0])		return 0;	mode &= 0xfff3;	mode |= (ucontrol->value.integer.value[0] << 2);	wm8753_write(codec, WM8753_IOCTL, mode);	wm8753_set_dai_mode(codec, ucontrol->value.integer.value[0]);	return 1;}static const struct snd_kcontrol_new wm8753_snd_controls[] = {SOC_DOUBLE_R("PCM Volume", WM8753_LDAC, WM8753_RDAC, 0, 255, 0),SOC_DOUBLE_R("ADC Capture Volume", WM8753_LADC, WM8753_RADC, 0, 255, 0),SOC_DOUBLE_R("Headphone Playback Volume", WM8753_LOUT1V, WM8753_ROUT1V, 0, 127, 0),SOC_DOUBLE_R("Speaker Playback Volume", WM8753_LOUT2V, WM8753_ROUT2V, 0, 127, 0),SOC_SINGLE("Mono Playback Volume", WM8753_MOUTV, 0, 127, 0),SOC_DOUBLE_R("Bypass Playback Volume", WM8753_LOUTM1, WM8753_ROUTM1, 4, 7, 1),SOC_DOUBLE_R("Sidetone Playback Volume", WM8753_LOUTM2, WM8753_ROUTM2, 4, 7, 1),SOC_DOUBLE_R("Voice Playback Volume", WM8753_LOUTM2, WM8753_ROUTM2, 0, 7, 1),SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8753_LOUT1V, WM8753_ROUT1V, 7, 1, 0),SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8753_LOUT2V, WM8753_ROUT2V, 7, 1, 0),SOC_SINGLE("Mono Bypass Playback Volume", WM8753_MOUTM1, 4, 7, 1),SOC_SINGLE("Mono Sidetone Playback Volume", WM8753_MOUTM2, 4, 7, 1),SOC_SINGLE("Mono Voice Playback Volume", WM8753_MOUTM2, 4, 7, 1),SOC_SINGLE("Mono Playback ZC Switch", WM8753_MOUTV, 7, 1, 0),SOC_ENUM("Bass Boost", wm8753_enum[0]),SOC_ENUM("Bass Filter", wm8753_enum[1]),SOC_SINGLE("Bass Volume", WM8753_BASS, 0, 15, 1),SOC_SINGLE("Treble Volume", WM8753_TREBLE, 0, 15, 1),SOC_ENUM("Treble Cut-off", wm8753_enum[2]),SOC_DOUBLE("Sidetone Capture Volume", WM8753_RECMIX1, 0, 4, 7, 1),SOC_SINGLE("Voice Sidetone Capture Volume", WM8753_RECMIX2, 0, 7, 1),SOC_DOUBLE_R("Capture Volume", WM8753_LINVOL, WM8753_RINVOL, 0, 63, 0),SOC_DOUBLE_R("Capture ZC Switch", WM8753_LINVOL, WM8753_RINVOL, 6, 1, 0),SOC_DOUBLE_R("Capture Switch", WM8753_LINVOL, WM8753_RINVOL, 7, 1, 1),SOC_ENUM("Capture Filter Select", wm8753_enum[23]),SOC_ENUM("Capture Filter Cut-off", wm8753_enum[24]),SOC_SINGLE("Capture Filter Switch", WM8753_ADC, 0, 1, 1),SOC_SINGLE("ALC Capture Target Volume", WM8753_ALC1, 0, 7, 0),SOC_SINGLE("ALC Capture Max Volume", WM8753_ALC1, 4, 7, 0),SOC_ENUM("ALC Capture Function", wm8753_enum[3]),SOC_SINGLE("ALC Capture ZC Switch", WM8753_ALC2, 8, 1, 0),SOC_SINGLE("ALC Capture Hold Time", WM8753_ALC2, 0, 15, 1),SOC_SINGLE("ALC Capture Decay Time", WM8753_ALC3, 4, 15, 1),SOC_SINGLE("ALC Capture Attack Time", WM8753_ALC3, 0, 15, 0),SOC_SINGLE("ALC Capture NG Threshold", WM8753_NGATE, 3, 31, 0),SOC_ENUM("ALC Capture NG Type", wm8753_enum[4]),SOC_SINGLE("ALC Capture NG Switch", WM8753_NGATE, 0, 1, 0),SOC_ENUM("3D Function", wm8753_enum[5]),SOC_ENUM("3D Upper Cut-off", wm8753_enum[6]),SOC_ENUM("3D Lower Cut-off", wm8753_enum[7]),SOC_SINGLE("3D Volume", WM8753_3D, 1, 15, 0),SOC_SINGLE("3D Switch", WM8753_3D, 0, 1, 0),SOC_SINGLE("Capture 6dB Attenuate", WM8753_ADCTL1, 2, 1, 0),SOC_SINGLE("Playback 6dB Attenuate", WM8753_ADCTL1, 1, 1, 0),SOC_ENUM("De-emphasis", wm8753_enum[8]),SOC_ENUM("Playback Mono Mix", wm8753_enum[9]),SOC_ENUM("Playback Phase", wm8753_enum[10]),SOC_SINGLE("Mic2 Capture Volume", WM8753_INCTL1, 7, 3, 0),SOC_SINGLE("Mic1 Capture Volume", WM8753_INCTL1, 5, 3, 0),SOC_ENUM_EXT("DAI Mode", wm8753_enum[26], wm8753_get_dai, wm8753_set_dai),SOC_ENUM("ADC Data Select", wm8753_enum[27]),};/* add non dapm controls */static int wm8753_add_controls(struct snd_soc_codec *codec){	int err, i;	for (i = 0; i < ARRAY_SIZE(wm8753_snd_controls); i++) {		err = snd_ctl_add(codec->card,				snd_soc_cnew(&wm8753_snd_controls[i],codec, NULL));		if (err < 0)			return err;	}	return 0;}/* * _DAPM_ Controls *//* Left Mixer */static const struct snd_kcontrol_new wm8753_left_mixer_controls[] = {SOC_DAPM_SINGLE("Voice Playback Switch", WM8753_LOUTM2, 8, 1, 0),SOC_DAPM_SINGLE("Sidetone Playback Switch", WM8753_LOUTM2, 7, 1, 0),SOC_DAPM_SINGLE("Left Playback Switch", WM8753_LOUTM1, 8, 1, 0),SOC_DAPM_SINGLE("Bypass Playback Switch", WM8753_LOUTM1, 7, 1, 0),};/* Right mixer */static const struct snd_kcontrol_new wm8753_right_mixer_controls[] = {SOC_DAPM_SINGLE("Voice Playback Switch", WM8753_ROUTM2, 8, 1, 0),SOC_DAPM_SINGLE("Sidetone Playback Switch", WM8753_ROUTM2, 7, 1, 0),SOC_DAPM_SINGLE("Right Playback Switch", WM8753_ROUTM1, 8, 1, 0),SOC_DAPM_SINGLE("Bypass Playback Switch", WM8753_ROUTM1, 7, 1, 0),};/* Mono mixer */static const struct snd_kcontrol_new wm8753_mono_mixer_controls[] = {SOC_DAPM_SINGLE("Left Playback Switch", WM8753_MOUTM1, 8, 1, 0),SOC_DAPM_SINGLE("Right Playback Switch", WM8753_MOUTM2, 8, 1, 0),SOC_DAPM_SINGLE("Voice Playback Switch", WM8753_MOUTM2, 3, 1, 0),SOC_DAPM_SINGLE("Sidetone Playback Switch", WM8753_MOUTM2, 7, 1, 0),SOC_DAPM_SINGLE("Bypass Playback Switch", WM8753_MOUTM1, 7, 1, 0),};/* Mono 2 Mux */static const struct snd_kcontrol_new wm8753_mono2_controls =SOC_DAPM_ENUM("Route", wm8753_enum[17]);/* Out 3 Mux */static const struct snd_kcontrol_new wm8753_out3_controls =SOC_DAPM_ENUM("Route", wm8753_enum[18]);/* Out 4 Mux */static const struct snd_kcontrol_new wm8753_out4_controls =SOC_DAPM_ENUM("Route", wm8753_enum[19]);/* ADC Mono Mix */static const struct snd_kcontrol_new wm8753_adc_mono_controls =SOC_DAPM_ENUM("Route", wm8753_enum[22]);/* Record mixer */static const struct snd_kcontrol_new wm8753_record_mixer_controls[] = {SOC_DAPM_SINGLE("Voice Capture Switch", WM8753_RECMIX2, 3, 1, 0),SOC_DAPM_SINGLE("Left Capture Switch", WM8753_RECMIX1, 3, 1, 0),SOC_DAPM_SINGLE("Right Capture Switch", WM8753_RECMIX1, 7, 1, 0),};/* Left ADC mux */static const struct snd_kcontrol_new wm8753_adc_left_controls =SOC_DAPM_ENUM("Route", wm8753_enum[21]);/* Right ADC mux */static const struct snd_kcontrol_new wm8753_adc_right_controls =SOC_DAPM_ENUM("Route", wm8753_enum[20]);/* MIC mux */static const struct snd_kcontrol_new wm8753_mic_mux_controls =SOC_DAPM_ENUM("Route", wm8753_enum[16]);/* ALC mixer */static const struct snd_kcontrol_new wm8753_alc_mixer_controls[] = {SOC_DAPM_SINGLE("Line Capture Switch", WM8753_INCTL2, 3, 1, 0),SOC_DAPM_SINGLE("Mic2 Capture Switch", WM8753_INCTL2, 2, 1, 0),SOC_DAPM_SINGLE("Mic1 Capture Switch", WM8753_INCTL2, 1, 1, 0),SOC_DAPM_SINGLE("Rx Capture Switch", WM8753_INCTL2, 0, 1, 0),};/* Left Line mux */static const struct snd_kcontrol_new wm8753_line_left_controls =SOC_DAPM_ENUM("Route", wm8753_enum[14]);/* Right Line mux */static const struct snd_kcontrol_new wm8753_line_right_controls =SOC_DAPM_ENUM("Route", wm8753_enum[13]);/* Mono Line mux */static const struct snd_kcontrol_new wm8753_line_mono_controls =SOC_DAPM_ENUM("Route", wm8753_enum[12]);/* Line mux and mixer */static const struct snd_kcontrol_new wm8753_line_mux_mix_controls =SOC_DAPM_ENUM("Route", wm8753_enum[11]);/* Rx mux and mixer */static const struct snd_kcontrol_new wm8753_rx_mux_mix_controls =SOC_DAPM_ENUM("Route", wm8753_enum[15]);/* Mic Selector Mux */static const struct snd_kcontrol_new wm8753_mic_sel_mux_controls =SOC_DAPM_ENUM("Route", wm8753_enum[25]);static const struct snd_soc_dapm_widget wm8753_dapm_widgets[] = {SND_SOC_DAPM_MICBIAS("Mic Bias", WM8753_PWR1, 5, 0),SND_SOC_DAPM_MIXER("Left Mixer", WM8753_PWR4, 0, 0,	&wm8753_left_mixer_controls[0], ARRAY_SIZE(wm8753_left_mixer_controls)),SND_SOC_DAPM_PGA("Left Out 1", WM8753_PWR3, 8, 0, NULL, 0),SND_SOC_DAPM_PGA("Left Out 2", WM8753_PWR3, 6, 0, NULL, 0),SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback", WM8753_PWR1, 3, 0),SND_SOC_DAPM_OUTPUT("LOUT1"),SND_SOC_DAPM_OUTPUT("LOUT2"),SND_SOC_DAPM_MIXER("Right Mixer", WM8753_PWR4, 1, 0,	&wm8753_right_mixer_controls[0], ARRAY_SIZE(wm8753_right_mixer_controls)),

⌨️ 快捷键说明

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