These can be applied with "patch -p1 <filename.patch" from the top of your kernel tree. Changes include - added REC LEVEL control - added MIC_BOOST (+20 dB) switch - added REC SOURCE control to switch capture input from MIC -> LINE, AUX, or STEREO MIX (output of Playback mixer tapped ahead of Master Volume). This is only useful if your board has been modified to bring these out to connectors -- see below. MIC is still the default. - added MUTE switch to all mixer sliders, toggling bit 15 in associated VT1613 AC97 registers -- helpful to remove all traces of input source from analog-out during full-duplex operation - auto-mute all vt1613 input pins that aren't wired to anything or exposed in the mixer (CD, Video, Phone, PC Beep) to slightly lower the noise floor - renamed mixer controls to match what they actually do -- MIC, LINE, AUX affect "Playback" only (live monitoring via the analog-out), and have no effect on the A/D side of the chip. Unless REC SOURCE is set to Stereo Mix (which rules out full-duplex), only the newly-added REC LEVEL, REC SOURCE, and MIC_BOOST affect audio capture/recording. This change also avoids the duplicate controls showing up in alsamixer. - removed "Mono Mix", "Jack Function", "Speaker Function", "Line In Function" controls, all of which seem to be no-ops on the UDOO's vt1613 - fixed reversal of stereo channels when left/right mixer controls are adjusted separately-- but passthrough playback (e.g. line -> analog out live monitoring) is still internally reversed for some reason, even though wiring is correct, and capture uses the right channel assignments. - fixed some mismatched pointers, and removed/commented unused functions and variables to clear compiler warnings Code: --- Kernel_Unico-dist/sound/soc/codecs/vt1613.c 2014-07-14 20:16:04.000000000 -0400 +++ kernel_unico_imp/sound/soc/codecs/vt1613.c 2014-07-14 21:35:29.000000000 -0400 @@ -41,27 +41,24 @@ #include "vt1613.h" #include <linux/delay.h> -#define WM9712_DAI_AC97_HIFI 0 -#define WM9712_DAI_AC97_AUX 1 - #define VT1613_CACHE_REG_NUM 30 static const u16 vt1613_ac97_reg[VT1613_CACHE_REG_NUM] = { 0x0000, /* 0x00 this register not used */ 0x0C0C, /* 0x02 Master Volume */ 0x0C0C, /* 0x04 Headphone Volume (optional) */ - 0x0C0C, /* 0x06 Master Volume Mono (optional) */ + 0x8000, /* 0x06 Master Volume Mono (optional) - N/C so mute */ 0x0808, /* 0x08 Master Tone (Bass & Treble) (optional) */ - 0x0000, /* 0x0A PC Beep Volume (optinal) */ - 0x0000, /* 0x0C Phone Volume (optional) */ + 0x8000, /* 0x0A PC Beep Volume - N/C, mute to reduce noise */ + 0x8000, /* 0x0C Phone Volume - N/C, mute to reduce noise */ 0x000C, /* 0x0E MIC Volume */ 0x0C0C, /* 0x10 Line In Volume */ - 0x0404, /* 0x12 CD Volume */ - 0x0404, /* 0x14 Video Volume (optional) */ + 0x8000, /* 0x12 CD Volume - N/C, mute to reduce noise */ + 0x8000, /* 0x14 Video Volume - N/C, mute to reduce noise */ 0x0404, /* 0x16 AUX Volume (optional) */ 0x0C0C, /* 0x18 PCM Volume */ 0x0000, /* 0x1A Record Select */ - 0x0404, /* 0x1C Record Gain */ + 0x0000, /* 0x1C Record Gain */ 0x0000, /* 0x1E Record Gain MIC (optional) */ 0x0000, /* 0x20 General Purpose (optional) */ 0x0000, /* 0x22 3D Control (optional) */ @@ -233,30 +230,53 @@ static DECLARE_TLV_DB_SCALE(line_tvl, -3450, 150, 0); /* - * Mono Volume gain - * -46.5 dB to 0 dB in 1.5 dB steps + * Recording Volume gain + * 0 dB to +22.5 dB in 1.5 dB steps */ -static DECLARE_TLV_DB_SCALE(mono_tlv, -4650, 150, 0); +static DECLARE_TLV_DB_SCALE(recgain_tlv, 0, 150, 0); + +static const char *vt1613_rec_sel[] = {"Mic", "CD", "n/c", "AUX", + "Line", "Stereo Mix", "Mono Mix", "Phone"}; + +static const SOC_ENUM_SINGLE_DECL(rec_source_l, AC97_REC_SEL, 8, vt1613_rec_sel); + +static const SOC_ENUM_SINGLE_DECL(rec_source_r, AC97_REC_SEL, 0, vt1613_rec_sel); static const struct snd_kcontrol_new vt1613_snd_controls[] = { - SOC_DOUBLE_TLV("Stereo Master Volume", - AC97_MASTER, 0, 8, 0x1f, 1, stereo_tvl), + SOC_DOUBLE_TLV("Master Playback Volume", + AC97_MASTER, 8, 0, 0x1f, 1, stereo_tvl), + + SOC_SINGLE("Master Playback Switch", AC97_MASTER, 15, 1, 1), SOC_DOUBLE_TLV("PCM Playback Volume", - AC97_PCM, 0, 8, 0x1f, 1, pcm_tvl), + AC97_PCM, 8, 0, 0x1f, 1, pcm_tvl), + + SOC_SINGLE("PCM Playback Switch", AC97_PCM, 15, 1, 1), - SOC_SINGLE_TLV("MIC Recording Volume", + SOC_SINGLE_TLV("MIC Playback Volume", AC97_MIC, 0, 0x1f, 1, mic_tvl), - SOC_DOUBLE_TLV("LINE Recording Volume", + SOC_SINGLE("MIC Playback Switch", AC97_MIC, 15, 1, 1), + + SOC_SINGLE("MIC_BOOST Switch", AC97_MIC, 6, 1, 0), + + SOC_DOUBLE_TLV("LINE Playback Volume", AC97_LINE, 0, 8, 0x1f, 1, line_tvl), - SOC_SINGLE_TLV("Mono Master Volume", - AC97_MASTER_MONO, 0, 0x1f, 1, mono_tlv), + SOC_SINGLE("LINE Playback Switch", AC97_LINE, 15, 1, 1), - SOC_DOUBLE_TLV("AUX Recording Volume", + SOC_DOUBLE_TLV("AUX Playback Volume", AC97_AUX, 0, 8, 0x1f, 1, line_tvl), + + SOC_SINGLE("AUX Playback Switch", AC97_AUX, 15, 1, 1), + + SOC_DOUBLE_TLV("Rec Level Capture Volume", + AC97_REC_GAIN, 0, 8, 0x1f, 0, recgain_tlv), + + SOC_ENUM("Rec Source L Capture Route", rec_source_l), + + SOC_ENUM("Rec Source R Capture Route", rec_source_r), }; static const struct snd_soc_dapm_route intercon[] = { @@ -449,12 +469,14 @@ return ret; } +/* UNUSED static int vt1613_digital_mute(struct snd_soc_dai *codec_dai, int mute) { struct snd_soc_codec *codec = codec_dai->codec; return __vt1613_digital_mute(codec, mute); } + */ static int vt1613_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { @@ -561,35 +583,36 @@ return snd_soc_write(codec, reg, runtime->rate); } -static void vt1613_constraints(struct vt1613_priv *vt1613, - struct snd_pcm_substream *mst_substream) -{ - struct snd_pcm_substream *slv_substream; - - /* Pick the stream, which need to be constrained */ - if (mst_substream == vt1613->master_substream) - slv_substream = vt1613->slave_substream; - else if (mst_substream == vt1613->slave_substream) - slv_substream = vt1613->master_substream; - else /* This should not happen.. */ - return; - - /* Set the constraints according to the already configured stream */ - snd_pcm_hw_constraint_minmax(slv_substream->runtime, - SNDRV_PCM_HW_PARAM_RATE, - vt1613->rate, - vt1613->rate); - - snd_pcm_hw_constraint_minmax(slv_substream->runtime, - SNDRV_PCM_HW_PARAM_SAMPLE_BITS, - vt1613->sample_bits, - vt1613->sample_bits); - - snd_pcm_hw_constraint_minmax(slv_substream->runtime, - SNDRV_PCM_HW_PARAM_CHANNELS, - vt1613->channels, - vt1613->channels); -} +// UNUSED FUNCTION +//static void vt1613_constraints(struct vt1613_priv *vt1613, +// struct snd_pcm_substream *mst_substream) +//{ +// struct snd_pcm_substream *slv_substream; +// +// /* Pick the stream, which need to be constrained */ +// if (mst_substream == vt1613->master_substream) +// slv_substream = vt1613->slave_substream; +// else if (mst_substream == vt1613->slave_substream) +// slv_substream = vt1613->master_substream; +// else /* This should not happen.. */ +// return; +// +// /* Set the constraints according to the already configured stream */ +// snd_pcm_hw_constraint_minmax(slv_substream->runtime, +// SNDRV_PCM_HW_PARAM_RATE, +// vt1613->rate, +// vt1613->rate); +// +// snd_pcm_hw_constraint_minmax(slv_substream->runtime, +// SNDRV_PCM_HW_PARAM_SAMPLE_BITS, +// vt1613->sample_bits, +// vt1613->sample_bits); +// +// snd_pcm_hw_constraint_minmax(slv_substream->runtime, +// SNDRV_PCM_HW_PARAM_CHANNELS, +// vt1613->channels, +// vt1613->channels); +//} static int vt1613_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) @@ -829,7 +852,7 @@ }, }; -static int vt1613_volatile_register(unsigned int reg) +static int vt1613_volatile_register(struct snd_soc_codec *codec, unsigned int reg) { if (reg == VT1613_CHIP_ID || reg == VT1613_CHIP_ADCDAC_CTRL || @@ -881,8 +904,8 @@ static int vt1613_driver_probe(struct snd_soc_codec *codec) { struct vt1613_priv *vt1613 = snd_soc_codec_get_drvdata(codec); - u16 reg, ana_pwr, lreg_ctrl; - int vag; +// UNUSED u16 reg, ana_pwr, lreg_ctrl; +// UNUSED int vag; int ret; int i; @@ -967,8 +990,8 @@ { struct vt1613_priv *vt1613 = platform_get_drvdata(pdev); - snd_soc_unregister_dais(&vt1613_dai[0], ARRAY_SIZE(vt1613_dai)); - snd_soc_unregister_codec(&vt1613->codec); + snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(vt1613_dai)); + snd_soc_unregister_codec(&pdev->dev); kfree(vt1613->codec.reg_cache); kfree(vt1613); Code: --- Kernel_Unico-dist/sound/soc/imx/imx-ac97-vt1613.c 2014-07-14 20:16:05.000000000 -0400 +++ kernel_unico_imp/sound/soc/imx/imx-ac97-vt1613.c 2014-07-14 21:18:51.000000000 -0400 @@ -228,12 +228,14 @@ struct snd_soc_codec *codec = rtd->codec; struct snd_soc_dapm_context *dapm = &codec->dapm; +/* skip adding no-op controls to mixer int ret; ret = snd_soc_add_controls(codec, vt1613_machine_controls, ARRAY_SIZE(vt1613_machine_controls)); if (ret) return ret; +*/ /* Add imx_3stack specific widgets */ snd_soc_dapm_new_controls(dapm, imx_3stack_dapm_widgets, @@ -272,6 +274,7 @@ static struct platform_device *imx_vt1613_snd_device; +/* unused function static int imx_audmux_config(int slave, int master) { unsigned int ptcr, pdcr; @@ -292,6 +295,7 @@ mxc_audmux_v2_configure_port(master, ptcr, pdcr); return 0; } +*/ static int imx_audmux_ac97_config(int slave, int master) { @@ -318,7 +322,7 @@ { struct mxc_audio_platform_data *plat = pdev->dev.platform_data; - int ret = 0; +// unused int ret = 0; card_priv.pdev = pdev; imx_audmux_ac97_config(plat->src_port, plat->ext_port); return 0; To bring out the vt1613 chip's Line and AUX inputs, I soldered a 9-pin header to the bottom of my board, directly under the MIC and Speaker/Line output jacks-- the ground (sleeve) pins on those jacks are spaced just right to be straddled by two header pins in this pattern: L(aux), GND, GND, R(aux), L(line), GND, GND, L(aux) which allows standard 4-pin CD-ROM analog-audio cables to be used for the input connections. Inputs on the chip have a DC bias, and so must be AC-coupled through a 0.1uF capacitor. Also, there needs to be a voltage divider in front of the cap to drop the levels down-- I had to use a surprisingly high 28:1 ratio to avoid clipping in the ADC, even at the lowest possible record gain setting. This surprised me... on other AC97 chips 2:1 or 4:1 has worked fine. It wouldn't clip on analog pass-through, only on capture via the ADC. Analog supply voltage to the vt1613 being +3.3V rather than the more common +5V may be a factor. I'll try to attach a photo of the board mod below-- note that this still shows my first attempt using 8.2k resistors for both the top and bottom halves of each divider, but I ended up having to use much larger resistors on the input side: (audio source pin) -> 220k -> (cap to vt1613 pin) -> 8.2k -> GND The trickiest part of this mod, of course is soldering to those closely-spaced surface-mount pins of the VT1613 chip, which takes a steady hand. Use wire wrap wire and a fine-tipped iron, and some extra flux to avoid shorts. Search for "VT1613_R100.pdf" to find the chip datasheet, which includes pinouts as well as a detailed register map. All four line/aux-in wires attach to the "right" side of the vt1613, which is toward the front of the Udoo (facing main CPU and Arduino header area). It isn't an especially crowded area of the board, at least. That single wire between the far chip corner and the "NOT USED" Arduino pin is for the VT1613's SP-DIF output, which I haven't been able to get working just yet-- probably some bad register setting, but with the direct i.MX6 SP-DIF output now available I probably won't put much more time into this one. The thicker, gray wire is for an unrelated mod (allowing +5V HDMI power to be switched on/off under software control).
What kernel sources do these patches apply cleanly to? I tried the kernel_unico sources from github, but they didn't apply cleanly. Okay, so I tried again and they applied cleanly this time (once compensating for an extra 4 spaces from my browser...). Thanks for sharing your work! Simultaneous record and playback is vital to my project.
Re: patch for vt1613 driver: improved mixer + extra inputs m Awesome! Let's see if we can integrate in official UDOO kernel