From: Juha Yrjola Date: Fri, 10 Feb 2006 14:13:09 +0000 (+0200) Subject: ARM: OMAP: Magic update to the AIC23 mixer driver X-Git-Tag: v2.6.16-omap1~75 X-Git-Url: http://pilppa.com/gitweb/?a=commitdiff_plain;h=e4bf29d8de75fbd4f3948988c9951ea0d501edba;p=linux-2.6-omap-h63xx.git ARM: OMAP: Magic update to the AIC23 mixer driver Signed-off-by: Juha Yrjölä --- diff --git a/drivers/i2c/chips/tlv320aic23.c b/drivers/i2c/chips/tlv320aic23.c index 097bf8f5a44..b62ea5318c9 100644 --- a/drivers/i2c/chips/tlv320aic23.c +++ b/drivers/i2c/chips/tlv320aic23.c @@ -1,7 +1,8 @@ /* * Texas Instrumens TLV320AIC23 audio codec's i2c interface. - * + * * Copyright (c) by Kai Svahn + * Copyright (c) by Jussi Laako * * 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 @@ -24,36 +25,65 @@ #include #include #include +#include +#include #include #include +#include -#define TLV320AIC23_VERSION "0.1" -#define TLV320AIC23_DATE "12-Aug-2004" +#define TLV320AIC23_VERSION "1.8" +#define TLV320AIC23_DATE "10-Feb-2006" +#define MAX_VOL 100 +#define MIN_VOL 0 +#define MAX_GAIN 100 +#define MIN_GAIN 0 +#define OUTPUT_VOLUME_MIN LHV_MIN +#define OUTPUT_VOLUME_MAX LHV_MAX +#define OUTPUT_VOLUME_RANGE (OUTPUT_VOLUME_MAX - OUTPUT_VOLUME_MIN) +#define INPUT_VOLUME_MIN LIV_MIN +#define INPUT_VOLUME_MAX LIV_MAX +#define INPUT_VOLUME_RANGE (INPUT_VOLUME_MAX - INPUT_VOLUME_MIN) /* I2C Addresses to scan */ -static unsigned short normal_i2c[] = { TLV320AIC23ID1, TLV320AIC23ID2, I2C_CLIENT_END }; +static unsigned short normal_i2c[] = { TLV320AIC23ID1, TLV320AIC23ID2, \ + I2C_CLIENT_END }; +/*static unsigned short normal_i2c_range[] = { I2C_CLIENT_END };*/ /* This makes all addr_data:s */ I2C_CLIENT_INSMOD; -static struct i2c_driver tlv320aic23_driver; +static struct i2c_driver tlv320aic23_driver; static struct i2c_client *new_client; -//static struct i2c_client *client; +static int selftest; + +static struct tlv320aic23_info { + u16 volume_reg_left; + u16 volume_reg_right; + u16 input_gain_reg_left; + u16 input_gain_reg_right; + u16 power; /* For POWER_DOWN_CONTROL_ADDR */ + u16 mask; /* For ANALOG_AUDIO_CONTROL_ADDR */ + int mic_loopback; + int mic_enable; + int sta; + int power_down; + int initialization; +} tlv320aic23_info_l; static int _tlv320aic23_write_value(struct i2c_client *client, u8 reg, u16 value) { u8 val, wreg; - + /* TLV320AIC23 has 7 bit address and 9 bits of data * so we need to switch one data bit into reg and rest * of data into val */ - + wreg = (reg << 1); val = (0x01 & (value >> 8)); wreg = (wreg | val); val = (0x00ff & value); - + return i2c_smbus_write_byte_data(client, wreg, val); } @@ -62,54 +92,55 @@ int tlv320aic23_write_value(u8 reg, u16 value) static struct i2c_client *client; client = new_client; _tlv320aic23_write_value(client, reg, value); - + return 0; } -static int tlv320aic23_detect_client(struct i2c_adapter *adapter, int address, +static int tlv320aic23_detect_client(struct i2c_adapter *adapter, int address, int kind) { int err = 0; - const char *client_name = "TLV320AIC23 Audio Codec"; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA | + const char *client_name = "TLV320AIC23 Audio Codec"; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_WRITE_BYTE)) { - printk(KERN_WARNING "%s functinality check failed\n", client_name); + printk(KERN_WARNING "%s functinality check failed\n", + client_name); return err; } - + if (!(new_client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL))) { err = -ENOMEM; - printk(KERN_WARNING "Couldn't allocate memory for %s\n", client_name); + printk(KERN_WARNING "Couldn't allocate memory for %s\n", + client_name); return err; } - + memset(new_client, 0x00, sizeof(struct i2c_client)); new_client->addr = address; new_client->adapter = adapter; new_client->driver = &tlv320aic23_driver; new_client->flags = 0; strlcpy(new_client->name, client_name, I2C_NAME_SIZE); - + if ((err = i2c_attach_client(new_client))) { - printk(KERN_WARNING "Couldn't attach %s\n", client_name); + printk(KERN_WARNING "Couldn't attach %s\n", client_name); kfree(new_client); return err; } - return 0; } - + static int tlv320aic23_detach_client(struct i2c_client *client) { int err; - + if ((err = i2c_detach_client(client))) { - printk("tlv320aic23.o: Client deregistration failed, client not detached.\n"); + printk("tlv320aic23.o: Client deregistration failed, \ + client not detached.\n"); return err; } - kfree(client); return 0; } @@ -117,48 +148,513 @@ static int tlv320aic23_detach_client(struct i2c_client *client) static int tlv320aic23_attach_adapter(struct i2c_adapter *adapter) { int res; - + res = i2c_probe(adapter, &addr_data, &tlv320aic23_detect_client); return res; } -/*-----------------------------------------------------------------------*/ - static struct i2c_driver tlv320aic23_driver = { .driver { - .name = "OMAP+TLV320AIC23 codec", + .name = "OMAP+TLV320AIC23 codec", + /*.flags = I2C_DF_NOTIFY,*/ }, - .id = I2C_DRIVERID_MISC, /* Experimental ID */ - .attach_adapter = tlv320aic23_attach_adapter, - .detach_client = tlv320aic23_detach_client, + .id = I2C_DRIVERID_MISC, /* Experimental ID */ + .attach_adapter = tlv320aic23_attach_adapter, + .detach_client = tlv320aic23_detach_client, }; /* - * INIT part + * Configures the McBSP3 which is used to send clock to the AIC23 codec. + * The input clock rate from DSP is 12MHz. + * The DSP clock must be on before this is called. */ - -static int __init tlv320aic23_init(void) +static int omap_mcbsp3_tlv320aic23_clock_init(void) { - int res; - struct i2c_client *client = client; + u16 w; + + /* enable 12MHz clock to mcbsp 1 & 3 */ + __raw_writew(__raw_readw(DSP_IDLECT2) | (1<<1), DSP_IDLECT2); + __raw_writew(__raw_readw(DSP_RSTCT2) | 1 | 1<<1, DSP_RSTCT2); + + /* disable sample rate generator */ + OMAP_MCBSP_WRITE(OMAP1610_MCBSP3_BASE, SPCR1, 0x0000); + OMAP_MCBSP_WRITE(OMAP1610_MCBSP3_BASE, SPCR2, 0x0000); + + /* pin control register */ + OMAP_MCBSP_WRITE(OMAP1610_MCBSP3_BASE, PCR0,(CLKXM | CLKXP | CLKRP)); + + /* configure srg to send 12MHz pulse from dsp peripheral clock */ + OMAP_MCBSP_WRITE(OMAP1610_MCBSP3_BASE, SRGR1, 0x0000); + OMAP_MCBSP_WRITE(OMAP1610_MCBSP3_BASE, SRGR2, CLKSM); + + /* enable sample rate generator */ + w = OMAP_MCBSP_READ(OMAP1610_MCBSP3_BASE, SPCR2); + OMAP_MCBSP_WRITE(OMAP1610_MCBSP3_BASE, SPCR2, (w | FREE | GRST)); + printk("Clock enabled to MCBSP1 & 3 \n"); - if ((res = i2c_add_driver(&tlv320aic23_driver))) { - printk("tlv320aic23 i2c: Driver registration failed, module not inserted.\n"); - return res; + return 0; +} + +static void update_volume_left(int volume) +{ + u16 val = 0; + val = ((volume * OUTPUT_VOLUME_RANGE) / 100) + OUTPUT_VOLUME_MIN; + tlv320aic23_write_value(LEFT_CHANNEL_VOLUME_ADDR, val); + tlv320aic23_info_l.volume_reg_left = volume; +} + +static void update_volume_right(int volume) +{ + u16 val = 0; + val = ((volume * OUTPUT_VOLUME_RANGE) / 100) + OUTPUT_VOLUME_MIN; + tlv320aic23_write_value(RIGHT_CHANNEL_VOLUME_ADDR, val); + tlv320aic23_info_l.volume_reg_right = volume; +} + +static void set_mic(int mic_en) +{ + u16 dg_ctrl; + + if (mic_en) { + tlv320aic23_info_l.power = OSC_OFF | LINE_OFF; + dg_ctrl = ADCHP_ON; + tlv320aic23_info_l.mask &= ~MICM_MUTED; + tlv320aic23_info_l.mask |= MICB_20DB; /* STE_ENABLED */ + } else { + tlv320aic23_info_l.power = + OSC_OFF | ADC_OFF | MIC_OFF | LINE_OFF; + dg_ctrl = 0x00; + tlv320aic23_info_l.mask = + DAC_SELECTED | INSEL_MIC | MICM_MUTED; } + tlv320aic23_write_value(POWER_DOWN_CONTROL_ADDR, + tlv320aic23_info_l.power); + tlv320aic23_write_value(DIGITAL_AUDIO_CONTROL_ADDR, dg_ctrl); + tlv320aic23_write_value(ANALOG_AUDIO_CONTROL_ADDR, + tlv320aic23_info_l.mask); + tlv320aic23_info_l.mic_enable = mic_en; + + printk(KERN_INFO "tlv320aic23 mic state: %i\n", mic_en); +} + +static void tlv320aic23_init_power(void) +{ + tlv320aic23_write_value(RESET_CONTROL_ADDR, 0x00); - printk("TLV320AIC23 I2C version %s (%s)\n", TLV320AIC23_VERSION, - TLV320AIC23_DATE); + if (tlv320aic23_info_l.initialization == 0) { + tlv320aic23_write_value(LEFT_CHANNEL_VOLUME_ADDR, LHV_MIN); + tlv320aic23_write_value(RIGHT_CHANNEL_VOLUME_ADDR, LHV_MIN); + } + else { + update_volume_left(tlv320aic23_info_l.volume_reg_left); + update_volume_right(tlv320aic23_info_l.volume_reg_right); + } + tlv320aic23_info_l.mask = DAC_SELECTED | INSEL_MIC | MICM_MUTED; + tlv320aic23_write_value(ANALOG_AUDIO_CONTROL_ADDR, + tlv320aic23_info_l.mask); + tlv320aic23_write_value(DIGITAL_AUDIO_CONTROL_ADDR, 0x00); + tlv320aic23_write_value(DIGITAL_AUDIO_FORMAT_ADDR, LRP_ON | FOR_DSP); + tlv320aic23_write_value(SAMPLE_RATE_CONTROL_ADDR, USB_CLK_ON); + tlv320aic23_write_value(DIGITAL_INTERFACE_ACT_ADDR, ACT_ON); + tlv320aic23_info_l.power = OSC_OFF | ADC_OFF | MIC_OFF | LINE_OFF; + tlv320aic23_write_value(POWER_DOWN_CONTROL_ADDR, + tlv320aic23_info_l.power); + + /* enable mic input */ + if (tlv320aic23_info_l.mic_enable) + set_mic(tlv320aic23_info_l.mic_enable); + + printk(KERN_INFO "tlv320aic23_init_power() done\n"); +} + +void tlv320aic23_power_down(void) +{ + printk("tlv320aic23 powering down\n"); + tlv320aic23_write_value(POWER_DOWN_CONTROL_ADDR, 0xff); + tlv320aic23_info_l.power_down = 1; +} + +void tlv320aic23_power_up(void) +{ + printk("tlv320aic23 powering up\n"); + tlv320aic23_init_power(); + tlv320aic23_info_l.power_down = 0; +} + +/*----------------------------------------------------------------------*/ +/* sysfs initializations */ +/*----------------------------------------------------------------------*/ + +static ssize_t store_volume_left(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + signed volume; + + sscanf(buf, "%i", &volume); + + if (volume < MIN_VOL) { + tlv320aic23_power_down(); + return count; + } else if (volume > MIN_VOL && tlv320aic23_info_l.power_down) { + tlv320aic23_info_l.volume_reg_left = volume; + tlv320aic23_power_up(); + return count; + } + if (volume > MAX_VOL) + volume = MAX_VOL; + + update_volume_left(volume); + return count; +} + +static ssize_t show_volume_left(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", tlv320aic23_info_l.volume_reg_left); +} + +static DEVICE_ATTR(volume_left, S_IRUGO | S_IWUGO, + show_volume_left, store_volume_left); + +static ssize_t store_volume_right(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + signed volume; + + sscanf(buf, "%i", &volume); + if (volume < MIN_VOL) { + tlv320aic23_power_down(); + return count; + } else if (volume > MIN_VOL && tlv320aic23_info_l.power_down) { + tlv320aic23_info_l.volume_reg_right = volume; + tlv320aic23_power_up(); + return count; + } + if (volume > MAX_VOL) + volume = MAX_VOL; + + update_volume_right(volume); + return count; +} + +static ssize_t show_volume_right(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", tlv320aic23_info_l.volume_reg_right); +} + +static DEVICE_ATTR(volume_right, S_IRUGO | S_IWUGO, + show_volume_right, store_volume_right); + +static ssize_t store_gain_left(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + u16 val = 0; + unsigned gain; + + sscanf(buf, "%u", &gain); + if (gain > MAX_VOL) + gain = MAX_VOL; + + val = ((gain * INPUT_VOLUME_RANGE) / 100) + INPUT_VOLUME_MIN; + tlv320aic23_write_value(LEFT_LINE_VOLUME_ADDR, val); + tlv320aic23_info_l.input_gain_reg_left = gain; + + return count; +} + +static ssize_t show_gain_left(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", tlv320aic23_info_l.input_gain_reg_left); +} + +static DEVICE_ATTR(gain_left, S_IRUGO | S_IWUSR, show_gain_left, + store_gain_left); + +static ssize_t store_gain_right(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + u16 val = 0; + unsigned gain; + + sscanf(buf, "%u", &gain); + if (gain > MAX_VOL) + gain = MAX_VOL; + + val = ((gain * INPUT_VOLUME_RANGE) / 100) + INPUT_VOLUME_MIN; + tlv320aic23_write_value(RIGHT_LINE_VOLUME_ADDR, val); + tlv320aic23_info_l.input_gain_reg_right = gain; + + return count; +} + +static ssize_t show_gain_right(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", tlv320aic23_info_l.input_gain_reg_right); +} + +static DEVICE_ATTR(gain_right, S_IRUGO | S_IWUSR, show_gain_right, + store_gain_right); + +static ssize_t store_mic_loopback(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int mic; + + sscanf(buf, "%i", &mic); + if (mic > 0) { + tlv320aic23_write_value(POWER_DOWN_CONTROL_ADDR, \ + OSC_OFF | ADC_OFF | LINE_OFF); + tlv320aic23_info_l.mask = STE_ENABLED | DAC_SELECTED \ + | INSEL_MIC | MICB_20DB; + tlv320aic23_write_value(ANALOG_AUDIO_CONTROL_ADDR, + tlv320aic23_info_l.mask); + mic = 1; + } + else { + tlv320aic23_write_value(POWER_DOWN_CONTROL_ADDR, \ + OSC_OFF | ADC_OFF | MIC_OFF | LINE_OFF); + mic = 0; + } + tlv320aic23_info_l.mic_loopback = mic; + + return count; +} + +static ssize_t show_mic_loopback(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%i\n", tlv320aic23_info_l.mic_loopback); +} + +static DEVICE_ATTR(mic_loopback, S_IRUGO | S_IWUSR, + show_mic_loopback, store_mic_loopback); + +static ssize_t store_st_attenuation(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned sta; + u16 tmp; + + sscanf(buf, "%u", &sta); + if (sta > 3) + sta = 3; + + tmp = tlv320aic23_info_l.mask; + tmp &= 0x3f; + + tlv320aic23_info_l.mask = tmp | STA_REG(sta); + tlv320aic23_write_value(ANALOG_AUDIO_CONTROL_ADDR, + tlv320aic23_info_l.mask); + tlv320aic23_info_l.sta = sta; + + return count; +} + +static ssize_t show_st_attenuation(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%i\n", tlv320aic23_info_l.sta); +} + +static DEVICE_ATTR(st_attenuation, S_IRUGO | S_IWUSR, + show_st_attenuation, store_st_attenuation); + +static ssize_t store_mic_enable(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int mic; + + sscanf(buf, "%i", &mic); + set_mic(mic); + + return count; +} + +static ssize_t show_mic_enable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%i\n", tlv320aic23_info_l.mic_enable); +} + +static DEVICE_ATTR(mic_enable, S_IRUGO | S_IWUSR, + show_mic_enable, store_mic_enable); + +static ssize_t show_audio_selftest(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%i\n", selftest); +} + +static DEVICE_ATTR(audio_selftest, S_IRUGO | S_IWUSR, + show_audio_selftest, NULL); + +static int audio_i2c_probe(struct platform_device *dev) +{ + int r; + + if ((r = device_create_file(&dev->dev, &dev_attr_volume_left)) != 0) + return r; + else if ((r = device_create_file(&dev->dev, + &dev_attr_volume_right)) != 0) + goto err_volume_left; + else if ((r = device_create_file(&dev->dev, + &dev_attr_gain_right)) != 0) + goto err_volume_right; + else if ((r = device_create_file(&dev->dev, + &dev_attr_gain_left)) != 0) + goto err_gain_right; + else if ((r = device_create_file(&dev->dev, + &dev_attr_mic_loopback)) != 0) + goto err_gain_left; + else if ((r = device_create_file(&dev->dev, + &dev_attr_mic_enable)) != 0) + goto err_mic_loopback; + else if ((r = device_create_file(&dev->dev, + &dev_attr_st_attenuation)) != 0) + goto err_mic_enable; + else if ((r = device_create_file(&dev->dev, + &dev_attr_audio_selftest)) != 0) + goto err_st_attenuation; + else + return r; + +err_st_attenuation: + device_remove_file(&dev->dev, &dev_attr_st_attenuation); +err_mic_enable: + device_remove_file(&dev->dev, &dev_attr_mic_enable); +err_mic_loopback: + device_remove_file(&dev->dev, &dev_attr_mic_loopback); +err_gain_left: + device_remove_file(&dev->dev, &dev_attr_gain_left); +err_gain_right: + device_remove_file(&dev->dev, &dev_attr_gain_right); +err_volume_right: + device_remove_file(&dev->dev, &dev_attr_volume_right); +err_volume_left: + device_remove_file(&dev->dev, &dev_attr_volume_left); + + return r; +} + +static int audio_i2c_remove(struct platform_device *dev) +{ + device_remove_file(&dev->dev, &dev_attr_st_attenuation); + device_remove_file(&dev->dev, &dev_attr_mic_enable); + device_remove_file(&dev->dev, &dev_attr_mic_loopback); + device_remove_file(&dev->dev, &dev_attr_gain_left); + device_remove_file(&dev->dev, &dev_attr_gain_right); + device_remove_file(&dev->dev, &dev_attr_volume_right); + device_remove_file(&dev->dev, &dev_attr_volume_left); + return 0; } +/*----------------------------------------------------------------*/ +/* PM functions */ +/*----------------------------------------------------------------*/ + +static void audio_i2c_shutdown(struct platform_device *dev) +{ + /* Let's mute the codec before powering off to prevent + * glitch in the sound + */ + tlv320aic23_write_value(LEFT_CHANNEL_VOLUME_ADDR, LHV_MIN); + tlv320aic23_write_value(RIGHT_CHANNEL_VOLUME_ADDR, LHV_MIN); + tlv320aic23_power_down(); +} + +static int audio_i2c_suspend(struct platform_device *dev, pm_message_t state) +{ + /* Let's mute the codec before powering off to prevent + * glitch in the sound + */ + tlv320aic23_write_value(LEFT_CHANNEL_VOLUME_ADDR, LHV_MIN); + tlv320aic23_write_value(RIGHT_CHANNEL_VOLUME_ADDR, LHV_MIN); + tlv320aic23_power_down(); + + return 0; +} + +static int audio_i2c_resume(struct platform_device *dev) +{ + tlv320aic23_power_up(); + + return 0; +} + +static struct platform_driver audio_i2c_driver = { + .driver { + .owner = THIS_MODULE, + .name = "audio-i2c", + }, + .shutdown = audio_i2c_shutdown, + .probe = audio_i2c_probe, + .remove = audio_i2c_remove, + .suspend = audio_i2c_suspend, + .resume = audio_i2c_resume, +}; + +static struct platform_device audio_i2c_device = { + .name = "audio-i2c", + .id = -1, +}; + +/*----------------------------------------------------------------*/ + +static int __init tlv320aic23_init(void) +{ + selftest = 0; + tlv320aic23_info_l.initialization = 0; + + if (i2c_add_driver(&tlv320aic23_driver)) { + printk("tlv320aic23 i2c: Driver registration failed, \ + module not inserted.\n"); + selftest= -ENODEV; + return selftest; + } + + if (platform_driver_register(&audio_i2c_driver)) { + printk(KERN_WARNING "Failed to register audio i2c driver\n"); + selftest = -ENODEV; + return selftest; + } + + if (platform_device_register(&audio_i2c_device)) { + printk(KERN_WARNING "Failed to register audio i2c device\n"); + platform_driver_unregister(&audio_i2c_driver); + selftest = -ENODEV; + return selftest; + } + omap_mcbsp3_tlv320aic23_clock_init(); + tlv320aic23_power_up(); + tlv320aic23_info_l.initialization = 1; + printk("TLV320AIC23 I2C version %s (%s)\n", + TLV320AIC23_VERSION, TLV320AIC23_DATE); + + return selftest; +} + static void __exit tlv320aic23_exit(void) { int res; + tlv320aic23_power_down(); if ((res = i2c_del_driver(&tlv320aic23_driver))) printk("tlv320aic23 i2c: Driver remove failed, module not removed.\n"); + + platform_device_unregister(&audio_i2c_device); + platform_driver_unregister(&audio_i2c_driver); } MODULE_AUTHOR("Kai Svahn "); @@ -169,3 +665,5 @@ module_init(tlv320aic23_init) module_exit(tlv320aic23_exit) EXPORT_SYMBOL(tlv320aic23_write_value); +EXPORT_SYMBOL(tlv320aic23_power_up); +EXPORT_SYMBOL(tlv320aic23_power_down); diff --git a/include/asm-arm/arch-omap/aic23.h b/include/asm-arm/arch-omap/aic23.h index 590bac25b7c..b46885f3a92 100644 --- a/include/asm-arm/arch-omap/aic23.h +++ b/include/asm-arm/arch-omap/aic23.h @@ -57,6 +57,7 @@ #define LHV_MIN 0x0000 // Analog audio path control register +#define STA_REG(x) ((x)<<6) #define STE_ENABLED 0x0020 #define DAC_SELECTED 0x0010 #define BYPASS_ON 0x0008