got SPDIF working!

Discussion in 'General Discussion' started by fetcher, Jul 13, 2014.

  1. fetcher

    fetcher Member

    Joined:
    Mar 9, 2014
    Messages:
    166
    Likes Received:
    20
    The output side, at least. I haven't tried input/receive due to a pin conflict (more on that below).

    Anyway, here's how to enable it...

    STEP 1--
    Edit kernel file arch/arm/mach-mx6/board-mx6qd_seco_UDOO.h (Quad core)

    search for the label "pin 44". Comment out the line just above that,

    // MX6Q_PAD_GPIO_19__GPIO_4_5,

    and UNCOMMENT (remove // marks) this line below it,

    MX6Q_PAD_GPIO_19__SPDIF_OUT1,

    For a dual-core board, the file to edit is board-mx6sdl_seco_UDOO.h in that same directory, and pinmux symbols begin with MX6DL rather than MX6Q, but it's otherwise the same.

    STEP 2--

    Still working in arch/arm/mach-mx6/ kernel source directory, two insertions need to be made into "board-mx6_seco_UDOO.c", starting around line 620 (can't say exactly since mine has a lot of other customizations). This is code from board-mx6q_arm2.c and board-mx6sl_evk.c, only slightly modified --

    // INSERT FOLLOWING BLOCK *AFTER* THIS SECTION:
    static struct mxc_audio_platform_data mx6_seco_UDOO_audio_data = {
    .ssi_num = SSI_CH_NUMBER,
    .src_port = SSI_CH_NUMBER,
    .ext_port = 6, // OUTPUT ON AUD6 FOR AUDIO MUXING
    .hp_gpio = -1,
    };
    Code:
    // BEGIN INSERTION #1:
    #ifdef CONFIG_SND_SOC_IMX_SPDIF
    static int spdif_clk_set_rate(struct clk *clk, unsigned long rate)
    {
            unsigned long rate_actual;
            rate_actual = clk_round_rate(clk, rate);
            clk_set_rate(clk, rate_actual);
            return 0;
    }
    
    static struct mxc_spdif_platform_data mxc_spdif_data = {
            .spdif_tx               = 1,            /* enable tx */
            .spdif_rx               = 0,            /* disable rx */
            /*
             * spdif0_clk will be 454.7MHz divided by ccm dividers.
             *
             * 44.1KHz: 454.7MHz / 7 (ccm) / 23 (spdif) = 44,128 Hz ~ 0.06% error
             * 48KHz:   454.7MHz / 4 (ccm) / 37 (spdif) = 48,004 Hz ~ 0.01% error
             * 32KHz:   454.7MHz / 6 (ccm) / 37 (spdif) = 32,003 Hz ~ 0.01% error
             */
            .spdif_clk_44100        = 1,    /* tx clk from spdif0_clk_root */
            .spdif_clk_48000        = 1,    /* tx clk from spdif0_clk_root */
            .spdif_div_44100        = 23,
            .spdif_div_48000        = 37,
            .spdif_div_32000        = 37,
            .spdif_rx_clk           = 0,    /* rx clk from spdif stream */
            .spdif_clk_set_rate     = spdif_clk_set_rate,
            .spdif_clk              = NULL, /* spdif bus clk */
    };
    #endif
    // END INSERTION #1
    
    // ADD NEXT SECTION AFTER
    imx6_add_armpmu();

    // BUT BEFORE:
    imx6q_add_perfmon(0);
    Code:
    // BEGIN INSERTION #2
    #ifdef CONFIG_SND_SOC_IMX_SPDIF
            mxc_spdif_data.spdif_core_clk = clk_get_sys("mxc_spdif.0", NULL);
            clk_put(mxc_spdif_data.spdif_core_clk);
            imx6q_add_spdif(&mxc_spdif_data);
            imx6q_add_spdif_dai();
            imx6q_add_spdif_audio_device();
    #endif
    
    STEP 3--

    In kernel configuration (menuconfig, etc.), enable this driver, just above the IMX - HDMI option:
    Code:
      -> Device Drivers
        -> Sound card support
          -> Advanced Linux Sound Architecture
            -> ALSA for SoC audio support
              -> SoC Audio for Freescale i.MX CPUs
               -> SoC Audio support for IMX - S/PDIF  <-- ENABLE THIS
    
    (SND_SOC_IMX_SPDIF = * or m)

    and rebuild the kernel.

    If you decide to make this a module, the three resulting modules must be loaded in this order:

    1) modprobe snd_soc_imx_spdif
    2) modprobe snd_soc_imx_spdif_dai
    3) modprobe snd_soc_mxc_spdif

    Just building them into the kernel also works, and results in this order of ALSA devices:

    root@imp:/rw/home# aplay -l
    **** List of PLAYBACK Hardware Devices ****
    card 0: vt1613audio [vt1613-audio], device 0: HiFi vt1613-0 []
    Subdevices: 1/1
    Subdevice #0: subdevice #0
    card 1: imxspdif [imx-spdif], device 0: IMX SPDIF mxc-spdif-0 []
    Subdevices: 1/1
    Subdevice #0: subdevice #0
    card 2: imxhdmisoc [imx-hdmi-soc], device 0: IMX HDMI TX mxc-hdmi-soc-0 []
    Subdevices: 1/1
    Subdevice #0: subdevice #0

    So,

    mpg321 -a hw:1,0 somefile.mp3

    mplayer -ao alsa:device=hw=1.0 otherfile.avi

    etc. There are no mixer controls for the digital output.

    I made a few further patches to allow building the vt1613 analog-codec driver as a module, to allow it to be loaded last, with SP-DIF as the primary/default device, but just re-ordering them at the ALSA userland level via a new !default in /etc/asound.conf is probably a better way to go. When modularized, Freescale's snd_soc_imx_ac97_vt1613.ko and snd_soc_vt1613.ko (codec part) apparently don't unload cleanly-- they work fine when initially loaded, but rmmod'ing them and then attempting to re-insert results in a kernel oops. I'd also hoped to hack some on that driver (working on better mixer controls & support for extra analog-ins) without having to reboot so much, but oh well...

    There is another SPDIF kernel option under
    System Type -> Freescale MXC Implementations -> UDOO

    (CONFIG_SPDIF_SUPPORT = y)

    which seems intended to make the *UDOO.h pinmuxing change for you. I went ahead and turned that on, but it appears to be a no-op (consistent with its "not working yet" tag) and so probably isn't necessary.


    I haven't tried to get the S/PDIF-IN (receiver) working, since I don't need it, and pin-muxing constraints mean turning on the only exposed SPDIF-In pin requires sacrificing i.MX6 I2C bus #1, the sole I2C available on Arduino headers, which I'm using for quite a few other things...

    If someone wants to try this, though, just set .spdif_rx = 1 in the initialization section, and enable it at "pin 21" in the pinmux list.

    Or, if you're willing to do some minor hardware surgery on your Udoo, and give up the ability to turn VBUS USB power on/off to the SAM3X (either hardwiring that to +5V or disabling it) SPDIF-In is available also at GPIO_16, which isn't exposed on headers but does go to VBUS_EN in the Arduino area via a resistor and pair of FETs. Its other two possible pinmux pads mentioned in Freescale's datasheet appear not to be bonded out on the Udoo.
     
  2. fetcher

    fetcher Member

    Joined:
    Mar 9, 2014
    Messages:
    166
    Likes Received:
    20
    Below are proper patches against a clean kernel tree, which should be easier to apply. You'll stil need to turn on the "SoC Audio support for IMX - S/PDIF" option under Device Drivers -> Sound...

    The main initialization part:
    Code:
    --- Kernel_Unico-dist/arch/arm/mach-mx6/board-mx6_seco_UDOO.c   2014-07-14 20:15:43.000000000 -0400
    +++ kernel_unico_imp/arch/arm/mach-mx6/board-mx6_seco_UDOO.c    2014-07-14 21:51:32.000000000 -0400
    @@ -616,6 +628,37 @@
            .hp_gpio = -1,
     };
     
    +
    +#ifdef CONFIG_SND_SOC_IMX_SPDIF
    +static int spdif_clk_set_rate(struct clk *clk, unsigned long rate)
    +{
    +        unsigned long rate_actual;
    +        rate_actual = clk_round_rate(clk, rate);
    +        clk_set_rate(clk, rate_actual);
    +        return 0;
    +}
    +
    +static struct mxc_spdif_platform_data mxc_spdif_data = {
    +        .spdif_tx               = 1,            /* enable tx */
    +        .spdif_rx               = 0,            /* enable rx */
    +        /*
    +         * spdif0_clk will be 454.7MHz divided by ccm dividers.
    +         *
    +         * 44.1KHz: 454.7MHz / 7 (ccm) / 23 (spdif) = 44,128 Hz ~ 0.06% error
    +         * 48KHz:   454.7MHz / 4 (ccm) / 37 (spdif) = 48,004 Hz ~ 0.01% error
    +         * 32KHz:   454.7MHz / 6 (ccm) / 37 (spdif) = 32,003 Hz ~ 0.01% error
    +         */
    +        .spdif_clk_44100        = 1,    /* tx clk from spdif0_clk_root */
    +        .spdif_clk_48000        = 1,    /* tx clk from spdif0_clk_root */
    +        .spdif_div_44100        = 23,
    +        .spdif_div_48000        = 37,
    +        .spdif_div_32000        = 37,
    
    And the trivial pinmuxing change:
    Code:
    --- Kernel_Unico-dist/arch/arm/mach-mx6/board-mx6qd_seco_UDOO.h 2014-07-14 20:15:43.000000000 -0400
    +++ kernel_unico_imp/arch/arm/mach-mx6/board-mx6qd_seco_UDOO.h  2014-07-14 21:52:33.000000000 -0400
    @@ -341,8 +341,8 @@
                    // MX6Q_PAD_NANDF_D2__USDHC1_DAT6,
            MX6Q_PAD_NANDF_D1__GPIO_2_1,                                           // pin 43
                    // MX6Q_PAD_NANDF_D1__USDHC1_DAT5,
    -       MX6Q_PAD_GPIO_19__GPIO_4_5,                                            // pin 44
    -               // MX6Q_PAD_GPIO_19__SPDIF_OUT1,
    +       // MX6Q_PAD_GPIO_19__GPIO_4_5,                                         // pin 44
    +               MX6Q_PAD_GPIO_19__SPDIF_OUT1,
                    // MX6Q_PAD_GPIO_19__CCM_CLKO,  
            MX6Q_PAD_DISP0_DAT22__GPIO_5_16,                                       // pin 45
                    // MX6Q_PAD_DISP0_DAT22__ECSPI1_MISO,
    
    Maybe UDOO devs could consider integrating that first part into their distributed kernel? The pinmux change probably shouldn't be enabled by default.

    By the way, this is the circuit I'm using to buffer the SP-DIF signal and drive a coax-type receiver input:
    (from http://sound.westhost.com/project85.htm)

    except that I'm using a 54ACT241 chip in place of the 74HC04, since I already had one on my prototyping shield for converting various GPIO outputs from 3.3V to 5V levels. Inverting vs. non-inverting doesn't matter for SP-DIF; only the state transitions are significant. I put two of the HCT241's eight gates in parallel as in the schematic, which works great even over a long ~10m cable, but plan to try it soon with just one-- I'd like to use that that last gate for something else.
     

    Attached Files:

  3. Komet

    Komet New Member

    Joined:
    Jun 8, 2014
    Messages:
    2
    Likes Received:
    0
    Great to hear you got it working! I will try it out when I got my 74HC04.
     
  4. indianerjones

    indianerjones New Member

    Joined:
    Mar 10, 2014
    Messages:
    18
    Likes Received:
    1
    Hi, is there anybody here who tried to use the SPDIF input? I got the pin muxing set up, but can't record anything using arecord. I'll try to see if there is a problem with the software or some kernel settings still need readjusting.

    Edit: To contibute to the thread: I can confirm that the patch does give you an SPDIF device in ALSA. Playing files on the output side I was able to see some signal being generated with an analyzer device, cannot confirm if audio output is working correctly though.

    One more edit: It works! Not sure if every source works correctly, but on one of our test setups (the important one, thank god!) we could record audio over spdif!
    Use the following command to try it out:
    Code:
    arecord -D plughw:1,0 | cat 
    Your console should then be flooded with input, if the spdif receives data.
    Code:
    arecord -D plughw:1,0  test.wav -f S24_LE -r 48000 -c 2
    produces a stereo wav file of the audio. You have to know and specify what format your input is here, as the PCM stream from the spdif is already in the source format and setting wrong parameters here results in strange sound.
     
  5. fetcher

    fetcher Member

    Joined:
    Mar 9, 2014
    Messages:
    166
    Likes Received:
    20
    I found a couple of bugs in the mxc_spdif.c driver, where registers controlling two SPDIF flag bits were being incorrectly set. Until now I hadn't noticed, because not all DACs/amps/receivers act on these bits.

    First, the "validity" flag (bit 28 in each subframe, counting from preamble bit 0) was going out as 1 rather than 0, and per SP-DIF standard '1' denotes an *invalid* sample, like from a scratched CD giving read errors (though it's reversed again in i.MX6 SPDIF_SCR register, where the power-on default '0' in bit 5 means "send out 1s").

    DACs that honor this bit will either mute their output, or try to interpolate from previous samples, but either way, a constant stream of Validity=1 samples means no audio. Some are smart enough to ignore Validity when it seems to be stuck high.

    Second, for some strange reason the "pre-emphasis" option within channel-status data (muxed into bit 30 of 192 consecutive frames) was defaulting to "ON" (50us/15us), despite pre-emphasis hardly ever being used these days (a few early CDs were mastered with it). DACs which respond to this bit will apply a filter that causes noticable rolloff of the high frequency range, resulting in dull / muddy sound.

    Here is a kernel patch to fix these issues. Apply with patch -p1 in sound/soc/codecs --

    Code:
    --- a/mxc_spdif.c       2014-07-13 09:56:24.000000000 -0400
    +++ b/mxc_spdif.c       2015-02-02 04:08:28.000000000 -0500
    @@ -575,7 +575,7 @@
            regval = __raw_readl(spdif_base_addr + SPDIF_REG_SCR);
            regval &= 0xfc33e3;
            regval &= ~SCR_LOW_POWER;
    -       regval |= SCR_TXFIFO_AUTOSYNC | SCR_TXFIFO_NORMAL |
    +       regval |= SCR_TXFIFO_AUTOSYNC | SCR_TXFIFO_NORMAL | SCR_VAL_CLEAR |
                SCR_TXSEL_NORMAL | SCR_USRC_SEL_CHIP | (2 << SCR_TXFIFO_ESEL_BIT);
            __raw_writel(regval, SPDIF_REG_SCR + spdif_base_addr);
     
    @@ -1310,7 +1310,7 @@
                    /* init tx channel status default value */
                    mxc_spdif_control.ch_status[0] =
                        IEC958_AES0_CON_NOT_COPYRIGHT |
    -                   IEC958_AES0_CON_EMPHASIS_5015;
    +                   IEC958_AES0_CON_EMPHASIS_NONE;
                    mxc_spdif_control.ch_status[1] = IEC958_AES1_CON_DIGDIGCONV_ID;
                    mxc_spdif_control.ch_status[2] = 0x00;
                    mxc_spdif_control.ch_status[3] =
    
     

Share This Page