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

Reply via email to