I've cleaned up the little hacking I've did here and there this week. The patch works against build 1015 (CDDL). Comments about code quality would be nice, since I'm jumping into cold water here in regards to writing C.
Supports front output on the Xonar DX only. I'll be looking into the other outputs somewhen next week. I'm using it right now and works fine for simple stereo playback. -mg
--- cmi8788.orig 2008-04-13 16:52:11.163003622 +0200 +++ cmi8788.c 2008-04-13 16:56:49.779174247 +0200 @@ -19,6 +19,7 @@ #define CMEDIA_VENDOR_ID 0x13F6 #define CMEDIA_CMI8788 0x8788 + /* * CM8338 registers definition */ @@ -96,6 +97,19 @@ #define CODEC_VERSION (devc->base+0xE4) #define CTRL_VERSION (devc->base+0xE6) +/* Device IDs */ +#define ASUS_VENDOR_ID 0x1043 // Is it? I see it all over prtconf. + // Then again, I'm testing on an Asus board. +#define SUBID_XONAR_D2 0x8269 +#define SUBID_XONAR_D2X 0x82b7 +#define SUBID_XONAR_DX 0x8275 + +#define SUBID_GENERIC 0x0000 + +/* Xonar specific */ +#define XONAR_DX_FRONTDAC 0x9e +#define XONAR_DX_GPIO_OUTPUT 0x01 + /* defs for AKM 4396 DAC */ #define AK4396_CTL1 0x00 #define AK4396_CTL2 0x01 @@ -103,6 +117,37 @@ #define AK4396_LchATTCtl 0x03 #define AK4396_RchATTCtl 0x04 +/* defs for CS4398 DAC */ +#define CS4398_CHIP_ID 0x01 +#define CS4398_MODE_CTRL 0x02 +#define CS4398_MIXING 0x03 +#define CS4398_MUTE_CTRL 0x04 +#define CS4398_VOLA 0x05 +#define CS4398_VOLB 0x06 +#define CS4398_RAMP_CTRL 0x07 +#define CS4398_MISC_CTRL 0x08 +#define CS4398_MISC2_CTRL 0x09 + +#define CS4398_POWER_DOWN (1<<7) // Obvious +#define CS4398_CPEN (1<<6) // Control Port Enable +#define CS4398_FREEZE (1<<5) // Freezes registers, unfreeze to + // accept changed registers +#define CS4398_MCLKDIV2 (1<<4) // Divide MCLK by 2 +#define CS4398_MCLKDIV3 (1<<3) // Divive MCLK by 3 +#define CS4398_I2S (1<<4) // Set I2S mode + +/* Alias only for now. */ +#define CS4398_WRITE(devc, codec, reg, val) \ + two_wire_write(devc, codec, reg, val) + +/* 0-100. Start at -96dB. */ +#define CS4398_VOL(x) \ + ((x) == 0 ? 0xFF : (0xC0 - ((x)*192/100))) + +/* Xonar DX */ +#define XONAR_DX_OUTPUT 0x0001 +#define XONAR_ADDR_FRONT 0x9e + #define UNUSED_CMI9780_CONTROLS ( \ SOUND_MASK_VOLUME | \ SOUND_MASK_PCM | \ @@ -165,7 +210,8 @@ volatile unsigned char input_byte; int midi_dev; int mpu_attached; - + /* Card model */ + int card_model; } cmi8788_devc; @@ -277,32 +323,39 @@ return 1; } -#if 0 static int -two_wire_write (void *devc_, int codec_num, unsigned char reg, - unsigned int data) +two_wire_write (void *devc_, unsigned char codec_num, unsigned char reg, + unsigned char data) { cmi8788_devc *devc = devc_; oss_native_word flags; unsigned char status; + int count = 50; + + /* Wait for it to stop being busy */ + while((INW(devc->osdev, TWO_WIRE_CTRL) & 0x1) && (count > 0)) + { + oss_udelay(10); + count--; + } + if(count == 0) + { + cmn_err(CE_WARN, "Time out on Two-Wire interface (busy)."); + return -EIO; + } MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags); - status = INW (devc->osdev, TWO_WIRE_CTRL); - if (status & 0x1) - { - cmn_err (CE_WARN, "Two-Wire interface busy\n"); - return -EIO; - } /* first write the Register Address into the MAP register */ OUTB (devc->osdev, reg, TWO_WIRE_MAP); /* now write the data */ - OUTW (devc->osdev, data, TWO_WIRE_DATA); + OUTB (devc->osdev, data, TWO_WIRE_DATA); /* select the codec number to address */ - OUTB (devc->osdev, codec_num << 1 | 0x1, TWO_WIRE_ADDR); - + OUTB (devc->osdev, codec_num, TWO_WIRE_ADDR); + oss_udelay(100); + MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); return 1; @@ -314,7 +367,64 @@ vol = mix_cvt[vol]; return (vol * ((1 << bits) - 1) / 100); } -#endif + +static int +cs4398_init (void *devc_, int codec_) +{ + cmi8788_devc *devc = devc_; + + DDB(cmn_err(CE_WARN, "Initializing CS4398 on address %2x", codec_)); + + // Fast Two-Wire. Reduces the wire ready time. + OUTW(devc->osdev, 0x0100, TWO_WIRE_CTRL); + + // Power down, enable control mode. + CS4398_WRITE(devc_, codec_, CS4398_MISC_CTRL, + CS4398_CPEN | CS4398_POWER_DOWN); + // Left justified PCM (DAC and 8788 support I2S, but doesn't work). + CS4398_WRITE(devc_, codec_, CS4398_MODE_CTRL, 0); + // That's the DAC default, set anyway. + CS4398_WRITE(devc_, codec_, 3, 0x09); + // PCM auto-mute. + CS4398_WRITE(devc_, codec_, 4, 0x82); + // Vol A+B to -64dB. + CS4398_WRITE(devc_, codec_, 5, 0x80); + CS4398_WRITE(devc_, codec_, 6, 0x80); + // Soft-ramping. + CS4398_WRITE(devc_, codec_, 7, 0xF0); + // Remove power down flag. + CS4398_WRITE(devc_, codec_, CS4398_MISC_CTRL, CS4398_CPEN); + + return 1; +} + +static int +cs4398_cleanup(void * devc_, int codec_) +{ + cmi8788_devc *devc = devc_; + + /* Simply power down. Keep control port mode up. */ + CS4398_WRITE(devc_, codec_, CS4398_MISC_CTRL, + CS4398_POWER_DOWN | CS4398_CPEN); + + return 1; +} + +static void +xonar_dx_set_play_volume(cmi8788_devc * devc, int codec_id, int value) +{ + unsigned char vol; + + switch(codec_id) + { + case 0: + vol = CS4398_VOL(value & 0x00FF); + CS4398_WRITE(devc, XONAR_DX_FRONTDAC, CS4398_VOLA, vol); + vol = CS4398_VOL((value & 0xFF00) >> 8); + CS4398_WRITE(devc, XONAR_DX_FRONTDAC, CS4398_VOLB, vol); + break; + } +} static int cmi8788_set_play_volume (cmi8788_devc * devc, int codec_id, int value) @@ -327,13 +437,21 @@ devc->playvol[codec_id] = left | (right << 8); - data[0] = left; - data[1] = AK4396_LchATTCtl | 0x20; - spi_write (devc, codec_id, data); - - data[0] = right; - data[1] = AK4396_RchATTCtl | 0x20; - spi_write (devc, codec_id, data); + switch(devc->card_model) + { + case SUBID_XONAR_DX: + xonar_dx_set_play_volume(devc, codec_id, value); + break; + default: + /* Assume default AKM DACs */ + data[0] = left; + data[1] = AK4396_LchATTCtl | 0x20; + spi_write (devc, codec_id, data); + data[0] = right; + data[1] = AK4396_RchATTCtl | 0x20; + spi_write (devc, codec_id, data); + break; + } return devc->playvol[codec_id]; } @@ -1937,6 +2055,7 @@ init_cmi8788 (cmi8788_devc * devc) { unsigned short sVal; + unsigned short sDac; unsigned char bVal; int i, first_dev = -1, count; int default_vol; @@ -1952,11 +2071,37 @@ } bVal = INB (devc->osdev, FUNCTION); - bVal |= 0x82; /*reset codec */ + bVal |= 0x02; /* Reset codec*/ + + if(devc->card_model == SUBID_XONAR_DX) + { + /* Two-Wire communication */ + bVal |= 0x40; + } + else + { + /* SPI communication */ + bVal |= 0x80; + } + OUTB (devc->osdev, bVal, FUNCTION); + /* I2S to 16bit, see below. */ + sDac = 0x010A; + + /* Non-generic DAC initialization */ + switch(devc->card_model) + { + case SUBID_XONAR_DX: + /* Front DAC only for now. */ + cs4398_init(devc, XONAR_DX_FRONTDAC); + /* Must set master clock. */ + sDac |= 0x0010; + break; + } + /* Setup I2S to use 16bit instead of 24Bit */ - OUTW (devc->osdev, 0x010A, I2S_MULTICH_FORMAT); + OUTW (devc->osdev, sDac, I2S_MULTICH_FORMAT); OUTW (devc->osdev, 0x010A, I2S_ADC1_FORMAT); OUTW (devc->osdev, 0x010A, I2S_ADC2_FORMAT); OUTW (devc->osdev, 0x010A, I2S_ADC3_FORMAT); @@ -2170,6 +2315,17 @@ cmi8788_mixer_ioctl (devc->spi_mixer_dev, first_dev, MIXER_WRITE (SOUND_MIXER_SIDEVOL), &default_vol); + /* Enable Xonar output */ + unsigned short output_enable; + switch(devc->card_model) + { + case SUBID_XONAR_DX: + output_enable = XONAR_DX_OUTPUT; + break; + } + OUTW(devc->osdev, output_enable, GPIO_CONTROL); + OUTW(devc->osdev, output_enable, GPIO_DATA); + return 1; } @@ -2179,12 +2335,15 @@ unsigned char pci_irq_line, pci_revision; unsigned short pci_command, vendor, device; unsigned int pci_ioaddr; + unsigned short sub_vendor, sub_id; int err; cmi8788_devc *devc; DDB (cmn_err (CE_CONT, "Entered CMEDIA CMI8788 attach routine\n")); pci_read_config_word (osdev, PCI_VENDOR_ID, &vendor); pci_read_config_word (osdev, PCI_DEVICE_ID, &device); + pci_read_config_word (osdev, PCI_SUBSYSTEM_VENDOR_ID, &sub_vendor); + pci_read_config_word (osdev, PCI_SUBSYSTEM_ID, &sub_id); if (vendor != CMEDIA_VENDOR_ID || device != CMEDIA_CMI8788) return 0; @@ -2214,6 +2373,31 @@ return 0; } + /* Detect device */ + if(sub_vendor == ASUS_VENDOR_ID) + { + switch(sub_id) + { + case SUBID_XONAR_DX: + DDB(cmn_err(CE_WARN, "Detected Asus Xonar DX PCIe card.\n")); + break; + case SUBID_XONAR_D2: + DDB(cmn_err(CE_WARN, "Detected Asus Xonar D2 PCI card (not yet supported).\n")); + break; + case SUBID_XONAR_D2X: + DDB(cmn_err(CE_WARN, "Detected Asus Xonar D2X PCIe card (not yet supported).\n")); + break; + default: + DDB(cmn_err(CE_WARN, "Unknown Asus sound card.\n")); + sub_id = SUBID_GENERIC; + break; + } + devc->card_model = sub_id; + } + else + { + devc->card_model = SUBID_GENERIC; + } devc->osdev = osdev; osdev->devc = devc;
_______________________________________________ oss-devel mailing list oss-devel@mailman.opensound.com http://mailman.opensound.com/mailman/listinfo/oss-devel