当前位置: 首页 > news >正文

电子商务网站建设的目标是企业网站建设感想

电子商务网站建设的目标是,企业网站建设感想,用php做网站视频,seo 视频一、应用测试工具的使用1.在external/tinyalsa下有以C语言实现的alsa的测试程序#xff0c;编译后生成tinypcminfo tinyplay tinycap tinymix 四个elf格式的测试工具(1) tinypcminfo #xff1a;获取PCM In和PCM# tinypcminfo -D /dev/snd/controlC0# tinypcminfo -D /dev/sn…一、应用测试工具的使用1.在external/tinyalsa下有以C语言实现的alsa的测试程序编译后生成tinypcminfo tinyplay tinycap tinymix 四个elf格式的测试工具(1) tinypcminfo 获取PCM In和PCM# tinypcminfo -D /dev/snd/controlC0# tinypcminfo -D /dev/snd/pcmC0D0pInfo for card 0, device 0:PCM out:Access: 0x000009Format[0]: 0x000044Format[1]: 00000000Format Name: S16_LE, S24_LESubformat: 0x000001Rate: min8000Hz max48000HzChannels: min2 max2Sample bits: min16 max32Period size: min512 max8192Period count: min2 max64PCM in:Access: 0x000009Format[0]: 0x000044Format[1]: 00000000Format Name: S16_LE, S24_LESubformat: 0x000001Rate: min8000Hz max48000HzChannels: min1 max2Sample bits: min16 max32Period size: min512 max16384Period count: min2 max128View Code(2) tinymix 通过/dev/snd/controlC0节点设置获取控制信息进行控件的设置。比如设置链路音量调节等。# tinymixMixer name: ‘S3C2440_UDA1341‘Number of controls: 50ctl type num name value0 INT 2 Capture Volume 51 511 INT 2 Capture Volume ZC Switch 0 02 BOOL 2 Capture Switch Off On3 INT 2 Playback Volume 255 2554 INT 2 Headphone Playback Volume 121 1215 BOOL 2 Headphone Playback ZC Switch Off Off6 INT 2 Speaker Playback Volume 121 1217 BOOL 2 Speaker Playback ZC Switch Off Off....View Code# tinymix 打印出所有的snd_kcontrol项可以通过名字操作# ./tinymix Capture Volume //单获取这项的值Capture Volume: 51 51 (range 0-63)# ./tinymix Capture Volume 10 //单设置这项的值为10# ./tinymix Capture VolumeCapture Volume: 10 10 (range 0-63)也可以通过序号操作# ./tinymix 0Capture Volume: 10 10 (range 0-63)# ./tinymix 0 20# ./tinymix 0Capture Volume: 20 20 (range 0-63)驱动中对应的file_operations是struct file_operations snd_ctl_f_ops(3) tinycap : 使用/dev/snd/pcmC0D0c录音# tinycap a.wavconst struct file_operations snd_pcm_f_ops[1](4) tinyplay : 使用/dev/snd/pcmC0D0p播放声音# tinyplay a.wavconst struct file_operations snd_pcm_f_ops[0]二、内核导出信息1.devtmpfs信息(设备节点)[email protected]:/system # ls /dev/snd/ -ltotal 0crw-rw---- 1 1000 1005 116, 0 Jan 1 12:00 controlC0crw-rw---- 1 1000 1005 116, 24 Jan 1 12:00 pcmC0D0ccrw-rw---- 1 1000 1005 116, 16 Jan 1 12:00 pcmC0D0pcrw-rw---- 1 1000 1005 116, 25 Jan 1 12:00 pcmC0D1ccrw-rw---- 1 1000 1005 116, 17 Jan 1 12:00 pcmC0D1pcrw-rw---- 1 1000 1005 116, 33 Jan 1 12:00 timercontrolC0: 起控制作用C0表示Card0pcmC0D0c: Card 0,Device 0 capture,用来录音。pcmC0D0p: Card 0,Device 0 playback,用来录音。pcmC0D1c: Card 0,Device 1 capture,用来录音。pcmC0D1p: Card 0,Device 1 playback,用来录音。timer: 很少使用暂时不用管。pcmC0D1c/pcmC0D1p是一个辅助的备份的音频设备先不管。ALSA框架中一个声卡可以有多个逻辑Device上面的pcmC0D0X和pcmC0D1X就是两个逻辑设备一个Device又有播放、录音通道。2.procfs文件信息[email protected]:/system # ls /proc/asound/card0 cards devices hwdep pcm timers tiny4412 version3.sysfs文件信息/sys/class/sound/有声卡的id,number,pcm_class等信息4.debugfs文件信息/sys/kernel/debug/asoc/导出信息包括① Codec各个组件的dapm信息TINY4412_UDA8960/wm8960-codec.0-001a/dapm下的[email protected]:/sys/kernel/debug/asoc/TINY4412_UDA8960/wm8960-codec.0-001a/dapm # lsHP_L Left Speaker Output Right Boost MixerHP_R Left Speaker PGA Right DACLINPUT1 MICB Right Input MixerLINPUT2 Mic Onboard Right Output MixerLINPUT3 Mono Output Mixer Right Speaker OutputLOUT1 PGA OUT3 Right Speaker PGALeft ADC RINPUT1 SPK_LNLeft Boost Mixer RINPUT2 SPK_LPLeft DAC RINPUT3 SPK_RNLeft Input Mixer ROUT1 PGA SPK_RPLeft Output Mixer Right ADC bias_level② Codec的寄存器信息[email protected]:/sys/kernel/debug/asoc/TINY4412_UDA8960/wm8960-codec.0-001a # lscache_only cache_sync codec_reg③ 为消除pop音的延时时间/sys/kernel/debug/asoc/TINY4412_UDA8960# lsdapm_pop_time④ dapm的bias_level/sys/kernel/debug/asoc/TINY4412_UDA8960/dapm # cat bias_levelOff⑤ 系统中所有注册的Codecdais和Platform驱动的名字/sys/kernel/debug/asoc # lsS3C2440_UDA1341 codecs dais platforms三、驱动实战驱动分Platform驱动Codec驱动和Machine驱动。我们对于4412开发板主要任务就是实现Machine驱动的平台设备端移植调试Codec驱动wm8960.c。Platform驱动Soc厂商已经实现好了是sound/soc/samsung/dma.cSoc端的dai驱动Soc厂商已经实现好了是sound/soc/samsung/i2s.cMachine平台设备驱动驱动端sound子系统已经实现好了是sound/soc/soc-core.cCodec驱动和Codec端的dai驱动都在Codec驱动中实现。(1) 调试好的Codec驱动wm8960.c/** wm8960.c -- WM8960 ALSA SoC Audio driver** Author: 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include wm8960.h/* R25 - Power 1 */#define WM8960_VMID_MASK 0x180#define WM8960_VREF 0x40/* R26 - Power 2 */#define WM8960_PWR2_LOUT1 0x40#define WM8960_PWR2_ROUT1 0x20#define WM8960_PWR2_OUT3 0x02/* R28 - Anti-pop 1 */#define WM8960_POBCTRL 0x80#define WM8960_BUFDCOPEN 0x10#define WM8960_BUFIOEN 0x08#define WM8960_SOFT_ST 0x04#define WM8960_HPSTBY 0x01/* R29 - Anti-pop 2 */#define WM8960_DISOP 0x40#define WM8960_DRES_MASK 0x30/** wm8960 register cache* We can‘t read the WM8960 register space when we are* using 2 wire for device control, so we cache them instead.*/static const u16 wm8960_reg[WM8960_CACHEREGNUM] {0x0097, 0x0097, 0x0000, 0x0000,0x0000, 0x0008, 0x0000, 0x000a,0x01c0, 0x0000, 0x00ff, 0x00ff,0x0000, 0x0000, 0x0000, 0x0000,0x0000, 0x007b, 0x0100, 0x0032,0x0000, 0x00c3, 0x00c3, 0x01c0,0x0000, 0x0000, 0x0000, 0x0000,0x0000, 0x0000, 0x0000, 0x0000,0x0100, 0x0100, 0x0050, 0x0050,0x0050, 0x0050, 0x0000, 0x0000,0x0000, 0x0000, 0x0040, 0x0000,0x0000, 0x0050, 0x0050, 0x0000,0x0002, 0x0037, 0x004d, 0x0080,0x0008, 0x0031, 0x0026, 0x00e9,};struct wm8960_priv {enum snd_soc_control_type control_type;void *control_data;int (*set_bias_level)(struct snd_soc_codec *, enum snd_soc_bias_level level);struct snd_soc_dapm_widget *lout1;struct snd_soc_dapm_widget *rout1;struct snd_soc_dapm_widget *out3;bool deemph;int playback_fs;};#define wm8960_reset(c) snd_soc_write(c, WM8960_RESET, 0)/* enumerated controls */ /*极性*/static const char *wm8960_polarity[] {No Inversion, Left Inverted,Right Inverted, Stereo Inversion}; /*立体声反转*/static const char *wm8960_3d_upper_cutoff[] {High, Low};static const char *wm8960_3d_lower_cutoff[] {Low, High};static const char *wm8960_alcfunc[] {Off, Right, Left, Stereo};static const char *wm8960_alcmode[] {ALC, Limiter};/*#define SOC_ENUM_SINGLE(xreg, xshift, xmax, xtexts){.reg xreg,.shift_l xshift,.shift_r xshift,.max xmax,.texts xtexts}*//*soc_mixer_control来描述mixer控件的寄存器信息soc_enum 来描述mux控件的寄存器信息*/static const struct soc_enum wm8960_enum[] {SOC_ENUM_SINGLE(WM8960_DACCTL1, 5, 4, wm8960_polarity),SOC_ENUM_SINGLE(WM8960_DACCTL2, 5, 4, wm8960_polarity),SOC_ENUM_SINGLE(WM8960_3D, 6, 2, wm8960_3d_upper_cutoff),SOC_ENUM_SINGLE(WM8960_3D, 5, 2, wm8960_3d_lower_cutoff),SOC_ENUM_SINGLE(WM8960_ALC1, 7, 4, wm8960_alcfunc),SOC_ENUM_SINGLE(WM8960_ALC3, 8, 2, wm8960_alcmode),};/*采样率设置*/static const int deemph_settings[] { 0, 32000, 44100, 48000 };static int wm8960_set_deemph(struct snd_soc_codec *codec){struct wm8960_priv *wm8960 snd_soc_codec_get_drvdata(codec);int val, i, best;/* If we‘re using deemphasis select the nearest available sample* rate.*/if (wm8960-deemph) {best 1;for (i 2; i ARRAY_SIZE(deemph_settings); i) {if (abs(deemph_settings[i] - wm8960-playback_fs) abs(deemph_settings[best] - wm8960-playback_fs))best i;}val best 1;} else {val 0;}dev_dbg(codec-dev, Set deemphasis %d\n, val);return snd_soc_update_bits(codec, WM8960_DACCTL1,0x6, val);}static int wm8960_get_deemph(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_value *ucontrol){struct snd_soc_codec *codec snd_kcontrol_chip(kcontrol);struct wm8960_priv *wm8960 snd_soc_codec_get_drvdata(codec);ucontrol-value.enumerated.item[0] wm8960-deemph;return 0;}static int wm8960_put_deemph(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_value *ucontrol){struct snd_soc_codec *codec snd_kcontrol_chip(kcontrol);struct wm8960_priv *wm8960 snd_soc_codec_get_drvdata(codec);int deemph ucontrol-value.enumerated.item[0];if (deemph 1)return -EINVAL;wm8960-deemph deemph;return wm8960_set_deemph(codec);}/*补充介绍:DECLARE_TLV_DB_LINEAR(beep_tlv, -4500, 0);DECLARE_TLV_DB_LINEAR宏定义的mixer control它的输出随值的变化而线性变化。 该宏的:第一个参数是要定义变量的名字第二个参数是最小值以0.01dB为单位。第三个参数是最大值以0.01dB为单位。如果该control处于最小值时会做出mute时需要把第二个参数设为TLV_DB_GAIN_MUTE。这两个宏实际上就是定义一个整形数组所谓tlv就是Type-Lenght-Value的意思数组的第0各元素代表数据的类型第1个元素代表数据的长度第三个元素和之后的元素保存该变量的数据。*//*这些定义的数组adc_tlv[]会被放在snd_kcontrol_new.tlv.p中单位是db.DECLARE_TLV_DB_SCALE宏定义的mixer control它所代表的值按一个固定的dB值的步长变化。该宏的:第一个参数是要定义变量的名字第二个参数是最小值以0.01dB为单位。第三个参数是变化的步长也是以0.01dB为单位。如果该control处于最小值时会做出mute时需要把第四个参数设为1。*/static const DECLARE_TLV_DB_SCALE(adc_tlv, -9700, 50, 0);static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 50, 1);static const DECLARE_TLV_DB_SCALE(bypass_tlv, -2100, 300, 0);static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);/** 这些snd_kcontrol_new定义的控制项可以通过tinymix工具读取出来可供app设置.* 这些是没有使用DAPM的kcontrol,对比SOC_SINGLE和SOC_DAPM_SINGLE可知其内部指定* 的回调函数不同。*/static const struct snd_kcontrol_new wm8960_snd_controls[] {SOC_DOUBLE_R_TLV(Capture Volume, WM8960_LINVOL, WM8960_RINVOL,0, 63, 0, adc_tlv),SOC_DOUBLE_R(Capture Volume ZC Switch, WM8960_LINVOL, WM8960_RINVOL,6, 1, 0),SOC_DOUBLE_R(Capture Switch, WM8960_LINVOL, WM8960_RINVOL,7, 1, 0),SOC_DOUBLE_R_TLV(Playback Volume, WM8960_LDAC, WM8960_RDAC,0, 255, 0, dac_tlv),SOC_DOUBLE_R_TLV(Headphone Playback Volume, WM8960_LOUT1, WM8960_ROUT1,0, 127, 0, out_tlv),SOC_DOUBLE_R(Headphone Playback ZC Switch, WM8960_LOUT1, WM8960_ROUT1,7, 1, 0),SOC_DOUBLE_R_TLV(Speaker Playback Volume, WM8960_LOUT2, WM8960_ROUT2,0, 127, 0, out_tlv),SOC_DOUBLE_R(Speaker Playback ZC Switch, WM8960_LOUT2, WM8960_ROUT2,7, 1, 0),SOC_SINGLE(Speaker DC Volume, WM8960_CLASSD3, 3, 5, 0),SOC_SINGLE(Speaker AC Volume, WM8960_CLASSD3, 0, 5, 0),SOC_SINGLE(PCM Playback -6dB Switch, WM8960_DACCTL1, 7, 1, 0),SOC_ENUM(ADC Polarity, wm8960_enum[0]),SOC_SINGLE(ADC High Pass Filter Switch, WM8960_DACCTL1, 0, 1, 0),SOC_ENUM(DAC Polarity, wm8960_enum[2]),SOC_SINGLE_BOOL_EXT(DAC Deemphasis Switch, 0,wm8960_get_deemph, wm8960_put_deemph),SOC_ENUM(3D Filter Upper Cut-Off, wm8960_enum[2]),SOC_ENUM(3D Filter Lower Cut-Off, wm8960_enum[3]),SOC_SINGLE(3D Volume, WM8960_3D, 1, 15, 0),SOC_SINGLE(3D Switch, WM8960_3D, 0, 1, 0),SOC_ENUM(ALC Function, wm8960_enum[4]),SOC_SINGLE(ALC Max Gain, WM8960_ALC1, 4, 7, 0),SOC_SINGLE(ALC Target, WM8960_ALC1, 0, 15, 1),SOC_SINGLE(ALC Min Gain, WM8960_ALC2, 4, 7, 0),SOC_SINGLE(ALC Hold Time, WM8960_ALC2, 0, 15, 0),SOC_ENUM(ALC Mode, wm8960_enum[5]),SOC_SINGLE(ALC Decay, WM8960_ALC3, 4, 15, 0),SOC_SINGLE(ALC Attack, WM8960_ALC3, 0, 15, 0),SOC_SINGLE(Noise Gate Threshold, WM8960_NOISEG, 3, 31, 0),SOC_SINGLE(Noise Gate Switch, WM8960_NOISEG, 0, 1, 0),SOC_DOUBLE_R(ADC PCM Capture Volume, WM8960_LINPATH, WM8960_RINPATH,4, 3, 0),SOC_SINGLE_TLV(Left Output Mixer Boost Bypass Volume,WM8960_BYPASS1, 4, 7, 1, bypass_tlv),SOC_SINGLE_TLV(Left Output Mixer LINPUT3 Volume,WM8960_LOUTMIX, 4, 7, 1, bypass_tlv),SOC_SINGLE_TLV(Right Output Mixer Boost Bypass Volume,WM8960_BYPASS2, 4, 7, 1, bypass_tlv),SOC_SINGLE_TLV(Right Output Mixer RINPUT3 Volume,WM8960_ROUTMIX, 4, 7, 1, bypass_tlv),};/** 宏中带DAPM的表示是使用DAPM的kcontrol.** dapm kcontrol的put回调函数不仅仅会更新控件本身的状态他还会把这种变化传* 递到相邻的dapm kcontrol相邻的dapm kcontrol又会传递这个变化到他自己相邻* 的dapm kcontrol知道音频路径的末端通过这种机制只要改变其中一个widget* 的连接状态与之相关的所有widget都会被扫描并测试一下自身是否还在有效的音频* 路径中从而可以动态地改变自身的电源状态这就是dapm的精髓所在。*/static const struct snd_kcontrol_new wm8960_lin_boost[] {SOC_DAPM_SINGLE(LINPUT2 Switch, WM8960_LINPATH, 6, 1, 0),SOC_DAPM_SINGLE(LINPUT3 Switch, WM8960_LINPATH, 7, 1, 0),SOC_DAPM_SINGLE(LINPUT1 Switch, WM8960_LINPATH, 8, 1, 0),};/** snd_kcontrol_new是定义个控件*/static const struct snd_kcontrol_new wm8960_lin[] {SOC_DAPM_SINGLE(Boost Switch, WM8960_LINPATH, 3, 1, 0),};static const struct snd_kcontrol_new wm8960_rin_boost[] {SOC_DAPM_SINGLE(RINPUT2 Switch, WM8960_RINPATH, 6, 1, 0),SOC_DAPM_SINGLE(RINPUT3 Switch, WM8960_RINPATH, 7, 1, 0),SOC_DAPM_SINGLE(RINPUT1 Switch, WM8960_RINPATH, 8, 1, 0),};static const struct snd_kcontrol_new wm8960_rin[] {SOC_DAPM_SINGLE(Boost Switch, WM8960_RINPATH, 3, 1, 0),};static const struct snd_kcontrol_new wm8960_loutput_mixer[] {SOC_DAPM_SINGLE(PCM Playback Switch, WM8960_LOUTMIX, 8, 1, 0),SOC_DAPM_SINGLE(LINPUT3 Switch, WM8960_LOUTMIX, 7, 1, 0),SOC_DAPM_SINGLE(Boost Bypass Switch, WM8960_BYPASS1, 7, 1, 0),};static const struct snd_kcontrol_new wm8960_routput_mixer[] {SOC_DAPM_SINGLE(PCM Playback Switch, WM8960_ROUTMIX, 8, 1, 0),SOC_DAPM_SINGLE(RINPUT3 Switch, WM8960_ROUTMIX, 7, 1, 0),SOC_DAPM_SINGLE(Boost Bypass Switch, WM8960_BYPASS2, 7, 1, 0),};static const struct snd_kcontrol_new wm8960_mono_out[] {SOC_DAPM_SINGLE(Left Switch, WM8960_MONOMIX1, 7, 1, 0),SOC_DAPM_SINGLE(Right Switch, WM8960_MONOMIX2, 7, 1, 0),};/*不同的组件初始化的成员不同:SND_SOC_DAPM_INPUT中没有snd_kcontrol_newSND_SOC_DAPM_MIXER中有多个snd_kcontrol_new将上面定义的控件转化为widget*/static const struct snd_soc_dapm_widget wm8960_dapm_widgets[] {SND_SOC_DAPM_INPUT(LINPUT1),SND_SOC_DAPM_INPUT(RINPUT1),SND_SOC_DAPM_INPUT(LINPUT2),SND_SOC_DAPM_INPUT(RINPUT2),SND_SOC_DAPM_INPUT(LINPUT3),SND_SOC_DAPM_INPUT(RINPUT3),SND_SOC_DAPM_MICBIAS(MICB, WM8960_POWER1, 1, 0),/** 若arg2SND_SOC_NOPM则表示此widget不具备电源属性但是mux的* 切换会影响到与之相连的其它具备电源属性的电源状态。*/SND_SOC_DAPM_MIXER(Left Boost Mixer, WM8960_POWER1, 5, 0,wm8960_lin_boost, ARRAY_SIZE(wm8960_lin_boost)),SND_SOC_DAPM_MIXER(Right Boost Mixer, WM8960_POWER1, 4, 0,wm8960_rin_boost, ARRAY_SIZE(wm8960_rin_boost)),SND_SOC_DAPM_MIXER(Left Input Mixer, WM8960_POWER3, 5, 0,wm8960_lin, ARRAY_SIZE(wm8960_lin)),SND_SOC_DAPM_MIXER(Right Input Mixer, WM8960_POWER3, 4, 0,wm8960_rin, ARRAY_SIZE(wm8960_rin)),SND_SOC_DAPM_ADC(Left ADC, Capture, WM8960_POWER1, 3, 0),SND_SOC_DAPM_ADC(Right ADC, Capture, WM8960_POWER1, 2, 0),SND_SOC_DAPM_DAC(Left DAC, Playback, WM8960_POWER2, 8, 0),SND_SOC_DAPM_DAC(Right DAC, Playback, WM8960_POWER2, 7, 0),SND_SOC_DAPM_MIXER(Left Output Mixer, WM8960_POWER3, 3, 0,wm8960_loutput_mixer[0], ARRAY_SIZE(wm8960_loutput_mixer)),SND_SOC_DAPM_MIXER(Right Output Mixer, WM8960_POWER3, 2, 0,wm8960_routput_mixer[0], ARRAY_SIZE(wm8960_routput_mixer)),SND_SOC_DAPM_PGA(LOUT1 PGA, WM8960_POWER2, 6, 0, NULL, 0),SND_SOC_DAPM_PGA(ROUT1 PGA, WM8960_POWER2, 5, 0, NULL, 0),SND_SOC_DAPM_PGA(Left Speaker PGA, WM8960_POWER2, 4, 0, NULL, 0),SND_SOC_DAPM_PGA(Right Speaker PGA, WM8960_POWER2, 3, 0, NULL, 0),SND_SOC_DAPM_PGA(Right Speaker Output, WM8960_CLASSD1, 7, 0, NULL, 0),SND_SOC_DAPM_PGA(Left Speaker Output, WM8960_CLASSD1, 6, 0, NULL, 0),SND_SOC_DAPM_OUTPUT(SPK_LP),SND_SOC_DAPM_OUTPUT(SPK_LN),SND_SOC_DAPM_OUTPUT(HP_L),SND_SOC_DAPM_OUTPUT(HP_R),SND_SOC_DAPM_OUTPUT(SPK_RP),SND_SOC_DAPM_OUTPUT(SPK_RN),SND_SOC_DAPM_OUTPUT(OUT3),};static const struct snd_soc_dapm_widget wm8960_dapm_widgets_out3[] {SND_SOC_DAPM_MIXER(Mono Output Mixer, WM8960_POWER2, 1, 0,wm8960_mono_out[0], ARRAY_SIZE(wm8960_mono_out)),};/* Represent OUT3 as a PGA so that it gets turned on with LOUT1/ROUT1 */static const struct snd_soc_dapm_widget wm8960_dapm_widgets_capless[] {SND_SOC_DAPM_PGA(OUT3 VMID, WM8960_POWER2, 1, 0, NULL, 0),};/*route的source和sink成员都是widget的名字control成员是两个widget之间连接通过的开关的snd_kcontrol, 若是两个widget之间有开关那么control成员就是这个开关的名字。若是没有开关而是直连的那么control成员就设置为NULL,此时这个连接成为静态连接。route会被解析成path使用snd_soc_dapm_path结构表示。从这个结构体里面可以看出声道的连接路由。*/static const struct snd_soc_dapm_route audio_paths[] {{ Left Boost Mixer, LINPUT1 Switch, LINPUT1 },{ Left Boost Mixer, LINPUT2 Switch, LINPUT2 },{ Left Boost Mixer, LINPUT3 Switch, LINPUT3 },{ Left Input Mixer, Boost Switch, Left Boost Mixer, },{ Left Input Mixer, NULL, LINPUT1, }, /* Really Boost Switch */{ Left Input Mixer, NULL, LINPUT2 },{ Left Input Mixer, NULL, LINPUT3 },{ Right Boost Mixer, RINPUT1 Switch, RINPUT1 },{ Right Boost Mixer, RINPUT2 Switch, RINPUT2 },{ Right Boost Mixer, RINPUT3 Switch, RINPUT3 },{ Right Input Mixer, Boost Switch, Right Boost Mixer, },{ Right Input Mixer, NULL, RINPUT1, }, /* Really Boost Switch */{ Right Input Mixer, NULL, RINPUT2 },{ Right Input Mixer, NULL, LINPUT3 },{ Left ADC, NULL, Left Input Mixer },{ Right ADC, NULL, Right Input Mixer },{ Left Output Mixer, LINPUT3 Switch, LINPUT3 },{ Left Output Mixer, Boost Bypass Switch, Left Boost Mixer} ,{ Left Output Mixer, PCM Playback Switch, Left DAC },{ Right Output Mixer, RINPUT3 Switch, RINPUT3 },{ Right Output Mixer, Boost Bypass Switch, Right Boost Mixer } ,{ Right Output Mixer, PCM Playback Switch, Right DAC },{ LOUT1 PGA, NULL, Left Output Mixer },{ ROUT1 PGA, NULL, Right Output Mixer },{ HP_L, NULL, LOUT1 PGA },{ HP_R, NULL, ROUT1 PGA },{ Left Speaker PGA, NULL, Left Output Mixer },{ Right Speaker PGA, NULL, Right Output Mixer },{ Left Speaker Output, NULL, Left Speaker PGA },{ Right Speaker Output, NULL, Right Speaker PGA },{ SPK_LN, NULL, Left Speaker Output },{ SPK_LP, NULL, Left Speaker Output },{ SPK_RN, NULL, Right Speaker Output },{ SPK_RP, NULL, Right Speaker Output },};static const struct snd_soc_dapm_route audio_paths_out3[] {{ Mono Output Mixer, Left Switch, Left Output Mixer },{ Mono Output Mixer, Right Switch, Right Output Mixer },{ OUT3, NULL, Mono Output Mixer, }};static const struct snd_soc_dapm_route audio_paths_capless[] {{ HP_L, NULL, OUT3 VMID },{ HP_R, NULL, OUT3 VMID },{ OUT3 VMID, NULL, Left Output Mixer },{ OUT3 VMID, NULL, Right Output Mixer },};static int wm8960_add_widgets(struct snd_soc_codec *codec){struct wm8960_data *pdata codec-dev-platform_data;struct wm8960_priv *wm8960 snd_soc_codec_get_drvdata(codec);struct snd_soc_dapm_context *dapm codec-dapm;struct snd_soc_dapm_widget *w;snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets, ARRAY_SIZE(wm8960_dapm_widgets));snd_soc_dapm_add_routes(dapm, audio_paths, ARRAY_SIZE(audio_paths));/* In capless mode OUT3 is used to provide VMID for the* headphone outputs, otherwise it is used as a mono mixer.*/if (pdata pdata-capless) {snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets_capless,ARRAY_SIZE(wm8960_dapm_widgets_capless));snd_soc_dapm_add_routes(dapm, audio_paths_capless,ARRAY_SIZE(audio_paths_capless));} else {snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets_out3,ARRAY_SIZE(wm8960_dapm_widgets_out3));snd_soc_dapm_add_routes(dapm, audio_paths_out3,ARRAY_SIZE(audio_paths_out3));}/* We need to power up the headphone output stage out of* sequence for capless mode. To save scanning the widget* list each time to find the desired power state do so now* and save the result.*/list_for_each_entry(w, codec-card-widgets, list) {if (w-dapm ! codec-dapm)continue;if (strcmp(w-name, LOUT1 PGA) 0)wm8960-lout1 w;if (strcmp(w-name, ROUT1 PGA) 0)wm8960-rout1 w;if (strcmp(w-name, OUT3 VMID) 0)wm8960-out3 w;}return 0;}static int wm8960_set_dai_fmt(struct snd_soc_dai *codec_dai,unsigned int fmt){struct snd_soc_codec *codec codec_dai-codec;u16 iface 0;/* set master/slave audio interface */switch (fmt SND_SOC_DAIFMT_MASTER_MASK) {case SND_SOC_DAIFMT_CBM_CFM:iface | 0x0040;break;case SND_SOC_DAIFMT_CBS_CFS:break;default:return -EINVAL;}/* interface format */switch (fmt SND_SOC_DAIFMT_FORMAT_MASK) {case SND_SOC_DAIFMT_I2S:iface | 0x0002;break;case SND_SOC_DAIFMT_RIGHT_J:break;case SND_SOC_DAIFMT_LEFT_J:iface | 0x0001;break;case SND_SOC_DAIFMT_DSP_A:iface | 0x0003;break;case SND_SOC_DAIFMT_DSP_B:iface | 0x0013;break;default:return -EINVAL;}/* clock inversion */switch (fmt SND_SOC_DAIFMT_INV_MASK) {case SND_SOC_DAIFMT_NB_NF:break;case SND_SOC_DAIFMT_IB_IF:iface | 0x0090;break;case SND_SOC_DAIFMT_IB_NF:iface | 0x0080;break;case SND_SOC_DAIFMT_NB_IF:iface | 0x0010;break;default:return -EINVAL;}/* set iface */snd_soc_write(codec, WM8960_IFACE1, iface);return 0;}static struct {int rate;unsigned int val;} alc_rates[] {{ 48000, 0 },{ 44100, 0 },{ 32000, 1 },{ 22050, 2 },{ 24000, 2 },{ 16000, 3 },{ 11250, 4 },{ 12000, 4 },{ 8000, 5 },};/*这个params从何而来???*/static int wm8960_hw_params(struct snd_pcm_substream *substream,struct snd_pcm_hw_params *params,struct snd_soc_dai *dai){struct snd_soc_pcm_runtime *rtd substream-private_data;struct snd_soc_codec *codec rtd-codec;struct wm8960_priv *wm8960 snd_soc_codec_get_drvdata(codec);u16 iface snd_soc_read(codec, WM8960_IFACE1) 0xfff3;int i;/* 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;}/* Update filters for the new rate */if (substream-stream SNDRV_PCM_STREAM_PLAYBACK) {wm8960-playback_fs params_rate(params);wm8960_set_deemph(codec);} else {for (i 0; i ARRAY_SIZE(alc_rates); i)if (alc_rates[i].rate params_rate(params))snd_soc_update_bits(codec,WM8960_ADDCTL3, 0x7,alc_rates[i].val);}/* set iface */snd_soc_write(codec, WM8960_IFACE1, iface);return 0;}static int wm8960_mute(struct snd_soc_dai *dai, int mute){struct snd_soc_codec *codec dai-codec;u16 mute_reg snd_soc_read(codec, WM8960_DACCTL1) 0xfff7;if (mute)snd_soc_write(codec, WM8960_DACCTL1, mute_reg | 0x8);elsesnd_soc_write(codec, WM8960_DACCTL1, mute_reg);return 0;}static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec,enum snd_soc_bias_level level){u16 reg;switch (level) {case SND_SOC_BIAS_ON:break;case SND_SOC_BIAS_PREPARE:/* Set VMID to 2x50k */reg snd_soc_read(codec, WM8960_POWER1);reg ~0x180;reg | 0x80;snd_soc_write(codec, WM8960_POWER1, reg);break;case SND_SOC_BIAS_STANDBY:if (codec-dapm.bias_level SND_SOC_BIAS_OFF) {/* Enable anti-pop features */snd_soc_write(codec, WM8960_APOP1,WM8960_POBCTRL | WM8960_SOFT_ST |WM8960_BUFDCOPEN | WM8960_BUFIOEN);/* Enable ramp VMID at 2x50k */reg snd_soc_read(codec, WM8960_POWER1);reg | 0x80;snd_soc_write(codec, WM8960_POWER1, reg);msleep(100);/* Enable VREF */snd_soc_write(codec, WM8960_POWER1, reg | WM8960_VREF);/* Disable anti-pop features */snd_soc_write(codec, WM8960_APOP1, WM8960_BUFIOEN);}/* Set VMID to 2x250k */reg snd_soc_read(codec, WM8960_POWER1);reg ~0x180;reg | 0x100;snd_soc_write(codec, WM8960_POWER1, reg);break;case SND_SOC_BIAS_OFF:/* Enable anti-pop features */snd_soc_write(codec, WM8960_APOP1,WM8960_POBCTRL | WM8960_SOFT_ST |WM8960_BUFDCOPEN | WM8960_BUFIOEN);/* Disable VMID and VREF, let them discharge */snd_soc_write(codec, WM8960_POWER1, 0);msleep(600);break;}codec-dapm.bias_level level;return 0;}static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec,enum snd_soc_bias_level level){struct wm8960_priv *wm8960 snd_soc_codec_get_drvdata(codec);int reg;switch (level) {case SND_SOC_BIAS_ON:break;case SND_SOC_BIAS_PREPARE:switch (codec-dapm.bias_level) {case SND_SOC_BIAS_STANDBY:/* Enable anti pop mode */snd_soc_update_bits(codec, WM8960_APOP1,WM8960_POBCTRL | WM8960_SOFT_ST |WM8960_BUFDCOPEN,WM8960_POBCTRL | WM8960_SOFT_ST |WM8960_BUFDCOPEN);/* Enable LOUT1, ROUT1 and OUT3 if they‘re enabled */reg 0;if (wm8960-lout1 wm8960-lout1-power)reg | WM8960_PWR2_LOUT1;if (wm8960-rout1 wm8960-rout1-power)reg | WM8960_PWR2_ROUT1;if (wm8960-out3 wm8960-out3-power)reg | WM8960_PWR2_OUT3;snd_soc_update_bits(codec, WM8960_POWER2,WM8960_PWR2_LOUT1 |WM8960_PWR2_ROUT1 |WM8960_PWR2_OUT3, reg);/* Enable VMID at 2*50k */snd_soc_update_bits(codec, WM8960_POWER1,WM8960_VMID_MASK, 0x80);/* Ramp */msleep(100);/* Enable VREF */snd_soc_update_bits(codec, WM8960_POWER1,WM8960_VREF, WM8960_VREF);msleep(100);break;case SND_SOC_BIAS_ON:/* Enable anti-pop mode */snd_soc_update_bits(codec, WM8960_APOP1,WM8960_POBCTRL | WM8960_SOFT_ST |WM8960_BUFDCOPEN,WM8960_POBCTRL | WM8960_SOFT_ST |WM8960_BUFDCOPEN);/* Disable VMID and VREF */snd_soc_update_bits(codec, WM8960_POWER1,WM8960_VREF | WM8960_VMID_MASK, 0);break;default:break;}break;case SND_SOC_BIAS_STANDBY:switch (codec-dapm.bias_level) {case SND_SOC_BIAS_PREPARE:/* Disable HP discharge */snd_soc_update_bits(codec, WM8960_APOP2,WM8960_DISOP | WM8960_DRES_MASK,0);/* Disable anti-pop features */snd_soc_update_bits(codec, WM8960_APOP1,WM8960_POBCTRL | WM8960_SOFT_ST |WM8960_BUFDCOPEN,WM8960_POBCTRL | WM8960_SOFT_ST |WM8960_BUFDCOPEN);break;default:break;}break;case SND_SOC_BIAS_OFF:break;}codec-dapm.bias_level level;return 0;}/* PLL divisors */struct _pll_div {u32 pre_div:1;u32 n:4;u32 k:24;};/* The size in bits of the pll divide multiplied by 10* to allow rounding later */#define FIXED_PLL_SIZE ((1 24) * 10)static int pll_factors(unsigned int source, unsigned int target,struct _pll_div *pll_div){unsigned long long Kpart;unsigned int K, Ndiv, Nmod;pr_debug(WM8960 PLL: setting %dHz-%dHz\n, source, target);/* Scale up target to PLL operating frequency */target * 4;Ndiv target / source;if (Ndiv 6) {source 1;pll_div-pre_div 1;Ndiv target / source;} elsepll_div-pre_div 0;if ((Ndiv 6) || (Ndiv 12)) {pr_err(WM8960 PLL: Unsupported N%d\n, Ndiv);return -EINVAL;}pll_div-n Ndiv;Nmod target % source;Kpart FIXED_PLL_SIZE * (long long)Nmod;do_div(Kpart, source);K Kpart 0xFFFFFFFF;/* Check if we need to round */if ((K % 10) 5)K 5;/* Move down to proper range now rounding is done */K / 10;pll_div-k K;pr_debug(WM8960 PLL: N%x K%x pre_div%d\n,pll_div-n, pll_div-k, pll_div-pre_div);return 0;}static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,int source, unsigned int freq_in, unsigned int freq_out){struct snd_soc_codec *codec codec_dai-codec;u16 reg;static struct _pll_div pll_div;int ret;if (freq_in freq_out) {ret pll_factors(freq_in, freq_out, pll_div);if (ret ! 0)return ret;}/* Disable the PLL: even if we are changing the frequency the* PLL needs to be disabled while we do so. */snd_soc_write(codec, WM8960_CLOCK1,snd_soc_read(codec, WM8960_CLOCK1) ~1);snd_soc_write(codec, WM8960_POWER2,snd_soc_read(codec, WM8960_POWER2) ~1);if (!freq_in || !freq_out)return 0;reg snd_soc_read(codec, WM8960_PLL1) ~0x3f;reg | pll_div.pre_div 4;reg | pll_div.n;if (pll_div.k) {reg | 0x20;snd_soc_write(codec, WM8960_PLL2, (pll_div.k 18) 0x3f);snd_soc_write(codec, WM8960_PLL3, (pll_div.k 9) 0x1ff);snd_soc_write(codec, WM8960_PLL4, pll_div.k 0x1ff);}snd_soc_write(codec, WM8960_PLL1, reg);/* Turn it on */snd_soc_write(codec, WM8960_POWER2,snd_soc_read(codec, WM8960_POWER2) | 1);msleep(250);snd_soc_write(codec, WM8960_CLOCK1,snd_soc_read(codec, WM8960_CLOCK1) | 1);return 0;}static int wm8960_set_dai_clkdiv(struct snd_soc_dai *codec_dai,int div_id, int div){struct snd_soc_codec *codec codec_dai-codec;u16 reg;switch (div_id) {case WM8960_SYSCLKDIV:reg snd_soc_read(codec, WM8960_CLOCK1) 0x1f9;snd_soc_write(codec, WM8960_CLOCK1, reg | div);break;case WM8960_DACDIV:reg snd_soc_read(codec, WM8960_CLOCK1) 0x1c7;snd_soc_write(codec, WM8960_CLOCK1, reg | div);break;case WM8960_OPCLKDIV:reg snd_soc_read(codec, WM8960_PLL1) 0x03f;snd_soc_write(codec, WM8960_PLL1, reg | div);break;case WM8960_DCLKDIV:reg snd_soc_read(codec, WM8960_CLOCK2) 0x03f;snd_soc_write(codec, WM8960_CLOCK2, reg | div);break;case WM8960_TOCLKSEL:reg snd_soc_read(codec, WM8960_ADDCTL1) 0x1fd;snd_soc_write(codec, WM8960_ADDCTL1, reg | div);break;default:return -EINVAL;}return 0;}static int wm8960_set_bias_level(struct snd_soc_codec *codec,enum snd_soc_bias_level level){struct wm8960_priv *wm8960 snd_soc_codec_get_drvdata(codec);return wm8960-set_bias_level(codec, level);}#define WM8960_RATES SNDRV_PCM_RATE_8000_48000#define WM8960_FORMATS \(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE)static struct snd_soc_dai_ops wm8960_dai_ops {.hw_params wm8960_hw_params,.digital_mute wm8960_mute,.set_fmt wm8960_set_dai_fmt,.set_clkdiv wm8960_set_dai_clkdiv,.set_pll wm8960_set_dai_pll,};/*这个是dai接口的驱动*/static struct snd_soc_dai_driver wm8960_dai {/** 这个name会传递给snd_soc_codec.name, 后者会和* codec_conf-dev_name匹配*/.name wm8960-hifi,.playback {.stream_name Playback,.channels_min 1,.channels_max 2,.rates WM8960_RATES,.formats WM8960_FORMATS,},.capture {.stream_name Capture,.channels_min 1,.channels_max 2,.rates WM8960_RATES,.formats WM8960_FORMATS,},.ops wm8960_dai_ops,.symmetric_rates 1,};static int wm8960_suspend(struct snd_soc_codec *codec, pm_message_t state){struct wm8960_priv *wm8960 snd_soc_codec_get_drvdata(codec);wm8960-set_bias_level(codec, SND_SOC_BIAS_OFF);return 0;}static int wm8960_resume(struct snd_soc_codec *codec){struct wm8960_priv *wm8960 snd_soc_codec_get_drvdata(codec);int i;u8 data[2];u16 *cache codec-reg_cache;/* Sync reg_cache with the hardware */for (i 0; i ARRAY_SIZE(wm8960_reg); i) {data[0] (i 1) | ((cache[i] 8) 0x0001);data[1] cache[i] 0x00ff;codec-hw_write(codec-control_data, data, 2);}wm8960-set_bias_level(codec, SND_SOC_BIAS_STANDBY);return 0;}static int wm8960_probe(struct snd_soc_codec *codec){struct wm8960_priv *wm8960 snd_soc_codec_get_drvdata(codec);struct wm8960_data *pdata dev_get_platdata(codec-dev);int ret;u16 reg;wm8960-set_bias_level wm8960_set_bias_level_out3;codec-control_data wm8960-control_data;if (!pdata) {dev_warn(codec-dev, No platform data supplied\n);} else {if (pdata-dres WM8960_DRES_MAX) {dev_err(codec-dev, Invalid DRES: %d\n, pdata-dres);pdata-dres 0;}if (pdata-capless)wm8960-set_bias_level wm8960_set_bias_level_capless;}/*选中codec的控制接口*/ret snd_soc_codec_set_cache_io(codec, 7, 9, wm8960-control_type);if (ret 0) {dev_err(codec-dev, Failed to set cache I/O: %d\n, ret);return ret;}ret wm8960_reset(codec);if (ret 0) {dev_err(codec-dev, Failed to issue reset\n);return ret;}wm8960-set_bias_level(codec, SND_SOC_BIAS_STANDBY);/*对芯片寄存器进行初始化*//* Latch the update bits */reg snd_soc_read(codec, WM8960_LINVOL);snd_soc_write(codec, WM8960_LINVOL, reg | 0x100);reg snd_soc_read(codec, WM8960_RINVOL);snd_soc_write(codec, WM8960_RINVOL, reg | 0x100);reg snd_soc_read(codec, WM8960_LADC);snd_soc_write(codec, WM8960_LADC, reg | 0x100);reg snd_soc_read(codec, WM8960_RADC);snd_soc_write(codec, WM8960_RADC, reg | 0x100);reg snd_soc_read(codec, WM8960_LDAC);snd_soc_write(codec, WM8960_LDAC, reg | 0x100);reg snd_soc_read(codec, WM8960_RDAC);snd_soc_write(codec, WM8960_RDAC, reg | 0x100);reg snd_soc_read(codec, WM8960_LOUT1);snd_soc_write(codec, WM8960_LOUT1, reg | 0x100);reg snd_soc_read(codec, WM8960_ROUT1);snd_soc_write(codec, WM8960_ROUT1, reg | 0x100);reg snd_soc_read(codec, WM8960_LOUT2);snd_soc_write(codec, WM8960_LOUT2, reg | 0x100);reg snd_soc_read(codec, WM8960_ROUT2);snd_soc_write(codec, WM8960_ROUT2, reg | 0x100);/* 如果不想每次上电录音之前都要执行这些命令, 就在此设置寄存器:* tinymix Capture Switch 0* tinymix Left Input Mixer Boost Switch 1*//* Capture Switch off */snd_soc_update_bits(codec, WM8960_LINVOL, (17), (07));/* Left Input Mixer Boost Switch */snd_soc_update_bits(codec, WM8960_LINPATH, (13), (13));/*初始化controls和widgets*/snd_soc_add_controls(codec, wm8960_snd_controls,ARRAY_SIZE(wm8960_snd_controls));wm8960_add_widgets(codec);return 0;}/* power down chip */static int wm8960_remove(struct snd_soc_codec *codec){struct wm8960_priv *wm8960 snd_soc_codec_get_drvdata(codec);wm8960-set_bias_level(codec, SND_SOC_BIAS_OFF);return 0;}/*这个是codec芯片的驱动*/static struct snd_soc_codec_driver soc_codec_dev_wm8960 {.probe wm8960_probe,.remove wm8960_remove,.suspend wm8960_suspend,.resume wm8960_resume,.set_bias_level wm8960_set_bias_level,.reg_cache_size ARRAY_SIZE(wm8960_reg),.reg_word_size sizeof(u16),.reg_cache_default wm8960_reg,};/*这是主机i2c控制接口的驱动*/#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)static __devinit int wm8960_i2c_probe(struct i2c_client *i2c,const struct i2c_device_id *id){struct wm8960_priv *wm8960;int ret;wm8960 kzalloc(sizeof(struct wm8960_priv), GFP_KERNEL);if (wm8960 NULL)return -ENOMEM;i2c_set_clientdata(i2c, wm8960);/*指定对codec芯片的控制接口是i2c*/wm8960-control_type SND_SOC_I2C;wm8960-control_data i2c;/** 注册一个codec需要提供snd_soc_codec_driver和snd_soc_dai_driver* 同时可以有多个dai* 这个函数内部会实例化一个snd_soc_codec若全局链表上有数据还会去实例化* 一个snd_soc_card(这个结构在其它地方也能实例化)*/ret snd_soc_register_codec(i2c-dev,soc_codec_dev_wm8960, wm8960_dai, 1);if (ret 0)kfree(wm8960);return ret;}static __devexit int wm8960_i2c_remove(struct i2c_client *client){snd_soc_unregister_codec(client-dev);kfree(i2c_get_clientdata(client));return 0;}/*设备端在mach-tiny4412.c中定义*/static const struct i2c_device_id wm8960_i2c_id[] {{ wm8960, 0 },{ }};MODULE_DEVICE_TABLE(i2c, wm8960_i2c_id);/*它是怎么通过名字匹配到设备的呢?*/static struct i2c_driver wm8960_i2c_driver {.driver {.name wm8960-codec,.owner THIS_MODULE,},.probe wm8960_i2c_probe,.remove __devexit_p(wm8960_i2c_remove),.id_table wm8960_i2c_id,};#endifstatic int __init wm8960_modinit(void){int ret 0;#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)ret i2c_add_driver(wm8960_i2c_driver);if (ret ! 0) {printk(KERN_ERR Failed to register WM8960 I2C driver: %d\n,ret);}#endifreturn ret;}module_init(wm8960_modinit);static void __exit wm8960_exit(void){#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)i2c_del_driver(wm8960_i2c_driver);#endif}module_exit(wm8960_exit);MODULE_DESCRIPTION(ASoC WM8960 driver);MODULE_AUTHOR(Liam Girdwood);MODULE_LICENSE(GPL);View Code(2) Machine平台设备驱动设备端(驱动端是: sound/soc/soc-core.c)#include #include #include #include #include #include #include i2s.hstatic int set_epll_rate(unsigned long rate){struct clk *fout_epll;fout_epll clk_get(NULL, fout_epll);if (IS_ERR(fout_epll)) {printk(KERN_ERR %s: failed to get fout_epll\n, __func__);return PTR_ERR(fout_epll);}if (rate clk_get_rate(fout_epll))goto out;clk_set_rate(fout_epll, rate);out:clk_put(fout_epll);return 0;}/*RFSIIS Root clk freq selectBFSbit clock freq select*/static int smdk_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_dai *codec_dai rtd-codec_dai;struct snd_soc_dai *cpu_dai rtd-cpu_dai;int bfs, psr, rfs, ret;unsigned long rclk;switch (params_format(params)) {case SNDRV_PCM_FORMAT_U24:case SNDRV_PCM_FORMAT_S24:bfs 48;break;case SNDRV_PCM_FORMAT_U16_LE:case SNDRV_PCM_FORMAT_S16_LE:bfs 32;break;default:return -EINVAL;}switch (params_rate(params)) {case 16000:case 22050:case 24000:case 32000:case 44100:case 48000:case 88200:case 96000:if (bfs 48)rfs 384; /*rfsbfs*8*/elserfs 256;break;case 64000:rfs 384;break;case 8000:case 11025:case 12000:if (bfs 48)rfs 768;elserfs 512;break;default:return -EINVAL;}rclk params_rate(params) * rfs;switch (rclk) {case 4096000:case 5644800:case 6144000:case 8467200:case 9216000:psr 8;break;case 8192000:case 11289600:case 12288000:case 16934400:case 18432000:psr 4;break;case 22579200:case 24576000:case 33868800:case 36864000:psr 2;break;case 67737600:case 73728000:psr 1;break;default:printk(Not yet supported!\n);return -EINVAL;}set_epll_rate(rclk * psr);/*调用platform驱动中的dai驱动的函数*/ret snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S| SND_SOC_DAIFMT_NB_NF| SND_SOC_DAIFMT_CBS_CFS);if (ret 0)return ret;ret snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S| SND_SOC_DAIFMT_NB_NF| SND_SOC_DAIFMT_CBS_CFS);if (ret 0)return ret;ret snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_CDCLK,0, SND_SOC_CLOCK_OUT);if (ret 0)return ret;ret snd_soc_dai_set_clkdiv(cpu_dai, SAMSUNG_I2S_DIV_BCLK, bfs);if (ret 0)return ret;return 0;}/* 参考sound\soc\samsung\s3c24xx_uda134x.c*//** 1. 分配注册一个名为soc-audio的平台设备* 2. 这个平台设备有一个私有数据 snd_soc_card* snd_soc_card里有一项snd_soc_dai_link* snd_soc_dai_link被用来决定ASOC各部分的驱动*/static struct snd_soc_ops tiny4412_wm8960_ops {/*这里面指定的params从哪里来????*/.hw_params smdk_hw_params,};/*#define SND_SOC_DAPM_MIC(wname, wevent){.id snd_soc_dapm_mic,.name Mic Onboard,.kcontrol_news NULL,.num_kcontrols 0,.reg SND_SOC_NOPM,.event NULL,.event_flags SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD关系的dapm事件:SND_SOC_DAPM_PRE_PMU: widget要上电前发出的事件SND_SOC_DAPM_POST_PMD: widget要下电后发出的事件}这也是一条输入line的一部分但是由于是板级的所以写在这个文件中.处理单板上的widget,定义的虚拟widget*/static const struct snd_soc_dapm_widget tiny4412_wm8960_widgets[] {SND_SOC_DAPM_MIC(Mic Onboard, NULL),};static const struct snd_soc_dapm_route tiny4412_wm8960_paths[] {{ MICB, NULL, Mic Onboard },{ LINPUT1, NULL, MICB },};static int tiny4412_wm8960_machine_init(struct snd_soc_pcm_runtime *rtd){struct snd_soc_codec *codec rtd-codec;struct snd_soc_dapm_context *dapm codec-dapm;/* 添加一个虚拟的MIC widget */snd_soc_dapm_new_controls(dapm, tiny4412_wm8960_widgets,ARRAY_SIZE(tiny4412_wm8960_widgets));/* 添加2个route */snd_soc_dapm_add_routes(dapm, tiny4412_wm8960_paths, ARRAY_SIZE(tiny4412_wm8960_paths));#define WM8960_IFACE2 0x9//设置Pin15(ADCLRC/GPIO)为GPIO 如果不设置而且Pin15上又没有外部时钟则ADC 工作异常snd_soc_update_bits(codec, WM8960_IFACE2, 0x40, 0x40);snd_soc_dapm_sync(dapm);return 0;}/** snd_soc_dai_link被用来决定ASOC各部分的驱动。* 通过名字的匹配过程是在soc_bind_dai_link()中完成的。*/static struct snd_soc_dai_link tiny4412_wm8960_dai_link {/*这两个名字不用与匹配信息*/.name NAME_UDA8960,.stream_name STREAM_NAME_UDA8960,/** wm8960-codec.0-001a这个名字来自codec驱动分为2部分* wm8960-codec是在wm8960.c中指定的i2c驱动的名字0-001a是i2c* 设备的设备地址组合起来称为snd_soc_codec-name。* 这个是用来匹配wm8960这个codec驱动的。*/.codec_name wm8960-codec.0-001a,/** 这个名字用于匹配wm8960.c中注册的dai而这个codec的dai的name* 来自snd_soc_dai_driver-name, 因此指定为codec中注册的* snd_soc_dai_driver中的name就可以了。* 作用: 用于匹配codec中的哪个codec_dai*/.codec_dai_name wm8960-hifi,/** 这个名字用于和sound\soc\samsung\I2s.c中使用snd_soc_register_dai注册的dai_name* 匹配上才行, 应该用于Soc与Codec之间通信的接口的选择。** codec_dai和cpu_dai都确定下来了那么一条dai连接就确定下来了。*/.cpu_dai_name samsung-i2s.0,.ops tiny4412_wm8960_ops,/** 用于选择使用哪个platform(Soc相关)驱动指定这个名字是要求* 使用sound\soc\samsung\dma.c中注册的snd_soc_platform* 一个Soc可能注册了多个snd_soc_platform这个用于选择使用哪个。*/.platform_name samsung-audio,.init tiny4412_wm8960_machine_init,};static struct snd_soc_card myalsa_card {/*这里面的几个名字又该如何赋值????*//*这个名字去掉_显示在//sys/devices/platform/soc-audio/sound/card0/id中*/.name TINY4412_UDA8960,.owner THIS_MODULE,/** 这里dai_link其实是个数组num_links是数组项个数* 这里只有1项就直接当指针使用了。*/.dai_link tiny4412_wm8960_dai_link,.num_links 1,};static void asoc_release(struct device * dev){}/** 驱动端:/sound/soc/soc-core.c, 驱动已经在core中抽象出来了。** 注册成平台设备snd_soc_card最为平台设备的data,在平台设备* 的驱动端soc-core.c注册。*/static struct platform_device asoc_dev {/*通过这个名字匹配驱动soc-core.c公有的驱动*/.name soc-audio,.id -1,.dev {.release asoc_release,},};static int tiny4412_wm8960_init(void){platform_set_drvdata(asoc_dev, myalsa_card);platform_device_register(asoc_dev);return 0;}static void tiny4412_wm8960_exit(void){platform_device_unregister(asoc_dev);}module_init(tiny4412_wm8960_init);module_exit(tiny4412_wm8960_exit);MODULE_LICENSE(GPL);View Code
http://wiki.neutronadmin.com/news/336685/

相关文章:

  • 南江红鱼洞水库建设管理局网站广州市市场监督管理局
  • 做蛋糕视频的网站富库网站建设
  • 温岭做网站公司企业网站建设需要提供什么内容
  • 基于微信的网站开发wordpress 手机首页
  • 做音乐的网站微信公众号平台开发文档
  • asp加dw做网站广西网站建设路
  • 烟台百度网站php网站建设文献综述
  • 国内知名网站太原市建设厅网站首页
  • 哪个网站有适合小学生做的题亿建联网站是谁做的
  • 只做瓶子包装设计的创意网站网上做效果图网站
  • asp网站实例wordpress中文破解主题下载
  • 一个网站做各种好玩的实验班级网站网页设计
  • 自己做网站需要做啥网站推广计划包括哪些
  • 南宁网站建设超薄网络珠海做网站优化
  • 百度上如何做优化网站上海市企业服务云网站
  • 网站建设与管理案例...免费咨询法律援助该打什么电话
  • 网站开发实战网络课个人网站html源码
  • 合肥做检查军大网站小程序是怎么开发的
  • 收费网站解决方案网站怎么设计制作
  • 用html做的网站加背景音乐企业运营公司
  • 网站建设公司的公司浙江省建设银行网站首页
  • 做静态网站d微信官方小程序开发工具
  • 手表网站西安做网站微信公司哪家好
  • 网站备案需要建设好网站吗建设电瓶车官方网站
  • 视频网站后台登陆wordpress简洁cms主题
  • 网站换稳定服务器做企业网站用drupal7
  • 和文化有关的吉网站建设模板企业网站网站建设公司
  • 做购物平台网站客户体验活动开发公司总经理竞聘报告
  • 网站怎么添加流量肉多各种地方做的网站
  • 网站开发属于哪个大学专业阿克苏建设局网站