diff --git a/dl/KPlatinum-0.0.1.1-db9b4dba391e92fb35c1966190d3148493fffd64.tar.gz b/dl/KPlatinum-0.0.1.1-db9b4dba391e92fb35c1966190d3148493fffd64.tar.gz index 433eac732..0d78b24e8 100644 Binary files a/dl/KPlatinum-0.0.1.1-db9b4dba391e92fb35c1966190d3148493fffd64.tar.gz and b/dl/KPlatinum-0.0.1.1-db9b4dba391e92fb35c1966190d3148493fffd64.tar.gz differ diff --git a/lichee/linux-4.9/arch/arm/boot/dts/Makefile b/lichee/linux-4.9/arch/arm/boot/dts/Makefile index 5414b36cd..fd9c386c4 100644 --- a/lichee/linux-4.9/arch/arm/boot/dts/Makefile +++ b/lichee/linux-4.9/arch/arm/boot/dts/Makefile @@ -836,8 +836,7 @@ dtb-$(CONFIG_ARCH_SUN8IW6P1) += \ sun8iw6p1-soc.dtb dtb-$(CONFIG_ARCH_SUN8IW15P1) += \ sun8iw15p1-soc.dtb \ - sun8iw15p1-mandolin-perf1.dtb\ - sun8iw15p1-mandolin-pv1.dtb + sun8iw15p1-mandolin-perf1.dtb dtb-$(CONFIG_ARCH_TANGO) += \ tango4-vantage-1172.dtb dtb-$(CONFIG_ARCH_TEGRA_2x_SOC) += \ diff --git a/lichee/linux-4.9/arch/arm/boot/dts/sun8iw15p1-mandolin-perf1.dts b/lichee/linux-4.9/arch/arm/boot/dts/sun8iw15p1-mandolin-perf1.dts index 6d39811a8..b4e39ee51 100644 --- a/lichee/linux-4.9/arch/arm/boot/dts/sun8iw15p1-mandolin-perf1.dts +++ b/lichee/linux-4.9/arch/arm/boot/dts/sun8iw15p1-mandolin-perf1.dts @@ -10,20 +10,9 @@ /{ soc@03000000 { - twi0: twi@0x05002000{ - status = "disabled"; - - ad82584f@31 { - compatible = "ESMT, ad82584f"; - reg = <0x31>; - reset_pin = <&r_pio PL 11 1 1 1 1>; - amp_en = <&r_pio PL 3 1 1 1 1>; - }; - }; - snddaudio0:sound@1{ - sunxi,snddaudio-codec = "ad82584f.0-0031"; - sunxi,snddaudio-codec-dai = "ad82584f"; + sunxi,snddaudio-codec = "netease1.mic.codec"; + sunxi,snddaudio-codec-dai = "netease1.mic.codecdai"; }; wlan:wlan { diff --git a/lichee/linux-4.9/arch/arm/boot/dts/sun8iw15p1-mandolin-pv1.dts b/lichee/linux-4.9/arch/arm/boot/dts/sun8iw15p1-mandolin-pv1.dts index 6d39811a8..b4e39ee51 100644 --- a/lichee/linux-4.9/arch/arm/boot/dts/sun8iw15p1-mandolin-pv1.dts +++ b/lichee/linux-4.9/arch/arm/boot/dts/sun8iw15p1-mandolin-pv1.dts @@ -10,20 +10,9 @@ /{ soc@03000000 { - twi0: twi@0x05002000{ - status = "disabled"; - - ad82584f@31 { - compatible = "ESMT, ad82584f"; - reg = <0x31>; - reset_pin = <&r_pio PL 11 1 1 1 1>; - amp_en = <&r_pio PL 3 1 1 1 1>; - }; - }; - snddaudio0:sound@1{ - sunxi,snddaudio-codec = "ad82584f.0-0031"; - sunxi,snddaudio-codec-dai = "ad82584f"; + sunxi,snddaudio-codec = "netease1.mic.codec"; + sunxi,snddaudio-codec-dai = "netease1.mic.codecdai"; }; wlan:wlan { diff --git a/lichee/linux-4.9/drivers/misc/xunfei/Makefile b/lichee/linux-4.9/drivers/misc/xunfei/Makefile index e5a7851af..5cd88b112 100755 --- a/lichee/linux-4.9/drivers/misc/xunfei/Makefile +++ b/lichee/linux-4.9/drivers/misc/xunfei/Makefile @@ -1 +1 @@ -obj-$(CONFIG_XUNFEI_CPLD) += cpld/i2c_operator.o cpld/fpga_loader.o cpld/fpga_config.o cpld/cx20810.o cpld/netease_config.o +obj-$(CONFIG_XUNFEI_CPLD) += cpld/fpga_loader.o cpld/i2c_operator.o cpld/cx20810.o cpld/netease_config.o diff --git a/lichee/linux-4.9/sound/soc/codecs/Kconfig b/lichee/linux-4.9/sound/soc/codecs/Kconfig index 028612f99..8e6f48134 100644 --- a/lichee/linux-4.9/sound/soc/codecs/Kconfig +++ b/lichee/linux-4.9/sound/soc/codecs/Kconfig @@ -1089,6 +1089,10 @@ config SND_SOC_TPA6130A2 tristate "Texas Instruments TPA6130A2 headphone amplifier" depends on I2C +config SND_SOC_ES7243 + tristate "SND_SOC_ES7243" + depends on I2C + endmenu # Wolfgang add ACX00 diff --git a/lichee/linux-4.9/sound/soc/codecs/Makefile b/lichee/linux-4.9/sound/soc/codecs/Makefile index 5c2ae8121..3dcbb68d4 100644 --- a/lichee/linux-4.9/sound/soc/codecs/Makefile +++ b/lichee/linux-4.9/sound/soc/codecs/Makefile @@ -446,3 +446,4 @@ obj-$(CONFIG_SND_SOC_TPA6130A2) += snd-soc-tpa6130a2.o # Wolfgang add ACX00 obj-$(CONFIG_SND_SOC_ACX00) += snd-soc-acx00.o obj-$(CONFIG_SND_SOC_AD82584F) += snd-soc-ad82584f.o +obj-$(CONFIG_SND_SOC_ES7243) += es7243.o diff --git a/lichee/linux-4.9/sound/soc/codecs/es7243.c b/lichee/linux-4.9/sound/soc/codecs/es7243.c new file mode 100644 index 000000000..e13a2c7d4 --- /dev/null +++ b/lichee/linux-4.9/sound/soc/codecs/es7243.c @@ -0,0 +1,972 @@ +/* + * ALSA SoC ES7243 adc driver + * + * Author: David Yang, + * or + * + * Copyright: (C) 2017 Everest Semiconductor Co Ltd., + * + * Based on sound/soc/codecs/wm8731.c by Richard Purdie + * + * 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. + * + * Notes: + * ES7243 is a stereo ADC of Everest + * + */ +#define DEBUG +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "es7243.h" + +#define ES7243_TDM_ENABLE 0 +#define ES7243_CHANNELS_MAX 2 +#define ES7243_I2C_BUS_NUM 0 +#define ES7243_CODEC_RW_TEST_EN 0 +#define ES7243_IDLE_RESET_EN 1 // reset ES7243 when in idle time +#define ES7243_MATCH_DTS_EN \ + 0 // ES7243 match method select: 0: i2c_detect, 1:of_device_id + +struct i2c_client *i2c_clt[(ES7243_CHANNELS_MAX) / 2]; + +/* codec private data */ +struct es7243_priv { + struct regmap *regmap; + struct i2c_client *i2c; + unsigned int dmic_amic; + unsigned int sysclk; + struct snd_pcm_hw_constraint_list *sysclk_constraints; + unsigned int tdm; + struct delayed_work pcm_pop_work; + struct delayed_work pcm_pop_work1; +}; +struct snd_soc_codec *tron_codec[4]; +int es7243_init_reg = 0; +static int es7243_codec_num = 0; + +static const struct regmap_config es7243_regmap_config = { + .reg_bits = 8, // Number of bits in a register address + .val_bits = 8, // Number of bits in a register value +}; +/* + * ES7243 register cache + */ +static const u8 es7243_reg[] = { + 0x00, 0x00, 0x10, 0x04, /* 0 */ + 0x02, 0x13, 0x00, 0x3f, /* 4 */ + 0x11, 0x00, 0xc0, 0xc0, /* 8 */ + 0x12, 0xa0, 0x40, /* 12 */ +}; +static const struct reg_default es7243_reg_defaults[] = { + {0x00, 0x00}, {0x01, 0x00}, {0x02, 0x10}, {0x03, 0x04}, /* 0 */ + {0x04, 0x02}, {0x05, 0x13}, {0x06, 0x00}, {0x07, 0x3f}, /* 4 */ + {0x08, 0x11}, {0x09, 0x00}, {0x0a, 0xc0}, {0x0b, 0xc0}, /* 8 */ + {0x0c, 0x12}, {0x0d, 0xa0}, {0x0e, 0x40}, /* 12 */ +}; + +static int es7243_read(u8 reg, u8 *rt_value, struct i2c_client *client) { + int ret; + u8 read_cmd[3] = {0}; + u8 cmd_len = 0; + + read_cmd[0] = reg; + cmd_len = 1; + + if (client->adapter == NULL) + pr_err("es7243_read client->adapter==NULL\n"); + + ret = i2c_master_send(client, read_cmd, cmd_len); + if (ret != cmd_len) { + pr_err("es7243_read error1\n"); + return -1; + } + + ret = i2c_master_recv(client, rt_value, 1); + if (ret != 1) { + pr_err("es7243_read error2, ret = %d.\n", ret); + return -1; + } + + return 0; +} + +static int es7243_write(u8 reg, unsigned char value, + struct i2c_client *client) { + int ret = 0; + u8 write_cmd[2] = {0}; + + write_cmd[0] = reg; + write_cmd[1] = value; + + ret = i2c_master_send(client, write_cmd, 2); + if (ret != 2) { + pr_err("es7243_write error->[REG-0x%02x,val-0x%02x]\n", reg, value); + return -1; + } + + return 0; +} + +static int es7243_update_bits(u8 reg, u8 mask, u8 value, + struct i2c_client *client) { + u8 val_old, val_new; + + es7243_read(reg, &val_old, client); + val_new = (val_old & ~mask) | (value & mask); + if (val_new != val_old) { + es7243_write(reg, val_new, client); + } + + return 0; +} +#if 0 +static int es7243_multi_chips_read(u8 reg, unsigned char *rt_value) +{ + u8 i; + + for(i=0; i<(ES7243_CHANNELS_MAX)/2; i++){ + es7243_read(reg, rt_value++, i2c_clt[i]); + } + + return 0; +} + + +static int es7243_multi_chips_write(u8 reg, unsigned char value) +{ + u8 i; + + for(i=0; i<(ES7243_CHANNELS_MAX)/2; i++){ + es7243_write(reg, value, i2c_clt[i]); + } + + return 0; +} +#endif + +#if 0 +static int es7243_multi_chips_update_bits(u8 reg, u8 mask, u8 value) +{ + u8 i; + + for(i=0; i<(ES7243_CHANNELS_MAX)/2; i++){ + es7243_update_bits(reg, mask, value, i2c_clt[i]); + } + + return 0; +} +#endif +struct _coeff_div { + u32 mclk; // mclk frequency + u32 sr_rate; // sample rate + u8 speedmode; // speed mode,0=single,1=double,2=quad + u8 adc_clk_div; // adcclk and dacclk divider + u8 lrckdiv; // adclrck divider and daclrck divider + u8 bclkdiv; // sclk divider + u8 osr; // adc osr +}; + +/* codec hifi mclk clock divider coefficients */ +static const struct _coeff_div coeff_div[] = { + /* 12.288MHZ */ + {12288000, 8000, 0, 0x0c, 0x60, 24, 32}, + {12288000, 12000, 0, 0x08, 0x40, 16, 32}, + {12288000, 16000, 0, 0x06, 0x30, 12, 32}, + {12288000, 24000, 0, 0x04, 0x20, 8, 32}, + {12288000, 32000, 0, 0x03, 0x18, 6, 32}, + {12288000, 48000, 0, 0x02, 0x10, 4, 32}, + {12288000, 64000, 1, 0x03, 0x0c, 3, 32}, + {12288000, 96000, 1, 0x02, 0x08, 2, 32}, + /* 11.2896MHZ */ + {11289600, 11025, 0, 0x08, 0x40, 16, 32}, + {11289600, 22050, 0, 0x04, 0x20, 8, 32}, + {11289600, 44100, 0, 0x02, 0x10, 4, 32}, + {11289600, 88200, 1, 0x02, 0x08, 2, 32}, + + /* 12.000MHZ */ + {12000000, 8000, 0, 0x0c, 0xbc, 30, 31}, + {12000000, 11025, 0, 0x08, 0x44, 17, 34}, + {12000000, 12000, 0, 0x08, 0xaa, 20, 31}, + {12000000, 16000, 0, 0x06, 0x9e, 15, 31}, + {12000000, 22050, 0, 0x04, 0x22, 8, 34}, + {12000000, 24000, 0, 0x04, 0x94, 10, 31}, + {12000000, 32000, 0, 0x03, 0x8a, 5, 31}, + {12000000, 44100, 0, 0x02, 0x11, 4, 34}, + {12000000, 48000, 0, 0x02, 0x85, 5, 31}, + {12000000, 96000, 1, 0x02, 0x85, 1, 31}, +}; +static inline int get_coeff(int mclk, int rate) { + int i; + + for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { + if (coeff_div[i].sr_rate == rate && coeff_div[i].mclk == mclk) + return i; + } + + return -EINVAL; +} + +/* The set of rates we can generate from the above for each SYSCLK */ + +static unsigned int rates_12288[] = { + 8000, 12000, 16000, 24000, 24000, 32000, 48000, 96000, +}; + +static struct snd_pcm_hw_constraint_list constraints_12288 = { + .count = ARRAY_SIZE(rates_12288), + .list = rates_12288, +}; + +static unsigned int rates_112896[] = { + 8000, + 11025, + 22050, + 44100, +}; + +static struct snd_pcm_hw_constraint_list constraints_112896 = { + .count = ARRAY_SIZE(rates_112896), + .list = rates_112896, +}; + +static unsigned int rates_12[] = { + 8000, 11025, 12000, 16000, 22050, 24000, + 32000, 44100, 48000, 48000, 88235, 96000, +}; + +static struct snd_pcm_hw_constraint_list constraints_12 = { + .count = ARRAY_SIZE(rates_12), + .list = rates_12, +}; + +/* + * Note that this should be called from init rather than from hw_params. + */ +static int es7243_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, + unsigned int freq, int dir) { + struct snd_soc_codec *codec = codec_dai->codec; + struct es7243_priv *es7243 = snd_soc_codec_get_drvdata(codec); + printk("es7243_set_dai_sysclk: %d\n", freq); + switch (freq) { + case 11289600: + case 22579200: + es7243->sysclk_constraints = &constraints_112896; + es7243->sysclk = freq; + return 0; + case 12288000: + case 24576000: + es7243->sysclk_constraints = &constraints_12288; + es7243->sysclk = freq; + return 0; + case 12000000: + case 24000000: + es7243->sysclk_constraints = &constraints_12; + es7243->sysclk = freq; + return 0; + } + return -EINVAL; +} + +static int es7243_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { + // struct snd_soc_codec *codec = codec_dai->codec; + u8 iface = 0; + u8 adciface = 0; + u8 i; + + printk("es7243_set_dai_fmt:%d\n", fmt); + + for (i = 0; i < (ES7243_CHANNELS_MAX) / 2; i++) { + es7243_read(ES7243_SDPFMT_REG01, &adciface, i2c_clt[i]); + es7243_read(ES7243_MODECFG_REG00, &iface, i2c_clt[i]); + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: // MASTER MODE + iface |= 0x02; + break; + case SND_SOC_DAIFMT_CBS_CFS: // SLAVE MODE + iface &= 0xfd; + break; + default: + return -EINVAL; + } + + /* interface format */ + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + adciface &= 0xFC; + break; + case SND_SOC_DAIFMT_RIGHT_J: + return -EINVAL; + case SND_SOC_DAIFMT_LEFT_J: + adciface &= 0xFC; + adciface |= 0x01; + break; + case SND_SOC_DAIFMT_DSP_A: + adciface &= 0xDC; + adciface |= 0x03; + break; + case SND_SOC_DAIFMT_DSP_B: + adciface &= 0xDC; + adciface |= 0x23; + break; + default: + return -EINVAL; + } + + /* clock inversion */ + adciface &= 0xbF; + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + adciface &= 0xbF; + break; + case SND_SOC_DAIFMT_IB_IF: + adciface |= 0x60; + break; + case SND_SOC_DAIFMT_IB_NF: + adciface |= 0x40; + break; + case SND_SOC_DAIFMT_NB_IF: + adciface |= 0x20; + break; + default: + return -EINVAL; + } + + // es7243_update_bits(ES7243_MODECFG_REG00, 0x02, iface, i2c_clt[i]); + // es7243_update_bits(ES7243_SDPFMT_REG01, 0x03, adciface, i2c_clt[i]); + } + return 0; +} +static void es7243_init_codec(struct snd_soc_codec *codec, u8 i) { + printk("Init es7243\n"); + es7243_write(ES7243_MODECFG_REG00, 0x41, + i2c_clt[i]); // enter into hardware mode, mclk / 3 + es7243_write(ES7243_STATECTL_REG06, 0x18, i2c_clt[i]); // soft reset codec + // es7243_write(ES7243_SDPFMT_REG01, 0xd3, i2c_clt[i]); //dsp for tdm mode, + // DSP-A, 32BIT, bclk invert + es7243_write(ES7243_SDPFMT_REG01, 0x0d, i2c_clt[i]); // lj-16bit + es7243_write(ES7243_LRCDIV_REG02, 0x10, i2c_clt[i]); + es7243_write(ES7243_BCKDIV_REG03, 0x04, i2c_clt[i]); + es7243_write(ES7243_CLKDIV_REG04, 0x02, i2c_clt[i]); + es7243_write(ES7243_MUTECTL_REG05, 0x1a, i2c_clt[i]); + es7243_write(ES7243_ANACTL1_REG08, 0x43, + i2c_clt[i]); // enable microphone input and pga gain for 27db + es7243_write(ES7243_ANACTL2_REG09, 0x3F, i2c_clt[i]); + es7243_write(ES7243_STATECTL_REG06, 0x00, i2c_clt[i]); + es7243_write(ES7243_MUTECTL_REG05, 0x12, i2c_clt[i]); + // if(i == 3) { + // es7243_write(ES7243_ANACTL1_REG08, 0x11, i2c_clt[i]); + //} + // else { + es7243_write(ES7243_ANACTL1_REG08, 0x43, + i2c_clt[i]); // enable microphone input and pga gain for 27db + //} + es7243_write(ES7243_STATECTL_REG06, 0x00, i2c_clt[i]); +} +static void es7243_unmute(struct snd_soc_codec *codec, u8 i) { + es7243_write(ES7243_MUTECTL_REG05, 0x13, i2c_clt[i]); +} +static void pcm_pop_work1_events(struct work_struct *work) { + int i; + + printk("es8316--------pcm_pop_work_events\n"); + + for (i = 0; i < (ES7243_CHANNELS_MAX) / 2; i++) { + es7243_unmute(tron_codec[(ES7243_CHANNELS_MAX / 2 - 1) - i], + (ES7243_CHANNELS_MAX / 2 - 1 - i)); + } + + es7243_init_reg = 1; +} +static void pcm_pop_work_events(struct work_struct *work) { + int i; + printk("es8316--------pcm_pop_work_events\n"); + + for (i = 0; i < (ES7243_CHANNELS_MAX) / 2; i++) { + + es7243_init_codec(tron_codec[i], i); + } + es7243_init_reg = 1; +} + +static int es7243_pcm_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) { + struct snd_soc_codec *codec = dai->codec; + struct es7243_priv *es7243 = snd_soc_codec_get_drvdata(codec); + return 0; + + /* The set of sample rates that can be supported depends on the + * MCLK supplied to the CODEC - enforce this. + */ + if (!es7243->sysclk) { + dev_err(codec->dev, "No MCLK configured, call set_sysclk() on init\n"); + return -EINVAL; + } + + snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + es7243->sysclk_constraints); + + return 0; +} +static int es7243_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) { + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec *codec = rtd->codec; + struct es7243_priv *es7243 = snd_soc_codec_get_drvdata(codec); + u8 i, val; + + if (es7243_init_reg == 0 || 1) { + printk( + "==================>>>>>>>>es8316_pcm_startup es8316_init_reg=0\n"); + // schedule_delayed_work(&es7243->pcm_pop_work, msecs_to_jiffies(100)); + schedule_delayed_work(&es7243->pcm_pop_work, 0); + schedule_delayed_work(&es7243->pcm_pop_work1, msecs_to_jiffies(700)); + } + + for (i = 0; i < (ES7243_CHANNELS_MAX) / 2; i++) { + es7243_read(ES7243_MODECFG_REG00, &val, + i2c_clt[i]); // enter into hardware mode + printk("%d -- Reg 00 ----> 0x%x!\n", __LINE__, val); + es7243_read(ES7243_STATECTL_REG06, &val, i2c_clt[i]); // soft reset + // codec + printk("%d -- Reg 06 ----> 0x%x!\n", __LINE__, val); + es7243_read(ES7243_SDPFMT_REG01, &val, + i2c_clt[i]); // dsp for tdm mode, DSP-A, 16BIT + printk("%d -- Reg 01 ----> 0x%x!\n", __LINE__, val); + es7243_read(ES7243_LRCDIV_REG02, &val, i2c_clt[i]); + printk("%d -- Reg 02 ----> 0x%x!\n", __LINE__, val); + es7243_read(ES7243_BCKDIV_REG03, &val, i2c_clt[i]); + printk("%d -- Reg 03 ----> 0x%x!\n", __LINE__, val); + es7243_read(ES7243_CLKDIV_REG04, &val, i2c_clt[i]); + printk("%d -- Reg 04 ----> 0x%x!\n", __LINE__, val); + es7243_read(ES7243_MUTECTL_REG05, &val, i2c_clt[i]); + printk("%d -- Reg 05 ----> 0x%x!\n", __LINE__, val); + es7243_read( + ES7243_ANACTL1_REG08, &val, + i2c_clt[i]); // enable microphone input and pga gain for 27db + printk("%d -- Reg 08 ----> 0x%x!\n", __LINE__, val); + es7243_read(ES7243_ANACTL2_REG09, &val, i2c_clt[i]); + printk("%d -- Reg 09 ----> 0x%x!\n", __LINE__, val); + es7243_read(ES7243_STATECTL_REG06, &val, i2c_clt[i]); + printk("%d -- Reg 06 ----> 0x%x!\n", __LINE__, val); + es7243_read(ES7243_ANACTL0_REG07, &val, + i2c_clt[i]); // power up adc and analog input + printk("%d -- Reg 07 ----> 0x%x!\n", __LINE__, val); + es7243_read(ES7243_MUTECTL_REG05, &val, i2c_clt[i]); + printk("%d -- Reg 05 ----> 0x%x!\n", __LINE__, val); + } + + for (i = 0; i < (ES7243_CHANNELS_MAX) / 2; i++) { + + /* bit size */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + es7243_update_bits(ES7243_SDPFMT_REG01, 0x1c, 0x0c, i2c_clt[i]); + break; + case SNDRV_PCM_FORMAT_S20_3LE: + es7243_update_bits(ES7243_SDPFMT_REG01, 0x1c, 0x04, i2c_clt[i]); + break; + case SNDRV_PCM_FORMAT_S24_LE: + es7243_update_bits(ES7243_SDPFMT_REG01, 0x1c, 0x00, i2c_clt[i]); + break; + case SNDRV_PCM_FORMAT_S32_LE: + es7243_update_bits(ES7243_SDPFMT_REG01, 0x1c, 0x10, i2c_clt[i]); + break; + } + } + return 0; +} + +static int es7243_mute(struct snd_soc_dai *dai, int mute) { + struct snd_soc_codec *codec = dai->codec; + u8 i; + for (i = 0; i < (ES7243_CHANNELS_MAX) / 2; i++) { + dev_dbg(codec->dev, "%s %d\n", __func__, mute); + + if (mute) { + // es7243_update_bits(ES7243_MUTECTL_REG05, 0x08, 0x08, + // i2c_clt[i]); + } else { + // es7243_update_bits(ES7243_MUTECTL_REG05, 0x08, 0x00, + // i2c_clt[i]); + } + } + return 0; +} + +static int es7243_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) { + u8 i; + + for (i = 0; i < (ES7243_CHANNELS_MAX) / 2; i++) { + switch (level) { + case SND_SOC_BIAS_ON: + dev_dbg(codec->dev, "%s on\n", __func__); + break; + case SND_SOC_BIAS_PREPARE: + dev_dbg(codec->dev, "%s prepare\n", __func__); + es7243_update_bits(ES7243_MUTECTL_REG05, 0x08, 0x00, i2c_clt[i]); + msleep(50); + break; + case SND_SOC_BIAS_STANDBY: + dev_dbg(codec->dev, "%s standby\n", __func__); + es7243_update_bits(ES7243_MUTECTL_REG05, 0x08, 0x08, i2c_clt[i]); + es7243_update_bits(ES7243_STATECTL_REG06, 0x40, 0x00, i2c_clt[i]); + msleep(50); + es7243_update_bits(ES7243_ANACTL0_REG07, 0x0f, 0x00, i2c_clt[i]); + break; + case SND_SOC_BIAS_OFF: + dev_dbg(codec->dev, "%s off\n", __func__); + es7243_update_bits(ES7243_MUTECTL_REG05, 0x08, 0x08, i2c_clt[i]); + msleep(50); + es7243_update_bits(ES7243_ANACTL0_REG07, 0x0f, 0x0f, i2c_clt[i]); + msleep(50); + es7243_update_bits(ES7243_STATECTL_REG06, 0x40, 0x40, i2c_clt[i]); + break; + } + } + // codec->dapm.bias_level = level; + return 0; +} + +#define es7243_RATES SNDRV_PCM_RATE_8000_96000 + +#define es7243_FORMATS \ + (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_ops es7243_ops = { + .startup = es7243_pcm_startup, + .hw_params = es7243_pcm_hw_params, + .set_fmt = es7243_set_dai_fmt, + .set_sysclk = es7243_set_dai_sysclk, + .digital_mute = es7243_mute, +}; + +static struct snd_soc_dai_driver es7243_dai0 = { + .name = "es7243", + .capture = + { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = es7243_RATES, + .formats = es7243_FORMATS, + }, + .ops = &es7243_ops, + .symmetric_rates = 1, +}; +static struct snd_soc_dai_driver es7243_dai1 = { + .name = "ES7243 HiFi 1", + .capture = + { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = es7243_RATES, + .formats = es7243_FORMATS, + }, + .ops = &es7243_ops, + .symmetric_rates = 1, +}; +static struct snd_soc_dai_driver es7243_dai2 = { + .name = "ES7243 HiFi 2", + .capture = + { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = es7243_RATES, + .formats = es7243_FORMATS, + }, + .ops = &es7243_ops, + .symmetric_rates = 1, +}; +static struct snd_soc_dai_driver es7243_dai3 = { + .name = "ES7243 HiFi 3", + .capture = + { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = es7243_RATES, + .formats = es7243_FORMATS, + }, + .ops = &es7243_ops, + .symmetric_rates = 1, +}; + +static struct snd_soc_dai_driver *es7243_dai[] = { +#if ES7243_CHANNELS_MAX > 0 + &es7243_dai0, +#endif +#if ES7243_CHANNELS_MAX > 2 + &es7243_dai1, +#endif +#if ES7243_CHANNELS_MAX > 4 + &es7243_dai2, +#endif +#if ES7243_CHANNELS_MAX > 6 + &es7243_dai3, +#endif +}; +static int es7243_suspend(struct snd_soc_codec *codec) { + es7243_set_bias_level(codec, SND_SOC_BIAS_OFF); + + return 0; +} + +static int es7243_resume(struct snd_soc_codec *codec) { + snd_soc_cache_sync(codec); + es7243_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + return 0; +} + +static int es7243_probe(struct snd_soc_codec *codec) { + struct es7243_priv *es7243 = snd_soc_codec_get_drvdata(codec); + int ret = 0; +#if !ES7243_CODEC_RW_TEST_EN + // ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);//8,8 +#else + codec->control_data = + devm_regmap_init_i2c(es7243->i2c, &es7243_regmap_config); + ret = PTR_RET(codec->control_data); +#endif + if (ret < 0) { + dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); + return ret; + } + printk("begin->>>>>>>>>>%s!\n", __func__); + + tron_codec[es7243_codec_num++] = codec; + INIT_DELAYED_WORK(&es7243->pcm_pop_work, pcm_pop_work_events); + INIT_DELAYED_WORK(&es7243->pcm_pop_work1, pcm_pop_work1_events); + return 0; +} + +static int es7243_remove(struct snd_soc_codec *codec) { + es7243_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +static struct snd_soc_codec_driver soc_codec_dev_es7243 = { + .probe = es7243_probe, + .remove = es7243_remove, + .suspend = es7243_suspend, + .resume = es7243_resume, + .set_bias_level = es7243_set_bias_level, + .idle_bias_off = true, + .reg_word_size = sizeof(u8), +#if ES7243_CODEC_RW_TEST_EN + .read = es7243_codec_read, + .write = es7243_codec_write, +#endif + +}; + +static ssize_t es7243_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; + + struct es7243_priv *es7243 = dev_get_drvdata(dev); + val = simple_strtol(buf, NULL, 16); + flag = (val >> 16) & 0xFF; + + if (flag) { + reg = (val >> 8) & 0xFF; + value_w = val & 0xFF; + printk("\nWrite: start REG:0x%02x,val:0x%02x,count:0x%02x\n", reg, + value_w, flag); + while (flag--) { + es7243_write(reg, value_w, es7243->i2c); + printk("Write 0x%02x to REG:0x%02x\n", value_w, reg); + 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; + es7243_read(reg, &value_r, es7243->i2c); + 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 es7243_show(struct device *dev, struct device_attribute *attr, + char *buf) { + printk("echo flag|reg|val > es7243\n"); + printk("eg read star addres=0x06,count 0x10:echo 0610 >es7243\n"); + printk("eg write star addres=0x90,value=0x3c,count=4:echo 4903c >es7243\n"); + // printk("eg write value:0xfe to address:0x06 :echo 106fe > es7243\n"); + return 0; +} + +static DEVICE_ATTR(es7243, 0644, es7243_show, es7243_store); + +static struct attribute *es7243_debug_attrs[] = { + &dev_attr_es7243.attr, + NULL, +}; + +static struct attribute_group es7243_debug_attr_group = { + .name = "es7243_debug", + .attrs = es7243_debug_attrs, +}; + +//#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +/* + * If the i2c layer weren't so broken, we could pass this kind of data + * around + */ +static int es7243_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *i2c_id) { + struct es7243_priv *es7243; + int ret; + // struct device_node *np = i2c->dev.of_node; + + printk("es7243_i2c_probe begin->>>>>>>>>>%s!\n", __func__); + + es7243 = devm_kzalloc(&i2c->dev, sizeof(struct es7243_priv), GFP_KERNEL); + if (es7243 == NULL) + return -ENOMEM; + es7243->i2c = i2c; + es7243->tdm = ES7243_TDM_ENABLE; // to initialize tdm mode + dev_set_drvdata(&i2c->dev, es7243); + // i2c_set_clientdata(i2c, es7243); + // es7243->regmap = devm_regmap_init_i2c(i2c, &es7243_regmap); + // if (IS_ERR(es7243->regmap)) { + // ret = PTR_ERR(es7243->regmap); + // dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + // ret); + // return ret; + // } + printk("%s, drivedata:%d\n", __func__, i2c_id->driver_data); + if (i2c_id->driver_data < (ES7243_CHANNELS_MAX) / 2) { + i2c_clt[i2c_id->driver_data] = i2c; + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_es7243, + es7243_dai[i2c_id->driver_data], 1); + if (ret < 0) { + kfree(es7243); + return ret; + } + printk("begin to registe codec:%s, ret:%d\n", + es7243_dai[i2c_id->driver_data]->name, ret); + } + ret = sysfs_create_group(&i2c->dev.kobj, &es7243_debug_attr_group); + if (ret) { + pr_err("failed to create attr group\n"); + } + return ret; +} +static int __exit es7243_i2c_remove(struct i2c_client *i2c) { + snd_soc_unregister_codec(&i2c->dev); + kfree(i2c_get_clientdata(i2c)); + return 0; +} + +static int es7243_i2c_detect(struct i2c_client *client, + struct i2c_board_info *info) { + struct i2c_adapter *adapter = client->adapter; + + if (adapter->nr == ES7243_I2C_BUS_NUM) { + if (client->addr == 0x10) { + strlcpy(info->type, "MicArray_3", I2C_NAME_SIZE); + return 0; + } else if (client->addr == 0x11) { + strlcpy(info->type, "MicArray_2", I2C_NAME_SIZE); + return 0; + } else if (client->addr == 0x12) { + strlcpy(info->type, "MicArray_1", I2C_NAME_SIZE); + return 0; + } else if (client->addr == 0x13) { + strlcpy(info->type, "MicArray_0", I2C_NAME_SIZE); + return 0; + } + } + + return -ENODEV; +} + +static const unsigned short es7243_i2c_addr[] = { +#if ES7243_CHANNELS_MAX > 0 + 0x13, +#endif + +#if ES7243_CHANNELS_MAX > 2 + 0x11, +#endif + +#if ES7243_CHANNELS_MAX > 4 + 0x12, +#endif + +#if ES7243_CHANNELS_MAX > 6 + 0x10, +#endif + + I2C_CLIENT_END, +}; + +/* + * device tree source or i2c_board_info both use to transfer hardware + * information to linux kernel, use one of them wil be OK + */ +static struct i2c_board_info es7243_i2c_board_info[] = { +#if ES7243_CHANNELS_MAX > 0 + { + I2C_BOARD_INFO("MicArray_0", 0x13), + }, // es7243_0 +#endif + +#if ES7243_CHANNELS_MAX > 2 + { + I2C_BOARD_INFO("MicArray_1", 0x11), + }, // es7243_1 +#endif + +#if ES7243_CHANNELS_MAX > 4 + { + I2C_BOARD_INFO("MicArray_2", 0x12), + }, // es7243_2 +#endif + +#if ES7243_CHANNELS_MAX > 6 + { + I2C_BOARD_INFO("MicArray_3", 0x10), + }, // es7243_3 +#endif +}; + +static const struct i2c_device_id es7243_i2c_id[] = { +#if ES7243_CHANNELS_MAX > 0 + {"MicArray_0", 0}, // es7243_0 +#endif + +#if ES7243_CHANNELS_MAX > 2 + {"MicArray_1", 1}, // es7243_1 +#endif + +#if ES7243_CHANNELS_MAX > 4 + {"MicArray_2", 2}, // es7243_2 +#endif + +#if ES7243_CHANNELS_MAX > 6 + {"MicArray_3", 3}, // es7243_3 +#endif + {}}; +MODULE_DEVICE_TABLE(i2c, es7243_i2c_id); + +static const struct of_device_id es7243_dt_ids[] = { +#if ES7243_CHANNELS_MAX > 0 + { + .compatible = "MicArray_0", + }, // es7243_0 +#endif + +#if ES7243_CHANNELS_MAX > 2 + { + .compatible = "MicArray_1", + }, // es7243_1 +#endif + +#if ES7243_CHANNELS_MAX > 4 + { + .compatible = "MicArray_2", + }, // es7243_2 +#endif + +#if ES7243_CHANNELS_MAX > 6 + { + .compatible = "MicArray_3", + }, // es7243_3 +#endif +}; +MODULE_DEVICE_TABLE(of, es7243_dt_ids); + +static struct i2c_driver es7243_i2c_driver = { + .driver = + { + .name = "es7243", + .owner = THIS_MODULE, +#if ES7243_MATCH_DTS_EN + .of_match_table = es7243_dt_ids, +#endif + }, + .probe = es7243_i2c_probe, + .remove = __exit_p(es7243_i2c_remove), + .class = I2C_CLASS_HWMON, + .id_table = es7243_i2c_id, +#if !ES7243_MATCH_DTS_EN + .address_list = es7243_i2c_addr, + .detect = es7243_i2c_detect, +#endif +}; + +static int __init es7243_modinit(void) { + int ret, i; + struct i2c_adapter *adapter; + struct i2c_client *client; + printk("%s enter es7243\n", __func__); + + // i2c_new_device(adapter, &tlv320_i2c_board_info[i]); + adapter = i2c_get_adapter(ES7243_I2C_BUS_NUM); + if (!adapter) { + printk("i2c_get_adapter() fail!\n"); + return -ENODEV; + } + printk("%s() begin0000", __func__); + + for (i = 0; i < ES7243_CHANNELS_MAX / 2; i++) { + client = i2c_new_device(adapter, &es7243_i2c_board_info[i]); + printk("%s() i2c_new_device, type:0x%x, addr:0x%x\n", __func__, + es7243_i2c_board_info[i].type, es7243_i2c_board_info[i].addr); + if (!client) + return -ENODEV; + } + i2c_put_adapter(adapter); + ret = i2c_add_driver(&es7243_i2c_driver); + if (ret != 0) + pr_err("Failed to register es7243 i2c driver : %d \n", ret); + return ret; +} +module_init(es7243_modinit); +static void __exit es7243_exit(void) { + //#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + i2c_del_driver(&es7243_i2c_driver); + //#endif +} +module_exit(es7243_exit); +MODULE_DESCRIPTION("ASoC ES7243 audio adc driver"); +MODULE_AUTHOR( + "David Yang / info@everest-semi.com"); +MODULE_LICENSE("GPL v2"); diff --git a/lichee/linux-4.9/sound/soc/codecs/es7243.h b/lichee/linux-4.9/sound/soc/codecs/es7243.h new file mode 100644 index 000000000..34bc826cd --- /dev/null +++ b/lichee/linux-4.9/sound/soc/codecs/es7243.h @@ -0,0 +1,34 @@ +/* + * ALSA SoC ES7243 codec driver + * + * Author: David Yang, + * or + * + * Copyright: (C) 2017 Everest Semiconductor Co., Ltd + * + * 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 _ES7243_H +#define _ES7243_H + +/* Codec TLV320AIC23 */ +#define ES7243_MODECFG_REG00 0x00 +#define ES7243_SDPFMT_REG01 0x01 +#define ES7243_LRCDIV_REG02 0x02 +#define ES7243_BCKDIV_REG03 0x03 +#define ES7243_CLKDIV_REG04 0x04 +#define ES7243_MUTECTL_REG05 0x05 +#define ES7243_STATECTL_REG06 0x06 +#define ES7243_ANACTL0_REG07 0x07 +#define ES7243_ANACTL1_REG08 0x08 +#define ES7243_ANACTL2_REG09 0x09 +#define ES7243_ANACHARG_REG0A 0x0A +#define ES7243_INISTATE_REG0B 0x0B +#define ES7243_BIAS_REG0C 0x0C +#define ES7243_STMOSR_REG0D 0x0D +#define ES7243_CHIPID_REG0E 0x0E + +#endif /* _ES7243_H_ */ \ No newline at end of file diff --git a/lichee/linux-4.9/sound/soc/codecs/netease-codec.c b/lichee/linux-4.9/sound/soc/codecs/netease-codec.c new file mode 100755 index 000000000..271e807b8 --- /dev/null +++ b/lichee/linux-4.9/sound/soc/codecs/netease-codec.c @@ -0,0 +1,1229 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "netease-codec.h" + +struct netease_priv { + int cpld_rst_gpio; +}; +#define DEBUG +#ifdef DEBUG +#define dprintk(fmt,...) \ + printf("%s(%d)-<%s>: "##fmt, __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__) +#else +#define dprintk +#endif + +#define HPL_MIXER 0 +#define HPR_MIXER 1 + +static const char *netease_mic_mixer[] = {"Stereo", "Mic 1", "Mic 2", "Mute"}; +static const char *netease_rec_mux[] = {"Stereo", "Left", "Right", "Mute"}; +static const char *netease_rec_src[] = + {"Mic 1", "Mic 2", "Line", "Mono In", "Headphone", "Speaker", + "Mono Out", "Zh"}; +static const char *netease_rec_gain[] = {"+1.5dB Steps", "+0.75dB Steps"}; +static const char *netease_alc_select[] = {"None", "Left", "Right", "Stereo"}; +static const char *netease_mono_pga[] = {"Vmid", "Zh", "Mono", "Inv"}; +static const char *netease_spk_pga[] = + {"Vmid", "Zh", "Headphone", "Speaker", "Inv", "Headphone Vmid", + "Speaker Vmid", "Inv Vmid"}; +static const char *netease_hp_pga[] = {"Vmid", "Zh", "Headphone", + "Headphone Vmid"}; +static const char *netease_out3_pga[] = {"Vmid", "Zh", "Inv 1", "Inv 1 Vmid"}; +static const char *netease_out4_pga[] = {"Vmid", "Zh", "Inv 2", "Inv 2 Vmid"}; +static const char *netease_dac_inv[] = + {"Off", "Mono", "Speaker", "Left Headphone", "Right Headphone", + "Headphone Mono", "NC", "Vmid"}; +static const char *netease_bass[] = {"Linear Control", "Adaptive Boost"}; +static const char *netease_ng_type[] = {"Constant Gain", "Mute"}; +static const char *netease_mic_select[] = {"Mic 1", "Mic 2 A", "Mic 2 B"}; +static const char *netease_micb_select[] = {"MPB", "MPA"}; + +static const struct soc_enum netease_enum[] = { +SOC_ENUM_SINGLE(AC97_LINE, 3, 4, netease_mic_mixer), /* record mic mixer 0 */ +SOC_ENUM_SINGLE(AC97_VIDEO, 14, 4, netease_rec_mux), /* record mux hp 1 */ +SOC_ENUM_SINGLE(AC97_VIDEO, 9, 4, netease_rec_mux), /* record mux mono 2 */ +SOC_ENUM_SINGLE(AC97_VIDEO, 3, 8, netease_rec_src), /* record mux left 3 */ +SOC_ENUM_SINGLE(AC97_VIDEO, 0, 8, netease_rec_src), /* record mux right 4*/ +SOC_ENUM_DOUBLE(AC97_CD, 14, 6, 2, netease_rec_gain), /* record step size 5 */ +SOC_ENUM_SINGLE(AC97_PCI_SVID, 14, 4, netease_alc_select), /* alc source select 6*/ +SOC_ENUM_SINGLE(AC97_REC_GAIN, 14, 4, netease_mono_pga), /* mono input select 7 */ +SOC_ENUM_SINGLE(AC97_REC_GAIN, 11, 8, netease_spk_pga), /* speaker left input select 8 */ +SOC_ENUM_SINGLE(AC97_REC_GAIN, 8, 8, netease_spk_pga), /* speaker right input select 9 */ +SOC_ENUM_SINGLE(AC97_REC_GAIN, 6, 3, netease_hp_pga), /* headphone left input 10 */ +SOC_ENUM_SINGLE(AC97_REC_GAIN, 4, 3, netease_hp_pga), /* headphone right input 11 */ +SOC_ENUM_SINGLE(AC97_REC_GAIN, 2, 4, netease_out3_pga), /* out 3 source 12 */ +SOC_ENUM_SINGLE(AC97_REC_GAIN, 0, 4, netease_out4_pga), /* out 4 source 13 */ +SOC_ENUM_SINGLE(AC97_REC_GAIN_MIC, 13, 8, netease_dac_inv), /* dac invert 1 14 */ +SOC_ENUM_SINGLE(AC97_REC_GAIN_MIC, 10, 8, netease_dac_inv), /* dac invert 2 15 */ +SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 15, 2, netease_bass), /* bass control 16 */ +SOC_ENUM_SINGLE(AC97_PCI_SVID, 5, 2, netease_ng_type), /* noise gate type 17 */ +SOC_ENUM_SINGLE(AC97_3D_CONTROL, 12, 3, netease_mic_select), /* mic selection 18 */ +SOC_ENUM_SINGLE_VIRT(2, netease_micb_select), /* mic selection 19 */ +}; + +static const DECLARE_TLV_DB_SCALE(out_tlv, -4650, 150, 0); +static const DECLARE_TLV_DB_SCALE(main_tlv, -3450, 150, 0); +static const DECLARE_TLV_DB_SCALE(misc_tlv, -1500, 300, 0); +static const DECLARE_TLV_DB_RANGE(mic_tlv, + 0, 2, TLV_DB_SCALE_ITEM(1200, 600, 0), + 3, 3, TLV_DB_SCALE_ITEM(3000, 0, 0) +); + +static const struct snd_kcontrol_new netease_snd_ac97_controls[] = { +SOC_DOUBLE_TLV("Speaker Playback Volume", AC97_MASTER, 8, 0, 31, 1, out_tlv), +SOC_DOUBLE("Speaker Playback Switch", AC97_MASTER, 15, 7, 1, 1), +SOC_DOUBLE_TLV("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1, + out_tlv), +SOC_DOUBLE("Headphone Playback Switch", AC97_HEADPHONE, 15, 7, 1, 1), +SOC_DOUBLE_TLV("Line In Volume", AC97_PC_BEEP, 8, 0, 31, 1, main_tlv), +SOC_DOUBLE_TLV("PCM Playback Volume", AC97_PHONE, 8, 0, 31, 1, main_tlv), +SOC_SINGLE_TLV("Mic 1 Vo lume", AC97_MIC, 8, 31, 1, main_tlv), +SOC_SINGLE_TLV("Mic 2 Volume", AC97_MIC, 0, 31, 1, main_tlv), +SOC_SINGLE_TLV("Mic 1 Preamp Volume", AC97_3D_CONTROL, 10, 3, 0, mic_tlv), +SOC_SINGLE_TLV("Mic 2 Preamp Volume", AC97_3D_CONTROL, 12, 3, 0, mic_tlv), + +SOC_SINGLE("Mic Boost (+20dB) Switch", AC97_LINE, 5, 1, 0), +SOC_SINGLE("Mic Headphone Mixer Volume", AC97_LINE, 0, 7, 1), + +SOC_SINGLE("Capture Switch", AC97_CD, 15, 1, 1), +SOC_ENUM("Capture Volume Steps", netease_enum[5]), +SOC_DOUBLE("Capture Volume", AC97_CD, 8, 0, 31, 0), +SOC_SINGLE("Capture ZC Switch", AC97_CD, 7, 1, 0), + +SOC_SINGLE_TLV("Capture to Headphone Volume", AC97_VIDEO, 11, 7, 1, misc_tlv), +SOC_SINGLE("Capture to Mono Boost (+20dB) Switch", AC97_VIDEO, 8, 1, 0), +SOC_SINGLE("Capture ADC Boost (+20dB) Switch", AC97_VIDEO, 6, 1, 0), + +SOC_SINGLE("ALC Target Volume", AC97_CODEC_CLASS_REV, 12, 15, 0), +SOC_SINGLE("ALC Hold Time", AC97_CODEC_CLASS_REV, 8, 15, 0), +SOC_SINGLE("ALC Decay Time", AC97_CODEC_CLASS_REV, 4, 15, 0), +SOC_SINGLE("ALC Attack Time", AC97_CODEC_CLASS_REV, 0, 15, 0), +SOC_ENUM("ALC Function", netease_enum[6]), +SOC_SINGLE("ALC Max Volume", AC97_PCI_SVID, 11, 7, 0), +SOC_SINGLE("ALC ZC Timeout", AC97_PCI_SVID, 9, 3, 0), +SOC_SINGLE("ALC ZC Switch", AC97_PCI_SVID, 8, 1, 0), +SOC_SINGLE("ALC NG Switch", AC97_PCI_SVID, 7, 1, 0), +SOC_ENUM("ALC NG Type", netease_enum[17]), +SOC_SINGLE("ALC NG Threshold", AC97_PCI_SVID, 0, 31, 0), + +SOC_DOUBLE("Speaker Playback ZC Switch", AC97_MASTER, 14, 6, 1, 0), +SOC_DOUBLE("Headphone Playback ZC Switch", AC97_HEADPHONE, 14, 6, 1, 0), + +SOC_SINGLE("Out4 Playback Switch", AC97_MASTER_MONO, 15, 1, 1), +SOC_SINGLE("Out4 Playback ZC Switch", AC97_MASTER_MONO, 14, 1, 0), +SOC_SINGLE_TLV("Out4 Playback Volume", AC97_MASTER_MONO, 8, 31, 1, out_tlv), + +SOC_SINGLE("Out3 Playback Switch", AC97_MASTER_MONO, 7, 1, 1), +SOC_SINGLE("Out3 Playback ZC Switch", AC97_MASTER_MONO, 6, 1, 0), +SOC_SINGLE_TLV("Out3 Playback Volume", AC97_MASTER_MONO, 0, 31, 1, out_tlv), + +SOC_SINGLE_TLV("Mono Capture Volume", AC97_MASTER_TONE, 8, 31, 1, main_tlv), +SOC_SINGLE("Mono Playback Switch", AC97_MASTER_TONE, 7, 1, 1), +SOC_SINGLE("Mono Playback ZC Switch", AC97_MASTER_TONE, 6, 1, 0), +SOC_SINGLE_TLV("Mono Playback Volume", AC97_MASTER_TONE, 0, 31, 1, out_tlv), + +SOC_SINGLE_TLV("Headphone Mixer Beep Playback Volume", AC97_AUX, 12, 7, 1, + misc_tlv), +SOC_SINGLE_TLV("Speaker Mixer Beep Playback Volume", AC97_AUX, 8, 7, 1, + misc_tlv), +SOC_SINGLE_TLV("Mono Mixer Beep Playback Volume", AC97_AUX, 4, 7, 1, misc_tlv), + +SOC_SINGLE_TLV("Voice Playback Headphone Volume", AC97_PCM, 12, 7, 1, + misc_tlv), +SOC_SINGLE("Voice Playback Master Volume", AC97_PCM, 8, 7, 1), +SOC_SINGLE("Voice Playback Mono Volume", AC97_PCM, 4, 7, 1), + +SOC_SINGLE_TLV("Headphone Mixer Aux Playback Volume", AC97_REC_SEL, 12, 7, 1, + misc_tlv), + +SOC_SINGLE_TLV("Speaker Mixer Voice Playback Volume", AC97_PCM, 8, 7, 1, + misc_tlv), +SOC_SINGLE_TLV("Speaker Mixer Aux Playback Volume", AC97_REC_SEL, 8, 7, 1, + misc_tlv), + +SOC_SINGLE_TLV("Mono Mixer Voice Playback Volume", AC97_PCM, 4, 7, 1, + misc_tlv), +SOC_SINGLE_TLV("Mono Mixer Aux Playback Volume", AC97_REC_SEL, 4, 7, 1, + misc_tlv), + +SOC_SINGLE("Aux Playback Headphone Volume", AC97_REC_SEL, 12, 7, 1), +SOC_SINGLE("Aux Playback Master Volume", AC97_REC_SEL, 8, 7, 1), + +SOC_ENUM("Bass Control", netease_enum[16]), +SOC_SINGLE("Bass Cut-off Switch", AC97_GENERAL_PURPOSE, 12, 1, 1), +SOC_SINGLE("Tone Cut-off Switch", AC97_GENERAL_PURPOSE, 4, 1, 1), +SOC_SINGLE("Playback Attenuate (-6dB) Switch", AC97_GENERAL_PURPOSE, 6, 1, 0), +SOC_SINGLE("Bass Volume", AC97_GENERAL_PURPOSE, 8, 15, 1), +SOC_SINGLE("Tone Volume", AC97_GENERAL_PURPOSE, 0, 15, 1), + +SOC_SINGLE("3D Upper Cut-off Switch", AC97_REC_GAIN_MIC, 5, 1, 0), +SOC_SINGLE("3D Lower Cut-off Switch", AC97_REC_GAIN_MIC, 4, 1, 0), +SOC_SINGLE("3D Depth", AC97_REC_GAIN_MIC, 0, 15, 1), +}; + +static int netease_voice_shutdown(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + if (WARN_ON(event != SND_SOC_DAPM_PRE_PMD)) + return -EINVAL; + + /* Gracefully shut down the voice interface. */ + snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x0f00, 0x0200); + schedule_timeout_interruptible(msecs_to_jiffies(1)); + snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x0f00, 0x0f00); + snd_soc_update_bits(codec, AC97_EXTENDED_MID, 0x1000, 0x1000); + + return 0; +} + +static const unsigned int netease_mixer_mute_regs[] = { + AC97_PC_BEEP, + AC97_MASTER_TONE, + AC97_PHONE, + AC97_REC_SEL, + AC97_PCM, + AC97_AUX, +}; + +/* We have to create a fake left and right HP mixers because + * the codec only has a single control that is shared by both channels. + * This makes it impossible to determine the audio path using the current + * register map, thus we add a new (virtual) register to help determine the + * audio route within the device. + */ +static int netease_hp_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm); + struct netease_priv *netease = snd_soc_codec_get_drvdata(codec); + unsigned int val = ucontrol->value.integer.value[0]; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int mixer, mask, shift, old; + struct snd_soc_dapm_update update; + bool change; + + mixer = mc->shift >> 8; + shift = mc->shift & 0xff; + mask = (1 << shift); + + mutex_lock(&netease->lock); + old = netease->hp_mixer[mixer]; + if (ucontrol->value.integer.value[0]) + netease->hp_mixer[mixer] |= mask; + else + netease->hp_mixer[mixer] &= ~mask; + + change = old != netease->hp_mixer[mixer]; + if (change) { + update.kcontrol = kcontrol; + update.reg = netease_mixer_mute_regs[shift]; + update.mask = 0x8000; + if ((netease->hp_mixer[0] & mask) || + (netease->hp_mixer[1] & mask)) + update.val = 0x0; + else + update.val = 0x8000; + + snd_soc_dapm_mixer_update_power(dapm, kcontrol, val, + &update); + } + + mutex_unlock(&netease->lock); + + return change; +} + +static int netease_hp_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm); + struct netease_priv *netease = snd_soc_codec_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int mixer, shift; + + mixer = mc->shift >> 8; + shift = mc->shift & 0xff; + + ucontrol->value.integer.value[0] = + (netease->hp_mixer[mixer] >> shift) & 1; + + return 0; +} + +#define netease_HP_MIXER_CTRL(xname, xmixer, xshift) { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_volsw, \ + .get = netease_hp_mixer_get, .put = netease_hp_mixer_put, \ + .private_value = SOC_DOUBLE_VALUE(SND_SOC_NOPM, \ + xshift, xmixer, 1, 0, 0) \ +} + +/* Left Headphone Mixers */ +static const struct snd_kcontrol_new netease_hpl_mixer_controls[] = { +netease_HP_MIXER_CTRL("Beep Playback Switch", HPL_MIXER, 5), +netease_HP_MIXER_CTRL("Voice Playback Switch", HPL_MIXER, 4), +netease_HP_MIXER_CTRL("Aux Playback Switch", HPL_MIXER, 3), +netease_HP_MIXER_CTRL("PCM Playback Switch", HPL_MIXER, 2), +netease_HP_MIXER_CTRL("MonoIn Playback Switch", HPL_MIXER, 1), +netease_HP_MIXER_CTRL("Bypass Playback Switch", HPL_MIXER, 0), +}; + +/* Right Headphone Mixers */ +static const struct snd_kcontrol_new netease_hpr_mixer_controls[] = { +netease_HP_MIXER_CTRL("Beep Playback Switch", HPR_MIXER, 5), +netease_HP_MIXER_CTRL("Voice Playback Switch", HPR_MIXER, 4), +netease_HP_MIXER_CTRL("Aux Playback Switch", HPR_MIXER, 3), +netease_HP_MIXER_CTRL("PCM Playback Switch", HPR_MIXER, 2), +netease_HP_MIXER_CTRL("MonoIn Playback Switch", HPR_MIXER, 1), +netease_HP_MIXER_CTRL("Bypass Playback Switch", HPR_MIXER, 0), +}; + +/* headphone capture mux */ +static const struct snd_kcontrol_new netease_hp_rec_mux_controls = +SOC_DAPM_ENUM("Route", netease_enum[1]); + +/* headphone mic mux */ +static const struct snd_kcontrol_new netease_hp_mic_mux_controls = +SOC_DAPM_ENUM("Route", netease_enum[0]); + +/* Speaker Mixer */ +static const struct snd_kcontrol_new netease_speaker_mixer_controls[] = { +SOC_DAPM_SINGLE("Beep Playback Switch", AC97_AUX, 11, 1, 1), +SOC_DAPM_SINGLE("Voice Playback Switch", AC97_PCM, 11, 1, 1), +SOC_DAPM_SINGLE("Aux Playback Switch", AC97_REC_SEL, 11, 1, 1), +SOC_DAPM_SINGLE("PCM Playback Switch", AC97_PHONE, 14, 1, 1), +SOC_DAPM_SINGLE("MonoIn Playback Switch", AC97_MASTER_TONE, 14, 1, 1), +SOC_DAPM_SINGLE("Bypass Playback Switch", AC97_PC_BEEP, 14, 1, 1), +}; + +/* Mono Mixer */ +static const struct snd_kcontrol_new netease_mono_mixer_controls[] = { +SOC_DAPM_SINGLE("Beep Playback Switch", AC97_AUX, 7, 1, 1), +SOC_DAPM_SINGLE("Voice Playback Switch", AC97_PCM, 7, 1, 1), +SOC_DAPM_SINGLE("Aux Playback Switch", AC97_REC_SEL, 7, 1, 1), +SOC_DAPM_SINGLE("PCM Playback Switch", AC97_PHONE, 13, 1, 1), +SOC_DAPM_SINGLE("MonoIn Playback Switch", AC97_MASTER_TONE, 13, 1, 1), +SOC_DAPM_SINGLE("Bypass Playback Switch", AC97_PC_BEEP, 13, 1, 1), +SOC_DAPM_SINGLE("Mic 1 Sidetone Switch", AC97_LINE, 7, 1, 1), +SOC_DAPM_SINGLE("Mic 2 Sidetone Switch", AC97_LINE, 6, 1, 1), +}; + +/* mono mic mux */ +static const struct snd_kcontrol_new netease_mono_mic_mux_controls = +SOC_DAPM_ENUM("Route", netease_enum[2]); + +/* mono output mux */ +static const struct snd_kcontrol_new netease_mono_mux_controls = +SOC_DAPM_ENUM("Route", netease_enum[7]); + +/* speaker left output mux */ +static const struct snd_kcontrol_new netease_hp_spkl_mux_controls = +SOC_DAPM_ENUM("Route", netease_enum[8]); + +/* speaker right output mux */ +static const struct snd_kcontrol_new netease_hp_spkr_mux_controls = +SOC_DAPM_ENUM("Route", netease_enum[9]); + +/* headphone left output mux */ +static const struct snd_kcontrol_new netease_hpl_out_mux_controls = +SOC_DAPM_ENUM("Route", netease_enum[10]); + +/* headphone right output mux */ +static const struct snd_kcontrol_new netease_hpr_out_mux_controls = +SOC_DAPM_ENUM("Route", netease_enum[11]); + +/* Out3 mux */ +static const struct snd_kcontrol_new netease_out3_mux_controls = +SOC_DAPM_ENUM("Route", netease_enum[12]); + +/* Out4 mux */ +static const struct snd_kcontrol_new netease_out4_mux_controls = +SOC_DAPM_ENUM("Route", netease_enum[13]); + +/* DAC inv mux 1 */ +static const struct snd_kcontrol_new netease_dac_inv1_mux_controls = +SOC_DAPM_ENUM("Route", netease_enum[14]); + +/* DAC inv mux 2 */ +static const struct snd_kcontrol_new netease_dac_inv2_mux_controls = +SOC_DAPM_ENUM("Route", netease_enum[15]); + +/* Capture source left */ +static const struct snd_kcontrol_new netease_rec_srcl_mux_controls = +SOC_DAPM_ENUM("Route", netease_enum[3]); + +/* Capture source right */ +static const struct snd_kcontrol_new netease_rec_srcr_mux_controls = +SOC_DAPM_ENUM("Route", netease_enum[4]); + +/* mic source */ +static const struct snd_kcontrol_new netease_mic_sel_mux_controls = +SOC_DAPM_ENUM("Route", netease_enum[18]); + +/* mic source B virtual control */ +static const struct snd_kcontrol_new netease_micb_sel_mux_controls = +SOC_DAPM_ENUM("Route", netease_enum[19]); + +static const struct snd_soc_dapm_widget netease_dapm_widgets[] = { +SND_SOC_DAPM_MUX("Capture Headphone Mux", SND_SOC_NOPM, 0, 0, + &netease_hp_rec_mux_controls), +SND_SOC_DAPM_MUX("Sidetone Mux", SND_SOC_NOPM, 0, 0, + &netease_hp_mic_mux_controls), +SND_SOC_DAPM_MUX("Capture Mono Mux", SND_SOC_NOPM, 0, 0, + &netease_mono_mic_mux_controls), +SND_SOC_DAPM_MUX("Mono Out Mux", SND_SOC_NOPM, 0, 0, + &netease_mono_mux_controls), +SND_SOC_DAPM_MUX("Left Speaker Out Mux", SND_SOC_NOPM, 0, 0, + &netease_hp_spkl_mux_controls), +SND_SOC_DAPM_MUX("Right Speaker Out Mux", SND_SOC_NOPM, 0, 0, + &netease_hp_spkr_mux_controls), +SND_SOC_DAPM_MUX("Left Headphone Out Mux", SND_SOC_NOPM, 0, 0, + &netease_hpl_out_mux_controls), +SND_SOC_DAPM_MUX("Right Headphone Out Mux", SND_SOC_NOPM, 0, 0, + &netease_hpr_out_mux_controls), +SND_SOC_DAPM_MUX("Out 3 Mux", SND_SOC_NOPM, 0, 0, + &netease_out3_mux_controls), +SND_SOC_DAPM_MUX("Out 4 Mux", SND_SOC_NOPM, 0, 0, + &netease_out4_mux_controls), +SND_SOC_DAPM_MUX("DAC Inv Mux 1", SND_SOC_NOPM, 0, 0, + &netease_dac_inv1_mux_controls), +SND_SOC_DAPM_MUX("DAC Inv Mux 2", SND_SOC_NOPM, 0, 0, + &netease_dac_inv2_mux_controls), +SND_SOC_DAPM_MUX("Left Capture Source", SND_SOC_NOPM, 0, 0, + &netease_rec_srcl_mux_controls), +SND_SOC_DAPM_MUX("Right Capture Source", SND_SOC_NOPM, 0, 0, + &netease_rec_srcr_mux_controls), +SND_SOC_DAPM_MUX("Mic A Source", SND_SOC_NOPM, 0, 0, + &netease_mic_sel_mux_controls), +SND_SOC_DAPM_MUX("Mic B Source", SND_SOC_NOPM, 0, 0, + &netease_micb_sel_mux_controls), +SND_SOC_DAPM_MIXER("Left HP Mixer", AC97_EXTENDED_MID, 3, 1, + &netease_hpl_mixer_controls[0], ARRAY_SIZE(netease_hpl_mixer_controls)), +SND_SOC_DAPM_MIXER("Right HP Mixer", AC97_EXTENDED_MID, 2, 1, + &netease_hpr_mixer_controls[0], ARRAY_SIZE(netease_hpr_mixer_controls)), +SND_SOC_DAPM_MIXER("Mono Mixer", AC97_EXTENDED_MID, 0, 1, + &netease_mono_mixer_controls[0], ARRAY_SIZE(netease_mono_mixer_controls)), +SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_EXTENDED_MID, 1, 1, + &netease_speaker_mixer_controls[0], + ARRAY_SIZE(netease_speaker_mixer_controls)), +SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback", AC97_EXTENDED_MID, 7, 1), +SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback", AC97_EXTENDED_MID, 6, 1), +SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_MIXER("HP Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_MIXER("Line Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_MIXER("Capture Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_DAC_E("Voice DAC", "Voice Playback", AC97_EXTENDED_MID, 12, 1, + netease_voice_shutdown, SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_DAC("Aux DAC", "Aux Playback", AC97_EXTENDED_MID, 11, 1), +SND_SOC_DAPM_PGA("Left ADC", AC97_EXTENDED_MID, 5, 1, NULL, 0), +SND_SOC_DAPM_PGA("Right ADC", AC97_EXTENDED_MID, 4, 1, NULL, 0), +SND_SOC_DAPM_ADC("Left HiFi ADC", "Left HiFi Capture", SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_ADC("Right HiFi ADC", "Right HiFi Capture", SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_ADC("Left Voice ADC", "Left Voice Capture", SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_ADC("Right Voice ADC", "Right Voice Capture", SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_PGA("Left Headphone", AC97_EXTENDED_MSTATUS, 10, 1, NULL, 0), +SND_SOC_DAPM_PGA("Right Headphone", AC97_EXTENDED_MSTATUS, 9, 1, NULL, 0), +SND_SOC_DAPM_PGA("Left Speaker", AC97_EXTENDED_MSTATUS, 8, 1, NULL, 0), +SND_SOC_DAPM_PGA("Right Speaker", AC97_EXTENDED_MSTATUS, 7, 1, NULL, 0), +SND_SOC_DAPM_PGA("Out 3", AC97_EXTENDED_MSTATUS, 11, 1, NULL, 0), +SND_SOC_DAPM_PGA("Out 4", AC97_EXTENDED_MSTATUS, 12, 1, NULL, 0), +SND_SOC_DAPM_PGA("Mono Out", AC97_EXTENDED_MSTATUS, 13, 1, NULL, 0), +SND_SOC_DAPM_PGA("Left Line In", AC97_EXTENDED_MSTATUS, 6, 1, NULL, 0), +SND_SOC_DAPM_PGA("Right Line In", AC97_EXTENDED_MSTATUS, 5, 1, NULL, 0), +SND_SOC_DAPM_PGA("Mono In", AC97_EXTENDED_MSTATUS, 4, 1, NULL, 0), +SND_SOC_DAPM_PGA("Mic A PGA", AC97_EXTENDED_MSTATUS, 3, 1, NULL, 0), +SND_SOC_DAPM_PGA("Mic B PGA", AC97_EXTENDED_MSTATUS, 2, 1, NULL, 0), +SND_SOC_DAPM_PGA("Mic A Pre Amp", AC97_EXTENDED_MSTATUS, 1, 1, NULL, 0), +SND_SOC_DAPM_PGA("Mic B Pre Amp", AC97_EXTENDED_MSTATUS, 0, 1, NULL, 0), +SND_SOC_DAPM_MICBIAS("Mic Bias", AC97_EXTENDED_MSTATUS, 14, 1), +SND_SOC_DAPM_OUTPUT("MONO"), +SND_SOC_DAPM_OUTPUT("HPL"), +SND_SOC_DAPM_OUTPUT("HPR"), +SND_SOC_DAPM_OUTPUT("SPKL"), +SND_SOC_DAPM_OUTPUT("SPKR"), +SND_SOC_DAPM_OUTPUT("OUT3"), +SND_SOC_DAPM_OUTPUT("OUT4"), +SND_SOC_DAPM_INPUT("LINEL"), +SND_SOC_DAPM_INPUT("LINER"), +SND_SOC_DAPM_INPUT("MONOIN"), +SND_SOC_DAPM_INPUT("PCBEEP"), +SND_SOC_DAPM_INPUT("MIC1"), +SND_SOC_DAPM_INPUT("MIC2A"), +SND_SOC_DAPM_INPUT("MIC2B"), +SND_SOC_DAPM_VMID("VMID"), +}; + +static const struct snd_soc_dapm_route netease_audio_map[] = { + /* left HP mixer */ + {"Left HP Mixer", "Beep Playback Switch", "PCBEEP"}, + {"Left HP Mixer", "Voice Playback Switch", "Voice DAC"}, + {"Left HP Mixer", "Aux Playback Switch", "Aux DAC"}, + {"Left HP Mixer", "Bypass Playback Switch", "Left Line In"}, + {"Left HP Mixer", "PCM Playback Switch", "Left DAC"}, + {"Left HP Mixer", "MonoIn Playback Switch", "Mono In"}, + {"Left HP Mixer", NULL, "Capture Headphone Mux"}, + + /* right HP mixer */ + {"Right HP Mixer", "Beep Playback Switch", "PCBEEP"}, + {"Right HP Mixer", "Voice Playback Switch", "Voice DAC"}, + {"Right HP Mixer", "Aux Playback Switch", "Aux DAC"}, + {"Right HP Mixer", "Bypass Playback Switch", "Right Line In"}, + {"Right HP Mixer", "PCM Playback Switch", "Right DAC"}, + {"Right HP Mixer", "MonoIn Playback Switch", "Mono In"}, + {"Right HP Mixer", NULL, "Capture Headphone Mux"}, + + /* virtual mixer - mixes left & right channels for spk and mono */ + {"AC97 Mixer", NULL, "Left DAC"}, + {"AC97 Mixer", NULL, "Right DAC"}, + {"Line Mixer", NULL, "Right Line In"}, + {"Line Mixer", NULL, "Left Line In"}, + {"HP Mixer", NULL, "Left HP Mixer"}, + {"HP Mixer", NULL, "Right HP Mixer"}, + {"Capture Mixer", NULL, "Left Capture Source"}, + {"Capture Mixer", NULL, "Right Capture Source"}, + + /* speaker mixer */ + {"Speaker Mixer", "Beep Playback Switch", "PCBEEP"}, + {"Speaker Mixer", "Voice Playback Switch", "Voice DAC"}, + {"Speaker Mixer", "Aux Playback Switch", "Aux DAC"}, + {"Speaker Mixer", "Bypass Playback Switch", "Line Mixer"}, + {"Speaker Mixer", "PCM Playback Switch", "AC97 Mixer"}, + {"Speaker Mixer", "MonoIn Playback Switch", "Mono In"}, + + /* mono mixer */ + {"Mono Mixer", "Beep Playback Switch", "PCBEEP"}, + {"Mono Mixer", "Voice Playback Switch", "Voice DAC"}, + {"Mono Mixer", "Aux Playback Switch", "Aux DAC"}, + {"Mono Mixer", "Bypass Playback Switch", "Line Mixer"}, + {"Mono Mixer", "PCM Playback Switch", "AC97 Mixer"}, + {"Mono Mixer", "Mic 1 Sidetone Switch", "Mic A PGA"}, + {"Mono Mixer", "Mic 2 Sidetone Switch", "Mic B PGA"}, + {"Mono Mixer", NULL, "Capture Mono Mux"}, + + /* DAC inv mux 1 */ + {"DAC Inv Mux 1", "Mono", "Mono Mixer"}, + {"DAC Inv Mux 1", "Speaker", "Speaker Mixer"}, + {"DAC Inv Mux 1", "Left Headphone", "Left HP Mixer"}, + {"DAC Inv Mux 1", "Right Headphone", "Right HP Mixer"}, + {"DAC Inv Mux 1", "Headphone Mono", "HP Mixer"}, + + /* DAC inv mux 2 */ + {"DAC Inv Mux 2", "Mono", "Mono Mixer"}, + {"DAC Inv Mux 2", "Speaker", "Speaker Mixer"}, + {"DAC Inv Mux 2", "Left Headphone", "Left HP Mixer"}, + {"DAC Inv Mux 2", "Right Headphone", "Right HP Mixer"}, + {"DAC Inv Mux 2", "Headphone Mono", "HP Mixer"}, + + /* headphone left mux */ + {"Left Headphone Out Mux", "Headphone", "Left HP Mixer"}, + + /* headphone right mux */ + {"Right Headphone Out Mux", "Headphone", "Right HP Mixer"}, + + /* speaker left mux */ + {"Left Speaker Out Mux", "Headphone", "Left HP Mixer"}, + {"Left Speaker Out Mux", "Speaker", "Speaker Mixer"}, + {"Left Speaker Out Mux", "Inv", "DAC Inv Mux 1"}, + + /* speaker right mux */ + {"Right Speaker Out Mux", "Headphone", "Right HP Mixer"}, + {"Right Speaker Out Mux", "Speaker", "Speaker Mixer"}, + {"Right Speaker Out Mux", "Inv", "DAC Inv Mux 2"}, + + /* mono mux */ + {"Mono Out Mux", "Mono", "Mono Mixer"}, + {"Mono Out Mux", "Inv", "DAC Inv Mux 1"}, + + /* out 3 mux */ + {"Out 3 Mux", "Inv 1", "DAC Inv Mux 1"}, + + /* out 4 mux */ + {"Out 4 Mux", "Inv 2", "DAC Inv Mux 2"}, + + /* output pga */ + {"HPL", NULL, "Left Headphone"}, + {"Left Headphone", NULL, "Left Headphone Out Mux"}, + {"HPR", NULL, "Right Headphone"}, + {"Right Headphone", NULL, "Right Headphone Out Mux"}, + {"OUT3", NULL, "Out 3"}, + {"Out 3", NULL, "Out 3 Mux"}, + {"OUT4", NULL, "Out 4"}, + {"Out 4", NULL, "Out 4 Mux"}, + {"SPKL", NULL, "Left Speaker"}, + {"Left Speaker", NULL, "Left Speaker Out Mux"}, + {"SPKR", NULL, "Right Speaker"}, + {"Right Speaker", NULL, "Right Speaker Out Mux"}, + {"MONO", NULL, "Mono Out"}, + {"Mono Out", NULL, "Mono Out Mux"}, + + /* input pga */ + {"Left Line In", NULL, "LINEL"}, + {"Right Line In", NULL, "LINER"}, + {"Mono In", NULL, "MONOIN"}, + {"Mic A PGA", NULL, "Mic A Pre Amp"}, + {"Mic B PGA", NULL, "Mic B Pre Amp"}, + + /* left capture select */ + {"Left Capture Source", "Mic 1", "Mic A Pre Amp"}, + {"Left Capture Source", "Mic 2", "Mic B Pre Amp"}, + {"Left Capture Source", "Line", "LINEL"}, + {"Left Capture Source", "Mono In", "MONOIN"}, + {"Left Capture Source", "Headphone", "Left HP Mixer"}, + {"Left Capture Source", "Speaker", "Speaker Mixer"}, + {"Left Capture Source", "Mono Out", "Mono Mixer"}, + + /* right capture select */ + {"Right Capture Source", "Mic 1", "Mic A Pre Amp"}, + {"Right Capture Source", "Mic 2", "Mic B Pre Amp"}, + {"Right Capture Source", "Line", "LINER"}, + {"Right Capture Source", "Mono In", "MONOIN"}, + {"Right Capture Source", "Headphone", "Right HP Mixer"}, + {"Right Capture Source", "Speaker", "Speaker Mixer"}, + {"Right Capture Source", "Mono Out", "Mono Mixer"}, + + /* left ADC */ + {"Left ADC", NULL, "Left Capture Source"}, + {"Left Voice ADC", NULL, "Left ADC"}, + {"Left HiFi ADC", NULL, "Left ADC"}, + + /* right ADC */ + {"Right ADC", NULL, "Right Capture Source"}, + {"Right Voice ADC", NULL, "Right ADC"}, + {"Right HiFi ADC", NULL, "Right ADC"}, + + /* mic */ + {"Mic A Pre Amp", NULL, "Mic A Source"}, + {"Mic A Source", "Mic 1", "MIC1"}, + {"Mic A Source", "Mic 2 A", "MIC2A"}, + {"Mic A Source", "Mic 2 B", "Mic B Source"}, + {"Mic B Pre Amp", "MPB", "Mic B Source"}, + {"Mic B Source", NULL, "MIC2B"}, + + /* headphone capture */ + {"Capture Headphone Mux", "Stereo", "Capture Mixer"}, + {"Capture Headphone Mux", "Left", "Left Capture Source"}, + {"Capture Headphone Mux", "Right", "Right Capture Source"}, + + /* mono capture */ + {"Capture Mono Mux", "Stereo", "Capture Mixer"}, + {"Capture Mono Mux", "Left", "Left Capture Source"}, + {"Capture Mono Mux", "Right", "Right Capture Source"}, +}; + +static bool netease_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AC97_RESET ... AC97_PCM_SURR_DAC_RATE: + case AC97_PCM_LR_ADC_RATE: + case AC97_CENTER_LFE_MASTER: + case AC97_SPDIF ... AC97_LINE1_LEVEL: + case AC97_GPIO_CFG ... 0x5c: + case AC97_CODEC_CLASS_REV ... AC97_PCI_SID: + case 0x74 ... AC97_VENDOR_ID2: + return true; + default: + return false; + } +} + +static bool netease_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AC97_VENDOR_ID1: + case AC97_VENDOR_ID2: + return false; + default: + return netease_readable_reg(dev, reg); + } +} + +static const struct reg_default netease_reg_defaults[] = { + { 0x02, 0x8080 }, /* Speaker Output Volume */ + { 0x04, 0x8080 }, /* Headphone Output Volume */ + { 0x06, 0x8080 }, /* Out3/OUT4 Volume */ + { 0x08, 0xc880 }, /* Mono Volume */ + { 0x0a, 0xe808 }, /* LINEIN Volume */ + { 0x0c, 0xe808 }, /* DAC PGA Volume */ + { 0x0e, 0x0808 }, /* MIC PGA Volume */ + { 0x10, 0x00da }, /* MIC Routing Control */ + { 0x12, 0x8000 }, /* Record PGA Volume */ + { 0x14, 0xd600 }, /* Record Routing */ + { 0x16, 0xaaa0 }, /* PCBEEP Volume */ + { 0x18, 0xaaa0 }, /* VxDAC Volume */ + { 0x1a, 0xaaa0 }, /* AUXDAC Volume */ + { 0x1c, 0x0000 }, /* Output PGA Mux */ + { 0x1e, 0x0000 }, /* DAC 3D control */ + { 0x20, 0x0f0f }, /* DAC Tone Control*/ + { 0x22, 0x0040 }, /* MIC Input Select & Bias */ + { 0x24, 0x0000 }, /* Output Volume Mapping & Jack */ + { 0x26, 0x7f00 }, /* Powerdown Ctrl/Stat*/ + { 0x28, 0x0405 }, /* Extended Audio ID */ + { 0x2a, 0x0410 }, /* Extended Audio Start/Ctrl */ + { 0x2c, 0xbb80 }, /* Audio DACs Sample Rate */ + { 0x2e, 0xbb80 }, /* AUXDAC Sample Rate */ + { 0x32, 0xbb80 }, /* Audio ADCs Sample Rate */ + { 0x36, 0x4523 }, /* PCM codec control */ + { 0x3a, 0x2000 }, /* SPDIF control */ + { 0x3c, 0xfdff }, /* Powerdown 1 */ + { 0x3e, 0xffff }, /* Powerdown 2 */ + { 0x40, 0x0000 }, /* General Purpose */ + { 0x42, 0x0000 }, /* Fast Power-Up Control */ + { 0x44, 0x0080 }, /* MCLK/PLL Control */ + { 0x46, 0x0000 }, /* MCLK/PLL Control */ + { 0x4c, 0xfffe }, /* GPIO Pin Configuration */ + { 0x4e, 0xffff }, /* GPIO Pin Polarity / Type */ + { 0x50, 0x0000 }, /* GPIO Pin Sticky */ + { 0x52, 0x0000 }, /* GPIO Pin Wake-Up */ + /* GPIO Pin Status */ + { 0x56, 0xfffe }, /* GPIO Pin Sharing */ + { 0x58, 0x4000 }, /* GPIO PullUp/PullDown */ + { 0x5a, 0x0000 }, /* Additional Functions 1 */ + { 0x5c, 0x0000 }, /* Additional Functions 2 */ + { 0x60, 0xb032 }, /* ALC Control */ + { 0x62, 0x3e00 }, /* ALC / Noise Gate Control */ + { 0x64, 0x0000 }, /* AUXDAC input control */ + { 0x74, 0x0000 }, /* Digitiser Reg 1 */ + { 0x76, 0x0006 }, /* Digitiser Reg 2 */ + { 0x78, 0x0001 }, /* Digitiser Reg 3 */ + { 0x7a, 0x0000 }, /* Digitiser Read Back */ +}; + +static const struct regmap_config netease_regmap_config = { + .reg_bits = 16, + .reg_stride = 2, + .val_bits = 16, + .max_register = 0x7e, + .cache_type = REGCACHE_RBTREE, + + .reg_defaults = netease_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(netease_reg_defaults), + .volatile_reg = regmap_ac97_default_volatile, + .readable_reg = netease_readable_reg, + .writeable_reg = netease_writeable_reg, +}; + +/* PLL divisors */ +struct _pll_div { + u32 divsel:1; + u32 divctl:1; + u32 lf:1; + u32 n:4; + u32 k:24; +}; + +/* The size in bits of the PLL divide multiplied by 10 + * to allow rounding later */ +#define FIXED_PLL_SIZE ((1 << 22) * 10) + +static void pll_factors(struct snd_soc_codec *codec, + struct _pll_div *pll_div, unsigned int source) +{ + u64 Kpart; + unsigned int K, Ndiv, Nmod, target; + + /* The the PLL output is always 98.304MHz. */ + target = 98304000; + + /* If the input frequency is over 14.4MHz then scale it down. */ + if (source > 14400000) { + source >>= 1; + pll_div->divsel = 1; + + if (source > 14400000) { + source >>= 1; + pll_div->divctl = 1; + } else + pll_div->divctl = 0; + + } else { + pll_div->divsel = 0; + pll_div->divctl = 0; + } + + /* Low frequency sources require an additional divide in the + * loop. + */ + if (source < 8192000) { + pll_div->lf = 1; + target >>= 2; + } else + pll_div->lf = 0; + + Ndiv = target / source; + if ((Ndiv < 5) || (Ndiv > 12)) + dev_warn(codec->dev, + "netease PLL N value %u out of recommended range!\n", + Ndiv); + + pll_div->n = Ndiv; + Nmod = target % source; + Kpart = FIXED_PLL_SIZE * (long long)Nmod; + + do_div(Kpart, source); + + K = Kpart & 0xFFFFFFFF; + + /* Check if we need to round */ + if ((K % 10) >= 5) + K += 5; + + /* Move down to proper range now rounding is done */ + K /= 10; + + pll_div->k = K; +} + +/** + * Please note that changing the PLL input frequency may require + * resynchronisation with the AC97 controller. + */ +static int netease_set_pll(struct snd_soc_codec *codec, + int pll_id, unsigned int freq_in, unsigned int freq_out) +{ + struct netease_priv *netease = snd_soc_codec_get_drvdata(codec); + u16 reg, reg2; + struct _pll_div pll_div; + + /* turn PLL off ? */ + if (freq_in == 0) { + /* disable PLL power and select ext source */ + snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x0080, 0x0080); + snd_soc_update_bits(codec, AC97_EXTENDED_MID, 0x0200, 0x0200); + netease->pll_in = 0; + return 0; + } + + pll_factors(codec, &pll_div, freq_in); + + if (pll_div.k == 0) { + reg = (pll_div.n << 12) | (pll_div.lf << 11) | + (pll_div.divsel << 9) | (pll_div.divctl << 8); + snd_soc_write(codec, AC97_LINE1_LEVEL, reg); + } else { + /* write the fractional k to the reg 0x46 pages */ + reg2 = (pll_div.n << 12) | (pll_div.lf << 11) | (1 << 10) | + (pll_div.divsel << 9) | (pll_div.divctl << 8); + + /* K [21:20] */ + reg = reg2 | (0x5 << 4) | (pll_div.k >> 20); + snd_soc_write(codec, AC97_LINE1_LEVEL, reg); + + /* K [19:16] */ + reg = reg2 | (0x4 << 4) | ((pll_div.k >> 16) & 0xf); + snd_soc_write(codec, AC97_LINE1_LEVEL, reg); + + /* K [15:12] */ + reg = reg2 | (0x3 << 4) | ((pll_div.k >> 12) & 0xf); + snd_soc_write(codec, AC97_LINE1_LEVEL, reg); + + /* K [11:8] */ + reg = reg2 | (0x2 << 4) | ((pll_div.k >> 8) & 0xf); + snd_soc_write(codec, AC97_LINE1_LEVEL, reg); + + /* K [7:4] */ + reg = reg2 | (0x1 << 4) | ((pll_div.k >> 4) & 0xf); + snd_soc_write(codec, AC97_LINE1_LEVEL, reg); + + reg = reg2 | (0x0 << 4) | (pll_div.k & 0xf); /* K [3:0] */ + snd_soc_write(codec, AC97_LINE1_LEVEL, reg); + } + + /* turn PLL on and select as source */ + snd_soc_update_bits(codec, AC97_EXTENDED_MID, 0x0200, 0x0000); + snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x0080, 0x0000); + netease->pll_in = freq_in; + + /* wait 10ms AC97 link frames for the link to stabilise */ + schedule_timeout_interruptible(msecs_to_jiffies(10)); + return 0; +} + +static int netease_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, + int source, unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_codec *codec = codec_dai->codec; + return netease_set_pll(codec, pll_id, freq_in, freq_out); +} + +/* + * Tristate the PCM DAI lines, tristate can be disabled by calling + * netease_set_dai_fmt() + */ +static int netease_set_dai_tristate(struct snd_soc_dai *codec_dai, + int tristate) +{ + struct snd_soc_codec *codec = codec_dai->codec; + + if (tristate) + snd_soc_update_bits(codec, AC97_CENTER_LFE_MASTER, + 0x6000, 0x0000); + + return 0; +} + +/* + * Configure netease clock dividers. + * Voice DAC needs 256 FS + */ +static int netease_set_dai_clkdiv(struct snd_soc_dai *codec_dai, + int div_id, int div) +{ + struct snd_soc_codec *codec = codec_dai->codec; + + switch (div_id) { + case netease_PCMCLK_DIV: + snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x0f00, div); + break; + case netease_CLKA_MULT: + snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x0002, div); + break; + case netease_CLKB_MULT: + snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x0004, div); + break; + case netease_HIFI_DIV: + snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x7000, div); + break; + case netease_PCMBCLK_DIV: + snd_soc_update_bits(codec, AC97_CENTER_LFE_MASTER, 0x0e00, div); + break; + case netease_PCMCLK_PLL_DIV: + snd_soc_update_bits(codec, AC97_LINE1_LEVEL, + 0x007f, div | 0x60); + break; + case netease_HIFI_PLL_DIV: + snd_soc_update_bits(codec, AC97_LINE1_LEVEL, + 0x007f, div | 0x70); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int netease_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u16 gpio = snd_soc_read(codec, AC97_GPIO_CFG) & 0xffc5; + u16 reg = 0x8000; + + /* clock masters */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + reg |= 0x4000; + gpio |= 0x0010; + break; + case SND_SOC_DAIFMT_CBM_CFS: + reg |= 0x6000; + gpio |= 0x0018; + break; + case SND_SOC_DAIFMT_CBS_CFS: + reg |= 0x2000; + gpio |= 0x001a; + break; + case SND_SOC_DAIFMT_CBS_CFM: + gpio |= 0x0012; + break; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_IB_IF: + reg |= 0x00c0; + break; + case SND_SOC_DAIFMT_IB_NF: + reg |= 0x0080; + break; + case SND_SOC_DAIFMT_NB_IF: + reg |= 0x0040; + break; + } + + /* DAI format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + reg |= 0x0002; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + reg |= 0x0001; + break; + case SND_SOC_DAIFMT_DSP_A: + reg |= 0x0003; + break; + case SND_SOC_DAIFMT_DSP_B: + reg |= 0x0043; + break; + } + + snd_soc_write(codec, AC97_GPIO_CFG, gpio); + snd_soc_write(codec, AC97_CENTER_LFE_MASTER, reg); + return 0; +} + +static int netease_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + + /* enable PCM interface in master mode */ + switch (params_width(params)) { + case 16: + break; + case 20: + snd_soc_update_bits(codec, AC97_CENTER_LFE_MASTER, + 0x000c, 0x0004); + break; + case 24: + snd_soc_update_bits(codec, AC97_CENTER_LFE_MASTER, + 0x000c, 0x0008); + break; + case 32: + snd_soc_update_bits(codec, AC97_CENTER_LFE_MASTER, + 0x000c, 0x000c); + break; + } + return 0; +} + +static int ac97_hifi_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct snd_pcm_runtime *runtime = substream->runtime; + int reg; + + snd_soc_update_bits(codec, AC97_EXTENDED_STATUS, 0x0001, 0x0001); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + reg = AC97_PCM_FRONT_DAC_RATE; + else + reg = AC97_PCM_LR_ADC_RATE; + + return snd_soc_write(codec, reg, runtime->rate); +} + +static int ac97_aux_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct snd_pcm_runtime *runtime = substream->runtime; + + snd_soc_update_bits(codec, AC97_EXTENDED_STATUS, 0x0001, 0x0001); + snd_soc_update_bits(codec, AC97_PCI_SID, 0x8000, 0x8000); + + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) + return -ENODEV; + + return snd_soc_write(codec, AC97_PCM_SURR_DAC_RATE, runtime->rate); +} + +#define netease_RATES (SNDRV_PCM_RATE_8000 | \ + SNDRV_PCM_RATE_11025 | \ + SNDRV_PCM_RATE_22050 | \ + SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000) + +#define netease_PCM_RATES (SNDRV_PCM_RATE_8000 | \ + SNDRV_PCM_RATE_11025 | \ + SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_22050 | \ + SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000) + +#define netease_PCM_FORMATS \ + (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +static const struct snd_soc_dai_ops netease_dai_ops_hifi = { + .prepare = ac97_hifi_prepare, + .set_clkdiv = netease_set_dai_clkdiv, + .set_pll = netease_set_dai_pll, +}; + +static const struct snd_soc_dai_ops netease_dai_ops_aux = { + .prepare = ac97_aux_prepare, + .set_clkdiv = netease_set_dai_clkdiv, + .set_pll = netease_set_dai_pll, +}; + +static const struct snd_soc_dai_ops netease_dai_ops_voice = { + .hw_params = netease_pcm_hw_params, + .set_clkdiv = netease_set_dai_clkdiv, + .set_pll = netease_set_dai_pll, + .set_fmt = netease_set_dai_fmt, + .set_tristate = netease_set_dai_tristate, +}; + +static struct snd_soc_dai_driver netease_dai[] = { +{ + .name = "netease-hifi", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = netease_RATES, + .formats = SND_SOC_STD_AC97_FMTS,}, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = netease_RATES, + .formats = SND_SOC_STD_AC97_FMTS,}, + .ops = &netease_dai_ops_hifi, + }, + { + .name = "netease-aux", + .playback = { + .stream_name = "Aux Playback", + .channels_min = 1, + .channels_max = 1, + .rates = netease_RATES, + .formats = SND_SOC_STD_AC97_FMTS,}, + .ops = &netease_dai_ops_aux, + }, + { + .name = "netease-voice", + .playback = { + .stream_name = "Voice Playback", + .channels_min = 1, + .channels_max = 1, + .rates = netease_PCM_RATES, + .formats = netease_PCM_FORMATS,}, + .capture = { + .stream_name = "Voice Capture", + .channels_min = 1, + .channels_max = 2, + .rates = netease_PCM_RATES, + .formats = netease_PCM_FORMATS,}, + .ops = &netease_dai_ops_voice, + .symmetric_rates = 1, + }, +}; + +static int netease_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + printk("netease_set_bias_level:%d\n", level); + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + break; + case SND_SOC_BIAS_OFF: + break; + } + return 0; +} + +static int netease_soc_suspend(struct snd_soc_codec *codec) +{ + printk("netease_soc_suspend\n"); + + return 0; +} + +static int netease_soc_resume(struct snd_soc_codec *codec) +{ + struct netease_priv *netease = snd_soc_codec_get_drvdata(codec); + int ret; + + ret = snd_ac97_reset(netease->ac97, true, netease_VENDOR_ID, + netease_VENDOR_ID_MASK); + if (ret < 0) + return ret; + + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY); + + /* do we need to re-start the PLL ? */ + if (netease->pll_in) + netease_set_pll(codec, 0, netease->pll_in, 0); + + /* only synchronise the codec if warm reset failed */ + if (ret == 0) { + regcache_mark_dirty(codec->component.regmap); + snd_soc_cache_sync(codec); + } + + return ret; +} + +static int netease_soc_probe(struct snd_soc_codec *codec) +{ + struct netease_priv *netease = snd_soc_codec_get_drvdata(codec); + dprintk(""); + + return 0; +} + +static int netease_soc_remove(struct snd_soc_codec *codec) +{ + struct netease_priv *netease = snd_soc_codec_get_drvdata(codec); + dprintk("netease_soc_remove\n"); + + return 0; +} + +static const struct snd_soc_codec_driver soc_codec_dev_netease = { + .probe = netease_soc_probe, + .remove = netease_soc_remove, + .suspend = netease_soc_suspend, + .resume = netease_soc_resume, + .set_bias_level = netease_set_bias_level, +}; + +static int netease_probe(struct platform_device *pdev) +{ + struct netease_priv *priv; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + + platform_set_drvdata(pdev, priv); + + return snd_soc_register_codec(&pdev->dev, + &soc_codec_dev_netease, netease_dai, 1); +} + +static int netease_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + return 0; +} + +static struct platform_driver netease_codec_driver = { + .driver = { + .name = "netease-codec", + }, + + .probe = netease_probe, + .remove = netease_remove, +}; + +module_platform_driver(netease_codec_driver); + +MODULE_DESCRIPTION("Netease codec driver"); +MODULE_AUTHOR("Wang zijiao"); diff --git a/lichee/linux-4.9/sound/soc/codecs/netease-codec.h b/lichee/linux-4.9/sound/soc/codecs/netease-codec.h new file mode 100755 index 000000000..8328e4b0b --- /dev/null +++ b/lichee/linux-4.9/sound/soc/codecs/netease-codec.h @@ -0,0 +1,21 @@ +#ifndef _AD82584F_H +#define _AD82584F_H + +#define MVOL 0x03 +#define C1VOL 0x04 +#define C2VOL 0x05 +#define CFADDR 0x1d +#define A1CF1 0x1e +#define A1CF2 0x1f +#define A1CF3 0x20 +#define CFUD 0x2d + +#define AD82584F_REGISTER_COUNT 0x86 +#define AD82584F_RAM_TABLE_COUNT 0x80 + +struct ad82584f_platform_data { + int reset_pin; + int amp_en; +}; + +#endif diff --git a/lichee/linux-4.9/sound/soc/soc-core.c b/lichee/linux-4.9/sound/soc/soc-core.c index 885fab464..6f06860ea 100644 --- a/lichee/linux-4.9/sound/soc/soc-core.c +++ b/lichee/linux-4.9/sound/soc/soc-core.c @@ -21,7 +21,7 @@ * o Add more codecs and platforms to ensure good API coverage. * o Support TDM on PCM and I2S */ - +#define DEBUG #include #include #include @@ -985,6 +985,8 @@ struct snd_soc_dai *snd_soc_find_dai( if (dlc->name && strcmp(component->name, dlc->name)) continue; list_for_each_entry(dai, &component->dai_list, list) { + if(dai && dai->name) + printk("Wzj,snd_soc_find_dai cur:%s\n", dai->name); if (dlc->dai_name && strcmp(dai->name, dlc->dai_name)) continue; @@ -3407,6 +3409,8 @@ int snd_soc_register_codec(struct device *dev, dev_dbg(codec->dev, "ASoC: Registered codec '%s'\n", codec->component.name); + printk("ASoC: Registered codec '%s'\n", + codec->component.name); return 0; err_cleanup: diff --git a/lichee/linux-4.9/sound/soc/sunxi/sunxi-daudio.c b/lichee/linux-4.9/sound/soc/sunxi/sunxi-daudio.c index db2d420e4..195c9f98b 100644 --- a/lichee/linux-4.9/sound/soc/sunxi/sunxi-daudio.c +++ b/lichee/linux-4.9/sound/soc/sunxi/sunxi-daudio.c @@ -834,6 +834,7 @@ static int sunxi_daudio_set_clkdiv(struct snd_soc_dai *dai, int clk_id, else /* PCM mode */ div_ratio = clk_div / sunxi_daudio->pdata->pcm_lrck_period; + printk("0.0000...sunxi_daudio_set_clkdiv...div_ratio=%d,clk_div=%d\n",div_ratio,clk_div); switch (div_ratio) { case 1: diff --git a/package/netease/netease_control_center/Makefile b/package/netease/netease_control_center/Makefile index 5a62b3988..9d20aebe7 100644 --- a/package/netease/netease_control_center/Makefile +++ b/package/netease/netease_control_center/Makefile @@ -90,15 +90,15 @@ define Package/$(PKG_NAME)/install $(INSTALL_BIN) ./backuplocalfiles.sh $(1)/usr/sbin/ $(INSTALL_BIN) $(PKG_BUILD_DIR)/src/$(PKG_NAME)/modules/hardware/mcu_leds/mcu_ota $(TOPDIR)/prebuilt/r16/ $(INSTALL_BIN) $(PKG_BUILD_DIR)/src/$(PKG_NAME)/nembd/evdev/bin/netease_keytest $(1)/usr/sbin/ - $(INSTALL_BIN) ./netease.init $(1)/etc/init.d/netease_services - $(INSTALL_BIN) ./neteasewifi.init $(1)/etc/init.d/netease_wifi_service +# $(INSTALL_BIN) ./netease.init $(1)/etc/init.d/netease_services +# $(INSTALL_BIN) ./neteasewifi.init $(1)/etc/init.d/netease_wifi_service # $(INSTALL_BIN) ./neteasevoice.init $(1)/etc/init.d/netease_voice_service # $(INSTALL_BIN) ./neteaseplayer.init $(1)/etc/init.d/netease_player_service - $(INSTALL_BIN) ./neteasemanufacture_cc.init $(1)/etc/init.d/netease_cc_manufacture_service - $(INSTALL_BIN) ./neteasemanufacture_mcu.init $(1)/etc/init.d/netease_mcu_manufacture_service - $(INSTALL_BIN) ./neteasemanufacture_rf.init $(1)/etc/init.d/netease_rf_manufacture_service +# $(INSTALL_BIN) ./neteasemanufacture_cc.init $(1)/etc/init.d/netease_cc_manufacture_service +# $(INSTALL_BIN) ./neteasemanufacture_mcu.init $(1)/etc/init.d/netease_mcu_manufacture_service +# $(INSTALL_BIN) ./neteasemanufacture_rf.init $(1)/etc/init.d/netease_rf_manufacture_service # $(INSTALL_BIN) ./neteasecc.init $(1)/etc/init.d/netease_cc_service - $(INSTALL_BIN) ./neteasebt.init $(1)/etc/init.d/netease_bt_service +# $(INSTALL_BIN) ./neteasebt.init $(1)/etc/init.d/netease_bt_service $(INSTALL_DATA) mcu/* $(1)/usr/share/golang/mcu/ $(INSTALL_DATA) vol_adc.config $(1)/usr/share/golang/ # $(INSTALL_DATA) ./ca-certificates.crt $(1)/etc/ssl/certs/ diff --git a/package/netease/netease_voice/src/base.c b/package/netease/netease_voice/src/base.c index f8fcc048a..ef3d97b16 100644 --- a/package/netease/netease_voice/src/base.c +++ b/package/netease/netease_voice/src/base.c @@ -57,10 +57,18 @@ static int voice_major; static int voice_status; static double voice_confidence; static VoiceSessionStatus vss = VoiceSession_Init; +static WorkMode wm = WorkMode_R311_PV1; /* ------------------------------------------------------------------------ ** Function Definitions ** ------------------------------------------------------------------------ */ +void setWorkMode(WorkMode mode) { + wm = mode; +} + +WorkMode getWorkMode() { + return wm; +} void setVss(VoiceSessionStatus st) { vss = st; } VoiceSessionStatus getVss() { return vss; } diff --git a/package/netease/netease_voice/src/comm/AudioQueue.c b/package/netease/netease_voice/src/comm/AudioQueue.c index 37ff8a5e1..992dd3644 100644 --- a/package/netease/netease_voice/src/comm/AudioQueue.c +++ b/package/netease/netease_voice/src/comm/AudioQueue.c @@ -117,7 +117,7 @@ int queue_write(audio_queue_t *queue, char data[], int dataLen) { pthread_mutex_lock(&(queue->mutex)); if (queue_left_asyn(queue) < dataLen) { pthread_mutex_unlock(&(queue->mutex)); - n_error("queue_left_asyn queue < dataLen \n"); + n_error("queue_left_asyn queue(%d) < dataLen(%d) \n",queue_left_asyn(queue), dataLen); LOG_EX(LOG_Error, "ALSA Queue: queue_left_asyn queue < dataLen \n"); //return false; queue_reset(queue); @@ -153,13 +153,13 @@ int queue_read(audio_queue_t *queue, char **data) { if (queue == NULL || data == NULL) { return 0; } - // printf("queue_read pthread_cond_wait begin\n"); + //printf("queue_read pthread_cond_wait begin\n"); native_event_wait(queue->sync_event, 0x7fffffff); pthread_mutex_lock(&(queue->mutex)); queueBase = (char *)(queue + 1); begin = &((queueBase)[queue->front]); queueLen = queue_len_asyn(queue); - // printf("queue_read queueLen %d\n", queueLen); + //printf("queue_read queueLen %d\n", queueLen); temp_buff = (char *)malloc(queueLen); if (NULL == temp_buff) { diff --git a/package/netease/netease_voice/src/include/base.h b/package/netease/netease_voice/src/include/base.h index 4b656a925..ee92ef654 100644 --- a/package/netease/netease_voice/src/include/base.h +++ b/package/netease/netease_voice/src/include/base.h @@ -26,6 +26,12 @@ extern "C" { #include +/* ------------------------------------------------------------------------ +** Defines +** ------------------------------------------------------------------------ */ + +#define BOARD_DMIC + /* ------------------------------------------------------------------------ ** Types ** ------------------------------------------------------------------------ */ @@ -51,6 +57,10 @@ typedef enum { VoiceSession_Created = 2 } VoiceSessionStatus; +typedef enum { + WorkMode_R311_PV1 = 0 +} WorkMode; + void setVss(VoiceSessionStatus st); VoiceSessionStatus getVss(); @@ -86,6 +96,9 @@ void setVoiceConfidence(double voiceConfidence); double getVoiceConfidence(); void resetAdc(); +void setWorkMode(WorkMode mode); +WorkMode getWorkMode(); + #ifdef __cplusplus } /* extern "C" */ #endif /* C++ */ diff --git a/package/netease/netease_voice/src/include/error.h b/package/netease/netease_voice/src/include/error.h index 8c27e0ebe..bf28286b0 100644 --- a/package/netease/netease_voice/src/include/error.h +++ b/package/netease/netease_voice/src/include/error.h @@ -46,7 +46,7 @@ enum { #endif #ifdef NETEASE_DEBUG -#define n_debug(fmt, arg...) printf(fmt, ##arg) +#define n_debug(fmt, arg...) do{printf(fmt, ##arg);}while(0) #else #define n_debug(fmt, arg...) \ do { \ diff --git a/package/netease/netease_voice/src/include/formats.h b/package/netease/netease_voice/src/include/formats.h new file mode 100644 index 000000000..d309c6931 --- /dev/null +++ b/package/netease/netease_voice/src/include/formats.h @@ -0,0 +1,135 @@ +#ifndef FORMATS_H +#define FORMATS_H 1 + +#include +#include +#include + +/* Definitions for .VOC files */ + +#define VOC_MAGIC_STRING "Creative Voice File\x1A" +#define VOC_ACTUAL_VERSION 0x010A +#define VOC_SAMPLESIZE 8 + +#define VOC_MODE_MONO 0 +#define VOC_MODE_STEREO 1 + +#define VOC_DATALEN(bp) ((u_long)(bp->datalen) | \ + ((u_long)(bp->datalen_m) << 8) | \ + ((u_long)(bp->datalen_h) << 16) ) + +typedef struct voc_header { + u_char magic[20]; /* must be MAGIC_STRING */ + u_short headerlen; /* Headerlength, should be 0x1A */ + u_short version; /* VOC-file version */ + u_short coded_ver; /* 0x1233-version */ +} VocHeader; + +typedef struct voc_blocktype { + u_char type; + u_char datalen; /* low-byte */ + u_char datalen_m; /* medium-byte */ + u_char datalen_h; /* high-byte */ +} VocBlockType; + +typedef struct voc_voice_data { + u_char tc; + u_char pack; +} VocVoiceData; + +typedef struct voc_ext_block { + u_short tc; + u_char pack; + u_char mode; +} VocExtBlock; + +/* Definitions for Microsoft WAVE format */ + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define COMPOSE_ID(a,b,c,d) ((a) | ((b)<<8) | ((c)<<16) | ((d)<<24)) +#define LE_SHORT(v) (v) +#define LE_INT(v) (v) +#define BE_SHORT(v) bswap_16(v) +#define BE_INT(v) bswap_32(v) +#elif __BYTE_ORDER == __BIG_ENDIAN +#define COMPOSE_ID(a,b,c,d) ((d) | ((c)<<8) | ((b)<<16) | ((a)<<24)) +#define LE_SHORT(v) bswap_16(v) +#define LE_INT(v) bswap_32(v) +#define BE_SHORT(v) (v) +#define BE_INT(v) (v) +#else +#error "Wrong endian" +#endif + +/* Note: the following macros evaluate the parameter v twice */ +#define TO_CPU_SHORT(v, be) \ + ((be) ? BE_SHORT(v) : LE_SHORT(v)) +#define TO_CPU_INT(v, be) \ + ((be) ? BE_INT(v) : LE_INT(v)) + +#define WAV_RIFF COMPOSE_ID('R','I','F','F') +#define WAV_RIFX COMPOSE_ID('R','I','F','X') +#define WAV_WAVE COMPOSE_ID('W','A','V','E') +#define WAV_FMT COMPOSE_ID('f','m','t',' ') +#define WAV_DATA COMPOSE_ID('d','a','t','a') + +/* WAVE fmt block constants from Microsoft mmreg.h header */ +#define WAV_FMT_PCM 0x0001 +#define WAV_FMT_IEEE_FLOAT 0x0003 +#define WAV_FMT_DOLBY_AC3_SPDIF 0x0092 +#define WAV_FMT_EXTENSIBLE 0xfffe + +/* Used with WAV_FMT_EXTENSIBLE format */ +#define WAV_GUID_TAG "\x00\x00\x00\x00\x10\x00\x80\x00\x00\xAA\x00\x38\x9B\x71" + +/* it's in chunks like .voc and AMIGA iff, but my source say there + are in only in this combination, so I combined them in one header; + it works on all WAVE-file I have + */ +typedef struct { + u_int magic; /* 'RIFF' */ + u_int length; /* filelen */ + u_int type; /* 'WAVE' */ +} WaveHeader; + +typedef struct { + u_short format; /* see WAV_FMT_* */ + u_short channels; + u_int sample_fq; /* frequence of sample */ + u_int byte_p_sec; + u_short byte_p_spl; /* samplesize; 1 or 2 bytes */ + u_short bit_p_spl; /* 8, 12 or 16 bit */ +} WaveFmtBody; + +typedef struct { + WaveFmtBody format; + u_short ext_size; + u_short bit_p_spl; + u_int channel_mask; + u_short guid_format; /* WAV_FMT_* */ + u_char guid_tag[14]; /* WAV_GUID_TAG */ +} WaveFmtExtensibleBody; + +typedef struct { + u_int type; /* 'data' */ + u_int length; /* samplecount */ +} WaveChunkHeader; + +/* Definitions for Sparc .au header */ + +#define AU_MAGIC COMPOSE_ID('.','s','n','d') + +#define AU_FMT_ULAW 1 +#define AU_FMT_LIN8 2 +#define AU_FMT_LIN16 3 + +typedef struct au_header { + u_int magic; /* '.snd' */ + u_int hdr_size; /* size of header (min 24) */ + u_int data_size; /* size of data */ + u_int encoding; /* see to AU_FMT_XXXX */ + u_int sample_rate; /* sample rate */ + u_int channels; /* number of channels (voices) */ +} AuHeader; + +#endif /* FORMATS */ diff --git a/package/netease/netease_voice/src/include/record.h b/package/netease/netease_voice/src/include/record.h index 341bbb332..79642afa3 100644 --- a/package/netease/netease_voice/src/include/record.h +++ b/package/netease/netease_voice/src/include/record.h @@ -29,6 +29,7 @@ extern "C" { ** Types ** ------------------------------------------------------------------------ */ typedef void* REACORD_HANDLE; +typedef void (*recordrecord_audio_fn)(const void *audio, unsigned int audio_len, int err_code); typedef void (*record_audio_fn)(const void *audio, unsigned int audio_len, int err_code); @@ -39,13 +40,29 @@ typedef struct _RecordData{ audio_queue_t *queue; char *queue_buff; char *buffer; - int buff_size; snd_pcm_uframes_t frames; record_audio_fn cb; void *user_data; pthread_t tid_pcm_read; pthread_t tid_queue_read; int runing; + int backupfilefd; + int finalfilefd; + struct pcm_config *pcm_cfg; + struct pcm_config *adc; + size_t significant_bits_per_sample; + size_t bits_per_sample; + size_t bits_per_frame; + snd_pcm_format_t format; + size_t chunk_bytes; + size_t chunk_size; + size_t buff_size; + size_t buffer_time; + size_t period_time; + + size_t buffer_frames; + size_t period_frames; + }RecordData; struct pcm_config { @@ -56,11 +73,17 @@ struct pcm_config { unsigned int period_count; record_audio_fn audio_cb; RecordData *recordata; + RecordData *adc; + char *format; + char *backupfilename; + char *finalfilename; + int backupfile_waveformat; }; int Neteasae_record_start(struct pcm_config *pcm_cfg); void Netease_record_stop(struct pcm_config *pcm_cfg); - +void device_list(void); +void pcm_list(void); #ifdef __cplusplus } /* extern "C" */ diff --git a/package/netease/netease_voice/src/main.c b/package/netease/netease_voice/src/main.c index 116a23b39..e4ee4f324 100644 --- a/package/netease/netease_voice/src/main.c +++ b/package/netease/netease_voice/src/main.c @@ -29,10 +29,12 @@ when who why #include "srcwsapi.h" #include "uvdbus/smart_sound.h" #include "yunxin.h" +#include #include #include #include #include +#include #include #include @@ -46,20 +48,10 @@ when who why /* ------------------------------------------------------------------------ ** Macros ** ------------------------------------------------------------------------ */ -#define BACKUP_ORIG_AUDIO -//#define BACKUP_FINAL_AUDIO /* ------------------------------------------------------------------------ ** Defines ** ------------------------------------------------------------------------ */ -#ifdef BACKUP_ORIG_AUDIO -#define ORIG_AUDIO_FILENAME "/mnt/UDISK/origaudio.pcm" -static int fd_audio_orig; -#endif - -#ifdef BACKUP_FINAL_AUDIO -static int fd_audio_final; -#endif //#define ENABLE_OEM_DBUS @@ -73,7 +65,8 @@ static int fd_audio_final; ** Global Variable Definitions ** ------------------------------------------------------------------------ */ // unsigned int g_bIsUsedTestServer; - +static int in_aborting = 0; +static int justrecord = 0; /* ------------------------------------------------------------------------ ** Functions ** ------------------------------------------------------------------------ */ @@ -87,10 +80,26 @@ const char *g_fmaeCfgPath = "/usr/share/fmae_res/config.ini"; CAEDATA *caeconfig = NULL; #endif -struct pcm_config recordconfig; +struct pcm_config recordconfig = {0}; struct audio_bypass *audiobypassconfig = NULL; static char been_wakedup = 0; +static long parse_long(const char *str, int *err) +{ + long val; + char *endptr; + + errno = 0; + val = strtol(str, &endptr, 0); + + if (errno != 0 || *endptr != '\0') + *err = -1; + else + *err = 0; + + return val; +} + #if USED_NETEASE_DC static struct kfifo g_AudioCacheBuf; // static DECLARE_KFIFO(g_AudioCacheBuf, unsigned char, WAKE_CACHE_DATA_SIZE); @@ -221,11 +230,13 @@ int Duilite_doa_cb(void *userdata, int type, char *msg, int len) { } int Duilite_beforming_cb(void *userdata, int type, char *msg, int len) { + int fd; + size_t hasWritten; + if (type == DUILITE_MSG_TYPE_BINARY) { -// n_debug("Get beforming binary data:%d\n", len); -#ifdef BACKUP_FINAL_AUDIO - write(fd_audio_final, msg, len); -#endif + if (recordconfig.recordata && recordconfig.recordata->finalfilefd > 0) { + write(recordconfig.recordata->finalfilefd, msg, len); + } if (!!audiobypassconfig) { Netease_audiobypass_write(audiobypassconfig, msg, len); } @@ -272,6 +283,7 @@ static void CAEAudioCb(const void *audioData, unsigned int audioLen, int param1, static void record_audio_cb(const void *audio, unsigned int audio_len, int err_code) { unsigned long long cachetime; + #ifdef DEBUG_WASTE_TIME Netease_caltime(&cachetime, 1); #endif @@ -280,11 +292,6 @@ static void record_audio_cb(const void *audio, unsigned int audio_len, int ret = 0; #endif -#ifdef BACKUP_ORIG_AUDIO - // n_debug("Begin to write audio, size:%d\n", audio_len); - write(fd_audio_orig, audio, audio_len); - return; -#endif #if USED_NETEASE_FMAE if (1) { #elif USED_NETEASE_DUILITE @@ -499,23 +506,153 @@ void LogFunc(int logLevel, const char *log, void *userData) { IHW_LogRawString(logLevel, (char *)log); } +static void signal_handler(int sig) { + if (in_aborting) + return; + + in_aborting = 1; + n_debug("Aborted by signal:%d, %s\n", sig, strsignal(sig)); + Netease_record_stop(&recordconfig); + + signal(sig, SIG_DFL); +} + +static void usage() { + printf( + "Usage:\n" + "-h, --help help\n" + "-l, --list-devices list all soundcards and digital audio " + "devices\n" + "-L, --list-pcms list device names\n" + "-w, --work-mode=# work mode: 0: r311_pv1\n" + "-f, --list-format list all format in alsa\n" + "-r, --just-record Only record voice to file\n" + "-t, --type=# sample format (case insensitive): raw,wav\n" + "--rawfile=# record raw data to filepath\n" + "--finalfile=# record duilite output to filepath\n"); +} +enum { + OPT_RAW_FILE = 1, + OPT_FINAL_FILE +}; + void main(int argc, char **argv) { - int ret; - unsigned int card = 1; - unsigned int device = 0; - unsigned int channels = 2; - unsigned int rate = 96000; + int ret, c, option_index, err,tmp; + char *rawfile = "/dev/null"; + char *finalfile = "/dev/null"; + static const char short_options[] = "hlt:w:Lfr"; + static const struct option long_options[] = { + {"help", 0, 0, 'h'}, + {"list-devices", 0, 0, 'l'}, + {"list-pcms", 0, 0, 'L'}, + {"list-format", 0, 0, 'f'}, + {"just-record", 0, 0, 'r'}, + {"rawfile", 1, 0, OPT_RAW_FILE}, + {"finalfile", 1, 0, OPT_FINAL_FILE}, + {"type", 1, 0, 't'}, + {"work-mode", 1, 0, 'w'}, + {0, 0, 0, 0}}; - // unsigned int frames; - unsigned int period_size = 1535; - unsigned int period_count = 4; + recordconfig.backupfile_waveformat = 1; + recordconfig.audio_cb = record_audio_cb; + setWorkMode(WorkMode_R311_PV1); - char sound_device_name[30]; + if (argc == 1) { + printf("Not enough arg!\n"); + usage(); + return; + } + + while ((c = getopt_long(argc, argv, short_options, long_options, + &option_index)) != -1) { + switch (c) { + case 'h': + usage(); + return; + + case 'l': + device_list(); + return; + + case 'L': + pcm_list(); + return; + + case 'r': + justrecord = 1; + break; + + case 't': + if (strcasecmp(optarg, "wav") == 0) { + recordconfig.backupfile_waveformat = 1; + } else if (strcasecmp(optarg, "raw") == 0) { + recordconfig.backupfile_waveformat = 0; + } + printf("Wave format:%d\n", recordconfig.backupfile_waveformat); + break; + + case 'w': + tmp = parse_long(optarg, &err); + if (err < 0) { + printf("invalid work mode!%s\n", optarg); + return; + } else { + setWorkMode((WorkMode)tmp); + } + break; + + case OPT_RAW_FILE: + rawfile = optarg; + printf("Raw file:%s\n", optarg); + break; + + case OPT_FINAL_FILE: + finalfile = optarg; + printf("Final file:%s\n", optarg); + break; + + default: + printf("Unknow cmd!(%d)\n", c); + usage(); + exit(0); + } + } + printf("parse arg end!\n"); + + signal(SIGINT, signal_handler); + signal(SIGTERM, signal_handler); + signal(SIGABRT, signal_handler); + // Base module init + BaseInit(); + + printf("Work mode:%d\n", getWorkMode()); + + // resetAdc(); + + if (justrecord == 1) { + if (rawfile == NULL && finalfile == NULL) { + printf("Must input rawfile or finalfile!\n"); + return; + } + if (rawfile != NULL) { + recordconfig.backupfilename = rawfile; + printf("Will record raw to file:%s\n", rawfile); + } + if (finalfile != NULL) { + recordconfig.finalfilename = finalfile; + printf("Will record final data to file:%s\n", finalfile); + } + + recordconfig.audio_cb = NULL; + Neteasae_record_start(&recordconfig); + while (1) + usleep(1000000); + } #ifdef ENABLE_OEM_DBUS ret = Netease_dbus_oem_init(DBusMessageCb); #else - // ret = Netease_Dbus_Init(DBusMessageCb); + ret = Netease_Dbus_Init(DBusMessageCb); #endif if (NETEASE_SUCCESS == ret) { n_debug("Dbus init success\n"); @@ -546,42 +683,6 @@ void main(int argc, char **argv) { } #endif - snprintf(sound_device_name, sizeof(sound_device_name), "plughw:%u,%u", card, - device); - - recordconfig.device_name = sound_device_name; - recordconfig.channels = channels; - recordconfig.rate = rate; - recordconfig.period_size = period_size; - recordconfig.period_count = period_count; - recordconfig.audio_cb = record_audio_cb; - // todo write something - printf("++++++++++++++++++++++++++\n"); - printf("device name is %s\n", recordconfig.device_name); - printf("Channle is %d\n", recordconfig.channels); - printf("rate name is %d\n", recordconfig.rate); - printf("period_size name is %d\n", recordconfig.period_size); - printf("++++++++++++++++++++++++++\n"); - n_debug("Begin to init netease_voice modules!\n"); - - extern void listAlsaDev(); - resetAdc(); - listAlsaDev(); -#ifdef BACKUP_ORIG_AUDIO - fd_audio_orig = open(argv[1], O_CREAT | O_APPEND | O_RDWR); - n_debug("Open file: %s, fd:%d\n", argv[1], fd_audio_orig); - Neteasae_record_start(&recordconfig); - while (1) - usleep(1000000); -#endif - -#ifdef BACKUP_FINAL_AUDIO - fd_audio_final = open("/tmp/finalaudio", O_CREAT | O_APPEND | O_RDWR); -#endif - - // Base module init - BaseInit(); - #if USED_NETEASE_FMAE ret = Netease_audiobypass_init(&audiobypassconfig, NULL, AUDIO_BYPASS_CHANNEL_MSC_IAT); @@ -666,10 +767,27 @@ void main(int argc, char **argv) { #ifdef ENABLE_OEM_DBUS pthread_create(&tid, NULL, Netease_dbus_oem_start, NULL); #else - // pthread_create(&tid, NULL, Netease_Dbus_Start_Sync, NULL); + pthread_create(&tid, NULL, Netease_Dbus_Start_Sync, NULL); #endif // Netease_yunxin_test(); +#if 0 + while (1) { + if (1 == Netease_nduilite_init(Duilite_wakeup_cb, Duilite_doa_cb, + Duilite_beforming_cb, Duilite_vad_cb)) { + n_toast("Nduilite init success, exit!\n"); + break; + } else { + n_toast("Nduilite init fail, sleep and redo!\n"); + usleep(1000000); + } + } + + while (1) + usleep(1000000); + +#endif + while (1) { if (strlen(getUuid()) == 0 || strlen(getYxToken()) == 0) { // n_toast("Get args frome cc\n"); diff --git a/package/netease/netease_voice/src/nduilite.c b/package/netease/netease_voice/src/nduilite.c index 7968b5d0f..de08fe703 100644 --- a/package/netease/netease_voice/src/nduilite.c +++ b/package/netease/netease_voice/src/nduilite.c @@ -352,6 +352,29 @@ int Netease_nduilite_init(duilite_callback wakeup_callback, return 1; } +void Dmic_data_convert(const void *audioData, unsigned int audio_len) { + short *p = audioData; + short tmp; + int bitPerChan = 16; + int chanNum = 8; + //16bit 8 channel, 0,1 is null + if (audio_len%(bitPerChan*chanNum/8) != 0) { + n_error("Input data len(%d) error, not full frame!", audio_len); + } + + while(p < (audioData+audio_len)) { + p[0] = p[2]; + p[1] = p[3]; + p[2] = p[4]; + p[3] = p[5]; + p[4] = p[6]; + p[5] = p[7]; + p[6] = 0x0000; + p[7] = 0x0000; + p += 8; + } +} + void Netease_nduilite_writeaudio(const void *audioData, unsigned int audio_len) { int len = 0; @@ -372,6 +395,10 @@ void Netease_nduilite_writeaudio(const void *audioData, return; } + //n_debug("Write nduilite data, len:%d\n", audio_len); + ret = duilite_fespa_feed(fespa, audioData, audio_len); + +#if 0 newbuf = (char *)malloc(audio_len + 60); if (newbuf == NULL) { @@ -388,4 +415,5 @@ void Netease_nduilite_writeaudio(const void *audioData, write(fd_audio_orig, newbuf, len); #endif free(newbuf); +#endif } diff --git a/package/netease/netease_voice/src/record.c b/package/netease/netease_voice/src/record.c index 7e776c465..6d8ae5db2 100644 --- a/package/netease/netease_voice/src/record.c +++ b/package/netease/netease_voice/src/record.c @@ -28,6 +28,8 @@ when who why #include #include +#include "base.h" +#include "formats.h" #include "record.h" #include "error.h" @@ -35,6 +37,7 @@ when who why #include #include #include +#include /* ------------------------------------------------------------------------ ** Macros @@ -42,7 +45,7 @@ when who why #define ALSA_PCM_NEW_HW_PARAMS_API #define AUDIO_QUEUE_BUFF_LEN (1024 * 10) -#define QUEUE_BUFF_MULTIPLE 1000 +#define QUEUE_BUFF_MULTIPLE 100 /* ------------------------------------------------------------------------ ** Types @@ -64,14 +67,40 @@ static Proc_CAEGetChannel api_cae_get_channel; static Proc_CAESetShowLog api_cae_set_show_log; static Proc_CAEDestroy api_cae_destroy; +static off64_t fdcount = 0; +static snd_output_t *sndlog; + /* ------------------------------------------------------------------------ ** Function Definitions ** ------------------------------------------------------------------------ */ static void *QueueReadThread(void *param); static void *RecordThread(void *param); +static ssize_t xwrite(int fd, const void *buf, size_t count); static void record_audio_cb(const void *audio, unsigned int audio_len, int err_code); void _record_stop(void *record_hd); +static void end_wave(int fd); +static void begin_wave(RecordData *record, int fd, size_t cnt); +void _record_stop(void *record_hd); +static int init_params(struct pcm_config *pcm_cfg, int init_queue); + +/* + * make sure we write all bytes or return an error + */ +static ssize_t xwrite(int fd, const void *buf, size_t count) { + ssize_t written; + size_t offset = 0; + + while (offset < count) { + written = write(fd, buf + offset, count - offset); + if (written <= 0) + return written; + + offset += written; + }; + + return offset; +} static void *QueueReadThread(void *param) { RecordData *record = (RecordData *)param; @@ -83,7 +112,7 @@ static void *QueueReadThread(void *param) { if (0 == readLen) { // printf("queue_read readLen = 0\n"); // usleep(16000); - // continue; + continue; } if (record->buff_size != readLen) { // printf("\nqueue_read readLen %d\n", readLen); @@ -96,57 +125,380 @@ static void *QueueReadThread(void *param) { return NULL; } -static void *RecordThread(void *param) { - RecordData *record = (RecordData *)param; - int ret = 0; - // cpu_set_t mask; - n_toast("RecordThread record:%p, running:%d\n", record, record->runing); - // CPU_ZERO(&mask); - // CPU_SET(0,&mask); - // ret = sched_setaffinity(0, sizeof(mask), &mask); - // printf("sched_setaffinity return = %d\n", ret); - while (!!record->runing) { - ret = snd_pcm_readi(record->handle, record->buffer, record->frames); - // printf("snd_pcm_readi return = %d\n", ret); - if (ret == -EPIPE) { +static void do_test_position(RecordData *record) { + static long counter = 0; + static time_t tmr = -1; + time_t now; + static float availsum, delaysum, samples; + static snd_pcm_sframes_t maxavail, maxdelay; + static snd_pcm_sframes_t minavail, mindelay; + static snd_pcm_sframes_t badavail = 0, baddelay = 0; + snd_pcm_sframes_t outofrange; + snd_pcm_sframes_t avail, delay; + int err; + int test_coef = 8; + + err = snd_pcm_avail_delay(record->handle, &avail, &delay); + if (err < 0) + return; + outofrange = (test_coef * (snd_pcm_sframes_t)record->frames) / 2; + if (avail > outofrange || avail < -outofrange || delay > outofrange || + delay < -outofrange) { + badavail = avail; + baddelay = delay; + availsum = delaysum = samples = 0; + maxavail = maxdelay = 0; + minavail = mindelay = record->frames * 16; + fprintf(stderr, + "Suspicious buffer position (%li total): " + "avail = %li, delay = %li, buffer = %li\n", + ++counter, (long)avail, (long)delay, (long)record->frames); + } else { + time(&now); + if (tmr == (time_t)-1) { + tmr = now; + availsum = delaysum = samples = 0; + maxavail = maxdelay = 0; + minavail = mindelay = record->frames * 16; + } + if (avail > maxavail) + maxavail = avail; + if (delay > maxdelay) + maxdelay = delay; + if (avail < minavail) + minavail = avail; + if (delay < mindelay) + mindelay = delay; + availsum += avail; + delaysum += delay; + samples++; + if (avail != 0 && now != tmr) { + fprintf(stderr, + "BUFPOS: avg%li/%li " + "min%li/%li max%li/%li (%li) (%li:%li/%li)\n", + (long)(availsum / samples), (long)(delaysum / samples), + (long)minavail, (long)mindelay, (long)maxavail, + (long)maxdelay, (long)record->frames, counter, badavail, + baddelay); + tmr = now; + } + } +} + +static ssize_t pcm_read(RecordData *record) { + ssize_t r; + size_t result = 0; + size_t rcount = record->chunk_size; + size_t count = rcount; + char *data = record->buffer; + + while (count > 0 && !!record->runing) { + r = snd_pcm_readi(record->handle, data, count); + if (r == -EAGAIN || (r >= 0 && (size_t)r < count)) { + n_debug("read data == EAGAIN\n"); + snd_pcm_wait(record->handle, 50); + } else if (r == -EPIPE) { /* EPIPE means overrun */ fprintf(stderr, "overrun occurred/n"); LOG_EX(LOG_Error, "ALSA Read Data = -EPIPE\n"); snd_pcm_prepare(record->handle); continue; - } else if (ret < 0) { - fprintf(stderr, "error from read: %s/n", snd_strerror(ret)); - LOG_EX(LOG_Error, "ALSA Read Data Error: %s(%d)\n", snd_strerror(ret), ret); - record->cb(NULL, 0, ret); - return NULL; - } else if (ret != (int)record->frames) { - fprintf(stderr, "short read, read %d frames/n", ret); - LOG_EX(LOG_Error, "ALSA Short Read, Read %d Frames: %d\n", ret); + } else if (r < 0) { + fprintf(stderr, "error from read: %s/n", snd_strerror(r)); + LOG_EX(LOG_Error, "ALSA Read Data Error: %s(%d)\n", snd_strerror(r), + r); + return 0; } - queue_write(record->queue, record->buffer, record->buff_size); + if (r > 0) { + result += r; + count -= r; + data += r * record->bits_per_frame / 8; + } + } + + return result; +} +static void end_wave(int fd) { /* only close output */ + WaveChunkHeader cd; + off64_t length_seek; + off64_t filelen; + u_int rifflen; + + length_seek = + sizeof(WaveHeader) + sizeof(WaveChunkHeader) + sizeof(WaveFmtBody); + cd.type = WAV_DATA; + cd.length = fdcount > 0x7fffffff ? LE_INT(0x7fffffff) : LE_INT(fdcount); + filelen = fdcount + 2 * sizeof(WaveChunkHeader) + sizeof(WaveFmtBody) + 4; + rifflen = filelen > 0x7fffffff ? LE_INT(0x7fffffff) : LE_INT(filelen); + if (lseek64(fd, 4, SEEK_SET) == 4) + xwrite(fd, &rifflen, 4); + if (lseek64(fd, length_seek, SEEK_SET) == length_seek) + xwrite(fd, &cd, sizeof(WaveChunkHeader)); + if (fd != 1) + close(fd); +} + +/* write a WAVE-header */ +static void begin_wave(RecordData *record, int fd, size_t cnt) { + WaveHeader h; + WaveFmtBody f; + WaveChunkHeader cf, cd; + int bits; + u_int tmp; + u_short tmp2; + + /* WAVE cannot handle greater than 32bit (signed?) int */ + if (cnt == (size_t)-2) + cnt = 0x7fffff00; + + bits = 8; + switch ((unsigned long)record->format) { + case SND_PCM_FORMAT_U8: + bits = 8; + break; + case SND_PCM_FORMAT_S16_LE: + bits = 16; + break; + case SND_PCM_FORMAT_S32_LE: + case SND_PCM_FORMAT_FLOAT_LE: + bits = 32; + break; + case SND_PCM_FORMAT_S24_LE: + case SND_PCM_FORMAT_S24_3LE: + bits = 24; + break; + default: + n_error("Wave doesn't support %s format..."), + snd_pcm_format_name(record->format); + return; + } + h.magic = WAV_RIFF; + tmp = cnt + sizeof(WaveHeader) + sizeof(WaveChunkHeader) + + sizeof(WaveFmtBody) + sizeof(WaveChunkHeader) - 8; + h.length = LE_INT(tmp); + h.type = WAV_WAVE; + + cf.type = WAV_FMT; + cf.length = LE_INT(16); + + if (record->format == SND_PCM_FORMAT_FLOAT_LE) + f.format = LE_SHORT(WAV_FMT_IEEE_FLOAT); + else + f.format = LE_SHORT(WAV_FMT_PCM); + f.channels = LE_SHORT(record->pcm_cfg->channels); + f.sample_fq = LE_INT(record->pcm_cfg->rate); +#if 0 + tmp2 = (samplesize == 8) ? 1 : 2; + f.byte_p_spl = LE_SHORT(tmp2); + tmp = dsp_speed * hwparams.channels * (u_int) tmp2; +#else + tmp2 = record->pcm_cfg->channels * + snd_pcm_format_physical_width(record->format) / 8; + f.byte_p_spl = LE_SHORT(tmp2); + tmp = (u_int)tmp2 * record->pcm_cfg->rate; +#endif + f.byte_p_sec = LE_INT(tmp); + f.bit_p_spl = LE_SHORT(bits); + + cd.type = WAV_DATA; + cd.length = LE_INT(cnt); + + if (xwrite(fd, &h, sizeof(WaveHeader)) != sizeof(WaveHeader) || + xwrite(fd, &cf, sizeof(WaveChunkHeader)) != sizeof(WaveChunkHeader) || + xwrite(fd, &f, sizeof(WaveFmtBody)) != sizeof(WaveFmtBody) || + xwrite(fd, &cd, sizeof(WaveChunkHeader)) != sizeof(WaveChunkHeader)) { + n_error("write error"); + return; + } +} + +static void *RecordThread(void *param) { + RecordData *record = (RecordData *)param; + int ret = 0; + int i = 0; + long long max_file_size = 2147483648LL; + + // cpu_set_t mask; + n_toast("RecordThread record:%p, running:%d\n", record, record->runing); + n_toast("record->pcm_cfg:%p\n", record->pcm_cfg); + n_toast("record->pcm_cfg->adc:%p\n", record->pcm_cfg->adc); + // CPU_ZERO(&mask); + // CPU_SET(0,&mask); + // ret = sched_setaffinity(0, sizeof(mask), &mask); + // printf("sched_setaffinity return = %d\n", ret); + if (record->backupfilefd > 0 && + record->pcm_cfg->backupfile_waveformat > 0) { + begin_wave(record, record->backupfilefd, (size_t)max_file_size); + fdcount = 0; + } + if (record->finalfilefd > 0 && record->pcm_cfg->backupfile_waveformat > 0) { + begin_wave(record, record->backupfilefd, (size_t)max_file_size); + } + while (!!record->runing) { + if (getWorkMode() == WorkMode_R311_PV1) { + if (pcm_read(record->pcm_cfg->adc) == + record->pcm_cfg->adc->chunk_size) { + if (pcm_read(record) == record->chunk_size) { + // Write adc data to dmic data, in first two channel + for (i = 0; i < record->chunk_size; i++) { + // printf("copy from %p to %p, size:%d, frame:%d\n", + // (record->pcm_cfg->adc->buffer + + // i * + // record->pcm_cfg->adc->bits_per_frame + // / 8), (record->buffer + i * + // record->bits_per_frame / 8), 2 * + // record->significant_bits_per_sample / + // 8, i); + // ch:1-6 dmic + // ch:7-8 adc + memmove( + (void *)(record->buffer + + i * record->bits_per_frame / 8), + (void *)(record->buffer + + i * record->bits_per_frame / 8 + + 2 * record->significant_bits_per_sample / + 8), + 6 * record->significant_bits_per_sample / 8); + + // printf("copy to 0x%p, size:%d\n", (record->buffer + + // i * record->bits_per_frame / 8 + + // 6 * record->significant_bits_per_sample / + // 8), 2 * record->significant_bits_per_sample / 8); + memcpy( + (void *)(record->buffer + + i * record->bits_per_frame / 8 + + 6 * record->significant_bits_per_sample / + 8), + (void *)(record->pcm_cfg->adc->buffer + + i * record->pcm_cfg->adc->bits_per_frame / + 8), + record->pcm_cfg->adc->bits_per_frame / 8); + // memcpy((void *)(record->buffer + + // i * record->bits_per_frame / 8), + // (void *)(record->pcm_cfg->adc->buffer + + // i * + // record->pcm_cfg->adc->bits_per_frame + // / 8), + // 2 * record->significant_bits_per_sample / 8); + } + + if (record->backupfilefd > 0) { + xwrite(record->backupfilefd, record->buffer, + record->chunk_bytes); + fdcount += record->chunk_bytes; + } + queue_write(record->queue, record->buffer, + record->chunk_bytes); + } else { + fprintf(stderr, "short read, read %d frames/n", ret); + LOG_EX(LOG_Error, "ALSA Short Read, Read %d Frames: %d\n", + ret); + } + } else { + // read dmic data and throw + pcm_read(record); + printf("Read adc codec error!\n"); + } + } else { + printf("Unknow work mode!\n"); + exit(0); + } + } + + if (record->backupfilefd > 0 && + record->pcm_cfg->backupfile_waveformat > 0) { + end_wave(record->backupfilefd); + record->backupfilefd = 0; + n_debug("Write backup file end finish!\n"); } n_debug("RecordThread end, running:%d\n", record->runing); return NULL; } +static void show_available_sample_formats(snd_pcm_t *handle, + snd_pcm_hw_params_t *params) { + snd_pcm_format_t format; + + fprintf(stderr, "Available formats:\n"); + for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) { + if (snd_pcm_hw_params_test_format(handle, params, format) == 0) + fprintf(stderr, "- %s\n", snd_pcm_format_name(format)); + } +} + int Neteasae_record_start(struct pcm_config *pcm_cfg) { + pthread_attr_t thread_attr; + struct sched_param thread_param; + struct pcm_config *tmp; + + printf("Begin to init netease record!\n"); + snd_output_stdio_attach(&sndlog, stderr, 0); + switch (getWorkMode()) { + case WorkMode_R311_PV1: + // dmic codec + pcm_cfg->device_name = "hw:1,0"; + pcm_cfg->channels = 8; + pcm_cfg->rate = 16000; + pcm_cfg->period_size = 1535; + pcm_cfg->format = "S16_LE"; + + printf("Begin to init dmic!\n"); + init_params(pcm_cfg, 1); + tmp = (struct pcm_config *)calloc(1, sizeof(struct pcm_config)); + if (tmp == NULL) { + printf("alloc fail!\n"); + exit(0); + } else { + printf("Begin to init adc codec!\n"); + // adc codec + tmp->device_name = "hw:2,0"; + tmp->channels = 2; + tmp->rate = 16000; + tmp->period_size = 1535; + tmp->format = "S16_LE"; + init_params(tmp, 0); + } + pcm_cfg->adc = tmp->recordata; + pcm_cfg->recordata->adc = tmp; + + pthread_attr_init(&thread_attr); + pthread_attr_setschedpolicy(&thread_attr, SCHED_RR); + thread_param.sched_priority = sched_get_priority_max(SCHED_RR); + pthread_attr_setschedparam(&thread_attr, &thread_param); + + pthread_create(&pcm_cfg->recordata->tid_pcm_read, &thread_attr, + RecordThread, (void *)pcm_cfg->recordata); + pthread_create(&pcm_cfg->recordata->tid_queue_read, NULL, + QueueReadThread, (void *)pcm_cfg->recordata); + + break; + + default: + printf("Unknow mode!\n"); + exit(0); + } +} +static int init_params(struct pcm_config *pcm_cfg, int init_queue) { int rc; int size; int ret = NETEASE_SUCCESS; - pthread_attr_t thread_attr; - struct sched_param thread_param; snd_pcm_hw_params_t *params; + snd_pcm_sw_params_t *swparams; unsigned int val, val2; int dir; snd_pcm_uframes_t frames; char *buffer; - + struct stat statbuf; + RecordData *record = (RecordData *)malloc(sizeof(RecordData)); if (NULL == record) { - return NETEASE_ERROR_OUT_OF_MEMORY; + printf("Malloc fail for record data!\n"); + return NETEASE_ERROR_OUT_OF_MEMORY; } - pcm_cfg->recordata = record; memset(record, 0, sizeof(RecordData)); + + pcm_cfg->recordata = record; + record->pcm_cfg = pcm_cfg; record->cb = pcm_cfg->audio_cb; //设置录音参数参数 @@ -154,11 +506,24 @@ int Neteasae_record_start(struct pcm_config *pcm_cfg) { SND_PCM_STREAM_CAPTURE, 0); if (rc < 0) { n_error("unable to open pcm device: %s/n", snd_strerror(rc)); - ret = NETEASE_ERROR_OPEN_FILE; + ret = NETEASE_ERROR_OPEN_FILE; goto error; } + + record->format = snd_pcm_format_value(pcm_cfg->format); + if (record->format == SND_PCM_FORMAT_UNKNOWN) { + n_error("Wrong format:%s\n", record->format); + ret = NETEASE_ERROR_FAIL; + goto error; + } + + n_debug("Format:%s\n", snd_pcm_format_description(record->format)); + n_debug("Rate %d Hz\n", pcm_cfg->rate); + n_debug("Channel %d\n", pcm_cfg->channels); + /* Allocate a hardware parameters object. */ snd_pcm_hw_params_alloca(¶ms); + snd_pcm_sw_params_alloca(&swparams); /* Fill it in with default values. */ snd_pcm_hw_params_any(record->handle, params); @@ -169,60 +534,127 @@ int Neteasae_record_start(struct pcm_config *pcm_cfg) { SND_PCM_ACCESS_RW_INTERLEAVED); /* Signed 16-bit little-endian format */ - snd_pcm_hw_params_set_format(record->handle, params, SND_PCM_FORMAT_S24_LE); - + rc = snd_pcm_hw_params_set_format(record->handle, params, record->format); + if (rc < 0) { + n_error("Sample format non available!\n"); + show_available_sample_formats(record->handle, params); + goto error; + } /* Two channels (stereo) */ - snd_pcm_hw_params_set_channels(record->handle, params, pcm_cfg->channels); - + rc = snd_pcm_hw_params_set_channels(record->handle, params, + pcm_cfg->channels); + if (rc < 0) { + n_error("Channel count(%d) not available!\n", pcm_cfg->channels); + goto error; + } /* 44100 bits/second sampling rate (CD quality) */ val = pcm_cfg->rate; - snd_pcm_hw_params_set_rate_near(record->handle, params, &val, &dir); + snd_pcm_hw_params_set_rate_near(record->handle, params, &val, 0); /* Set period size to frames. */ - frames = pcm_cfg->period_size; - snd_pcm_hw_params_set_period_size_near(record->handle, params, &frames, - &dir); - record->frames = frames; + // frames = pcm_cfg->period_size; + // snd_pcm_hw_params_set_period_size_near(record->handle, params, &frames, + // 0); + snd_pcm_hw_params_get_buffer_time_max(params, &record->buffer_time, 0); + if (record->buffer_time > 500000) { + record->buffer_time = 500000; + } + record->period_time = record->buffer_time / 4; + n_debug("Buffer time:%d, period time:%d\n", record->buffer_time, + record->period_time); + snd_pcm_hw_params_set_period_time_near(record->handle, params, + &record->period_time, 0); + snd_pcm_hw_params_set_buffer_time_near(record->handle, params, + &record->buffer_time, 0); + + snd_pcm_hw_params_get_period_size(params, &record->chunk_size, 0); + record->frames = record->chunk_size; + snd_pcm_hw_params_get_buffer_size(params, &record->buff_size); + + n_debug("Buffer size:%d, period size:%d\n", record->buff_size, + record->chunk_size); /* Write the parameters to the driver */ rc = snd_pcm_hw_params(record->handle, params); if (rc < 0) { n_error("unable to set hw parameters: %s/n", snd_strerror(rc)); + // snd_pcm_hw_dump(params, sndlog); ret = NETEASE_ERROR_FAIL; goto error; } + // snd_pcm_hw_params_dump(params, stdout); + snd_pcm_dump(record->handle, sndlog); /* Use a buffer large enough to hold one period */ - snd_pcm_hw_params_get_period_size(params, &frames, &dir); - size = frames * 8; /* 4 bytes/sample, 2 channels */ - record->buff_size = size; - n_toast("frames=%ld,record->buff_size=%d\n", frames, record->buff_size); - record->buffer = (char *)malloc(size); + record->bits_per_sample = snd_pcm_format_physical_width(record->format); + record->significant_bits_per_sample = snd_pcm_format_width(record->format); + record->bits_per_frame = record->bits_per_sample * pcm_cfg->channels; + record->chunk_bytes = record->chunk_size * record->bits_per_frame / 8; + + n_debug("bits_per_frame=%d, channel=%d, chunk_bytes=%d\n", + record->bits_per_frame, pcm_cfg->channels, record->chunk_bytes); + n_debug( + "frames=%ld,record->buff_size=%d, quene multiple:%d, quene size:%d\n", + record->chunk_size, record->buff_size, QUEUE_BUFF_MULTIPLE, + QUEUE_BUFF_MULTIPLE * record->buff_size); + record->buffer = (char *)malloc(record->chunk_bytes); if (NULL == record->buffer) { ret = NETEASE_ERROR_OUT_OF_MEMORY; goto error; } - record->queue_buff = - (char *)malloc(sizeof(audio_queue_t) + size * QUEUE_BUFF_MULTIPLE + 1); - if (NULL == record->queue_buff) { - ret = NETEASE_ERROR_OUT_OF_MEMORY; + if (init_queue > 0) { + record->queue_buff = (char *)malloc( + sizeof(audio_queue_t) + record->chunk_bytes * QUEUE_BUFF_MULTIPLE + 1); + if (NULL == record->queue_buff) { + ret = NETEASE_ERROR_OUT_OF_MEMORY; + goto error; + } + record->queue = queue_init(record->queue_buff, + record->chunk_bytes * QUEUE_BUFF_MULTIPLE + 1); + } else { + record->queue_buff = NULL; + record->queue = NULL; + } + + snd_pcm_sw_params_current(record->handle, swparams); + snd_pcm_sw_params_set_avail_min(record->handle, swparams, + record->chunk_size); + snd_pcm_sw_params_set_start_threshold(record->handle, swparams, 1); + snd_pcm_sw_params_set_stop_threshold(record->handle, swparams, 8000); + + if (snd_pcm_sw_params(record->handle, swparams) < 0) { + n_error("unable to install sw params:"); + snd_pcm_sw_params_dump(swparams, stdout); goto error; } - record->queue = - queue_init(record->queue_buff, size * QUEUE_BUFF_MULTIPLE + 1); - - pthread_attr_init(&thread_attr); - pthread_attr_setschedpolicy(&thread_attr, SCHED_RR); - thread_param.sched_priority = sched_get_priority_max(SCHED_RR); - pthread_attr_setschedparam(&thread_attr, &thread_param); record->runing = 1; + if (pcm_cfg->backupfilename != NULL) { + if (!lstat(pcm_cfg->backupfilename, &statbuf)) { + if (S_ISREG(statbuf.st_mode)) + remove(pcm_cfg->backupfilename); + n_debug("Remove file:%s\n", pcm_cfg->backupfilename); + } + record->backupfilefd = + open(pcm_cfg->backupfilename, O_TRUNC | O_WRONLY | O_CREAT, 06444); + n_debug("Create backup file:%s, fd:%d\n", pcm_cfg->backupfilename, + record->backupfilefd); + } + + if (pcm_cfg->finalfilename != NULL) { + if (!lstat(pcm_cfg->finalfilename, &statbuf)) { + if (S_ISREG(statbuf.st_mode)) + remove(pcm_cfg->finalfilename); + n_debug("Remove file:%s\n", pcm_cfg->finalfilename); + } + record->finalfilefd = + open(pcm_cfg->finalfilename, O_TRUNC | O_WRONLY | O_CREAT, 06444); + n_debug("Create final file:%s, fd:%d\n", pcm_cfg->finalfilename, + record->finalfilefd); + } + n_toast("Record_start record :%p\n", record); - pthread_create(&record->tid_pcm_read, &thread_attr, RecordThread, - (void *)record); - pthread_create(&record->tid_queue_read, NULL, QueueReadThread, - (void *)record); goto exit; @@ -239,13 +671,12 @@ void _record_stop(void *record_hd) { printf("\nrecord_stop in record:%p\n\n", record); if (NULL != record) { record->runing = 0; - if(NULL != record->tid_pcm_read) + if (NULL != record->tid_pcm_read) pthread_join(record->tid_pcm_read, NULL); - if(NULL != record->tid_queue_read) + if (NULL != record->tid_queue_read) pthread_join(record->tid_queue_read, NULL); - if(NULL != record->handle) - { + if (NULL != record->handle) { snd_pcm_drain(record->handle); snd_pcm_close(record->handle); } @@ -253,15 +684,15 @@ void _record_stop(void *record_hd) { if (NULL != record->queue) { queue_destroy(record->queue); } - + if (NULL != record->queue_buff) { free(record->queue_buff); } - + if (NULL != record->buffer) { free(record->buffer); } - + free(record); } printf("\nrecord_stop out\n"); @@ -269,6 +700,119 @@ void _record_stop(void *record_hd) { void Netease_record_stop(struct pcm_config *config) { if (config != NULL && config->recordata != NULL) { - _record_stop(config->recordata); + config->recordata->runing = 0; } } + +void device_list(void) { + snd_ctl_t *handle; + int card, err, dev, idx; + snd_ctl_card_info_t *info; + snd_pcm_info_t *pcminfo; + snd_pcm_stream_t stream = SND_PCM_STREAM_CAPTURE; + + snd_ctl_card_info_alloca(&info); + snd_pcm_info_alloca(&pcminfo); + + card = -1; + if (snd_card_next(&card) < 0 || card < 0) { + n_error("no soundcards found..."); + return; + } + printf("**** List of %s Hardware Devices ****\n", + snd_pcm_stream_name(stream)); + while (card >= 0) { + char name[32]; + sprintf(name, "hw:%d", card); + if ((err = snd_ctl_open(&handle, name, 0)) < 0) { + n_error("control open (%i): %s", card, snd_strerror(err)); + goto next_card; + } + if ((err = snd_ctl_card_info(handle, info)) < 0) { + n_error("control hardware info (%i): %s", card, snd_strerror(err)); + snd_ctl_close(handle); + goto next_card; + } + dev = -1; + while (1) { + unsigned int count; + if (snd_ctl_pcm_next_device(handle, &dev) < 0) + n_error("snd_ctl_pcm_next_device"); + if (dev < 0) + break; + snd_pcm_info_set_device(pcminfo, dev); + snd_pcm_info_set_subdevice(pcminfo, 0); + snd_pcm_info_set_stream(pcminfo, stream); + if ((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) { + if (err != -ENOENT) + n_error("control digital audio info (%i): %s", card, + snd_strerror(err)); + continue; + } + printf(("card %i: %s [%s], device %i: %s [%s]\n"), card, + snd_ctl_card_info_get_id(info), + snd_ctl_card_info_get_name(info), dev, + snd_pcm_info_get_id(pcminfo), + snd_pcm_info_get_name(pcminfo)); + count = snd_pcm_info_get_subdevices_count(pcminfo); + printf((" Subdevices: %i/%i\n"), + snd_pcm_info_get_subdevices_avail(pcminfo), count); + for (idx = 0; idx < (int)count; idx++) { + snd_pcm_info_set_subdevice(pcminfo, idx); + if ((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) { + n_error("control digital audio playback info (%i): %s", + card, snd_strerror(err)); + } else { + printf((" Subdevice #%i: %s\n"), idx, + snd_pcm_info_get_subdevice_name(pcminfo)); + } + } + } + snd_ctl_close(handle); + next_card: + if (snd_card_next(&card) < 0) { + n_error("snd_card_next"); + break; + } + } +} + +void pcm_list(void) { + void **hints, **n; + char *name, *descr, *descr1, *io; + const char *filter; + + if (snd_device_name_hint(-1, "pcm", &hints) < 0) + return; + n = hints; + // filter = stream == SND_PCM_STREAM_CAPTURE ? "Input" : "Output"; + filter = "Input"; + while (*n != NULL) { + name = snd_device_name_get_hint(*n, "NAME"); + descr = snd_device_name_get_hint(*n, "DESC"); + io = snd_device_name_get_hint(*n, "IOID"); + if (io != NULL && strcmp(io, filter) != 0) + goto __end; + printf("%s\n", name); + if ((descr1 = descr) != NULL) { + printf(" "); + while (*descr1) { + if (*descr1 == '\n') + printf("\n "); + else + putchar(*descr1); + descr1++; + } + putchar('\n'); + } + __end: + if (name != NULL) + free(name); + if (descr != NULL) + free(descr); + if (io != NULL) + free(io); + n++; + } + snd_device_name_free_hint(hints); +} \ No newline at end of file diff --git a/target/allwinner/mandolin-pv1/configs/sys_config.fex b/target/allwinner/mandolin-pv1/configs/sys_config.fex index abdfe5ca4..713ae0be9 100755 --- a/target/allwinner/mandolin-pv1/configs/sys_config.fex +++ b/target/allwinner/mandolin-pv1/configs/sys_config.fex @@ -1248,8 +1248,8 @@ snddmic_used = 1 ;-------------------------------------------------------------------------------- [snddaudio0] snddaudio0_used = 1 -sunxi,snddaudio-codec = -sunxi,snddaudio-codec-dai = +sunxi,snddaudio-codec = "es7243.0-0013" +sunxi,snddaudio-codec-dai = "es7243" ;----------------------------------------------------------------------------- [daudio0] diff --git a/target/allwinner/mandolin-pv1/configs/sys_partition.fex b/target/allwinner/mandolin-pv1/configs/sys_partition.fex index 0edd388d3..6a42b56b7 100755 --- a/target/allwinner/mandolin-pv1/configs/sys_partition.fex +++ b/target/allwinner/mandolin-pv1/configs/sys_partition.fex @@ -53,7 +53,7 @@ size = 512 [partition] name = rootfs - size = 65536 + size = 131072 downloadfile = "rootfs.fex" user_type = 0x8000 diff --git a/target/allwinner/mandolin-pv1/defconfig b/target/allwinner/mandolin-pv1/defconfig index 5c8911e52..6186a4b20 100644 --- a/target/allwinner/mandolin-pv1/defconfig +++ b/target/allwinner/mandolin-pv1/defconfig @@ -157,7 +157,7 @@ CONFIG_KERNEL_PRINTK_TIME=y # # Package build options # -# CONFIG_DEBUG is not set +CONFIG_DEBUG=y # CONFIG_IPV6 is not set CONFIG_PKG_BUILD_PARALLEL=y CONFIG_PKG_BUILD_USE_JOBSERVER=y @@ -165,9 +165,9 @@ CONFIG_PKG_BUILD_USE_JOBSERVER=y # # Stripping options # -# CONFIG_NO_STRIP is not set +CONFIG_NO_STRIP=y # CONFIG_USE_STRIP is not set -CONFIG_USE_SSTRIP=y +# CONFIG_USE_SSTRIP is not set # CONFIG_STRIP_KERNEL_EXPORTS is not set # CONFIG_USE_MKLIBS is not set # CONFIG_USE_UCLIBCXX is not set @@ -2626,25 +2626,25 @@ CONFIG_PACKAGE_zlib=y # # Translations # +# CONFIG_LUCI_LANG_uk is not set # CONFIG_LUCI_LANG_hu is not set # CONFIG_LUCI_LANG_pt is not set -# CONFIG_LUCI_LANG_sk is not set -# CONFIG_LUCI_LANG_no is not set +# CONFIG_LUCI_LANG_ro is not set # CONFIG_LUCI_LANG_en is not set -# CONFIG_LUCI_LANG_pl is not set -# CONFIG_LUCI_LANG_uk is not set -# CONFIG_LUCI_LANG_ru is not set +# CONFIG_LUCI_LANG_el is not set +# CONFIG_LUCI_LANG_sk is not set +# CONFIG_LUCI_LANG_ja is not set # CONFIG_LUCI_LANG_vi is not set # CONFIG_LUCI_LANG_he is not set -# CONFIG_LUCI_LANG_ro is not set +# CONFIG_LUCI_LANG_no is not set # CONFIG_LUCI_LANG_ms is not set +# CONFIG_LUCI_LANG_pl is not set # CONFIG_LUCI_LANG_zh-cn is not set # CONFIG_LUCI_LANG_de is not set # CONFIG_LUCI_LANG_zh-tw is not set # CONFIG_LUCI_LANG_tr is not set # CONFIG_LUCI_LANG_sv is not set -# CONFIG_LUCI_LANG_ja is not set -# CONFIG_LUCI_LANG_el is not set +# CONFIG_LUCI_LANG_ru is not set # CONFIG_LUCI_LANG_ca is not set # CONFIG_LUCI_LANG_es is not set # CONFIG_LUCI_LANG_pt-br is not set @@ -2837,7 +2837,7 @@ CONFIG_PACKAGE_libuvdbus=y CONFIG_RES_NORMAL_MODE=y # CONFIG_PACKAGE_log_ctrl is not set # CONFIG_PACKAGE_mcu_ota is not set -# CONFIG_PACKAGE_netease_control_center is not set +CONFIG_PACKAGE_netease_control_center=y # CONFIG_PACKAGE_netease_test is not set CONFIG_PACKAGE_netease_voice=y