[ALSA] oxygen: add 192 kHz SPDIF input support
Clemens Ladisch [Mon, 21 Jan 2008 07:53:30 +0000 (08:53 +0100)]
Change the oxygen_spdif_input_bits_changed() function so that clock
changes on the SPDIF input are correctly detected.  This means that
sample rates greater than 96 kHz are now supported.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Jaroslav Kysela <perex@perex.cz>

sound/pci/oxygen/oxygen_lib.c
sound/pci/oxygen/oxygen_pcm.c

index 0927e04..49eabda 100644 (file)
@@ -74,7 +74,9 @@ static irqreturn_t oxygen_interrupt(int dummy, void *dev_id)
        if (status & OXYGEN_INT_SPDIF_IN_DETECT) {
                spin_lock(&chip->reg_lock);
                i = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL);
-               if (i & OXYGEN_SPDIF_RATE_INT) {
+               if (i & (OXYGEN_SPDIF_SENSE_INT | OXYGEN_SPDIF_LOCK_INT |
+                        OXYGEN_SPDIF_RATE_INT)) {
+                       /* write the interrupt bit(s) to clear */
                        oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, i);
                        schedule_work(&chip->spdif_input_bits_work);
                }
@@ -94,30 +96,46 @@ static void oxygen_spdif_input_bits_changed(struct work_struct *work)
 {
        struct oxygen *chip = container_of(work, struct oxygen,
                                           spdif_input_bits_work);
+       u32 reg;
 
-       spin_lock_irq(&chip->reg_lock);
-       oxygen_write32_masked(chip, OXYGEN_SPDIF_CONTROL,
-                             OXYGEN_SPDIF_IN_CLOCK_96,
-                             OXYGEN_SPDIF_IN_CLOCK_MASK);
-       spin_unlock_irq(&chip->reg_lock);
+       /*
+        * This function gets called when there is new activity on the SPDIF
+        * input, or when we lose lock on the input signal, or when the rate
+        * changes.
+        */
        msleep(1);
-       if (!(oxygen_read32(chip, OXYGEN_SPDIF_CONTROL)
-             & OXYGEN_SPDIF_LOCK_STATUS)) {
-               spin_lock_irq(&chip->reg_lock);
-               oxygen_write32_masked(chip, OXYGEN_SPDIF_CONTROL,
-                                     OXYGEN_SPDIF_IN_CLOCK_192,
-                                     OXYGEN_SPDIF_IN_CLOCK_MASK);
+       spin_lock_irq(&chip->reg_lock);
+       reg = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL);
+       if ((reg & (OXYGEN_SPDIF_SENSE_STATUS |
+                   OXYGEN_SPDIF_LOCK_STATUS))
+           == OXYGEN_SPDIF_SENSE_STATUS) {
+               /*
+                * If we detect activity on the SPDIF input but cannot lock to
+                * a signal, the clock bit is likely to be wrong.
+                */
+               reg ^= OXYGEN_SPDIF_IN_CLOCK_MASK;
+               oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, reg);
                spin_unlock_irq(&chip->reg_lock);
                msleep(1);
-               if (!(oxygen_read32(chip, OXYGEN_SPDIF_CONTROL)
-                     & OXYGEN_SPDIF_LOCK_STATUS)) {
-                       spin_lock_irq(&chip->reg_lock);
-                       oxygen_write32_masked(chip, OXYGEN_SPDIF_CONTROL,
-                                             OXYGEN_SPDIF_IN_CLOCK_96,
-                                             OXYGEN_SPDIF_IN_CLOCK_MASK);
-                       spin_unlock_irq(&chip->reg_lock);
+               spin_lock_irq(&chip->reg_lock);
+               reg = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL);
+               if ((reg & (OXYGEN_SPDIF_SENSE_STATUS |
+                           OXYGEN_SPDIF_LOCK_STATUS))
+                   == OXYGEN_SPDIF_SENSE_STATUS) {
+                       /* nothing detected with either clock; give up */
+                       if ((reg & OXYGEN_SPDIF_IN_CLOCK_MASK)
+                           == OXYGEN_SPDIF_IN_CLOCK_192) {
+                               /*
+                                * Reset clock to <= 96 kHz because this is
+                                * more likely to be received next time.
+                                */
+                               reg &= ~OXYGEN_SPDIF_IN_CLOCK_MASK;
+                               reg |= OXYGEN_SPDIF_IN_CLOCK_96;
+                               oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, reg);
+                       }
                }
        }
+       spin_unlock_irq(&chip->reg_lock);
 
        if (chip->controls[CONTROL_SPDIF_INPUT_BITS]) {
                spin_lock_irq(&chip->reg_lock);
@@ -126,6 +144,10 @@ static void oxygen_spdif_input_bits_changed(struct work_struct *work)
                               chip->interrupt_mask);
                spin_unlock_irq(&chip->reg_lock);
 
+               /*
+                * We don't actually know that any channel status bits have
+                * changed, but let's send a notification just to be sure.
+                */
                snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
                               &chip->controls[CONTROL_SPDIF_INPUT_BITS]->id);
        }
@@ -225,7 +247,20 @@ static void __devinit oxygen_init(struct oxygen *chip)
                       OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST |
                       OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 |
                       OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
-       oxygen_set_bits32(chip, OXYGEN_SPDIF_CONTROL, OXYGEN_SPDIF_RATE_MASK);
+       oxygen_write32_masked(chip, OXYGEN_SPDIF_CONTROL,
+                             OXYGEN_SPDIF_SENSE_MASK |
+                             OXYGEN_SPDIF_LOCK_MASK |
+                             OXYGEN_SPDIF_RATE_MASK |
+                             OXYGEN_SPDIF_LOCK_PAR |
+                             OXYGEN_SPDIF_IN_CLOCK_96,
+                             OXYGEN_SPDIF_OUT_ENABLE |
+                             OXYGEN_SPDIF_LOOPBACK |
+                             OXYGEN_SPDIF_SENSE_MASK |
+                             OXYGEN_SPDIF_LOCK_MASK |
+                             OXYGEN_SPDIF_RATE_MASK |
+                             OXYGEN_SPDIF_SENSE_PAR |
+                             OXYGEN_SPDIF_LOCK_PAR |
+                             OXYGEN_SPDIF_IN_CLOCK_MASK);
        oxygen_write32(chip, OXYGEN_SPDIF_OUTPUT_BITS, chip->spdif_bits);
        oxygen_write16(chip, OXYGEN_PLAY_ROUTING,
                       OXYGEN_PLAY_MULTICH_I2S_DAC | OXYGEN_PLAY_SPDIF_SPDIF |
index 31b0ccd..2002900 100644 (file)
@@ -85,12 +85,16 @@ static struct snd_pcm_hardware oxygen_hardware[PCM_COUNT] = {
                        SNDRV_PCM_INFO_SYNC_START,
                .formats = SNDRV_PCM_FMTBIT_S16_LE |
                           SNDRV_PCM_FMTBIT_S32_LE,
-               .rates = SNDRV_PCM_RATE_44100 |
+               .rates = SNDRV_PCM_RATE_32000 |
+                        SNDRV_PCM_RATE_44100 |
                         SNDRV_PCM_RATE_48000 |
+                        SNDRV_PCM_RATE_64000 |
                         SNDRV_PCM_RATE_88200 |
-                        SNDRV_PCM_RATE_96000,
-               .rate_min = 44100,
-               .rate_max = 96000,
+                        SNDRV_PCM_RATE_96000 |
+                        SNDRV_PCM_RATE_176400 |
+                        SNDRV_PCM_RATE_192000,
+               .rate_min = 32000,
+               .rate_max = 192000,
                .channels_min = 2,
                .channels_max = 2,
                .buffer_bytes_max = 256 * 1024,