diff --git a/Makefile b/Makefile index d62649d..0fdf5e9 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,10 @@ -obj-m := wm8960.o +snd-soc-wm8960-objs := wm8960.o +snd-soc-ac108-objs := ac108.o + + +obj-m += snd-soc-wm8960.o +obj-m += snd-soc-ac108.o + all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules @@ -7,5 +13,6 @@ clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean install: - sudo cp wm8960.ko /lib/modules/$(shell uname -r) + sudo cp snd-soc-ac108.ko /lib/modules/$(shell uname -r)/kernel/sound/soc/codecs/ + sudo cp snd-soc-wm8960.ko /lib/modules/$(shell uname -r)/kernel/sound/soc/codecs/ sudo depmod -a diff --git a/README.md b/README.md index b0c5616..8518545 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,26 @@ # seeed-voicecard - -[![Join the chat at https://gitter.im/seeed-voicecard/Lobby](https://badges.gitter.im/seeed-voicecard/Lobby.svg)](https://gitter.im/seeed-voicecard/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +[![Join the chat at https://gitter.im/seeed-voicecard/Lobby](https://badges.gitter.im/seeed-voicecard/Lobby.svg)](https://gitter.im/seeed-voicecard/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) While the upstream wm8960 codec is not currently supported by current Pi kernel builds, upstream wm8960 has some bugs, we had fixed it. we must it build manually. +We also write ac108 rapberry pi linux kernel driver. + Get the seeed voice card source code. ``` git clone https://github.com/respeaker/seeed-voicecard cd seeed-voicecard -sudo ./install.sh +#2mic +sudo ./install.sh 2mic +#4mic +sudo ./install.sh 4mic reboot ``` Check that the sound card name matches the source code seeed-voicecard. ``` +#2mic pi@raspberrypi:~/seeed-voicecard$ aplay -l **** List of PLAYBACK Hardware Devices **** card 0: ALSA [bcm2835 ALSA], device 0: bcm2835 ALSA [bcm2835 ALSA] @@ -34,16 +40,45 @@ card 1: seeedvoicecard [seeed-voicecard], device 0: bcm2835-i2s-wm8960-hifi wm89 Subdevices: 1/1 Subdevice #0: subdevice #0 pi@raspberrypi:~/seeed-voicecard$ -``` -Next apply the alsa controls setting -``` -sudo alsactl --file=asound.state restore + +#4mic +pi@raspberrypi:~ $ arecord -L +null + Discard all samples (playback) or generate zero samples (capture) +playback +capture +dmixed +array +ac108 +default:CARD=seeed4micvoicec + seeed-4mic-voicecard, + Default Audio Device +sysdefault:CARD=seeed4micvoicec + seeed-4mic-voicecard, + Default Audio Device +dmix:CARD=seeed4micvoicec,DEV=0 + seeed-4mic-voicecard, + Direct sample mixing device +dsnoop:CARD=seeed4micvoicec,DEV=0 + seeed-4mic-voicecard, + Direct sample snooping device +hw:CARD=seeed4micvoicec,DEV=0 + seeed-4mic-voicecard, + Direct hardware device without any conversions +plughw:CARD=seeed4micvoicec,DEV=0 + seeed-4mic-voicecard, + Hardware device with all software conversions +pi@raspberrypi:~ $ ``` If you want to change the alsa settings, You can use `sudo alsactl --file=asound.state store` to save it. Test: -``` +``` +#2mic arecord -f cd -Dhw:1 | aplay -Dhw:1 + +#4mic +arecord -Dac108 -f S32_LE -r 16000 -c 4 a.wav ``` ### with Google Assistant diff --git a/ac108.c b/ac108.c new file mode 100644 index 0000000..03589e4 --- /dev/null +++ b/ac108.c @@ -0,0 +1,1297 @@ +/* + * ac108.c -- ac108 ALSA SoC Audio driver + * + * + * Author: Baozhu Zuo + * + * 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. + */ +#define DEBUG 1 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ac108.h" + +/** + * TODO: + * 1, add PM API: ac108_suspend,ac108_resume + * 2, add set_pll ,set_clkdiv + * 3,0x65-0x6a + * 4,0x76-0x79 high 4bit + */ +struct pll_div { + unsigned int freq_in; + unsigned int freq_out; + unsigned int m1; + unsigned int m2; + unsigned int n; + unsigned int k1; + unsigned int k2; +}; + + +struct ac108_priv { + struct i2c_client *i2c[4]; + int codec_index; + int sysclk; + int clk_id; + unsigned char i2s_mode; + unsigned char data_protocol; +}; +static struct ac108_priv *ac108; + +struct real_val_to_reg_val { + unsigned int real_val; + unsigned int reg_val; +}; + +static const struct real_val_to_reg_val ac108_sample_rate[] = { + { 8000, 0 }, + { 11025, 1 }, + { 12000, 2 }, + { 16000, 3 }, + { 22050, 4 }, + { 24000, 5 }, + { 32000, 6 }, + { 44100, 7 }, + { 48000, 8 }, + { 96000, 9 }, +}; + +static const struct real_val_to_reg_val ac108_sample_resolution[] = { + { 8, 1 }, + { 12, 2 }, + { 16, 3 }, + { 20, 4 }, + { 24, 5 }, + { 28, 6 }, + { 32, 7 }, +}; + +/* FOUT =(FIN * N) / [(M1+1) * (M2+1)*(K1+1)*(K2+1)] ; M1[0,31], M2[0,1], N[0,1023], K1[0,31], K2[0,1] */ +static const struct pll_div ac108_pll_div_list[] = { + { 400000, 24576000, 0, 0, 614, 4, 1 }, + { 512000, 24576000, 0, 0, 960, 9, 1 }, //24576000/48 + { 768000, 24576000, 0, 0, 640, 9, 1 }, //24576000/32 + { 800000, 24576000, 0, 0, 614, 9, 1 }, + { 1024000, 24576000, 0, 0, 480, 9, 1 }, //24576000/24 + { 1600000, 24576000, 0, 0, 307, 9, 1 }, + { 2048000, 24576000, 0, 0, 240, 9, 1 }, //24576000/12 + { 3072000, 24576000, 0, 0, 160, 9, 1 }, //24576000/8 + { 4096000, 24576000, 2, 0, 360, 9, 1 }, //24576000/6 + { 6000000, 24576000, 4, 0, 410, 9, 1 }, + { 12000000, 24576000, 9, 0, 410, 9, 1 }, + { 13000000, 24576000, 8, 0, 340, 9, 1 }, + { 15360000, 24576000, 12, 0, 415, 9, 1 }, + { 16000000, 24576000, 12, 0, 400, 9, 1 }, + { 19200000, 24576000, 15, 0, 410, 9, 1 }, + { 19680000, 24576000, 15, 0, 400, 9, 1 }, + { 24000000, 24576000, 9, 0, 205, 9, 1 }, + + { 400000, 22579200, 0, 0, 566, 4, 1 }, + { 512000, 22579200, 0, 0, 880, 9, 1 }, + { 768000, 22579200, 0, 0, 587, 9, 1 }, + { 800000, 22579200, 0, 0, 567, 9, 1 }, + { 1024000, 22579200, 0, 0, 440, 9, 1 }, + { 1600000, 22579200, 1, 0, 567, 9, 1 }, + { 2048000, 22579200, 0, 0, 220, 9, 1 }, + { 3072000, 22579200, 0, 0, 148, 9, 1 }, + { 4096000, 22579200, 2, 0, 330, 9, 1 }, + { 6000000, 22579200, 2, 0, 227, 9, 1 }, + { 12000000, 22579200, 8, 0, 340, 9, 1 }, + { 13000000, 22579200, 9, 0, 350, 9, 1 }, + { 15360000, 22579200, 10, 0, 325, 9, 1 }, + { 16000000, 22579200, 11, 0, 340, 9, 1 }, + { 19200000, 22579200, 13, 0, 330, 9, 1 }, + { 19680000, 22579200, 14, 0, 345, 9, 1 }, + { 24000000, 22579200, 16, 0, 320, 9, 1 }, + + { 12288000, 24576000, 9, 0, 400, 9, 1 }, //24576000/2 + { 11289600, 22579200, 9, 0, 400, 9, 1 }, //22579200/2 + + { 24576000 / 1, 24576000, 9, 0, 200, 9, 1 }, //24576000 + { 24576000 / 4, 24576000, 4, 0, 400, 9, 1 }, //6144000 + { 24576000 / 16, 24576000, 0, 0, 320, 9, 1 }, //1536000 + { 24576000 / 64, 24576000, 0, 0, 640, 4, 1 }, //384000 + { 24576000 / 96, 24576000, 0, 0, 960, 4, 1 }, //256000 + { 24576000 / 128, 24576000, 0, 0, 512, 1, 1 }, //192000 + { 24576000 / 176, 24576000, 0, 0, 880, 4, 0 }, //140000 + { 24576000 / 192, 24576000, 0, 0, 960, 4, 0 }, //128000 + + { 22579200 / 1, 22579200, 9, 0, 200, 9, 1 }, //22579200 + { 22579200 / 4, 22579200, 4, 0, 400, 9, 1 }, //5644800 + { 22579200 / 16, 22579200, 0, 0, 320, 9, 1 }, //1411200 + { 22579200 / 64, 22579200, 0, 0, 640, 4, 1 }, //352800 + { 22579200 / 96, 22579200, 0, 0, 960, 4, 1 }, //235200 + { 22579200 / 128, 22579200, 0, 0, 512, 1, 1 }, //176400 + { 22579200 / 176, 22579200, 0, 0, 880, 4, 0 }, //128290 + { 22579200 / 192, 22579200, 0, 0, 960, 4, 0 }, //117600 + + { 22579200 / 6, 22579200, 2, 0, 360, 9, 1 }, //3763200 + { 22579200 / 8, 22579200, 0, 0, 160, 9, 1 }, //2822400 + { 22579200 / 12, 22579200, 0, 0, 240, 9, 1 }, //1881600 + { 22579200 / 24, 22579200, 0, 0, 480, 9, 1 }, //940800 + { 22579200 / 32, 22579200, 0, 0, 640, 9, 1 }, //705600 + { 22579200 / 48, 22579200, 0, 0, 960, 9, 1 }, //470400 +}; + + + +//AC108 define +#define AC108_CHANNELS_MAX 16 //range[1, 16] +#define AC108_RATES (SNDRV_PCM_RATE_8000_96000 | SNDRV_PCM_RATE_KNOT) +#define AC108_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const DECLARE_TLV_DB_SCALE(adc1_pga_gain_tlv, 0, 100, 0); +static const DECLARE_TLV_DB_SCALE(adc2_pga_gain_tlv, 0, 100, 0); +static const DECLARE_TLV_DB_SCALE(adc3_pga_gain_tlv, 0, 100, 0); +static const DECLARE_TLV_DB_SCALE(adc4_pga_gain_tlv, 0, 100, 0); + +static const DECLARE_TLV_DB_SCALE(ch1_digital_vol_tlv, -11925,75,0); +static const DECLARE_TLV_DB_SCALE(ch2_digital_vol_tlv, -11925,75,0); +static const DECLARE_TLV_DB_SCALE(ch3_digital_vol_tlv, -11925,75,0); +static const DECLARE_TLV_DB_SCALE(ch4_digital_vol_tlv, -11925,75,0); + +static const DECLARE_TLV_DB_SCALE(digital_mix_vol_tlv, -600,600,0); + +static const DECLARE_TLV_DB_SCALE(channel_enable_tlv, -1500, 100, 0); + +/* Analog ADC */ +static const char *analog_adc_mux_text[] = { + "Analog ADC1", + "Analog ADC2", + "Analog ADC3", + "Analog ADC4", +}; + +/* Channel Mapping */ +static const char *channel_map_mux_text[] = { + "1st adc sample", + "2st adc sample", + "3st adc sample", + "4st adc sample", +}; + +/*Tx source select channel*/ +static const char *channels_src_mux_text[] = { + "1 channels ", + "2 channels ", + "3 channels ", + "4 channels ", + "5 channels ", + "6 channels ", + "7 channels ", + "8 channels ", + "9 channels ", + "10 channels ", + "11 channels ", + "12 channels ", + "13 channels ", + "14 channels ", + "15 channels ", + "16 channels ", +}; + +static const unsigned int ac108_channel_enable_values[] = { + 0x00, + 0x01, + 0x03, + 0x07, + 0x0f, + 0x1f, + 0x3f, + 0x7f, + 0xff, +}; + +static const char *const ac108_channel_low_enable_texts[] = { + "disable all", + "1-1 channels ", + "1-2 channels ", + "1-3 channels ", + "1-4 channels ", + "1-5 channels ", + "1-6 channels ", + "1-7 channels ", + "1-8 channels ", +}; +static const char *const ac108_channel_high_enable_texts[] = { + "disable all", + "8-9 channels ", + "8-10 channels ", + "8-11 channels ", + "8-12 channels ", + "8-13 channels ", + "8-14 channels ", + "8-15 channels ", + "8-16 channels ", +}; + +static const char *const ac108_data_source_texts[] = { + "disable all", + "ADC1 data", + "ADC2 data", + "ADC3 data", + "ADC4 data", +}; +static const unsigned int ac108_data_source_values[] = { + 0x00, + 0x01, + 0x02, + 0x04, + 0x08, +}; +static SOC_VALUE_ENUM_SINGLE_DECL(ac108_tx1_channel_low_enum, + I2S_TX1_CTRL2, 0, 0xff, + ac108_channel_low_enable_texts, + ac108_channel_enable_values); + +static SOC_VALUE_ENUM_SINGLE_DECL(ac108_tx1_channel_high_enum, + I2S_TX1_CTRL3, 0, 0xff, + ac108_channel_high_enable_texts, + ac108_channel_enable_values); + +static SOC_VALUE_ENUM_SINGLE_DECL(ac108_tx2_channel_low_enum, + I2S_TX2_CTRL2, 0, 0xff, + ac108_channel_low_enable_texts, + ac108_channel_enable_values); + +static SOC_VALUE_ENUM_SINGLE_DECL(ac108_tx2_channel_high_enum, + I2S_TX2_CTRL3, 0, 0xff, + ac108_channel_high_enable_texts, + ac108_channel_enable_values); +/*0x76: ADC1 Digital Mixer Source Control Register*/ +static SOC_VALUE_ENUM_SINGLE_DECL(ac108_adc1_data_src_enum, + ADC1_DMIX_SRC, 0, 0x0f, + ac108_data_source_texts, + ac108_data_source_values); +static SOC_VALUE_ENUM_SINGLE_DECL(ac108_adc1_data_gc_enum, + ADC1_DMIX_SRC, ADC1_ADC1_DMXL_GC, 0xf0, + ac108_data_source_texts, + ac108_data_source_values); +/*0x77: ADC2 Digital Mixer Source Control Register*/ +static SOC_VALUE_ENUM_SINGLE_DECL(ac108_adc2_data_src_enum, + ADC2_DMIX_SRC, 0, 0x0f, + ac108_data_source_texts, + ac108_data_source_values); +static SOC_VALUE_ENUM_SINGLE_DECL(ac108_adc2_data_gc_enum, + ADC2_DMIX_SRC, ADC2_ADC1_DMXL_GC, 0xf0, + ac108_data_source_texts, + ac108_data_source_values); +/*0x78: ADC3 Digital Mixer Source Control Register*/ +static SOC_VALUE_ENUM_SINGLE_DECL(ac108_adc3_data_src_enum, + ADC3_DMIX_SRC, 0, 0x0f, + ac108_data_source_texts, + ac108_data_source_values); +static SOC_VALUE_ENUM_SINGLE_DECL(ac108_adc3_data_gc_enum, + ADC3_DMIX_SRC, ADC3_ADC1_DMXL_GC, 0xf0, + ac108_data_source_texts, + ac108_data_source_values); +/*0x79: ADC4 Digital Mixer Source Control Register*/ +static SOC_VALUE_ENUM_SINGLE_DECL(ac108_adc4_data_src_enum, + ADC4_DMIX_SRC, 0, 0x0f, + ac108_data_source_texts, + ac108_data_source_values); +static SOC_VALUE_ENUM_SINGLE_DECL(ac108_adc4_data_gc_enum, + ADC4_DMIX_SRC, ADC4_ADC1_DMXL_GC, 0xf0, + ac108_data_source_texts, + ac108_data_source_values); + +static const struct soc_enum ac108_enum[] = { + /*0x38:TX1 Channel (slot) number Select for each output*/ + SOC_ENUM_SINGLE(I2S_TX1_CTRL1, TX1_CHSEL, 16, channels_src_mux_text), + /*0x40:TX1 Channel (slot) number Select for each output*/ + SOC_ENUM_SINGLE(I2S_TX2_CTRL1, TX2_CHSEL, 16, channels_src_mux_text), + /*0x3c: TX1 Channel Mapping Control 1*/ + SOC_ENUM_SINGLE(I2S_TX1_CHMP_CTRL1, TX1_CH1_MAP, 4, channel_map_mux_text), + SOC_ENUM_SINGLE(I2S_TX1_CHMP_CTRL1, TX1_CH2_MAP, 4, channel_map_mux_text), + SOC_ENUM_SINGLE(I2S_TX1_CHMP_CTRL1, TX1_CH3_MAP, 4, channel_map_mux_text), + SOC_ENUM_SINGLE(I2S_TX1_CHMP_CTRL1, TX1_CH4_MAP, 4, channel_map_mux_text), + + /*0x3d: TX1 Channel Mapping Control 2*/ + SOC_ENUM_SINGLE(I2S_TX1_CHMP_CTRL2, TX1_CH5_MAP, 4, channel_map_mux_text), + SOC_ENUM_SINGLE(I2S_TX1_CHMP_CTRL2, TX1_CH6_MAP, 4, channel_map_mux_text), + SOC_ENUM_SINGLE(I2S_TX1_CHMP_CTRL2, TX1_CH7_MAP, 4, channel_map_mux_text), + SOC_ENUM_SINGLE(I2S_TX1_CHMP_CTRL2, TX1_CH8_MAP, 4, channel_map_mux_text), + + /*0x3e: TX1 Channel Mapping Control 3*/ + SOC_ENUM_SINGLE(I2S_TX1_CHMP_CTRL3, TX1_CH9_MAP, 4, channel_map_mux_text), + SOC_ENUM_SINGLE(I2S_TX1_CHMP_CTRL3, TX1_CH10_MAP, 4, channel_map_mux_text), + SOC_ENUM_SINGLE(I2S_TX1_CHMP_CTRL3, TX1_CH11_MAP, 4, channel_map_mux_text), + SOC_ENUM_SINGLE(I2S_TX1_CHMP_CTRL3, TX1_CH12_MAP, 4, channel_map_mux_text), + + /*0x3f: TX1 Channel Mapping Control 4*/ + SOC_ENUM_SINGLE(I2S_TX1_CHMP_CTRL4, TX1_CH13_MAP, 4, channel_map_mux_text), + SOC_ENUM_SINGLE(I2S_TX1_CHMP_CTRL4, TX1_CH14_MAP, 4, channel_map_mux_text), + SOC_ENUM_SINGLE(I2S_TX1_CHMP_CTRL4, TX1_CH15_MAP, 4, channel_map_mux_text), + SOC_ENUM_SINGLE(I2S_TX1_CHMP_CTRL4, TX1_CH16_MAP, 4, channel_map_mux_text), + + /*0x44: TX2 Channel Mapping Control 1*/ + SOC_ENUM_SINGLE(I2S_TX2_CHMP_CTRL1, TX2_CH1_MAP, 4, channel_map_mux_text), + SOC_ENUM_SINGLE(I2S_TX2_CHMP_CTRL1, TX2_CH2_MAP, 4, channel_map_mux_text), + SOC_ENUM_SINGLE(I2S_TX2_CHMP_CTRL1, TX2_CH3_MAP, 4, channel_map_mux_text), + SOC_ENUM_SINGLE(I2S_TX2_CHMP_CTRL1, TX2_CH4_MAP, 4, channel_map_mux_text), + + /*0x45: TX2 Channel Mapping Control 2*/ + SOC_ENUM_SINGLE(I2S_TX2_CHMP_CTRL2, TX2_CH5_MAP, 4, channel_map_mux_text), + SOC_ENUM_SINGLE(I2S_TX2_CHMP_CTRL2, TX2_CH6_MAP, 4, channel_map_mux_text), + SOC_ENUM_SINGLE(I2S_TX2_CHMP_CTRL2, TX2_CH7_MAP, 4, channel_map_mux_text), + SOC_ENUM_SINGLE(I2S_TX2_CHMP_CTRL2, TX2_CH8_MAP, 4, channel_map_mux_text), + + /*0x46: TX2 Channel Mapping Control 3*/ + SOC_ENUM_SINGLE(I2S_TX2_CHMP_CTRL3, TX2_CH9_MAP, 4, channel_map_mux_text), + SOC_ENUM_SINGLE(I2S_TX2_CHMP_CTRL3, TX2_CH10_MAP, 4, channel_map_mux_text), + SOC_ENUM_SINGLE(I2S_TX2_CHMP_CTRL3, TX2_CH11_MAP, 4, channel_map_mux_text), + SOC_ENUM_SINGLE(I2S_TX2_CHMP_CTRL3, TX2_CH12_MAP, 4, channel_map_mux_text), + + /*0x47: TX2 Channel Mapping Control 4*/ + SOC_ENUM_SINGLE(I2S_TX2_CHMP_CTRL4, TX2_CH13_MAP, 4, channel_map_mux_text), + SOC_ENUM_SINGLE(I2S_TX2_CHMP_CTRL4, TX2_CH14_MAP, 4, channel_map_mux_text), + SOC_ENUM_SINGLE(I2S_TX2_CHMP_CTRL4, TX2_CH15_MAP, 4, channel_map_mux_text), + SOC_ENUM_SINGLE(I2S_TX2_CHMP_CTRL4, TX2_CH16_MAP, 4, channel_map_mux_text), + + /*0x63: ADC Digital Source Select Register*/ + SOC_ENUM_SINGLE(ADC_DSR, DIG_ADC4_SRS, 4, analog_adc_mux_text), + SOC_ENUM_SINGLE(ADC_DSR, DIG_ADC3_SRS, 4, analog_adc_mux_text), + SOC_ENUM_SINGLE(ADC_DSR, DIG_ADC2_SRS, 4, analog_adc_mux_text), + SOC_ENUM_SINGLE(ADC_DSR, DIG_ADC1_SRS, 4, analog_adc_mux_text), + +}; + + +static const struct snd_kcontrol_new ac108_snd_controls[] = { + + SOC_SINGLE("OUT1 Mute", I2S_FMT_CTRL3, 3, 1, 0), + SOC_SINGLE("OUT2 Mute", I2S_FMT_CTRL3, 4, 1, 0), + + /*0x39:TX1 Channel1 ~Channel8 (slot) enable*/ + SOC_ENUM("TX1 Channel1~8 enable", ac108_tx1_channel_low_enum), + /*0x3A:TX1 Channel1 ~Channel8 (slot) enable*/ + SOC_ENUM("TX1 Channel9~16 enable", ac108_tx1_channel_high_enum), + /*0x41:TX2 Channel1 ~Channel8 (slot) enable*/ + SOC_ENUM("TX2 Channel1~8 enable", ac108_tx2_channel_low_enum), + /*0x42:TX2 Channel1 ~Channel8 (slot) enable*/ + SOC_ENUM("TX2 Channel9~16 enable", ac108_tx2_channel_high_enum), + + /*0x70: ADC1 Digital Channel Volume Control Register*/ + SOC_SINGLE_TLV("CH1 digital volume", ADC1_DVOL_CTRL, 0, 0xff, 0, ch1_digital_vol_tlv), + /*0x71: ADC2 Digital Channel Volume Control Register*/ + SOC_SINGLE_TLV("CH2 digital volume", ADC2_DVOL_CTRL, 0, 0xff, 0, ch2_digital_vol_tlv), + /*0x72: ADC3 Digital Channel Volume Control Register*/ + SOC_SINGLE_TLV("CH3 digital volume", ADC3_DVOL_CTRL, 0, 0xff, 0, ch3_digital_vol_tlv), + /*0x73: ADC4 Digital Channel Volume Control Register*/ + SOC_SINGLE_TLV("CH4 digital volume", ADC4_DVOL_CTRL, 0, 0xff, 0, ch4_digital_vol_tlv), + + /*0x90: Analog PGA1 Control Register*/ + SOC_SINGLE_TLV("ADC1 PGA gain", ANA_PGA1_CTRL, ADC1_ANALOG_PGA, 0x1f, 0, adc1_pga_gain_tlv), + /*0x91: Analog PGA2 Control Register*/ + SOC_SINGLE_TLV("ADC2 PGA gain", ANA_PGA2_CTRL, ADC2_ANALOG_PGA, 0x1f, 0, adc2_pga_gain_tlv), + /*0x92: Analog PGA3 Control Register*/ + SOC_SINGLE_TLV("ADC3 PGA gain", ANA_PGA3_CTRL, ADC3_ANALOG_PGA, 0x1f, 0, adc3_pga_gain_tlv), + /*0x93: Analog PGA4 Control Register*/ + SOC_SINGLE_TLV("ADC4 PGA gain", ANA_PGA4_CTRL, ADC4_ANALOG_PGA, 0x1f, 0, adc4_pga_gain_tlv), + + /*0x96-0x9F: use the default value*/ + + SOC_ENUM("Tx1 Channels", ac108_enum[0]), + SOC_ENUM("Tx2 Channels", ac108_enum[1]), + + SOC_ENUM("Tx1 Channels 1 MAP", ac108_enum[2]), + SOC_ENUM("Tx1 Channels 2 MAP", ac108_enum[3]), + SOC_ENUM("Tx1 Channels 3 MAP", ac108_enum[4]), + SOC_ENUM("Tx1 Channels 4 MAP", ac108_enum[5]), + SOC_ENUM("Tx1 Channels 5 MAP", ac108_enum[6]), + SOC_ENUM("Tx1 Channels 6 MAP", ac108_enum[7]), + SOC_ENUM("Tx1 Channels 7 MAP", ac108_enum[8]), + SOC_ENUM("Tx1 Channels 8 MAP", ac108_enum[9]), + SOC_ENUM("Tx1 Channels 9 MAP", ac108_enum[10]), + SOC_ENUM("Tx1 Channels 10 MAP", ac108_enum[11]), + SOC_ENUM("Tx1 Channels 11 MAP", ac108_enum[12]), + SOC_ENUM("Tx1 Channels 12 MAP", ac108_enum[13]), + SOC_ENUM("Tx1 Channels 13 MAP", ac108_enum[14]), + SOC_ENUM("Tx1 Channels 14 MAP", ac108_enum[15]), + SOC_ENUM("Tx1 Channels 15 MAP", ac108_enum[16]), + SOC_ENUM("Tx1 Channels 16 MAP", ac108_enum[17]), + + SOC_ENUM("Tx2 Channels 1 MAP", ac108_enum[18]), + SOC_ENUM("Tx2 Channels 2 MAP", ac108_enum[19]), + SOC_ENUM("Tx2 Channels 3 MAP", ac108_enum[20]), + SOC_ENUM("Tx2 Channels 4 MAP", ac108_enum[21]), + SOC_ENUM("Tx2 Channels 5 MAP", ac108_enum[22]), + SOC_ENUM("Tx2 Channels 6 MAP", ac108_enum[23]), + SOC_ENUM("Tx2 Channels 7 MAP", ac108_enum[24]), + SOC_ENUM("Tx2 Channels 8 MAP", ac108_enum[25]), + SOC_ENUM("Tx2 Channels 9 MAP", ac108_enum[26]), + SOC_ENUM("Tx2 Channels 10 MAP", ac108_enum[27]), + SOC_ENUM("Tx2 Channels 11 MAP", ac108_enum[28]), + SOC_ENUM("Tx2 Channels 12 MAP", ac108_enum[29]), + SOC_ENUM("Tx2 Channels 13 MAP", ac108_enum[30]), + SOC_ENUM("Tx2 Channels 14 MAP", ac108_enum[31]), + SOC_ENUM("Tx2 Channels 15 MAP", ac108_enum[32]), + SOC_ENUM("Tx2 Channels 16 MAP", ac108_enum[33]), + + SOC_ENUM("ADC4 Source", ac108_enum[34]), + SOC_ENUM("ADC3 Source", ac108_enum[35]), + SOC_ENUM("ADC2 Source", ac108_enum[36]), + SOC_ENUM("ADC1 Source", ac108_enum[37]), + + SOC_ENUM("ADC1 Digital Mixer gc", ac108_adc1_data_gc_enum), + SOC_ENUM("ADC1 Digital Mixer src", ac108_adc1_data_src_enum), + + SOC_ENUM("ADC2 Digital Mixer gc", ac108_adc2_data_gc_enum), + SOC_ENUM("ADC2 Digital Mixer src", ac108_adc2_data_src_enum), + + SOC_ENUM("ADC3 Digital Mixer gc", ac108_adc3_data_gc_enum), + SOC_ENUM("ADC3 Digital Mixer src", ac108_adc3_data_src_enum), + + SOC_ENUM("ADC4 Digital Mixer gc", ac108_adc4_data_gc_enum), + SOC_ENUM("ADC4 Digital Mixer src", ac108_adc4_data_src_enum), +}; + + +static const struct snd_soc_dapm_widget ac108_dapm_widgets[] = { + //input widgets + SND_SOC_DAPM_INPUT("MIC1P"), + SND_SOC_DAPM_INPUT("MIC1N"), + + SND_SOC_DAPM_INPUT("MIC2P"), + SND_SOC_DAPM_INPUT("MIC2N"), + + SND_SOC_DAPM_INPUT("MIC3P"), + SND_SOC_DAPM_INPUT("MIC3N"), + + SND_SOC_DAPM_INPUT("MIC4P"), + SND_SOC_DAPM_INPUT("MIC4N"), + + SND_SOC_DAPM_INPUT("DMIC1"), + SND_SOC_DAPM_INPUT("DMIC2"), + + /*0xa0: ADC1 Analog Control 1 Register*/ + /*0xa1-0xa6:use the defualt value*/ + SND_SOC_DAPM_AIF_IN("Channel 1 AAF", "Capture", 0, ANA_ADC1_CTRL1, ADC1_DSM_ENABLE, 1), + SND_SOC_DAPM_SUPPLY("Channel 1 EN", ANA_ADC1_CTRL1, ADC1_PGA_ENABLE, 1, NULL, 0), + SND_SOC_DAPM_MICBIAS("MIC1BIAS", ANA_ADC1_CTRL1, ADC1_MICBIAS_EN, 1), + + /*0xa7: ADC2 Analog Control 1 Register*/ + /*0xa8-0xad:use the defualt value*/ + SND_SOC_DAPM_AIF_IN("Channel 2 AAF", "Capture", 0, ANA_ADC2_CTRL1, ADC2_DSM_ENABLE, 1), + SND_SOC_DAPM_SUPPLY("Channel 2 EN", ANA_ADC2_CTRL1, ADC2_PGA_ENABLE, 1, NULL, 0), + SND_SOC_DAPM_MICBIAS("MIC2BIAS", ANA_ADC2_CTRL1, ADC2_MICBIAS_EN, 1), + + /*0xae: ADC3 Analog Control 1 Register*/ + /*0xaf-0xb4:use the defualt value*/ + SND_SOC_DAPM_AIF_IN("Channel 3 AAF", "Capture", 0, ANA_ADC3_CTRL1, ADC3_DSM_ENABLE, 1), + SND_SOC_DAPM_SUPPLY("Channel 3 EN", ANA_ADC3_CTRL1, ADC3_PGA_ENABLE, 1, NULL, 0), + SND_SOC_DAPM_MICBIAS("MIC3BIAS", ANA_ADC3_CTRL1, ADC3_MICBIAS_EN, 1), + + /*0xb5: ADC4 Analog Control 1 Register*/ + /*0xb6-0xbb:use the defualt value*/ + SND_SOC_DAPM_AIF_IN("Channel 4 AAF", "Capture", 0, ANA_ADC4_CTRL1, ADC4_DSM_ENABLE, 1), + SND_SOC_DAPM_SUPPLY("Channel 4 EN", ANA_ADC4_CTRL1, ADC4_PGA_ENABLE, 1, NULL, 0), + SND_SOC_DAPM_MICBIAS("MIC4BIAS", ANA_ADC4_CTRL1, ADC4_MICBIAS_EN, 1), + + + /*0x61: ADC Digital Part Enable Register*/ + SND_SOC_DAPM_SUPPLY("ADC EN", ADC_DIG_EN, 4, 1, NULL, 0), + SND_SOC_DAPM_ADC("ADC1", "Capture", ADC_DIG_EN, 0, 1), + SND_SOC_DAPM_ADC("ADC2", "Capture", ADC_DIG_EN, 1, 1), + SND_SOC_DAPM_ADC("ADC3", "Capture", ADC_DIG_EN, 2, 1), + SND_SOC_DAPM_ADC("ADC4", "Capture", ADC_DIG_EN, 3, 1), + + SND_SOC_DAPM_SUPPLY("ADC1 CLK", ANA_ADC4_CTRL7, ADC1_CLK_GATING, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC2 CLK", ANA_ADC4_CTRL7, ADC2_CLK_GATING, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC3 CLK", ANA_ADC4_CTRL7, ADC3_CLK_GATING, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC4 CLK", ANA_ADC4_CTRL7, ADC4_CLK_GATING, 1, NULL, 0), + + SND_SOC_DAPM_SUPPLY("DSM EN", ANA_ADC4_CTRL6, DSM_DEMOFF, 1, NULL, 0), + + /*0x62:Digital MIC Enable Register*/ + SND_SOC_DAPM_MICBIAS("DMIC1 enable", DMIC_EN, 0, 0), + SND_SOC_DAPM_MICBIAS("DMIC2 enable", DMIC_EN, 1, 0), + + /**/ +}; + +static const struct snd_soc_dapm_route ac108_dapm_routes[] = { + + { "ADC1", NULL, "Channel 1 AAF" }, + { "ADC2", NULL, "Channel 2 AAF" }, + { "ADC3", NULL, "Channel 3 AAF" }, + { "ADC4", NULL, "Channel 4 AAF" }, + + { "Channel 1 AAF", NULL, "MIC1BIAS" }, + { "Channel 2 AAF", NULL, "MIC2BIAS" }, + { "Channel 3 AAF", NULL, "MIC3BIAS" }, + { "Channel 4 AAF", NULL, "MIC4BIAS" }, + + { "MIC1BIAS", NULL, "ADC1 CLK" }, + { "MIC2BIAS", NULL, "ADC2 CLK" }, + { "MIC3BIAS", NULL, "ADC3 CLK" }, + { "MIC4BIAS", NULL, "ADC4 CLK" }, + + + { "ADC1 CLK", NULL, "DSM EN" }, + { "ADC2 CLK", NULL, "DSM EN" }, + { "ADC3 CLK", NULL, "DSM EN" }, + { "ADC4 CLK", NULL, "DSM EN" }, + + + { "DSM EN", NULL, "ADC EN" }, + + { "Channel 1 EN", NULL, "DSM EN" }, + { "Channel 2 EN", NULL, "DSM EN" }, + { "Channel 3 EN", NULL, "DSM EN" }, + { "Channel 4 EN", NULL, "DSM EN" }, + + + { "MIC1P", NULL, "Channel 1 EN" }, + { "MIC1N", NULL, "Channel 1 EN" }, + + { "MIC2P", NULL, "Channel 2 EN" }, + { "MIC2N", NULL, "Channel 2 EN" }, + + { "MIC3P", NULL, "Channel 3 EN" }, + { "MIC3N", NULL, "Channel 3 EN" }, + + { "MIC4P", NULL, "Channel 4 EN" }, + { "MIC4N", NULL, "Channel 4 EN" }, + +}; +static int ac108_read(u8 reg, u8 *rt_value, struct i2c_client *client) { + int ret; + u8 read_cmd[3] = { reg, 0, 0 }; + u8 cmd_len = 1; + + ret = i2c_master_send(client, read_cmd, cmd_len); + if (ret != cmd_len) { + pr_err("ac108_read error1\n"); + return -1; + } + ret = i2c_master_recv(client, rt_value, 1); + if (ret != 1) { + pr_err("ac108_read error2, ret = %d.\n", ret); + return -1; + } + + return 0; +} + +static int ac108_write(u8 reg, unsigned char val, struct i2c_client *client) { + int ret = 0; + u8 write_cmd[2] = { reg, val }; + + ret = i2c_master_send(client, write_cmd, 2); + if (ret != 2) { + pr_err("ac108_write error->[REG-0x%02x,val-0x%02x]\n", reg, val); + return -1; + } + return 0; +} + +static int ac108_update_bits(u8 reg, u8 mask, u8 val, struct i2c_client *client) { + u8 val_old, val_new; + + ac108_read(reg, &val_old, client); + val_new = (val_old & ~mask) | (val & mask); + if (val_new != val_old) { + ac108_write(reg, val_new, client); + } + + return 0; +} + +static int ac108_multi_chips_read(u8 reg, u8 *rt_value, struct ac108_priv *ac108) { + u8 i; + for (i = 0; i < ac108->codec_index; i++) { + ac108_read(reg, rt_value++, ac108->i2c[i]); + } + + return 0; +} + + +static int ac108_multi_chips_write(u8 reg, u8 val, struct ac108_priv *ac108) { + u8 i; + for (i = 0; i < ac108->codec_index; i++) { + ac108_write(reg, val, ac108->i2c[i]); + } + return 0; +} + +static int ac108_multi_chips_update_bits(u8 reg, u8 mask, u8 val, struct ac108_priv *ac108) { + u8 i; + for (i = 0; i < ac108->codec_index; i++) { + ac108_update_bits(reg, mask, val, ac108->i2c[i]); + } + return 0; +} + +static unsigned int ac108_codec_read(struct snd_soc_codec *codec, unsigned int reg) { + unsigned char val_r; + struct ac108_priv *ac108 = dev_get_drvdata(codec->dev); + /*read one chip is fine*/ + ac108_read(reg, &val_r, ac108->i2c[0]); + return val_r; +} + +static int ac108_codec_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int val) { + struct ac108_priv *ac108 = dev_get_drvdata(codec->dev); + ac108_multi_chips_write(reg, val, ac108); + return 0; +} + +/** + * The Power management related registers are Reg01h~Reg09h + * 0x01-0x05,0x08,use the default value + * @author baozhu (17-6-21) + * + * @param ac108 + */ +static void ac108_configure_power(struct ac108_priv *ac108) { + /** + * 0x06:Enable Analog LDO + */ + ac108_multi_chips_update_bits(PWR_CTRL6, 0x01 << LDO33ANA_ENABLE, 0x01 << LDO33ANA_ENABLE, ac108); + /** + * 0x07: + * Control VREF output and micbias voltage ? + * REF faststart disable, enable Enable VREF (needed for Analog + * LDO and MICBIAS) + */ + ac108_multi_chips_update_bits(PWR_CTRL7, 0x1f << VREF_SEL | 0x01 << VREF_FASTSTART_ENABLE | 0x01 << VREF_ENABLE, + 0x13 << VREF_SEL | 0x00 << VREF_FASTSTART_ENABLE | 0x01 << VREF_ENABLE, ac108); + /** + * 0x09: + * Disable fast-start circuit on VREFP + * VREFP_RESCTRL=00=1 MOhm + * IGEN_TRIM=100=+25% + * Enable VREFP (needed by all audio input channels) + */ + ac108_multi_chips_update_bits(PWR_CTRL9, 0x01 << VREFP_FASTSTART_ENABLE | 0x03 << VREFP_RESCTRL | + 0x07 << IGEN_TRIM | 0x01 << VREFP_ENABLE, + 0x00 << VREFP_FASTSTART_ENABLE | 0x00 << VREFP_RESCTRL | + 0x04 << IGEN_TRIM | 0x01 << VREFP_ENABLE, ac108); +} + +/** + * The clock management related registers are Reg20h~Reg25h + * The PLL management related registers are Reg10h~Reg18h. + * @author baozhu (17-6-20) + * + * @param ac108 + * @param rate : sample rate + * + * @return int : fail or success + */ +static int ac108_configure_clocking(struct ac108_priv *ac108, unsigned int rate) { + unsigned int i = 0; + struct pll_div ac108_pll_div = { 0 }; + if (ac108->clk_id == SYSCLK_SRC_PLL) { + /* FOUT =(FIN * N) / [(M1+1) * (M2+1)*(K1+1)*(K2+1)] */ + for (i = 0; i < ARRAY_SIZE(ac108_pll_div_list); i++) { + if (ac108_pll_div_list[i].freq_in == ac108->sysclk && ac108_pll_div_list[i].freq_out % rate == 0) { + ac108_pll_div = ac108_pll_div_list[i]; + pr_err("AC108 PLL freq_in match:%u, freq_out:%u\n\n", ac108_pll_div.freq_in, ac108_pll_div.freq_out); + break; + } + } + /* 0x11,0x12,0x13,0x14: Config PLL DIV param M1/M2/N/K1/K2 */ + ac108_multi_chips_update_bits(PLL_CTRL5, 0x1f << PLL_POSTDIV1 | 0x01 << PLL_POSTDIV2, ac108_pll_div.k1 << PLL_POSTDIV1 | + ac108_pll_div.k2 << PLL_POSTDIV2, ac108); + ac108_multi_chips_update_bits(PLL_CTRL4, 0xff << PLL_LOOPDIV_LSB, (unsigned char)ac108_pll_div.n << PLL_LOOPDIV_LSB, ac108); + ac108_multi_chips_update_bits(PLL_CTRL3, 0x03 << PLL_LOOPDIV_MSB, (ac108_pll_div.n >> 8) << PLL_LOOPDIV_MSB, ac108); + ac108_multi_chips_update_bits(PLL_CTRL2, 0x1f << PLL_PREDIV1 | 0x01 << PLL_PREDIV2, + ac108_pll_div.m1 << PLL_PREDIV1 | ac108_pll_div.m2 << PLL_PREDIV2, ac108); + + /*0x18: PLL clk lock enable*/ + ac108_multi_chips_update_bits(PLL_LOCK_CTRL, 0x1 << PLL_LOCK_EN, 0x1 << PLL_LOCK_EN, ac108); + /*0x10: PLL Common voltage Enable, PLL Enable,PLL loop divider factor detection enable*/ + ac108_multi_chips_update_bits(PLL_CTRL1, 0x01 << PLL_EN | 0x01 << PLL_COM_EN | 0x01 << PLL_NDET, + 0x1 << PLL_EN | 0x1 << PLL_COM_EN | 0x01 << PLL_NDET, ac108); + + /** + * 0x20: enable pll,pll source from mclk, sysclk source from + * pll,enable sysclk + */ + ac108_multi_chips_update_bits(SYSCLK_CTRL, 0x01 << PLLCLK_EN | 0x03 << PLLCLK_SRC | 0x01 << SYSCLK_SRC | 0x01 << SYSCLK_EN, + 0x01 << PLLCLK_EN | 0x00 << PLLCLK_SRC | 0x01 << SYSCLK_SRC | 0x01 << SYSCLK_EN, ac108); + } + if (ac108->clk_id == SYSCLK_SRC_MCLK) { + /** + *0x20: sysclk source from mclk,enable sysclk + */ + ac108_multi_chips_update_bits(SYSCLK_CTRL, 0x01 << PLLCLK_EN | 0x01 << SYSCLK_SRC | 0x01 << SYSCLK_EN, + 0x00 << PLLCLK_EN | 0x00 << SYSCLK_SRC | 0x01 << SYSCLK_EN, ac108); + } + /*0x21: Module clock enable*/ + ac108_multi_chips_write(MOD_CLK_EN, 1 << I2S | 1 << ADC_DIGITAL | 1 << MIC_OFFSET_CALIBRATION | 1 << ADC_ANALOG, ac108); + /*0x22: Module reset de-asserted*/ + ac108_multi_chips_write(MOD_RST_CTRL, 1 << I2S | 1 << ADC_DIGITAL | 1 << MIC_OFFSET_CALIBRATION | 1 << ADC_ANALOG, ac108); + return 0; +} + +static int ac108_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { + unsigned int i, channels, sample_resolution, rate; + struct snd_soc_codec *codec = dai->codec; + struct ac108_priv *ac108 = snd_soc_codec_get_drvdata(codec); + rate = 99; + dev_dbg(dai->dev, "%s\n", __FUNCTION__); + + channels = params_channels(params); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S8: + sample_resolution = 0; + break; + case SNDRV_PCM_FORMAT_S16_LE: + sample_resolution = 2; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + sample_resolution = 3; + break; + case SNDRV_PCM_FORMAT_S24_LE: + sample_resolution = 4; + break; + case SNDRV_PCM_FORMAT_S32_LE: + sample_resolution = 6; + break; + default: + pr_err("AC108 don't supported the sample resolution: %u\n", params_format(params)); + return -EINVAL; + } + + dev_dbg(dai->dev,"rate:%d \n", params_rate(params)); + for (i = 0; i < ARRAY_SIZE(ac108_sample_rate); i++) { + if (ac108_sample_rate[i].real_val == params_rate(params)) { + rate = i; + break; + } + } + if (rate == 99) return -EINVAL; + + + dev_dbg(dai->dev, "rate: %d , channels: %d , sample_resolution: %d", + ac108_sample_rate[rate].real_val, + channels, + ac108_sample_resolution[sample_resolution].real_val); + + /** + * 0x33: + * The 8-Low bit of LRCK period value. It is used to program + * the number of BCLKs per channel of sample frame. This value + * is interpreted as follow: + * The 8-Low bit of LRCK period value. It is used to program + * the number of BCLKs per channel of sample frame. This value + * is interpreted as follow: PCM mode: Number of BCLKs within + * (Left + Right) channel width I2S / Left-Justified / + * Right-Justified mode: Number of BCLKs within each individual + * channel width (Left or Right) N+1 + * For example: + * n = 7: 8 BCLK width + * … + * n = 1023: 1024 BCLKs width + * 0X32[0:1]: + * The 2-High bit of LRCK period value. + */ + if (ac108->i2s_mode != PCM_FORMAT) { + if (ac108->data_protocol) { + ac108_multi_chips_write(I2S_LRCK_CTRL2, ac108_sample_resolution[sample_resolution].real_val - 1, ac108); + /*encoding mode, the max LRCK period value < 32,so the 2-High bit is zero*/ + ac108_multi_chips_update_bits(I2S_LRCK_CTRL1, 0x03 << 0, 0x00, ac108); + } else { + /*TDM mode or normal mode*/ + /** + * TODO: need test. + */ + } + + } else { + /** + * TODO: need test. + */ + } + + /** + * 0x35: + * TX Encoding mode will add 4bits to mark channel number + * TODO: need a chat to explain this + */ + ac108_multi_chips_update_bits(I2S_FMT_CTRL2, 0x07 << SAMPLE_RESOLUTION | 0x07 << SLOT_WIDTH_SEL, + ac108_sample_resolution[sample_resolution].reg_val << SAMPLE_RESOLUTION + | ac108_sample_resolution[sample_resolution].reg_val << SLOT_WIDTH_SEL, ac108); + + /** + * 0x60: + * ADC Sample Rate synchronised with I2S1 clock zone + */ + ac108_multi_chips_update_bits(ADC_SPRC, 0x0f << ADC_FS_I2S1, ac108_sample_rate[rate].reg_val << ADC_FS_I2S1, ac108); + + ac108_configure_clocking(ac108, rate); + return 0; +} + +static int ac108_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) { + + struct ac108_priv *ac108 = snd_soc_dai_get_drvdata(dai); + + pr_info("%s :%d\n", __FUNCTION__, freq); + switch (clk_id) { + case SYSCLK_SRC_MCLK: + ac108_multi_chips_update_bits(SYSCLK_CTRL, 0x1 << SYSCLK_SRC, SYSCLK_SRC_MCLK << SYSCLK_SRC, ac108); + break; + case SYSCLK_SRC_PLL: + ac108_multi_chips_update_bits(SYSCLK_CTRL, 0x1 << SYSCLK_SRC, SYSCLK_SRC_PLL << SYSCLK_SRC, ac108); + break; + default: + return -EINVAL; + } + ac108->sysclk = freq; + ac108->clk_id = clk_id; + + return 0; +} + +/** + * The i2s format management related registers are Reg + * 30h~Reg36h + * 33h,35h will be set in ac108_hw_params, It's BCLK width and + * Sample Resolution. + * @author baozhu (17-6-20) + * + * @param dai + * @param fmt + * + * @return int + */ +static int ac108_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { + + unsigned char tx_offset, lrck_polarity, brck_polarity; + struct ac108_priv *ac108 = dev_get_drvdata(dai->dev); + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: /*AC108 Master*/ + dev_dbg(dai->dev, "AC108 set to work as Master\n"); + /** + * 0x30:chip is master mode ,BCLK & LRCK output + */ + ac108_multi_chips_update_bits(I2S_CTRL, 0x03 << LRCK_IOEN, 0x03 << LRCK_IOEN, ac108); + /* multi_chips: only one chip set as Master, and the others also need to set as Slave */ + if (ac108->codec_index > 1) ac108_update_bits(I2S_CTRL, 0x3 << LRCK_IOEN, 0x0 << LRCK_IOEN, ac108->i2c[1]); + + break; + case SND_SOC_DAIFMT_CBS_CFS: /*AC108 Slave*/ + dev_dbg(dai->dev, "AC108 set to work as Slave\n"); + /** + * 0x30:chip is slave mode, BCLK & LRCK input,enable SDO1_EN and + * SDO2_EN, Transmitter Block Enable, Globe Enable + */ + ac108_multi_chips_update_bits(I2S_CTRL, 0x03 << LRCK_IOEN | 0x03 << SDO1_EN | 0x1 << TXEN | 0x1 << GEN, + 0x00 << LRCK_IOEN | 0x03 << SDO1_EN | 0x1 << TXEN | 0x1 << GEN, ac108); + break; + default: + pr_err("AC108 Master/Slave mode config error:%u\n\n", (fmt & SND_SOC_DAIFMT_MASTER_MASK) >> 12); + return -EINVAL; + } + + /*AC108 config I2S/LJ/RJ/PCM format*/ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + dev_dbg(dai->dev, "AC108 config I2S format\n"); + ac108->i2s_mode = LEFT_JUSTIFIED_FORMAT; + tx_offset = 1; + break; + case SND_SOC_DAIFMT_RIGHT_J: + dev_dbg(dai->dev, "AC108 config RIGHT-JUSTIFIED format\n"); + ac108->i2s_mode = RIGHT_JUSTIFIED_FORMAT; + tx_offset = 0; + break; + case SND_SOC_DAIFMT_LEFT_J: + dev_dbg(dai->dev, "AC108 config LEFT-JUSTIFIED format\n"); + ac108->i2s_mode = LEFT_JUSTIFIED_FORMAT; + tx_offset = 0; + break; + case SND_SOC_DAIFMT_DSP_A: + dev_dbg(dai->dev, "AC108 config PCM-A format\n"); + ac108->i2s_mode = PCM_FORMAT; + tx_offset = 1; + break; + case SND_SOC_DAIFMT_DSP_B: + dev_dbg(dai->dev, "AC108 config PCM-B format\n"); + ac108->i2s_mode = PCM_FORMAT; + tx_offset = 0; + break; + default: + ac108->i2s_mode = LEFT_JUSTIFIED_FORMAT; + tx_offset = 1; + return -EINVAL; + pr_err("AC108 I2S format config error:%u\n\n", fmt & SND_SOC_DAIFMT_FORMAT_MASK); + } + /*AC108 config BCLK&LRCK polarity*/ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + dev_dbg(dai->dev, "AC108 config BCLK&LRCK polarity: BCLK_normal,LRCK_normal\n"); + brck_polarity = BCLK_NORMAL_DRIVE_N_SAMPLE_P; + lrck_polarity = LRCK_LEFT_LOW_RIGHT_HIGH; + break; + case SND_SOC_DAIFMT_NB_IF: + dev_dbg(dai->dev, "AC108 config BCLK&LRCK polarity: BCLK_normal,LRCK_invert\n"); + brck_polarity = BCLK_NORMAL_DRIVE_N_SAMPLE_P; + lrck_polarity = LRCK_LEFT_HIGH_RIGHT_LOW; + break; + case SND_SOC_DAIFMT_IB_NF: + dev_dbg(dai->dev, "AC108 config BCLK&LRCK polarity: BCLK_invert,LRCK_normal\n"); + brck_polarity = BCLK_INVERT_DRIVE_P_SAMPLE_N; + lrck_polarity = LRCK_LEFT_LOW_RIGHT_HIGH; + break; + case SND_SOC_DAIFMT_IB_IF: + dev_dbg(dai->dev, "AC108 config BCLK&LRCK polarity: BCLK_invert,LRCK_invert\n"); + brck_polarity = BCLK_INVERT_DRIVE_P_SAMPLE_N; + lrck_polarity = LRCK_LEFT_HIGH_RIGHT_LOW; + break; + default: + pr_err("AC108 config BCLK/LRCLK polarity error:%u\n\n", (fmt & SND_SOC_DAIFMT_INV_MASK) >> 8); + return -EINVAL; + } + ac108_configure_power(ac108); + + /** + *0x31: 0: normal mode, negative edge drive and positive edge sample + 1: invert mode, positive edge drive and negative edge sample + */ + ac108_multi_chips_update_bits(I2S_BCLK_CTRL, 0x01 << BCLK_POLARITY, brck_polarity << BCLK_POLARITY, ac108); + /** + * 0x32: same as 0x31 + */ + ac108_multi_chips_update_bits(I2S_LRCK_CTRL1, 0x01 << LRCK_POLARITY, lrck_polarity << LRCK_POLARITY, ac108); + /** + * 0x34:Encoding Mode Selection,Mode + * Selection,data is offset by 1 BCLKs to LRCK + * normal mode for the last half cycle of BCLK in the slot ? + * turn to hi-z state (TDM) when not transferring slot ? + */ + ac108_multi_chips_update_bits(I2S_FMT_CTRL1, 0x01 << ENCD_SEL | 0x03 << MODE_SEL | 0x01 << TX2_OFFSET | + 0x01 << TX1_OFFSET | 0x01 << TX_SLOT_HIZ | 0x01 << TX_STATE, + ac108->data_protocol << ENCD_SEL | + ac108->i2s_mode << MODE_SEL | + tx_offset << TX2_OFFSET | + tx_offset << TX1_OFFSET | + 0x00 << TX_SLOT_HIZ | + 0x01 << TX_STATE, ac108); + + /** + * 0x60: + * MSB / LSB First Select: This driver only support MSB First + * Select . + * OUT2_MUTE,OUT1_MUTE shoule be set in widget. + * LRCK = 1 BCLK width + * Linear PCM + * + * TODO:pcm mode, bit[0:1] and bit[2] is special + */ + ac108_multi_chips_update_bits(I2S_FMT_CTRL3, 0x01 << TX_MLS | 0x03 << SEXT | 0x01 << LRCK_WIDTH | 0x03 << TX_PDM, + 0x00 << TX_MLS | 0x03 << SEXT | 0x00 << LRCK_WIDTH | 0x00 << TX_PDM, ac108); + + /**???*/ + ac108_multi_chips_write(HPF_EN,0x00,ac108); + return 0; +} + +static const struct snd_soc_dai_ops ac108_dai_ops = { + /*DAI clocking configuration*/ + .set_sysclk = ac108_set_sysclk, + + + /*ALSA PCM audio operations*/ + .hw_params = ac108_hw_params, +// .trigger = ac108_trigger, +// .hw_free = ac108_hw_free, +// +// /*DAI format configuration*/ + .set_fmt = ac108_set_fmt, +}; + + +static const struct regmap_config ac108_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = PRNG_CLK_CTRL, + .cache_type = REGCACHE_RBTREE, +}; +static struct snd_soc_dai_driver ac108_dai0 = { + .name = "ac108-codec0", + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = AC108_CHANNELS_MAX, + .rates = AC108_RATES, + .formats = AC108_FORMATS, + }, + .ops = &ac108_dai_ops, +}; + + +static struct snd_soc_dai_driver ac108_dai1 = { + .name = "ac108-codec1", + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = AC108_CHANNELS_MAX, + .rates = AC108_RATES, + .formats = AC108_FORMATS, + }, + .ops = &ac108_dai_ops, +}; + +static struct snd_soc_dai_driver ac108_dai2 = { + .name = "ac108-codec2", + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = AC108_CHANNELS_MAX, + .rates = AC108_RATES, + .formats = AC108_FORMATS, + }, + .ops = &ac108_dai_ops, +}; + +static struct snd_soc_dai_driver ac108_dai3 = { + .name = "ac108-codec3", + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = AC108_CHANNELS_MAX, + .rates = AC108_RATES, + .formats = AC108_FORMATS, + }, + .ops = &ac108_dai_ops, +}; + +static struct snd_soc_dai_driver *ac108_dai[] = { + &ac108_dai0, + + &ac108_dai1, + + &ac108_dai2, + + &ac108_dai3, +}; + +static int ac108_add_widgets(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + + snd_soc_add_codec_controls(codec, ac108_snd_controls, + ARRAY_SIZE(ac108_snd_controls)); + + + snd_soc_dapm_new_controls(dapm, ac108_dapm_widgets, + ARRAY_SIZE(ac108_dapm_widgets)); + + snd_soc_dapm_add_routes(dapm, ac108_dapm_routes, ARRAY_SIZE(ac108_dapm_routes)); + + return 0; +} +static int ac108_probe(struct snd_soc_codec *codec) { + + dev_set_drvdata(codec->dev, ac108); + ac108_add_widgets(codec); + + return 0; +} + + +static int ac108_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) { + struct ac108_priv *ac108 = snd_soc_codec_get_drvdata(codec); + dev_dbg(codec->dev, "AC108 level:%d\n", level); + switch (level) { + case SND_SOC_BIAS_ON: + ac108_multi_chips_update_bits(ANA_ADC1_CTRL1, 0x01 << ADC1_MICBIAS_EN, 0x01 << ADC1_MICBIAS_EN, ac108); + ac108_multi_chips_update_bits(ANA_ADC2_CTRL1, 0x01 << ADC2_MICBIAS_EN, 0x01 << ADC2_MICBIAS_EN, ac108); + ac108_multi_chips_update_bits(ANA_ADC3_CTRL1, 0x01 << ADC3_MICBIAS_EN, 0x01 << ADC3_MICBIAS_EN, ac108); + ac108_multi_chips_update_bits(ANA_ADC4_CTRL1, 0x01 << ADC4_MICBIAS_EN, 0x01 << ADC4_MICBIAS_EN, ac108); + break; + case SND_SOC_BIAS_PREPARE: + /* Put the MICBIASes into regulating mode */ + break; + + case SND_SOC_BIAS_STANDBY: + + break; + + case SND_SOC_BIAS_OFF: + ac108_multi_chips_update_bits(ANA_ADC1_CTRL1, 0x01 << ADC1_MICBIAS_EN, 0x00 << ADC1_MICBIAS_EN, ac108); + ac108_multi_chips_update_bits(ANA_ADC2_CTRL1, 0x01 << ADC2_MICBIAS_EN, 0x00 << ADC2_MICBIAS_EN, ac108); + ac108_multi_chips_update_bits(ANA_ADC3_CTRL1, 0x01 << ADC3_MICBIAS_EN, 0x00 << ADC3_MICBIAS_EN, ac108); + ac108_multi_chips_update_bits(ANA_ADC4_CTRL1, 0x01 << ADC4_MICBIAS_EN, 0x00 << ADC4_MICBIAS_EN, ac108); + break; + } + + return 0; +} + + +static const struct snd_soc_codec_driver ac108_soc_codec_driver = { + .probe = ac108_probe, + .set_bias_level = ac108_set_bias_level, + .read = ac108_codec_read, + .write = ac108_codec_write, +}; + + +static ssize_t ac108_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { + int val = 0, flag = 0; + u8 i = 0, reg, num, value_w, value_r; + + val = simple_strtol(buf, NULL, 16); + flag = (val >> 16) & 0xF; + + if (flag) { + reg = (val >> 8) & 0xFF; + value_w = val & 0xFF; + ac108_multi_chips_write(reg, value_w, ac108); + printk("Write 0x%02x to REG:0x%02x\n", value_w, reg); + } else { + reg = (val >> 8) & 0xFF; + num = val & 0xff; + printk("\nRead: start REG:0x%02x,count:0x%02x\n", reg, num); + + do { + value_r = 0; + ac108_multi_chips_read(reg, &value_r, ac108); + printk("REG[0x%02x]: 0x%02x; ", reg, value_r); + reg++; + i++; + if ((i == num) || (i % 4 == 0)) printk("\n"); + } while (i < num); + } + + return count; +} + +static ssize_t ac108_show(struct device *dev, struct device_attribute *attr, char *buf) { +#if 1 + printk("echo flag|reg|val > ac108\n"); + printk("eg read star addres=0x06,count 0x10:echo 0610 >ac108\n"); + printk("eg write value:0xfe to address:0x06 :echo 106fe > ac108\n"); + return 0; +#else + return snprintf(buf, PAGE_SIZE, + "echo flag|reg|val > ac108\n" + "eg read star addres=0x06,count 0x10:echo 0610 >ac108\n" + "eg write value:0xfe to address:0x06 :echo 106fe > ac108\n"); +#endif +} + +static DEVICE_ATTR(ac108, 0644, ac108_show, ac108_store); + +static struct attribute *ac108_debug_attrs[] = { + &dev_attr_ac108.attr, + NULL, +}; + +static struct attribute_group ac108_debug_attr_group = { + .name = "ac108_debug", + .attrs = ac108_debug_attrs, +}; + + + + +static int ac108_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *i2c_id) { + int ret = 0; + struct device_node *np = i2c->dev.of_node; + unsigned int val = 0; + + if (ac108 == NULL) { + ac108 = devm_kzalloc(&i2c->dev, sizeof(struct ac108_priv), GFP_KERNEL); + if (ac108 == NULL) { + dev_err(&i2c->dev, "Unable to allocate ac108 private data\n"); + return -ENOMEM; + } + } + ret = of_property_read_u32(np, "data-protocol", &val); + if (ret) { + pr_err("Please set data-protocol.\n"); + return -EINVAL; + } + ac108->data_protocol = val; + + + pr_err(" i2c_id number :%d\n", (int)(i2c_id->driver_data)); + pr_err(" ac108 codec_index :%d\n", ac108->codec_index); + pr_err(" ac108 I2S data protocol type :%d\n", ac108->data_protocol); + + ac108->i2c[i2c_id->driver_data] = i2c; + if (ac108->codec_index == 0) { + ret = snd_soc_register_codec(&i2c->dev, &ac108_soc_codec_driver, ac108_dai[i2c_id->driver_data], 1); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to register ac108 codec: %d\n", ret); + } + } + + ac108->codec_index++; + + /*Writing this register 0x12 resets all register to their default state.*/ + ac108_write(CHIP_RST, CHIP_RST_VAL, i2c); + + ret = sysfs_create_group(&i2c->dev.kobj, &ac108_debug_attr_group); + if (ret) { + pr_err("failed to create attr group\n"); + } + + return ret; +} + +static int ac108_i2c_remove(struct i2c_client *client) { + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static const struct i2c_device_id ac108_i2c_id[] = { + { "ac108_0", 0 }, + { "ac108_1", 1 }, + { "ac108_2", 2 }, + { "ac108_3", 3 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ac108_i2c_id); + +static const struct of_device_id ac108_of_match[] = { + { .compatible = "x-power,ac108_0", }, + { .compatible = "x-power,ac108_1", }, + { .compatible = "x-power,ac108_2", }, + { .compatible = "x-power,ac108_3", }, + { } +}; +MODULE_DEVICE_TABLE(of, ac108_of_match); + +static struct i2c_driver ac108_i2c_driver = { + .driver = { + .name = "ac108-codec", + .of_match_table = ac108_of_match, + }, + .probe = ac108_i2c_probe, + .remove = ac108_i2c_remove, + .id_table = ac108_i2c_id, +}; + +module_i2c_driver(ac108_i2c_driver); + +MODULE_DESCRIPTION("ASoC AC108 driver"); +MODULE_AUTHOR("Baozhu Zuo"); +MODULE_LICENSE("GPL"); diff --git a/ac108.h b/ac108.h new file mode 100755 index 0000000..8ab57bb --- /dev/null +++ b/ac108.h @@ -0,0 +1,774 @@ +/* + * ac108.h -- ac108 ALSA Soc Audio driver + * + * Author: panjunwen + * + * 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. + * + */ + +#ifndef _AC108_H +#define _AC108_H + + +/*** AC108 Codec Register Define***/ + +//Chip Reset +#define CHIP_RST 0x00 +#define CHIP_RST_VAL 0x12 + +//Power Control +#define PWR_CTRL1 0x01 +#define PWR_CTRL2 0x02 +#define PWR_CTRL3 0x03 +#define PWR_CTRL4 0x04 +#define PWR_CTRL5 0x05 +#define PWR_CTRL6 0x06 +#define PWR_CTRL7 0x07 +#define PWR_CTRL8 0x08 +#define PWR_CTRL9 0x09 + +//PLL Configure Control +#define PLL_CTRL1 0x10 +#define PLL_CTRL2 0x11 +#define PLL_CTRL3 0x12 +#define PLL_CTRL4 0x13 +#define PLL_CTRL5 0x14 +#define PLL_CTRL6 0x16 +#define PLL_CTRL7 0x17 +#define PLL_LOCK_CTRL 0x18 + +//System Clock Control +#define SYSCLK_CTRL 0x20 +#define MOD_CLK_EN 0x21 +#define MOD_RST_CTRL 0x22 +#define DSM_CLK_CTRL 0x25 + +//I2S Common Control +#define I2S_CTRL 0x30 +#define I2S_BCLK_CTRL 0x31 +#define I2S_LRCK_CTRL1 0x32 +#define I2S_LRCK_CTRL2 0x33 +#define I2S_FMT_CTRL1 0x34 +#define I2S_FMT_CTRL2 0x35 +#define I2S_FMT_CTRL3 0x36 + +//I2S TX1 Control +#define I2S_TX1_CTRL1 0x38 +#define I2S_TX1_CTRL2 0x39 +#define I2S_TX1_CTRL3 0x3A +#define I2S_TX1_CHMP_CTRL1 0x3C +#define I2S_TX1_CHMP_CTRL2 0x3D +#define I2S_TX1_CHMP_CTRL3 0x3E +#define I2S_TX1_CHMP_CTRL4 0x3F + +//I2S TX2 Control +#define I2S_TX2_CTRL1 0x40 +#define I2S_TX2_CTRL2 0x41 +#define I2S_TX2_CTRL3 0x42 +#define I2S_TX2_CHMP_CTRL1 0x44 +#define I2S_TX2_CHMP_CTRL2 0x45 +#define I2S_TX2_CHMP_CTRL3 0x46 +#define I2S_TX2_CHMP_CTRL4 0x47 + +//I2S RX1 Control +#define I2S_RX1_CTRL1 0x50 +#define I2S_RX1_CHMP_CTRL1 0x54 +#define I2S_RX1_CHMP_CTRL2 0x55 +#define I2S_RX1_CHMP_CTRL3 0x56 +#define I2S_RX1_CHMP_CTRL4 0x57 + +//I2S Loopback Debug +#define I2S_LPB_DEBUG 0x58 + +//ADC Common Control +#define ADC_SPRC 0x60 +#define ADC_DIG_EN 0x61 +#define DMIC_EN 0x62 +#define ADC_DSR 0x63 +#define ADC_FIR 0x64 +#define ADC_DDT_CTRL 0x65 + +//HPF Control +#define HPF_EN 0x66 +#define HPF_COEF_REGH1 0x67 +#define HPF_COEF_REGH2 0x68 +#define HPF_COEF_REGL1 0x69 +#define HPF_COEF_REGL2 0x6A +#define HPF_GAIN_REGH1 0x6B +#define HPF_GAIN_REGH2 0x6C +#define HPF_GAIN_REGL1 0x6D +#define HPF_GAIN_REGL2 0x6E + +//ADC Digital Channel Volume Control +#define ADC1_DVOL_CTRL 0x70 +#define ADC2_DVOL_CTRL 0x71 +#define ADC3_DVOL_CTRL 0x72 +#define ADC4_DVOL_CTRL 0x73 + +//ADC Digital Mixer Source and Gain Control +#define ADC1_DMIX_SRC 0x76 +#define ADC2_DMIX_SRC 0x77 +#define ADC3_DMIX_SRC 0x78 +#define ADC4_DMIX_SRC 0x79 + +//ADC Digital Debug Control +#define ADC_DIG_DEBUG 0x7F + +//I2S Pad Drive Control +#define I2S_DAT_PADDRV_CTRL 0x80 +#define I2S_CLK_PADDRV_CTRL 0x81 + +//Analog PGA Control +#define ANA_PGA1_CTRL 0x90 +#define ANA_PGA2_CTRL 0x91 +#define ANA_PGA3_CTRL 0x92 +#define ANA_PGA4_CTRL 0x93 + +//MIC Offset Control +#define MIC_OFFSET_CTRL1 0x96 +#define MIC_OFFSET_CTRL2 0x97 +#define MIC1_OFFSET_STATU1 0x98 +#define MIC1_OFFSET_STATU2 0x99 +#define MIC2_OFFSET_STATU1 0x9A +#define MIC2_OFFSET_STATU2 0x9B +#define MIC3_OFFSET_STATU1 0x9C +#define MIC3_OFFSET_STATU2 0x9D +#define MIC4_OFFSET_STATU1 0x9E +#define MIC4_OFFSET_STATU2 0x9F + +//ADC1 Analog Control +#define ANA_ADC1_CTRL1 0xA0 +#define ANA_ADC1_CTRL2 0xA1 +#define ANA_ADC1_CTRL3 0xA2 +#define ANA_ADC1_CTRL4 0xA3 +#define ANA_ADC1_CTRL5 0xA4 +#define ANA_ADC1_CTRL6 0xA5 +#define ANA_ADC1_CTRL7 0xA6 + +//ADC2 Analog Control +#define ANA_ADC2_CTRL1 0xA7 +#define ANA_ADC2_CTRL2 0xA8 +#define ANA_ADC2_CTRL3 0xA9 +#define ANA_ADC2_CTRL4 0xAA +#define ANA_ADC2_CTRL5 0xAB +#define ANA_ADC2_CTRL6 0xAC +#define ANA_ADC2_CTRL7 0xAD + +//ADC3 Analog Control +#define ANA_ADC3_CTRL1 0xAE +#define ANA_ADC3_CTRL2 0xAF +#define ANA_ADC3_CTRL3 0xB0 +#define ANA_ADC3_CTRL4 0xB1 +#define ANA_ADC3_CTRL5 0xB2 +#define ANA_ADC3_CTRL6 0xB3 +#define ANA_ADC3_CTRL7 0xB4 + +//ADC4 Analog Control +#define ANA_ADC4_CTRL1 0xB5 +#define ANA_ADC4_CTRL2 0xB6 +#define ANA_ADC4_CTRL3 0xB7 +#define ANA_ADC4_CTRL4 0xB8 +#define ANA_ADC4_CTRL5 0xB9 +#define ANA_ADC4_CTRL6 0xBA +#define ANA_ADC4_CTRL7 0xBB + +//GPIO Configure +#define GPIO_CFG1 0xC0 +#define GPIO_CFG2 0xC1 +#define GPIO_DAT 0xC2 +#define GPIO_DRV 0xC3 +#define GPIO_PULL 0xC4 +#define GPIO_INT_CFG 0xC5 +#define GPIO_INT_EN 0xC6 +#define GPIO_INT_STATUS 0xC7 + +//Misc +#define BGTC_DAT 0xD1 +#define BGVC_DAT 0xD2 +#define PRNG_CLK_CTRL 0xDF + + + +/*** AC108 Codec Register Bit Define***/ + +/*PWR_CTRL1*/ +#define CP12_CTRL 4 +#define CP12_SENSE_SELECT 3 + +/*PWR_CTRL2*/ +#define CP12_SENSE_FILT 6 +#define CP12_COMP_FF_EN 3 +#define CP12_FORCE_ENABLE 2 +#define CP12_FORCE_RSTB 1 + +/*PWR_CTRL3*/ +#define LDO33DIG_CTRL 0 + +/*PWR_CTRL6*/ +#define LDO33ANA_2XHDRM 2 +#define LDO33ANA_ENABLE 0 + +/*PWR_CTRL7*/ +#define VREF_SEL 3 +#define VREF_FASTSTART_ENABLE 1 +#define VREF_ENABLE 0 + +/*PWR_CTRL9*/ +#define VREFP_FASTSTART_ENABLE 7 +#define VREFP_RESCTRL 5 +#define VREFP_LPMODE 4 +#define IGEN_TRIM 1 +#define VREFP_ENABLE 0 + + +/*PLL_CTRL1*/ +#define PLL_IBIAS 4 +#define PLL_NDET 3 +#define PLL_LOCKED_STATUS 2 +#define PLL_COM_EN 1 +#define PLL_EN 0 + +/*PLL_CTRL2*/ +#define PLL_PREDIV2 5 +#define PLL_PREDIV1 0 + +/*PLL_CTRL3*/ +#define PLL_LOOPDIV_MSB 0 + +/*PLL_CTRL4*/ +#define PLL_LOOPDIV_LSB 0 + +/*PLL_CTRL5*/ +#define PLL_POSTDIV2 5 +#define PLL_POSTDIV1 0 + +/*PLL_CTRL6*/ +#define PLL_LDO 6 +#define PLL_CP 0 + +/*PLL_CTRL7*/ +#define PLL_CAP 6 +#define PLL_RES 4 +#define PLL_TEST_EN 0 + +/*PLL_LOCK_CTRL*/ +#define LOCK_LEVEL1 2 +#define LOCK_LEVEL2 1 +#define PLL_LOCK_EN 0 + + +/*SYSCLK_CTRL*/ +#define PLLCLK_EN 7 +#define PLLCLK_SRC 4 +#define SYSCLK_SRC 3 +#define SYSCLK_EN 0 + +/*MOD_CLK_EN & MOD_RST_CTRL*/ +#define I2S 7 +#define ADC_DIGITAL 4 +#define MIC_OFFSET_CALIBRATION 1 +#define ADC_ANALOG 0 + +/*DSM_CLK_CTRL*/ +#define MIC_OFFSET_DIV 4 +#define DSM_CLK_SEL 0 + + +/*I2S_CTRL*/ +#define BCLK_IOEN 7 +#define LRCK_IOEN 6 +#define SDO2_EN 5 +#define SDO1_EN 4 +#define TXEN 2 +#define RXEN 1 +#define GEN 0 + +/*I2S_BCLK_CTRL*/ +#define EDGE_TRANSFER 5 +#define BCLK_POLARITY 4 +#define BCLKDIV 0 + +/*I2S_LRCK_CTRL1*/ +#define LRCK_POLARITY 4 +#define LRCK_PERIODH 0 + +/*I2S_LRCK_CTRL2*/ +#define LRCK_PERIODL 0 + +/*I2S_FMT_CTRL1*/ +#define ENCD_SEL 6 +#define MODE_SEL 4 +#define TX2_OFFSET 3 +#define TX1_OFFSET 2 +#define TX_SLOT_HIZ 1 +#define TX_STATE 0 + +/*I2S_FMT_CTRL2*/ +#define SLOT_WIDTH_SEL 4 +#define SAMPLE_RESOLUTION 0 + +/*I2S_FMT_CTRL3*/ +#define TX_MLS 7 +#define SEXT 5 +#define OUT2_MUTE 4 +#define OUT1_MUTE 3 +#define LRCK_WIDTH 2 +#define TX_PDM 0 + + +/*I2S_TX1_CTRL1*/ +#define TX1_CHSEL 0 + +/*I2S_TX1_CTRL2*/ +#define TX1_CH8_EN 7 +#define TX1_CH7_EN 6 +#define TX1_CH6_EN 5 +#define TX1_CH5_EN 4 +#define TX1_CH4_EN 3 +#define TX1_CH3_EN 2 +#define TX1_CH2_EN 1 +#define TX1_CH1_EN 0 + +/*I2S_TX1_CTRL3*/ +#define TX1_CH16_EN 7 +#define TX1_CH15_EN 6 +#define TX1_CH14_EN 5 +#define TX1_CH13_EN 4 +#define TX1_CH12_EN 3 +#define TX1_CH11_EN 2 +#define TX1_CH10_EN 1 +#define TX1_CH9_EN 0 + +/*I2S_TX1_CHMP_CTRL1*/ +#define TX1_CH4_MAP 6 +#define TX1_CH3_MAP 4 +#define TX1_CH2_MAP 2 +#define TX1_CH1_MAP 0 + +/*I2S_TX1_CHMP_CTRL2*/ +#define TX1_CH8_MAP 6 +#define TX1_CH7_MAP 4 +#define TX1_CH6_MAP 2 +#define TX1_CH5_MAP 0 + +/*I2S_TX1_CHMP_CTRL3*/ +#define TX1_CH12_MAP 6 +#define TX1_CH11_MAP 4 +#define TX1_CH10_MAP 2 +#define TX1_CH9_MAP 0 + +/*I2S_TX1_CHMP_CTRL4*/ +#define TX1_CH16_MAP 6 +#define TX1_CH15_MAP 4 +#define TX1_CH14_MAP 2 +#define TX1_CH13_MAP 0 + + +/*I2S_TX2_CTRL1*/ +#define TX2_CHSEL 0 + +/*I2S_TX2_CHMP_CTRL1*/ +#define TX2_CH4_MAP 6 +#define TX2_CH3_MAP 4 +#define TX2_CH2_MAP 2 +#define TX2_CH1_MAP 0 + +/*I2S_TX2_CHMP_CTRL2*/ +#define TX2_CH8_MAP 6 +#define TX2_CH7_MAP 4 +#define TX2_CH6_MAP 2 +#define TX2_CH5_MAP 0 + +/*I2S_TX2_CHMP_CTRL3*/ +#define TX2_CH12_MAP 6 +#define TX2_CH11_MAP 4 +#define TX2_CH10_MAP 2 +#define TX2_CH9_MAP 0 + +/*I2S_TX2_CHMP_CTRL4*/ +#define TX2_CH16_MAP 6 +#define TX2_CH15_MAP 4 +#define TX2_CH14_MAP 2 +#define TX2_CH13_MAP 0 + + +/*I2S_RX1_CTRL1*/ +#define RX1_CHSEL 0 + +/*I2S_RX1_CHMP_CTRL1*/ +#define RX1_CH4_MAP 6 +#define RX1_CH3_MAP 4 +#define RX1_CH2_MAP 2 +#define RX1_CH1_MAP 0 + +/*I2S_RX1_CHMP_CTRL2*/ +#define RX1_CH8_MAP 6 +#define RX1_CH7_MAP 4 +#define RX1_CH6_MAP 2 +#define RX1_CH5_MAP 0 + +/*I2S_RX1_CHMP_CTRL3*/ +#define RX1_CH12_MAP 6 +#define RX1_CH11_MAP 4 +#define RX1_CH10_MAP 2 +#define RX1_CH9_MAP 0 + +/*I2S_RX1_CHMP_CTRL4*/ +#define RX1_CH16_MAP 6 +#define RX1_CH15_MAP 4 +#define RX1_CH14_MAP 2 +#define RX1_CH13_MAP 0 + + +/*I2S_LPB_DEBUG*/ +#define I2S_LPB_DEBUG_EN 0 + + +/*ADC_SPRC*/ +#define ADC_FS_I2S1 0 + +/*ADC_DIG_EN*/ +#define DG_EN 4 +#define ENAD4 3 +#define ENAD3 2 +#define ENAD2 1 +#define ENAD1 0 + +/*DMIC_EN*/ +#define DMIC2_EN 1 +#define DMIC1_EN 0 + +/*ADC_DSR*/ +#define DIG_ADC4_SRS 6 +#define DIG_ADC3_SRS 4 +#define DIG_ADC2_SRS 2 +#define DIG_ADC1_SRS 0 + +/*ADC_DDT_CTRL*/ +#define ADOUT_DLY_EN 2 +#define ADOUT_DTS 0 + + +/*HPF_EN*/ +#define DIG_ADC4_HPF_EN 3 +#define DIG_ADC3_HPF_EN 2 +#define DIG_ADC2_HPF_EN 1 +#define DIG_ADC1_HPF_EN 0 + + +/*ADC1_DMIX_SRC*/ +#define ADC1_ADC4_DMXL_GC 7 +#define ADC1_ADC3_DMXL_GC 6 +#define ADC1_ADC2_DMXL_GC 5 +#define ADC1_ADC1_DMXL_GC 4 +#define ADC1_ADC4_DMXL_SRC 3 +#define ADC1_ADC3_DMXL_SRC 2 +#define ADC1_ADC2_DMXL_SRC 1 +#define ADC1_ADC1_DMXL_SRC 0 + +/*ADC2_DMIX_SRC*/ +#define ADC2_ADC4_DMXL_GC 7 +#define ADC2_ADC3_DMXL_GC 6 +#define ADC2_ADC2_DMXL_GC 5 +#define ADC2_ADC1_DMXL_GC 4 +#define ADC2_ADC4_DMXL_SRC 3 +#define ADC2_ADC3_DMXL_SRC 2 +#define ADC2_ADC2_DMXL_SRC 1 +#define ADC2_ADC1_DMXL_SRC 0 + +/*ADC3_DMIX_SRC*/ +#define ADC3_ADC4_DMXL_GC 7 +#define ADC3_ADC3_DMXL_GC 6 +#define ADC3_ADC2_DMXL_GC 5 +#define ADC3_ADC1_DMXL_GC 4 +#define ADC3_ADC4_DMXL_SRC 3 +#define ADC3_ADC3_DMXL_SRC 2 +#define ADC3_ADC2_DMXL_SRC 1 +#define ADC3_ADC1_DMXL_SRC 0 + +/*ADC4_DMIX_SRC*/ +#define ADC4_ADC4_DMXL_GC 7 +#define ADC4_ADC3_DMXL_GC 6 +#define ADC4_ADC2_DMXL_GC 5 +#define ADC4_ADC1_DMXL_GC 4 +#define ADC4_ADC4_DMXL_SRC 3 +#define ADC4_ADC3_DMXL_SRC 2 +#define ADC4_ADC2_DMXL_SRC 1 +#define ADC4_ADC1_DMXL_SRC 0 + + +/*ADC_DIG_DEBUG*/ +#define ADC_PTN_SEL 0 + + +/*I2S_DAT_PADDRV_CTRL*/ +#define TX2_DAT_DRV 4 +#define TX1_DAT_DRV 0 + +/*I2S_CLK_PADDRV_CTRL*/ +#define LRCK_DRV 4 +#define BCLK_DRV 0 + + +/*ANA_PGA1_CTRL*/ +#define ADC1_ANALOG_PGA 1 +#define ADC1_ANALOG_PGA_STEP 0 + +/*ANA_PGA2_CTRL*/ +#define ADC2_ANALOG_PGA 1 +#define ADC2_ANALOG_PGA_STEP 0 + +/*ANA_PGA3_CTRL*/ +#define ADC3_ANALOG_PGA 1 +#define ADC3_ANALOG_PGA_STEP 0 + +/*ANA_PGA4_CTRL*/ +#define ADC4_ANALOG_PGA 1 +#define ADC4_ANALOG_PGA_STEP 0 + + +/*MIC_OFFSET_CTRL1*/ +#define MIC_OFFSET_CAL_EN4 3 +#define MIC_OFFSET_CAL_EN3 2 +#define MIC_OFFSET_CAL_EN2 1 +#define MIC_OFFSET_CAL_EN1 0 + +/*MIC_OFFSET_CTRL2*/ +#define MIC_OFFSET_CAL_GAIN 3 +#define MIC_OFFSET_CAL_CHANNEL 1 +#define MIC_OFFSET_CAL_EN_ONCE 0 + +/*MIC1_OFFSET_STATU1*/ +#define MIC1_OFFSET_CAL_DONE 7 +#define MIC1_OFFSET_CAL_RUN_STA 6 +#define MIC1_OFFSET_MSB 0 + +/*MIC1_OFFSET_STATU2*/ +#define MIC1_OFFSET_LSB 0 + +/*MIC2_OFFSET_STATU1*/ +#define MIC2_OFFSET_CAL_DONE 7 +#define MIC2_OFFSET_CAL_RUN_STA 6 +#define MIC2_OFFSET_MSB 0 + +/*MIC2_OFFSET_STATU2*/ +#define MIC2_OFFSET_LSB 0 + +/*MIC3_OFFSET_STATU1*/ +#define MIC3_OFFSET_CAL_DONE 7 +#define MIC3_OFFSET_CAL_RUN_STA 6 +#define MIC3_OFFSET_MSB 0 + +/*MIC3_OFFSET_STATU2*/ +#define MIC3_OFFSET_LSB 0 + +/*MIC4_OFFSET_STATU1*/ +#define MIC4_OFFSET_CAL_DONE 7 +#define MIC4_OFFSET_CAL_RUN_STA 6 +#define MIC4_OFFSET_MSB 0 + +/*MIC4_OFFSET_STATU2*/ +#define MIC4_OFFSET_LSB 0 + + +/*ANA_ADC1_CTRL1*/ +#define ADC1_PGA_BYPASS 7 +#define ADC1_PGA_BYP_RCM 6 +#define ADC1_PGA_CTRL_RCM 4 +#define ADC1_PGA_MUTE 3 +#define ADC1_DSM_ENABLE 2 +#define ADC1_PGA_ENABLE 1 +#define ADC1_MICBIAS_EN 0 + +/*ANA_ADC1_CTRL3*/ +#define ADC1_ANA_CAL_EN 5 +#define ADC1_SEL_OUT_EDGE 3 +#define ADC1_DSM_DISABLE 2 +#define ADC1_VREFP_DISABLE 1 +#define ADC1_AAF_DISABLE 0 + +/*ANA_ADC1_CTRL6*/ +#define PGA_CTRL_TC 6 +#define PGA_CTRL_RC 4 +#define PGA_CTRL_I_LIN 2 +#define PGA_CTRL_I_IN 0 + +/*ANA_ADC1_CTRL7*/ +#define PGA_CTRL_HI_Z 7 +#define PGA_CTRL_SHORT_RF 6 +#define PGA_CTRL_VCM_VG 4 +#define PGA_CTRL_VCM_IN 0 + + +/*ANA_ADC2_CTRL1*/ +#define ADC2_PGA_BYPASS 7 +#define ADC2_PGA_BYP_RCM 6 +#define ADC2_PGA_CTRL_RCM 4 +#define ADC2_PGA_MUTE 3 +#define ADC2_DSM_ENABLE 2 +#define ADC2_PGA_ENABLE 1 +#define ADC2_MICBIAS_EN 0 + +/*ANA_ADC2_CTRL3*/ +#define ADC2_ANA_CAL_EN 5 +#define ADC2_SEL_OUT_EDGE 3 +#define ADC2_DSM_DISABLE 2 +#define ADC2_VREFP_DISABLE 1 +#define ADC2_AAF_DISABLE 0 + +/*ANA_ADC2_CTRL6*/ +#define PGA_CTRL_IBOOST 7 +#define PGA_CTRL_IQCTRL 6 +#define PGA_CTRL_OABIAS 4 +#define PGA_CTRL_CMLP_DIS 3 +#define PGA_CTRL_PDB_RIN 2 +#define PGA_CTRL_PEAKDET 0 + +/*ANA_ADC2_CTRL7*/ +#define AAF_LPMODE_EN 7 +#define AAF_STG2_IB_SEL 4 +#define AAFDSM_IB_DIV2 3 +#define AAF_STG1_IB_SEL 0 + + +/*ANA_ADC3_CTRL1*/ +#define ADC3_PGA_BYPASS 7 +#define ADC3_PGA_BYP_RCM 6 +#define ADC3_PGA_CTRL_RCM 4 +#define ADC3_PGA_MUTE 3 +#define ADC3_DSM_ENABLE 2 +#define ADC3_PGA_ENABLE 1 +#define ADC3_MICBIAS_EN 0 + +/*ANA_ADC3_CTRL3*/ +#define ADC3_ANA_CAL_EN 5 +#define ADC3_INVERT_CLK 4 +#define ADC3_SEL_OUT_EDGE 3 +#define ADC3_DSM_DISABLE 2 +#define ADC3_VREFP_DISABLE 1 +#define ADC3_AAF_DISABLE 0 + +/*ANA_ADC3_CTRL7*/ +#define DSM_COMP_IB_SEL 6 +#define DSM_OTA_CTRL 4 +#define DSM_LPMODE 3 +#define DSM_OTA_IB_SEL 0 + + +/*ANA_ADC4_CTRL1*/ +#define ADC4_PGA_BYPASS 7 +#define ADC4_PGA_BYP_RCM 6 +#define ADC4_PGA_CTRL_RCM 4 +#define ADC4_PGA_MUTE 3 +#define ADC4_DSM_ENABLE 2 +#define ADC4_PGA_ENABLE 1 +#define ADC4_MICBIAS_EN 0 + +/*ANA_ADC4_CTRL3*/ +#define ADC4_ANA_CAL_EN 5 +#define ADC4_SEL_OUT_EDGE 3 +#define ADC4_DSM_DISABLE 2 +#define ADC4_VREFP_DISABLE 1 +#define ADC4_AAF_DISABLE 0 + +/*ANA_ADC4_CTRL6*/ +#define DSM_DEMOFF 5 +#define DSM_EN_DITHER 4 +#define DSM_VREFP_LPMODE 2 +#define DSM_VREFP_OUTCTRL 0 + +/*ANA_ADC4_CTRL7*/ +#define CK8M_EN 5 +#define OSC_EN 4 +#define ADC4_CLK_GATING 3 +#define ADC3_CLK_GATING 2 +#define ADC2_CLK_GATING 1 +#define ADC1_CLK_GATING 0 + + +/*GPIO_CFG1*/ +#define GPIO2_SELECT 4 +#define GPIO1_SELECT 0 + +/*GPIO_CFG2*/ +#define GPIO4_SELECT 4 +#define GPIO3_SELECT 0 + +/*GPIO_DAT*///order??? +#define GPIO4_DAT 3 +#define GPIO3_DAT 2 +#define GPIO2_DAT 1 +#define GPIO1_DAT 0 + +/*GPIO_DRV*/ +#define GPIO4_DRV 6 +#define GPIO3_DRV 4 +#define GPIO2_DRV 2 +#define GPIO1_DRV 0 + +/*GPIO_PULL*/ +#define GPIO4_PULL 6 +#define GPIO3_PULL 4 +#define GPIO2_PULL 2 +#define GPIO1_PULL 0 + +/*GPIO_INT_CFG*/ +#define GPIO4_EINT_CFG 6 +#define GPIO3_EINT_CFG 4 +#define GPIO2_EINT_CFG 2 +#define GPIO1_EINT_CFG 0 + +/*GPIO_INT_EN*///order??? +#define GPIO4_EINT_EN 3 +#define GPIO3_EINT_EN 2 +#define GPIO2_EINT_EN 1 +#define GPIO1_EINT_EN 0 + +/*GPIO_INT_STATUS*///order??? +#define GPIO4_EINT_STA 3 +#define GPIO3_EINT_STA 2 +#define GPIO2_EINT_STA 1 +#define GPIO1_EINT_STA 0 + + +/*PRNG_CLK_CTRL*/ +#define PRNG_CLK_EN 1 +#define PRNG_CLK_POS 0 + + + +/*** Some Config Value ***/ + +//[SYSCLK_CTRL]: PLLCLK_SRC +#define PLLCLK_SRC_MCLK 0 +#define PLLCLK_SRC_BCLK 1 +#define PLLCLK_SRC_GPIO2 2 +#define PLLCLK_SRC_GPIO3 3 + +//[SYSCLK_CTRL]: SYSCLK_SRC +#define SYSCLK_SRC_MCLK 0 +#define SYSCLK_SRC_PLL 1 + +//I2S BCLK POLARITY Control +#define BCLK_NORMAL_DRIVE_N_SAMPLE_P 0 +#define BCLK_INVERT_DRIVE_P_SAMPLE_N 1 + +//I2S LRCK POLARITY Control +#define LRCK_LEFT_LOW_RIGHT_HIGH 0 +#define LRCK_LEFT_HIGH_RIGHT_LOW 1 + +//I2S Format Selection +#define PCM_FORMAT 0 +#define LEFT_JUSTIFIED_FORMAT 1 +#define RIGHT_JUSTIFIED_FORMAT 2 + + +//I2S data protocol types + +#define IS_ENCODING_MODE 0 + +#endif + diff --git a/ac108_asound.state b/ac108_asound.state new file mode 100644 index 0000000..d99f487 --- /dev/null +++ b/ac108_asound.state @@ -0,0 +1,953 @@ +state.ALSA { + control.1 { + iface MIXER + name 'PCM Playback Volume' + value 400 + comment { + access 'read write' + type INTEGER + count 1 + range '-10239 - 400' + dbmin -9999999 + dbmax 400 + dbvalue.0 400 + } + } + control.2 { + iface MIXER + name 'PCM Playback Switch' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.3 { + iface MIXER + name 'PCM Playback Route' + value 0 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 2' + } + } + control.4 { + iface PCM + name 'IEC958 Playback Default' + value '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write' + type IEC958 + count 1 + } + } + control.5 { + iface PCM + name 'IEC958 Playback Con Mask' + value '0200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access read + type IEC958 + count 1 + } + } + control.6 { + iface PCM + name 'IEC958 Playback PCM Stream' + value '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + comment { + access 'read write inactive' + type IEC958 + count 1 + } + } +} +state.seeed4micvoicec { + control.1 { + iface MIXER + name 'OUT1 Mute' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.2 { + iface MIXER + name 'OUT2 Mute' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.3 { + iface MIXER + name 'TX1 Channel1~8 enable' + value '1-4 channels ' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'disable all' + item.1 '1-1 channels ' + item.2 '1-2 channels ' + item.3 '1-3 channels ' + item.4 '1-4 channels ' + item.5 '1-5 channels ' + item.6 '1-6 channels ' + item.7 '1-7 channels ' + item.8 '1-8 channels ' + } + } + control.4 { + iface MIXER + name 'TX1 Channel9~16 enable' + value 'disable all' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'disable all' + item.1 '8-9 channels ' + item.2 '8-10 channels ' + item.3 '8-11 channels ' + item.4 '8-12 channels ' + item.5 '8-13 channels ' + item.6 '8-14 channels ' + item.7 '8-15 channels ' + item.8 '8-16 channels ' + } + } + control.5 { + iface MIXER + name 'TX2 Channel1~8 enable' + value 'disable all' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'disable all' + item.1 '1-1 channels ' + item.2 '1-2 channels ' + item.3 '1-3 channels ' + item.4 '1-4 channels ' + item.5 '1-5 channels ' + item.6 '1-6 channels ' + item.7 '1-7 channels ' + item.8 '1-8 channels ' + } + } + control.6 { + iface MIXER + name 'TX2 Channel9~16 enable' + value 'disable all' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'disable all' + item.1 '8-9 channels ' + item.2 '8-10 channels ' + item.3 '8-11 channels ' + item.4 '8-12 channels ' + item.5 '8-13 channels ' + item.6 '8-14 channels ' + item.7 '8-15 channels ' + item.8 '8-16 channels ' + } + } + control.7 { + iface MIXER + name 'CH1 digital volume' + value 181 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 255' + dbmin -11925 + dbmax 7200 + dbvalue.0 1650 + } + } + control.8 { + iface MIXER + name 'CH2 digital volume' + value 181 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 255' + dbmin -11925 + dbmax 7200 + dbvalue.0 1650 + } + } + control.9 { + iface MIXER + name 'CH3 digital volume' + value 181 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 255' + dbmin -11925 + dbmax 7200 + dbvalue.0 1650 + } + } + control.10 { + iface MIXER + name 'CH4 digital volume' + value 181 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 255' + dbmin -11925 + dbmax 7200 + dbvalue.0 1650 + } + } + control.11 { + iface MIXER + name 'ADC1 PGA gain' + value 27 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 31' + dbmin 0 + dbmax 3100 + dbvalue.0 2700 + } + } + control.12 { + iface MIXER + name 'ADC2 PGA gain' + value 27 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 31' + dbmin 0 + dbmax 3100 + dbvalue.0 2700 + } + } + control.13 { + iface MIXER + name 'ADC3 PGA gain' + value 27 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 31' + dbmin 0 + dbmax 3100 + dbvalue.0 2700 + } + } + control.14 { + iface MIXER + name 'ADC4 PGA gain' + value 27 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 31' + dbmin 0 + dbmax 3100 + dbvalue.0 2700 + } + } + control.15 { + iface MIXER + name 'Tx1 Channels' + value '1 channels ' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 '1 channels ' + item.1 '2 channels ' + item.2 '3 channels ' + item.3 '4 channels ' + item.4 '5 channels ' + item.5 '6 channels ' + item.6 '7 channels ' + item.7 '8 channels ' + item.8 '9 channels ' + item.9 '10 channels ' + item.10 '11 channels ' + item.11 '12 channels ' + item.12 '13 channels ' + item.13 '14 channels ' + item.14 '15 channels ' + item.15 '16 channels ' + } + } + control.16 { + iface MIXER + name 'Tx2 Channels' + value '1 channels ' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 '1 channels ' + item.1 '2 channels ' + item.2 '3 channels ' + item.3 '4 channels ' + item.4 '5 channels ' + item.5 '6 channels ' + item.6 '7 channels ' + item.7 '8 channels ' + item.8 '9 channels ' + item.9 '10 channels ' + item.10 '11 channels ' + item.11 '12 channels ' + item.12 '13 channels ' + item.13 '14 channels ' + item.14 '15 channels ' + item.15 '16 channels ' + } + } + control.17 { + iface MIXER + name 'Tx1 Channels 1 MAP' + value '1st adc sample' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 '1st adc sample' + item.1 '2st adc sample' + item.2 '3st adc sample' + item.3 '4st adc sample' + } + } + control.18 { + iface MIXER + name 'Tx1 Channels 2 MAP' + value '2st adc sample' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 '1st adc sample' + item.1 '2st adc sample' + item.2 '3st adc sample' + item.3 '4st adc sample' + } + } + control.19 { + iface MIXER + name 'Tx1 Channels 3 MAP' + value '3st adc sample' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 '1st adc sample' + item.1 '2st adc sample' + item.2 '3st adc sample' + item.3 '4st adc sample' + } + } + control.20 { + iface MIXER + name 'Tx1 Channels 4 MAP' + value '4st adc sample' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 '1st adc sample' + item.1 '2st adc sample' + item.2 '3st adc sample' + item.3 '4st adc sample' + } + } + control.21 { + iface MIXER + name 'Tx1 Channels 5 MAP' + value '1st adc sample' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 '1st adc sample' + item.1 '2st adc sample' + item.2 '3st adc sample' + item.3 '4st adc sample' + } + } + control.22 { + iface MIXER + name 'Tx1 Channels 6 MAP' + value '1st adc sample' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 '1st adc sample' + item.1 '2st adc sample' + item.2 '3st adc sample' + item.3 '4st adc sample' + } + } + control.23 { + iface MIXER + name 'Tx1 Channels 7 MAP' + value '1st adc sample' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 '1st adc sample' + item.1 '2st adc sample' + item.2 '3st adc sample' + item.3 '4st adc sample' + } + } + control.24 { + iface MIXER + name 'Tx1 Channels 8 MAP' + value '1st adc sample' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 '1st adc sample' + item.1 '2st adc sample' + item.2 '3st adc sample' + item.3 '4st adc sample' + } + } + control.25 { + iface MIXER + name 'Tx1 Channels 9 MAP' + value '1st adc sample' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 '1st adc sample' + item.1 '2st adc sample' + item.2 '3st adc sample' + item.3 '4st adc sample' + } + } + control.26 { + iface MIXER + name 'Tx1 Channels 10 MAP' + value '1st adc sample' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 '1st adc sample' + item.1 '2st adc sample' + item.2 '3st adc sample' + item.3 '4st adc sample' + } + } + control.27 { + iface MIXER + name 'Tx1 Channels 11 MAP' + value '1st adc sample' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 '1st adc sample' + item.1 '2st adc sample' + item.2 '3st adc sample' + item.3 '4st adc sample' + } + } + control.28 { + iface MIXER + name 'Tx1 Channels 12 MAP' + value '1st adc sample' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 '1st adc sample' + item.1 '2st adc sample' + item.2 '3st adc sample' + item.3 '4st adc sample' + } + } + control.29 { + iface MIXER + name 'Tx1 Channels 13 MAP' + value '1st adc sample' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 '1st adc sample' + item.1 '2st adc sample' + item.2 '3st adc sample' + item.3 '4st adc sample' + } + } + control.30 { + iface MIXER + name 'Tx1 Channels 14 MAP' + value '1st adc sample' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 '1st adc sample' + item.1 '2st adc sample' + item.2 '3st adc sample' + item.3 '4st adc sample' + } + } + control.31 { + iface MIXER + name 'Tx1 Channels 15 MAP' + value '1st adc sample' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 '1st adc sample' + item.1 '2st adc sample' + item.2 '3st adc sample' + item.3 '4st adc sample' + } + } + control.32 { + iface MIXER + name 'Tx1 Channels 16 MAP' + value '1st adc sample' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 '1st adc sample' + item.1 '2st adc sample' + item.2 '3st adc sample' + item.3 '4st adc sample' + } + } + control.33 { + iface MIXER + name 'Tx2 Channels 1 MAP' + value '1st adc sample' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 '1st adc sample' + item.1 '2st adc sample' + item.2 '3st adc sample' + item.3 '4st adc sample' + } + } + control.34 { + iface MIXER + name 'Tx2 Channels 2 MAP' + value '1st adc sample' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 '1st adc sample' + item.1 '2st adc sample' + item.2 '3st adc sample' + item.3 '4st adc sample' + } + } + control.35 { + iface MIXER + name 'Tx2 Channels 3 MAP' + value '1st adc sample' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 '1st adc sample' + item.1 '2st adc sample' + item.2 '3st adc sample' + item.3 '4st adc sample' + } + } + control.36 { + iface MIXER + name 'Tx2 Channels 4 MAP' + value '1st adc sample' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 '1st adc sample' + item.1 '2st adc sample' + item.2 '3st adc sample' + item.3 '4st adc sample' + } + } + control.37 { + iface MIXER + name 'Tx2 Channels 5 MAP' + value '1st adc sample' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 '1st adc sample' + item.1 '2st adc sample' + item.2 '3st adc sample' + item.3 '4st adc sample' + } + } + control.38 { + iface MIXER + name 'Tx2 Channels 6 MAP' + value '1st adc sample' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 '1st adc sample' + item.1 '2st adc sample' + item.2 '3st adc sample' + item.3 '4st adc sample' + } + } + control.39 { + iface MIXER + name 'Tx2 Channels 7 MAP' + value '1st adc sample' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 '1st adc sample' + item.1 '2st adc sample' + item.2 '3st adc sample' + item.3 '4st adc sample' + } + } + control.40 { + iface MIXER + name 'Tx2 Channels 8 MAP' + value '1st adc sample' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 '1st adc sample' + item.1 '2st adc sample' + item.2 '3st adc sample' + item.3 '4st adc sample' + } + } + control.41 { + iface MIXER + name 'Tx2 Channels 9 MAP' + value '1st adc sample' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 '1st adc sample' + item.1 '2st adc sample' + item.2 '3st adc sample' + item.3 '4st adc sample' + } + } + control.42 { + iface MIXER + name 'Tx2 Channels 10 MAP' + value '1st adc sample' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 '1st adc sample' + item.1 '2st adc sample' + item.2 '3st adc sample' + item.3 '4st adc sample' + } + } + control.43 { + iface MIXER + name 'Tx2 Channels 11 MAP' + value '1st adc sample' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 '1st adc sample' + item.1 '2st adc sample' + item.2 '3st adc sample' + item.3 '4st adc sample' + } + } + control.44 { + iface MIXER + name 'Tx2 Channels 12 MAP' + value '1st adc sample' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 '1st adc sample' + item.1 '2st adc sample' + item.2 '3st adc sample' + item.3 '4st adc sample' + } + } + control.45 { + iface MIXER + name 'Tx2 Channels 13 MAP' + value '1st adc sample' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 '1st adc sample' + item.1 '2st adc sample' + item.2 '3st adc sample' + item.3 '4st adc sample' + } + } + control.46 { + iface MIXER + name 'Tx2 Channels 14 MAP' + value '1st adc sample' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 '1st adc sample' + item.1 '2st adc sample' + item.2 '3st adc sample' + item.3 '4st adc sample' + } + } + control.47 { + iface MIXER + name 'Tx2 Channels 15 MAP' + value '1st adc sample' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 '1st adc sample' + item.1 '2st adc sample' + item.2 '3st adc sample' + item.3 '4st adc sample' + } + } + control.48 { + iface MIXER + name 'Tx2 Channels 16 MAP' + value '1st adc sample' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 '1st adc sample' + item.1 '2st adc sample' + item.2 '3st adc sample' + item.3 '4st adc sample' + } + } + control.49 { + iface MIXER + name 'ADC4 Source' + value 'Analog ADC4' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'Analog ADC1' + item.1 'Analog ADC2' + item.2 'Analog ADC3' + item.3 'Analog ADC4' + } + } + control.50 { + iface MIXER + name 'ADC3 Source' + value 'Analog ADC3' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'Analog ADC1' + item.1 'Analog ADC2' + item.2 'Analog ADC3' + item.3 'Analog ADC4' + } + } + control.51 { + iface MIXER + name 'ADC2 Source' + value 'Analog ADC2' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'Analog ADC1' + item.1 'Analog ADC2' + item.2 'Analog ADC3' + item.3 'Analog ADC4' + } + } + control.52 { + iface MIXER + name 'ADC1 Source' + value 'Analog ADC1' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'Analog ADC1' + item.1 'Analog ADC2' + item.2 'Analog ADC3' + item.3 'Analog ADC4' + } + } + control.53 { + iface MIXER + name 'ADC1 Digital Mixer gc' + value 'disable all' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'disable all' + item.1 'ADC1 data' + item.2 'ADC2 data' + item.3 'ADC3 data' + item.4 'ADC4 data' + } + } + control.54 { + iface MIXER + name 'ADC1 Digital Mixer src' + value 'ADC1 data' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'disable all' + item.1 'ADC1 data' + item.2 'ADC2 data' + item.3 'ADC3 data' + item.4 'ADC4 data' + } + } + control.55 { + iface MIXER + name 'ADC2 Digital Mixer gc' + value 'disable all' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'disable all' + item.1 'ADC1 data' + item.2 'ADC2 data' + item.3 'ADC3 data' + item.4 'ADC4 data' + } + } + control.56 { + iface MIXER + name 'ADC2 Digital Mixer src' + value 'ADC2 data' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'disable all' + item.1 'ADC1 data' + item.2 'ADC2 data' + item.3 'ADC3 data' + item.4 'ADC4 data' + } + } + control.57 { + iface MIXER + name 'ADC3 Digital Mixer gc' + value 'disable all' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'disable all' + item.1 'ADC1 data' + item.2 'ADC2 data' + item.3 'ADC3 data' + item.4 'ADC4 data' + } + } + control.58 { + iface MIXER + name 'ADC3 Digital Mixer src' + value 'ADC3 data' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'disable all' + item.1 'ADC1 data' + item.2 'ADC2 data' + item.3 'ADC3 data' + item.4 'ADC4 data' + } + } + control.59 { + iface MIXER + name 'ADC4 Digital Mixer gc' + value 'disable all' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'disable all' + item.1 'ADC1 data' + item.2 'ADC2 data' + item.3 'ADC3 data' + item.4 'ADC4 data' + } + } + control.60 { + iface MIXER + name 'ADC4 Digital Mixer src' + value 'ADC4 data' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'disable all' + item.1 'ADC1 data' + item.2 'ADC2 data' + item.3 'ADC3 data' + item.4 'ADC4 data' + } + } +} diff --git a/ac108_plugin/Makefile b/ac108_plugin/Makefile new file mode 100644 index 0000000..fede526 --- /dev/null +++ b/ac108_plugin/Makefile @@ -0,0 +1,58 @@ + +# Quiet (set to @ for a quite compile) +Q ?= @ +#Q ?= + +# Build Tools +CC := gcc +CFLAGS += -I. -Wall -funroll-loops -ffast-math -fPIC -DPIC -O0 -g +LD := gcc +LDFLAGS += -Wall -shared -lasound + +SND_PCM_OBJECTS = pcm_ac108.o +SND_PCM_LIBS = +SND_PCM_BIN = libasound_module_pcm_ac108.so + +#SND_CTL_OBJECTS = ctl_ac108.o ladspa_utils.o +#SND_CTL_LIBS = +#SND_CTL_BIN = libasound_module_ctl_ac108.so + +MULTIARCH:=$(shell gcc --print-multiarch) +LIBDIR = lib/$(MULTIARCH) + +.PHONY: all clean dep load_default + +all: Makefile $(SND_PCM_BIN) $(SND_CTL_BIN) + +dep: + @echo DEP $@ + $(Q)for i in *.c; do $(CC) -MM $(CFLAGS) "$${i}" ; done > makefile.dep + +-include makefile.dep + +$(SND_PCM_BIN): $(SND_PCM_OBJECTS) + @echo LD $@ + $(Q)$(LD) $(LDFLAGS) $(SND_PCM_LIBS) $(SND_PCM_OBJECTS) -o $(SND_PCM_BIN) + +#$(SND_CTL_BIN): $(SND_CTL_OBJECTS) +# @echo LD $@ +# $(Q)$(LD) $(LDFLAGS) $(SND_CTL_LIBS) $(SND_CTL_OBJECTS) -o $(SND_CTL_BIN) + +%.o: %.c + @echo GCC $< + $(Q)$(CC) -c $(CFLAGS) $(CPPFLAGS) $< + +clean: + @echo Cleaning... + $(Q)rm -vf *.o *.so + +install: all + @echo Installing... + $(Q)mkdir -p ${DESTDIR}/usr/$(LIBDIR)/alsa-lib/ + $(Q)install -m 644 $(SND_PCM_BIN) ${DESTDIR}/usr/$(LIBDIR)/alsa-lib/ + #$(Q)install -m 644 $(SND_CTL_BIN) ${DESTDIR}/usr/$(LIBDIR)/alsa-lib/ + +uninstall: + @echo Un-installing... + $(Q)rm ${DESTDIR}/usr/lib/alsa-lib/$(SND_PCM_BIN) + #$(Q)rm ${DESTDIR}/usr/lib/alsa-lib/$(SND_CTL_BIN) diff --git a/ac108_plugin/README.md b/ac108_plugin/README.md new file mode 100644 index 0000000..38137b2 --- /dev/null +++ b/ac108_plugin/README.md @@ -0,0 +1,5 @@ +#seeed-4mic-voicecard alsa plugin +``` +sudo apt install libasound2-dev +make && sudo make install +``` \ No newline at end of file diff --git a/ac108_plugin/libasound_module_pcm_ac108.so b/ac108_plugin/libasound_module_pcm_ac108.so new file mode 100755 index 0000000..a1ee3b3 Binary files /dev/null and b/ac108_plugin/libasound_module_pcm_ac108.so differ diff --git a/ac108_plugin/pcm_ac108.c b/ac108_plugin/pcm_ac108.c new file mode 100644 index 0000000..8b5da04 --- /dev/null +++ b/ac108_plugin/pcm_ac108.c @@ -0,0 +1,479 @@ +//https://github.com/HazouPH/android_device_motorola_smi-plus/blob/48029b4afc307c73181b108a5b0155b9f20856ca/smi-modules/alsa-lib_module_voice/pcm_voice.c +#include +#include +#include +#include +#include +#include + +#include + +#define ARRAY_SIZE(ary) (sizeof(ary)/sizeof(ary[0])) +#define AC108_FRAME_SIZE 4096 +struct ac108_t { + snd_pcm_ioplug_t io; + snd_pcm_t *slave; + snd_pcm_hw_params_t *hw_params; + unsigned int last_size; + unsigned int ptr; + void *buf; + unsigned int latency; // Delay in usec + unsigned int bufferSize; // Size of sample buffer +}; + +/* set up the fixed parameters of slave PCM hw_parmas */ +static int ac108_slave_hw_params_half(struct ac108_t *rec, unsigned int rate,snd_pcm_format_t format) { + int err; + snd_pcm_uframes_t bufferSize = rec->bufferSize; + unsigned int latency = rec->latency; + + unsigned int buffer_time = 0; + unsigned int period_time = 0; + if ((err = snd_pcm_hw_params_malloc(&rec->hw_params)) < 0) return err; + + if ((err = snd_pcm_hw_params_any(rec->slave, rec->hw_params)) < 0) { + SNDERR("Cannot get slave hw_params"); + goto out; + } + if ((err = snd_pcm_hw_params_set_access(rec->slave, rec->hw_params, + SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { + SNDERR("Cannot set slave access RW_INTERLEAVED"); + goto out; + } + if ((err = snd_pcm_hw_params_set_channels(rec->slave, rec->hw_params, 2)) < 0) { + SNDERR("Cannot set slave channels 2"); + goto out; + } + if ((err = snd_pcm_hw_params_set_format(rec->slave, rec->hw_params, + format)) < 0) { + SNDERR("Cannot set slave format"); + goto out; + } + if ((err = snd_pcm_hw_params_set_rate(rec->slave, rec->hw_params, rate, 0)) < 0) { + SNDERR("Cannot set slave rate %d", rate); + goto out; + } + + err = snd_pcm_hw_params_get_buffer_time_max(rec->hw_params, + &buffer_time, 0); + if (buffer_time > 80000) + buffer_time = 80000; + period_time = buffer_time / 4; + + err = snd_pcm_hw_params_set_period_time_near(rec->slave, rec->hw_params, + &period_time, 0); + if (err < 0) { + fprintf(stderr,"Unable to set_period_time_near"); + goto out; + } + err = snd_pcm_hw_params_set_buffer_time_near(rec->slave, rec->hw_params, + &buffer_time, 0); + if (err < 0) { + fprintf(stderr,"Unable to set_buffer_time_near"); + goto out; + } + + rec->bufferSize = bufferSize; + rec->latency = latency; + + return 0; + +out: + free(rec->hw_params); + rec->hw_params = NULL; + return err; +} + +/* + * start and stop callbacks - just trigger slave PCM + */ +static int ac108_start(snd_pcm_ioplug_t *io) { + struct ac108_t *rec = io->private_data; + + if(!rec->slave) { + fprintf(stderr, "slave is lost\n"); + } + + return snd_pcm_start(rec->slave); +} + +static int ac108_stop(snd_pcm_ioplug_t *io) { + struct ac108_t *rec = io->private_data; + + return snd_pcm_drop(rec->slave); +} +/* + * pointer callback + * + * Calculate the current position from the delay of slave PCM + */ +static snd_pcm_sframes_t ac108_pointer(snd_pcm_ioplug_t *io) { + struct ac108_t *rec = io->private_data; + int err, size; + + assert(rec); + + + size = snd_pcm_avail(rec->slave); + if (size < 0) return size; + size = size /2; + if (size > rec->last_size) { + rec->ptr += size - rec->last_size; + rec->ptr %= io->buffer_size; + } + + rec->last_size = size; + + //fprintf(stderr, "%s :%d %d %d %d\n", __func__, rec->ptr,size, io->appl_ptr, io->hw_ptr); + return rec->ptr; +} + +/* + * transfer callback + */ +static snd_pcm_sframes_t ac108_transfer(snd_pcm_ioplug_t *io, + const snd_pcm_channel_area_t *areas, + snd_pcm_uframes_t offset, + snd_pcm_uframes_t size) { + struct ac108_t *rec = io->private_data; + char *buf; + ssize_t result; + int err; + + + /* we handle only an interleaved buffer */ + buf = (char *)areas->addr + (areas->first + areas->step * offset) / 8; + result = snd_pcm_readi(rec->slave, buf, size*2); + if (result <= 0) { + fprintf(stderr, "%s out error:%d %d\n", __func__, result); + return result; + } + rec->last_size -= size; + + + return size; + +} + +/* + * poll-related callbacks - just pass to slave PCM + */ +static int ac108_poll_descriptors_count(snd_pcm_ioplug_t *io) { + struct ac108_t *rec = io->private_data; + + //fprintf(stderr, "%s\n", __FUNCTION__); + return snd_pcm_poll_descriptors_count(rec->slave); +} + +static int ac108_poll_descriptors(snd_pcm_ioplug_t *io, struct pollfd *pfd, + unsigned int space) { + struct ac108_t *rec = io->private_data; + + //fprintf(stderr, "%s\n", __FUNCTION__); + return snd_pcm_poll_descriptors(rec->slave, pfd, space); +} + +static int ac108_poll_revents(snd_pcm_ioplug_t *io, struct pollfd *pfd, + unsigned int nfds, unsigned short *revents) { + struct ac108_t *rec = io->private_data; + + //fprintf(stderr, "%s\n", __FUNCTION__); + return snd_pcm_poll_descriptors_revents(rec->slave, pfd, nfds, revents); +} + +/* + * close callback + */ +static int ac108_close(snd_pcm_ioplug_t *io) { + struct ac108_t *rec = io->private_data; + + if (rec->slave) return snd_pcm_close(rec->slave); + return 0; +} + +static int setSoftwareParams(struct ac108_t *rec) { + snd_pcm_sw_params_t *softwareParams; + int err; + + snd_pcm_uframes_t bufferSize = 0; + snd_pcm_uframes_t periodSize = 0; + snd_pcm_uframes_t startThreshold, stopThreshold; + snd_pcm_sw_params_alloca(&softwareParams); + + // Get the current software parameters + err = snd_pcm_sw_params_current(rec->slave, softwareParams); + if (err < 0) { + fprintf(stderr, "Unable to get software parameters: %s", snd_strerror(err)); + goto done; + } + + // Configure ALSA to start the transfer when the buffer is almost full. + snd_pcm_get_params(rec->slave, &bufferSize, &periodSize); + + + startThreshold = 1; + stopThreshold = bufferSize; + + + err = snd_pcm_sw_params_set_start_threshold(rec->slave, softwareParams, + startThreshold); + if (err < 0) { + fprintf(stderr, "Unable to set start threshold to %lu frames: %s", + startThreshold, snd_strerror(err)); + goto done; + } + + err = snd_pcm_sw_params_set_stop_threshold(rec->slave, softwareParams, + stopThreshold); + if (err < 0) { + fprintf(stderr, "Unable to set stop threshold to %lu frames: %s", + stopThreshold, snd_strerror(err)); + goto done; + } + // Allow the transfer to start when at least periodSize samples can be + // processed. + err = snd_pcm_sw_params_set_avail_min(rec->slave, softwareParams, + periodSize); + if (err < 0) { + fprintf(stderr, "Unable to configure available minimum to %lu: %s", + periodSize, snd_strerror(err)); + goto done; + } + + // Commit the software parameters back to the device. + err = snd_pcm_sw_params(rec->slave, softwareParams); + if (err < 0) fprintf(stderr, "Unable to configure software parameters: %s", + snd_strerror(err)); + + + + return 0; +done: + snd_pcm_sw_params_free(softwareParams); + + return err; +} +/* + * hw_params callback + * + * Set up slave PCM according to the current parameters + */ +//static int ac108_hw_params(snd_pcm_ioplug_t *io, +// snd_pcm_hw_params_t *params ATTRIBUTE_UNUSED) { +static int ac108_hw_params(snd_pcm_ioplug_t *io) { + struct ac108_t *rec = io->private_data; + snd_pcm_sw_params_t *sparams; + snd_pcm_uframes_t period_size; + snd_pcm_uframes_t buffer_size; + int err; + if (!rec->hw_params) { + err = ac108_slave_hw_params_half(rec, io->rate*2,io->format); + if (err < 0) { + fprintf(stderr, "ac108_slave_hw_params_half error\n"); + return err; + } + } + period_size = io->period_size; + if ((err = snd_pcm_hw_params_set_period_size_near(rec->slave, rec->hw_params, + &period_size, NULL)) < 0) { + SNDERR("Cannot set slave period size %ld", period_size); + return err; + } + buffer_size = io->buffer_size; + if ((err = snd_pcm_hw_params_set_buffer_size_near(rec->slave, rec->hw_params, + &buffer_size)) < 0) { + SNDERR("Cannot set slave buffer size %ld", buffer_size); + return err; + } + if ((err = snd_pcm_hw_params(rec->slave, rec->hw_params)) < 0) { + SNDERR("Cannot set slave hw_params"); + return err; + } + + setSoftwareParams(rec); + + return 0; +} +/* + * hw_free callback + */ +static int ac108_hw_free(snd_pcm_ioplug_t *io) { + struct ac108_t *rec = io->private_data; + free(rec->hw_params); + if (rec->buf != NULL) { + free(rec->buf); + rec->buf = NULL; + } + rec->hw_params = NULL; + + return snd_pcm_hw_free(rec->slave); + +} + + +static int ac108_prepare(snd_pcm_ioplug_t *io) { + struct ac108_t *rec = io->private_data; + rec->ptr = 0; + rec->last_size =0; + if (rec->buf == NULL) { + rec->buf = malloc(io->buffer_size); + } + + return snd_pcm_prepare(rec->slave); +} +static int ac108_drain(snd_pcm_ioplug_t *io) { + struct ac108_t *rec = io->private_data; + return snd_pcm_drain(rec->slave); +} +static int ac108_sw_params(snd_pcm_ioplug_t *io, snd_pcm_sw_params_t *params) { + struct ac108_t *rec = io->private_data; + + return 0; +} + +static int ac108_delay(snd_pcm_ioplug_t * io, snd_pcm_sframes_t * delayp){ + + return 0; +} +/* + * callback table + */ +static snd_pcm_ioplug_callback_t a108_ops = { + .start = ac108_start, + .stop = ac108_stop, + .pointer = ac108_pointer, + .transfer = ac108_transfer, + .poll_descriptors_count = ac108_poll_descriptors_count, + .poll_descriptors = ac108_poll_descriptors, + .poll_revents = ac108_poll_revents, + .close = ac108_close, + .hw_params = ac108_hw_params, + .hw_free = ac108_hw_free, +// .sw_params = ac108_sw_params, + .prepare = ac108_prepare, + .drain = ac108_drain, + .delay = ac108_delay, +}; + + +static int ac108_set_hw_constraint(struct ac108_t *rec) { + static unsigned int accesses[] = { + SND_PCM_ACCESS_RW_INTERLEAVED, + SND_PCM_ACCESS_RW_NONINTERLEAVED + }; + unsigned int formats[] = { SND_PCM_FORMAT_S32, + SND_PCM_FORMAT_S16 }; + int err; + snd_pcm_uframes_t buffer_max; + unsigned int period_bytes, max_periods; + + + err = snd_pcm_ioplug_set_param_list(&rec->io, + SND_PCM_IOPLUG_HW_ACCESS, + ARRAY_SIZE(accesses), + accesses); + if (err < 0) return err; + + if ((err = snd_pcm_ioplug_set_param_list(&rec->io, SND_PCM_IOPLUG_HW_FORMAT, + ARRAY_SIZE(formats), formats)) < 0 || + (err = snd_pcm_ioplug_set_param_minmax(&rec->io, SND_PCM_IOPLUG_HW_CHANNELS, + 1, 4)) < 0 || + (err = snd_pcm_ioplug_set_param_minmax(&rec->io, SND_PCM_IOPLUG_HW_RATE, + 8000, 96000)) < 0) return err; + err = snd_pcm_ioplug_set_param_minmax(&rec->io, + SND_PCM_IOPLUG_HW_BUFFER_BYTES, + 1, 4 * 1024 * 1024); + if (err < 0) return err; + + err = snd_pcm_ioplug_set_param_minmax(&rec->io, + SND_PCM_IOPLUG_HW_PERIOD_BYTES, + 128, 2 * 1024 * 1024); + if (err < 0) return err; + + err = snd_pcm_ioplug_set_param_minmax(&rec->io, SND_PCM_IOPLUG_HW_PERIODS, + 3, 1024); + return 0; +} + +/* +/* + * Main entry point + */ +SND_PCM_PLUGIN_DEFINE_FUNC(ac108) { + snd_config_iterator_t i, next; + int err; + const char *card = NULL; + const char *pcm_string = NULL; + snd_pcm_format_t format = SND_PCM_FORMAT_S32_LE; + char devstr[128], tmpcard[8]; + struct ac108_t *rec; + int channels; + struct pollfd fds; + if (stream != SND_PCM_STREAM_CAPTURE) { + SNDERR("a108 is only for capture"); + return -EINVAL; + } + + snd_config_for_each(i, next, conf) { + snd_config_t *n = snd_config_iterator_entry(i); + const char *id; + if (snd_config_get_id(n, &id) < 0) continue; + if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0 || strcmp(id, "hint") == 0) continue; + + if (strcmp(id, "slavepcm") == 0) { + if (snd_config_get_string(n, &pcm_string) < 0) { + SNDERR("ac108 slavepcm must be a string"); + return -EINVAL; + } + continue; + } + + if (strcmp(id, "channels") == 0) { + long val; + if (snd_config_get_integer(n, &val) < 0) { + SNDERR("Invalid type for %s", id); + return -EINVAL; + } + channels = val; + if (channels != 2 && channels != 4 && channels != 6) { + SNDERR("channels must be 2, 4 or 6"); + return -EINVAL; + } + continue; + } + } + + rec = calloc(1, sizeof(*rec)); + if (!rec) { + SNDERR("cannot allocate"); + return -ENOMEM; + } + err = snd_pcm_open(&rec->slave, pcm_string, stream, mode); + if (err < 0) goto error; + + + + //SND_PCM_NONBLOCK + rec->io.version = SND_PCM_IOPLUG_VERSION; + rec->io.name = "AC108 decode Plugin"; + rec->io.mmap_rw = 0; + rec->io.callback = &a108_ops; + rec->io.private_data = rec; + + err = snd_pcm_ioplug_create(&rec->io, name, stream, mode); + if (err < 0) goto error; + + if ((err = ac108_set_hw_constraint(rec)) < 0) { + snd_pcm_ioplug_delete(&rec->io); + return err; + } + *pcmp = rec->io.pcm; + return 0; + +error: + if (rec->slave) snd_pcm_close(rec->slave); + free(rec); + return err; +} + +SND_PCM_PLUGIN_SYMBOL(ac108); diff --git a/ac108_plugin/pcm_ac108.o b/ac108_plugin/pcm_ac108.o new file mode 100644 index 0000000..26d718d Binary files /dev/null and b/ac108_plugin/pcm_ac108.o differ diff --git a/asound.conf b/asound.conf index 4b13cb1..c696042 100644 --- a/asound.conf +++ b/asound.conf @@ -1,11 +1,11 @@ # The IPC key of dmix or dsnoop plugin must be unique # If 555555 or 666666 is used by other processes, use another one -pcm.!default { - type asym - playback.pcm "playback" - capture.pcm "capture" -} +# pcm.!default { +# type asym +# playback.pcm "playback" +# capture.pcm "capture" +# } pcm.playback { type plug @@ -32,3 +32,8 @@ pcm.array { ipc_key 666666 } +pcm.ac108 { + type ac108 + slavepcm "hw:1,0" + channels 4 +} diff --git a/builddtbo.sh b/builddtbo.sh index 7398aeb..9404804 100755 --- a/builddtbo.sh +++ b/builddtbo.sh @@ -1,4 +1,7 @@ -dtoverlay -r seeed-voicecard -dtc -@ -I dts -O dtb -o seeed-voicecard.dtbo seeed-voicecard-overlay.dts -cp seeed-voicecard.dtbo /boot/overlays -dtoverlay seeed-voicecard +#dtoverlay -r seeed-2mic-voicecard + +dtc -@ -I dts -O dtb -o seeed-2mic-voicecard.dtbo seeed-2mic-voicecard-overlay.dts +dtc -@ -I dts -O dtb -o seeed-4mic-voicecard.dtbo seeed-4mic-voicecard-overlay.dts + +# cp *.dtbo /boot/overlays +# dtoverlay seeed-2mic-voicecard diff --git a/dkms.conf b/dkms.conf index 70bc63f..5904bf6 100644 --- a/dkms.conf +++ b/dkms.conf @@ -1,5 +1,7 @@ PACKAGE_NAME="seeed-voicecard" -PACKAGE_VERSION="0.1" -BUILT_MODULE_NAME[0]="wm8960" +PACKAGE_VERSION="0.2" +BUILT_MODULE_NAME[0]="snd-soc-wm8960" +BUILT_MODULE_NAME[1]="snd-soc-ac108" DEST_MODULE_LOCATION[0]="/kernel/sound/soc/codecs" +DEST_MODULE_LOCATION[1]="/kernel/sound/soc/codecs" AUTOINSTALL="yes" diff --git a/install.sh b/install.sh index d6a7d5a..c386517 100755 --- a/install.sh +++ b/install.sh @@ -5,8 +5,19 @@ if [[ $EUID -ne 0 ]]; then exit 1 fi +is_Raspberry=$(cat /proc/device-tree/model | awk '{print $1}') +if [ "x${is_Raspberry}" != "xRaspberry" ] ; then + echo "Sorry, this drivers only works on raspberry pi" + exit 1 +fi -ver="0.1" +ver="0.2" +card=$1 + +if [ "x${card}" = "x" ] ; then + echo "Usage: ./install 2mic|4mic" + exit 1 +fi # we create a dir with this version to ensure that 'dkms remove' won't delete # the sources during kernel updates @@ -19,6 +30,7 @@ apt-get -y install dkms # locate currently installed kernels (may be different to running kernel if # it's just been updated) kernels=$(ls /lib/modules | sed "s/^/-k /") +uname_r=$(uname -r) function install_module { src=$1 @@ -39,28 +51,58 @@ function install_module { mkdir -p /var/lib/dkms/$mod/$ver/$marker } +if [ ! -f "/boot/overlays/seeed-4mic-voicecard.dtbo" ] && [ ! -f "/lib/modules/$(uname_r)/kernel/sound/soc/codecs/snd-soc-ac108.ko" ] ; then + install_module "./" "seeed-voicecard" + cp seeed-2mic-voicecard.dtbo /boot/overlays + cp seeed-4mic-voicecard.dtbo /boot/overlays + cp ac108_plugin/libasound_module_pcm_ac108.so /usr/lib/arm-linux-gnueabihf/alsa-lib/ + cp asound.conf /etc/ +else + echo "card driver already installed" +fi -install_module "./" "seeed-voicecard" +grep -q "snd-soc-ac108" /etc/modules || \ + echo "snd-soc-ac108" >> /etc/modules +grep -q "snd-soc-wm8960" /etc/modules || \ + echo "snd-soc-wm8960" >> /etc/modules -( - cp seeed-voicecard.dtbo /boot/overlays - cp asound.state /var/lib/alsa/asound.state -) - -echo 'wm8960' | sudo tee --append /etc/modules > /dev/null - -sed -i \ - -e "s/^dtparam=audio=on/#\0/" \ - -e "s/^#\(dtparam=i2s=on\)/\1/" \ - /boot/config.txt grep -q "dtoverlay=i2s-mmap" /boot/config.txt || \ echo "dtoverlay=i2s-mmap" >> /boot/config.txt -grep -q "dtoverlay=seeed-voicecard" /boot/config.txt || \ - echo "dtoverlay=seeed-voicecard" >> /boot/config.txt + + grep -q "dtparam=i2s=on" /boot/config.txt || \ echo "dtparam=i2s=on" >> /boot/config.txt +has_2mic=$(grep seeed-2mic-voicecard /boot/config.txt) +has_4mic=$(grep seeed-2mic-voicecard /boot/config.txt) +case "${card}" in + "2mic") + echo "cp wm8960_asound.state /var/lib/alsa/asound.state" + cp wm8960_asound.state /var/lib/alsa/asound.state + if [ "x${has_4mic}" != x ] ; then + echo "has 4mic before, now remove it" + sed -i "s/dtoverlay=seeed-4mic-voicecard//g" /boot/config.txt + fi + grep -q "dtoverlay=seeed-2mic-voicecard" /boot/config.txt || \ + echo "dtoverlay=seeed-2mic-voicecard" >> /boot/config.txt + + ;; + "4mic") + echo "cp ac108_asound.state /var/lib/alsa/asound.state" + cp ac108_asound.state /var/lib/alsa/asound.state + if [ "x${has_2mic}" != x ] ; then + echo "has 2mic before, now remove it" + sed -i "s/dtoverlay=seeed-2mic-voicecard//g" /boot/config.txt + fi + grep -q "dtoverlay=seeed-4mic-voicecard" /boot/config.txt || \ + echo "dtoverlay=seeed-4mic-voicecard" >> /boot/config.txt + ;; + *) + echo "Please use 2mic or 4mic" + ;; +esac + echo "------------------------------------------------------" echo "Please reboot your raspberry pi to apply all settings" echo "Enjoy!" diff --git a/seeed-voicecard-overlay.dts b/seeed-2mic-voicecard-overlay.dts similarity index 96% rename from seeed-voicecard-overlay.dts rename to seeed-2mic-voicecard-overlay.dts index 1503409..5aee876 100644 --- a/seeed-voicecard-overlay.dts +++ b/seeed-2mic-voicecard-overlay.dts @@ -43,7 +43,7 @@ master_overlay: __dormant__ { compatible = "simple-audio-card"; simple-audio-card,format = "i2s"; - simple-audio-card,name = "seeed-voicecard"; + simple-audio-card,name = "seeed-2mic-voicecard"; simple-audio-card,bitclock-master = <&dailink0_master>; simple-audio-card,frame-master = <&dailink0_master>; status = "okay"; @@ -80,7 +80,7 @@ slave_overlay: __overlay__ { compatible = "simple-audio-card"; simple-audio-card,format = "i2s"; - simple-audio-card,name = "seeed-voicecard"; + simple-audio-card,name = "seeed-2mic-voicecard"; status = "okay"; simple-audio-card,widgets = "Microphone", "Mic Jack", diff --git a/seeed-2mic-voicecard.dtbo b/seeed-2mic-voicecard.dtbo new file mode 100644 index 0000000..cbb75ab Binary files /dev/null and b/seeed-2mic-voicecard.dtbo differ diff --git a/seeed-4mic-voicecard-overlay.dts b/seeed-4mic-voicecard-overlay.dts new file mode 100644 index 0000000..4b81453 --- /dev/null +++ b/seeed-4mic-voicecard-overlay.dts @@ -0,0 +1,66 @@ +/dts-v1/; +/plugin/; + +/ { + compatible = "brcm,bcm2708"; + fragment@0 { + target = <&i2s>; + __overlay__ { + #sound-dai-cells = <0>; + status = "okay"; + }; + }; + fragment@1 { + target-path = "/clocks"; + __overlay__ { + ac108_mclk: codec-mclk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <24000000>; + }; + }; + }; + fragment@2 { + target = <&i2c1>; + __overlay__ { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + ac108_a: ac108@3b{ + compatible = "x-power,ac108_0"; + reg = <0x3b>; + #sound-dai-cells = <0>; + data-protocol = <1>; + }; + }; + }; + + + + fragment@3 { + target = <&sound>; + sound_overlay: __overlay__ { + compatible = "simple-audio-card"; + simple-audio-card,format = "i2s"; + simple-audio-card,name = "seeed-4mic-voicecard"; + status = "okay"; + + simple-audio-card,bitclock-master = <&dailink0_slave>; + simple-audio-card,frame-slave = <&dailink0_slave>; + dailink0_slave: simple-audio-card,cpu { + sound-dai = <&i2s>; + }; + codec_dai: simple-audio-card,codec { + sound-dai = <&ac108_a>; + clocks = <&ac108_mclk>; + }; + }; + }; + __overrides__ { + card-name = <&sound_overlay>,"seeed-voicecard,name"; + }; + + +}; + diff --git a/seeed-4mic-voicecard.dtbo b/seeed-4mic-voicecard.dtbo new file mode 100644 index 0000000..4ea2ece Binary files /dev/null and b/seeed-4mic-voicecard.dtbo differ diff --git a/seeed-voicecard.dtbo b/seeed-voicecard.dtbo deleted file mode 100644 index 76a2d24..0000000 Binary files a/seeed-voicecard.dtbo and /dev/null differ diff --git a/wm8960.ko b/wm8960.ko deleted file mode 100644 index 164ef7c..0000000 Binary files a/wm8960.ko and /dev/null differ diff --git a/asound.state b/wm8960_asound.state similarity index 100% rename from asound.state rename to wm8960_asound.state