ALSA: hda: option to enable arbitrary buffer/period sizes
Pierre-Louis Bossart [Thu, 4 Aug 2011 15:12:56 +0000 (10:12 -0500)]
Add new parameter to disable rounding of buffer/period sizes to
multiples of 128 bytes. This is more efficient in terms of memory
access but isn't required by the HDA spec and prevents users from
specifying exact period/buffer sizes. For example for 44.1kHz, a
period size set to 20ms will be rounded to 19.59ms.

Tested and enabled on Intel HDA controllers. Option is disabled by
default for other controllers.

Tested-by: Wu Fengguang <fengguang.wu@intel.com>
Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>

Documentation/sound/alsa/ALSA-Configuration.txt
sound/pci/hda/hda_intel.c

index 8975701..27126c4 100644 (file)
@@ -886,6 +886,11 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
                disable)
     power_save_controller - Reset HD-audio controller in power-saving mode
                (default = on)
+    align_buffer_size - Force rounding of buffer/period sizes to multiples
+                     of 128 bytes. This is more efficient in terms of memory
+                     access but isn't required by the HDA spec and prevents
+                     users from specifying exact period/buffer sizes.
+                     (default = on)
 
     This module supports multiple cards and autoprobe.
     
index be69822..2a8bed9 100644 (file)
@@ -116,6 +116,11 @@ module_param(power_save_controller, bool, 0644);
 MODULE_PARM_DESC(power_save_controller, "Reset controller in power save mode.");
 #endif
 
+static int align_buffer_size = 1;
+module_param(align_buffer_size, bool, 0644);
+MODULE_PARM_DESC(align_buffer_size,
+               "Force buffer and period sizes to be multiple of 128 bytes.");
+
 MODULE_LICENSE("GPL");
 MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
                         "{Intel, ICH6M},"
@@ -481,6 +486,7 @@ enum {
 #define AZX_DCAPS_NO_64BIT     (1 << 18)       /* No 64bit address */
 #define AZX_DCAPS_SYNC_WRITE   (1 << 19)       /* sync each cmd write */
 #define AZX_DCAPS_OLD_SSYNC    (1 << 20)       /* Old SSYNC reg for ICH */
+#define AZX_DCAPS_BUFSIZE      (1 << 21)       /* no buffer size alignment */
 
 /* quirks for ATI SB / AMD Hudson */
 #define AZX_DCAPS_PRESET_ATI_SB \
@@ -1599,6 +1605,7 @@ static int azx_pcm_open(struct snd_pcm_substream *substream)
        struct snd_pcm_runtime *runtime = substream->runtime;
        unsigned long flags;
        int err;
+       int buff_step;
 
        mutex_lock(&chip->open_mutex);
        azx_dev = azx_assign_device(chip, substream);
@@ -1613,10 +1620,25 @@ static int azx_pcm_open(struct snd_pcm_substream *substream)
        runtime->hw.rates = hinfo->rates;
        snd_pcm_limit_hw_rates(runtime);
        snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+       if (align_buffer_size)
+               /* constrain buffer sizes to be multiple of 128
+                  bytes. This is more efficient in terms of memory
+                  access but isn't required by the HDA spec and
+                  prevents users from specifying exact period/buffer
+                  sizes. For example for 44.1kHz, a period size set
+                  to 20ms will be rounded to 19.59ms. */
+               buff_step = 128;
+       else
+               /* Don't enforce steps on buffer sizes, still need to
+                  be multiple of 4 bytes (HDA spec). Tested on Intel
+                  HDA controllers, may not work on all devices where
+                  option needs to be disabled */
+               buff_step = 4;
+
        snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
-                                  128);
+                                  buff_step);
        snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
-                                  128);
+                                  buff_step);
        snd_hda_power_up(apcm->codec);
        err = hinfo->ops.open(hinfo, apcm->codec, substream);
        if (err < 0) {
@@ -2616,6 +2638,10 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
                gcap &= ~ICH6_GCAP_64OK;
        }
 
+       /* disable buffer size rounding to 128-byte multiples if supported */
+       if (chip->driver_caps & AZX_DCAPS_BUFSIZE)
+               align_buffer_size = 0;
+
        /* allow 64bit DMA address if supported by H/W */
        if ((gcap & ICH6_GCAP_64OK) && !pci_set_dma_mask(pci, DMA_BIT_MASK(64)))
                pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(64));
@@ -2817,37 +2843,49 @@ static void __devexit azx_remove(struct pci_dev *pci)
 static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
        /* CPT */
        { PCI_DEVICE(0x8086, 0x1c20),
-         .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP },
+         .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP |
+         AZX_DCAPS_BUFSIZE },
        /* PBG */
        { PCI_DEVICE(0x8086, 0x1d20),
-         .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP },
+         .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP |
+         AZX_DCAPS_BUFSIZE},
        /* Panther Point */
        { PCI_DEVICE(0x8086, 0x1e20),
-         .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP },
+         .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP |
+         AZX_DCAPS_BUFSIZE},
        /* SCH */
        { PCI_DEVICE(0x8086, 0x811b),
-         .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_SCH_SNOOP },
+         .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_SCH_SNOOP |
+         AZX_DCAPS_BUFSIZE},
        { PCI_DEVICE(0x8086, 0x2668),
-         .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC },  /* ICH6 */
+         .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC |
+         AZX_DCAPS_BUFSIZE },  /* ICH6 */
        { PCI_DEVICE(0x8086, 0x27d8),
-         .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC },  /* ICH7 */
+         .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC |
+         AZX_DCAPS_BUFSIZE },  /* ICH7 */
        { PCI_DEVICE(0x8086, 0x269a),
-         .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC },  /* ESB2 */
+         .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC |
+         AZX_DCAPS_BUFSIZE },  /* ESB2 */
        { PCI_DEVICE(0x8086, 0x284b),
-         .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC },  /* ICH8 */
+         .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC |
+         AZX_DCAPS_BUFSIZE },  /* ICH8 */
        { PCI_DEVICE(0x8086, 0x293e),
-         .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC },  /* ICH9 */
+         .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC |
+         AZX_DCAPS_BUFSIZE },  /* ICH9 */
        { PCI_DEVICE(0x8086, 0x293f),
-         .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC },  /* ICH9 */
+         .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC |
+         AZX_DCAPS_BUFSIZE },  /* ICH9 */
        { PCI_DEVICE(0x8086, 0x3a3e),
-         .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC },  /* ICH10 */
+         .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC |
+         AZX_DCAPS_BUFSIZE },  /* ICH10 */
        { PCI_DEVICE(0x8086, 0x3a6e),
-         .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC },  /* ICH10 */
+         .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC |
+         AZX_DCAPS_BUFSIZE },  /* ICH10 */
        /* Generic Intel */
        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_ANY_ID),
          .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
          .class_mask = 0xffffff,
-         .driver_data = AZX_DRIVER_ICH },
+         .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_BUFSIZE },
        /* ATI SB 450/600/700/800/900 */
        { PCI_DEVICE(0x1002, 0x437b),
          .driver_data = AZX_DRIVER_ATI | AZX_DCAPS_PRESET_ATI_SB },