Modifying USB DAC settings (select SPDIF)

Cleaned up code:

/ *
* Set SPDIFOUT - program to control the SPDIF-output on a CM6206 chip
*
* Based on: Set LED - program to control a USB LED device
* from user space using libusb (Writing a Real Driver—In User Space | Linux Journal)
*
* Copyright (C) 2004
* Greg Kroah-Hartman (greg@kroah.com)
*
* This program is free software; you can
* redistribute it and/or modify it under the terms
* of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the
* License.
*
*/

// https://www.linuxjournal.com/article/7466

#include <stdio.h>
#include <string.h>
#include <linux/types.h>
#include <usb.h>


//Id's for the  C-Media Electronics, Inc. CM106 Like Sound Device

// 0d8c:0102 
#define LED_VENDOR_ID   0x0d8c
#define LED_PRODUCT_ID  0x0102  

#define BIT(nr) (1UL << (nr))
/*
 * CM6206 registers from the CM6206 datasheet rev 2.1
 */
#define CM6206_REG0_DMA_MASTER BIT(15)
#define CM6206_REG0_SPDIFO_RATE_48K (2 << 12)
#define CM6206_REG0_SPDIFO_RATE_96K (7 << 12)
/* Bit 4 thru 11 is the S/PDIF category code */
#define CM6206_REG0_SPDIFO_CAT_CODE_GENERAL (0 << 4)
#define CM6206_REG0_SPDIFO_EMPHASIS_CD BIT(3)
#define CM6206_REG0_SPDIFO_COPYRIGHT_NA BIT(2)
#define CM6206_REG0_SPDIFO_NON_AUDIO BIT(1)
#define CM6206_REG0_SPDIFO_PRO_FORMAT BIT(0)

#define CM6206_REG1_TEST_SEL_CLK BIT(14)
#define CM6206_REG1_PLLBIN_EN BIT(13)
#define CM6206_REG1_SOFT_MUTE_EN BIT(12)
#define CM6206_REG1_GPIO4_OUT BIT(11)
#define CM6206_REG1_GPIO4_OE BIT(10)
#define CM6206_REG1_GPIO3_OUT BIT(9)
#define CM6206_REG1_GPIO3_OE BIT(8)
#define CM6206_REG1_GPIO2_OUT BIT(7)
#define CM6206_REG1_GPIO2_OE BIT(6)
#define CM6206_REG1_GPIO1_OUT BIT(5)
#define CM6206_REG1_GPIO1_OE BIT(4)
#define CM6206_REG1_SPDIFO_INVALID BIT(3)
#define CM6206_REG1_SPDIF_LOOP_EN BIT(2)
#define CM6206_REG1_SPDIFO_DIS BIT(1)
#define CM6206_REG1_SPDIFI_MIX BIT(0)

#define CM6206_REG2_DRIVER_ON BIT(15)
#define CM6206_REG2_HEADP_SEL_SIDE_CHANNELS (0 << 13)
#define CM6206_REG2_HEADP_SEL_SURROUND_CHANNELS (1 << 13)
#define CM6206_REG2_HEADP_SEL_CENTER_SUBW (2 << 13)
#define CM6206_REG2_HEADP_SEL_FRONT_CHANNELS (3 << 13)
#define CM6206_REG2_MUTE_HEADPHONE_RIGHT BIT(12)
#define CM6206_REG2_MUTE_HEADPHONE_LEFT BIT(11)
#define CM6206_REG2_MUTE_REAR_SURROUND_RIGHT BIT(10)
#define CM6206_REG2_MUTE_REAR_SURROUND_LEFT BIT(9)
#define CM6206_REG2_MUTE_SIDE_SURROUND_RIGHT BIT(8)
#define CM6206_REG2_MUTE_SIDE_SURROUND_LEFT BIT(7)
#define CM6206_REG2_MUTE_SUBWOOFER BIT(6)
#define CM6206_REG2_MUTE_CENTER BIT(5)
#define CM6206_REG2_MUTE_RIGHT_FRONT BIT(4)
#define CM6206_REG2_MUTE_LEFT_FRONT BIT(3)
#define CM6206_REG2_EN_BTL BIT(2)
#define CM6206_REG2_MCUCLKSEL_1_5_MHZ (0)
#define CM6206_REG2_MCUCLKSEL_3_MHZ (1)
#define CM6206_REG2_MCUCLKSEL_6_MHZ (2)
#define CM6206_REG2_MCUCLKSEL_12_MHZ (3)

/* Bit 11..13 sets the sensitivity to FLY tuner volume control VP/VD signal */
#define CM6206_REG3_FLYSPEED_DEFAULT (2 << 11)
#define CM6206_REG3_VRAP25EN BIT(10)
#define CM6206_REG3_MSEL1 BIT(9)
#define CM6206_REG3_SPDIFI_RATE_44_1K BIT(0 << 7)
#define CM6206_REG3_SPDIFI_RATE_48K BIT(2 << 7)
#define CM6206_REG3_SPDIFI_RATE_32K BIT(3 << 7)
#define CM6206_REG3_PINSEL BIT(6)
#define CM6206_REG3_FOE BIT(5)
#define CM6206_REG3_ROE BIT(4)
#define CM6206_REG3_CBOE BIT(3)
#define CM6206_REG3_LOSE BIT(2)
#define CM6206_REG3_HPOE BIT(1)
#define CM6206_REG3_SPDIFI_CANREC BIT(0)

#define CM6206_REG5_DA_RSTN BIT(13)
#define CM6206_REG5_AD_RSTN BIT(12)
#define CM6206_REG5_SPDIFO_AD2SPDO BIT(12)
#define CM6206_REG5_SPDIFO_SEL_FRONT (0 << 9)
#define CM6206_REG5_SPDIFO_SEL_SIDE_SUR (1 << 9)
#define CM6206_REG5_SPDIFO_SEL_CEN_LFE (2 << 9)
#define CM6206_REG5_SPDIFO_SEL_REAR_SUR (3 << 9)
#define CM6206_REG5_CODECM BIT(8)
#define CM6206_REG5_EN_HPF BIT(7)
#define CM6206_REG5_T_SEL_DSDA4 BIT(6)
#define CM6206_REG5_T_SEL_DSDA3 BIT(5)
#define CM6206_REG5_T_SEL_DSDA2 BIT(4)
#define CM6206_REG5_T_SEL_DSDA1 BIT(3)
#define CM6206_REG5_T_SEL_DSDAD_NORMAL 0
#define CM6206_REG5_T_SEL_DSDAD_FRONT 4
#define CM6206_REG5_T_SEL_DSDAD_S_SURROUND 5
#define CM6206_REG5_T_SEL_DSDAD_CEN_LFE 6
#define CM6206_REG5_T_SEL_DSDAD_R_SURROUND 7
// Sundry USB defines
#define USB_DIR_OUT			0		/* to device */
#define USB_REQ_SET_CONFIGURATION	0x09
#define USB_TYPE_CLASS			(0x01 << 5)
#define USB_RECIP_ENDPOINT		0x02

static struct usb_device *device_init(void)
{
    struct usb_bus *usb_bus;
    struct usb_device *dev;

    usb_init();
    usb_find_busses();
    usb_find_devices();

    for (usb_bus = usb_busses;
         usb_bus;
         usb_bus = usb_bus->next) {
        for (dev = usb_bus->devices;
             dev;
             dev = dev->next) {
            if ((dev->descriptor.idVendor
                  == LED_VENDOR_ID) &&
                (dev->descriptor.idProduct
                  == LED_PRODUCT_ID))
                return dev;
        }
    }
    return NULL;
}

/*
 * C-Media CM106/CM106+ have four 16-bit internal registers that are nicely
 * documented in the device's data sheet.
 */
static int snd_usb_cm106_write_int_reg(usb_dev_handle *handle, int reg, __u16 value)
{
	char buf[4];
	buf[0] = 0x20;
	buf[1] = value & 0xff;
	buf[2] = (value >> 8) & 0xff;
	buf[3] = reg;
	//https://sourceforge.net/p/libusb-win32/wiki/Documentation/
	return usb_control_msg(handle, 					// handle
		USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT,	// Request type
		USB_REQ_SET_CONFIGURATION,  				// Request
		0, 								// value
		0, 								// index
		buf,								// payload
		4,
		5000);								// length
}

static int snd_usb_cm106_read_int_reg(usb_dev_handle *handle, int reg, __u16 value)
{
	char buf[4];
	buf[0] = 0x30;
	buf[1] = value & 0xff;
	buf[2] = (value >> 8) & 0xff;
	buf[3] = reg;
	//https://sourceforge.net/p/libusb-win32/wiki/Documentation/
	return usb_control_msg(handle, 					// handle
		USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT,	// Request type
		USB_REQ_SET_CONFIGURATION,  				// Request
		0, 								// value
		0, 								// index
		buf,								// payload
		4,
		5000);								// length
}

int main(int argc, char **argv)
{
    struct usb_device *usb_dev;
    struct usb_dev_handle *usb_handle;
    int retval = 1;
    int i, rc, sel = 0;
    int val;
    char b[4];

    usb_set_debug(4);

    if(argc < 2)
    {
        fprintf(stderr, "Set CM6206 SPDIFOUT. Usage: setspdif n, where n is one of 0 - front, 1 - side surround, 2 - center, 3 - rear surround\n");
        return 0;
    }

    usb_dev = device_init();
    if (usb_dev == NULL) {
        fprintf(stderr, "CM6206 Device not found\n");
        goto exit;
    }

    usb_handle = usb_open(usb_dev);
    if (usb_handle == NULL) {
        fprintf(stderr, "Cannot open CM6206\n");
        goto exit;
    }

    usb_handle = usb_open(usb_dev);
    if (usb_handle == NULL) {
        fprintf(stderr,
             "Not able to claim the CM6206 USB device\n");
        goto exit;
    }

    if( argc == 2) sel = atoi(argv[1]);
    sel = sel << 9;
    fprintf(stderr, "Selector is %X\n", sel);
     

/* REG5: de-assert AD/DA reset signals */
    val = CM6206_REG5_DA_RSTN | CM6206_REG5_AD_RSTN | sel;// | CM6206_REG5_SPDIFO_SEL_SIDE_SUR; // | CM6206_REG5_SPDIFO_SEL_CEN_LFE;

    rc = snd_usb_cm106_write_int_reg(usb_handle, 5, val);
        fprintf(stderr,
             "Wrote %X to register 5, rc = %d\n", val, rc);

    retval = 0;

exit:
    usb_close(usb_handle);
    return retval;
}
1 Like