aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2008-06-10 11:53:20 -0400
committerJaroslav Kysela <perex@perex.cz>2008-06-13 10:30:20 -0400
commit675f25d4d3ff6501cbce608bcc2333a56ec4c105 (patch)
tree96c9fa245e2765179a58bac0b4a75969a7190d8b /sound
parent0a1b42db4bf9db233d1f29795086a9526e84c845 (diff)
ALSA: hda - Add bdl_pos_adj option
Added a new option, bdl_pos_adj, to adjust the delay of IRQ-wakeup timing. Most HD-audio hardwares have a problem that a BDL IRQ is issued before actually the data and the DMA pointer are updated. We have already a mechanism to force to delay snd_pcm_period_elapsed() calls via workq, but this costs much CPU, and typically the delay is within one sample. Thus, it's more clever to adjust the BDL entries instead. The new option adds the size of the delay in frames. As default, it's set to 1 -- that is, one sample delay. Even the hardware is really correct, one sample delay is relatively harmless in comparison with reporting wrong positions. Signed-off-by: Takashi Iwai <tiwai@suse.de> Signed-off-by: Jaroslav Kysela <perex@perex.cz>
Diffstat (limited to 'sound')
-rw-r--r--sound/pci/hda/hda_intel.c117
1 files changed, 85 insertions, 32 deletions
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index dc68709e7569..1ec3fd4c8940 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -58,6 +58,7 @@ static int position_fix[SNDRV_CARDS];
58static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1}; 58static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
59static int single_cmd; 59static int single_cmd;
60static int enable_msi; 60static int enable_msi;
61static int bdl_pos_adj = 1;
61 62
62module_param_array(index, int, NULL, 0444); 63module_param_array(index, int, NULL, 0444);
63MODULE_PARM_DESC(index, "Index value for Intel HD audio interface."); 64MODULE_PARM_DESC(index, "Index value for Intel HD audio interface.");
@@ -77,6 +78,8 @@ MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs "
77 "(for debugging only)."); 78 "(for debugging only).");
78module_param(enable_msi, int, 0444); 79module_param(enable_msi, int, 0444);
79MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)"); 80MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)");
81module_param(bdl_pos_adj, int, 0644);
82MODULE_PARM_DESC(bdl_pos_adj, "BDL position adjustment offset");
80 83
81#ifdef CONFIG_SND_HDA_POWER_SAVE 84#ifdef CONFIG_SND_HDA_POWER_SAVE
82/* power_save option is defined in hda_codec.c */ 85/* power_save option is defined in hda_codec.c */
@@ -309,7 +312,8 @@ struct azx_dev {
309 312
310 unsigned int opened :1; 313 unsigned int opened :1;
311 unsigned int running :1; 314 unsigned int running :1;
312 unsigned int irq_pending: 1; 315 unsigned int irq_pending :1;
316 unsigned int irq_ignore :1;
313}; 317};
314 318
315/* CORB/RIRB */ 319/* CORB/RIRB */
@@ -943,6 +947,11 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
943 azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK); 947 azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK);
944 if (!azx_dev->substream || !azx_dev->running) 948 if (!azx_dev->substream || !azx_dev->running)
945 continue; 949 continue;
950 /* ignore the first dummy IRQ (due to pos_adj) */
951 if (azx_dev->irq_ignore) {
952 azx_dev->irq_ignore = 0;
953 continue;
954 }
946 /* check whether this IRQ is really acceptable */ 955 /* check whether this IRQ is really acceptable */
947 if (azx_position_ok(chip, azx_dev)) { 956 if (azx_position_ok(chip, azx_dev)) {
948 azx_dev->irq_pending = 0; 957 azx_dev->irq_pending = 0;
@@ -977,14 +986,53 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
977 986
978 987
979/* 988/*
989 * set up a BDL entry
990 */
991static int setup_bdle(struct snd_pcm_substream *substream,
992 struct azx_dev *azx_dev, u32 **bdlp,
993 int ofs, int size, int with_ioc)
994{
995 struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);
996 u32 *bdl = *bdlp;
997
998 while (size > 0) {
999 dma_addr_t addr;
1000 int chunk;
1001
1002 if (azx_dev->frags >= AZX_MAX_BDL_ENTRIES)
1003 return -EINVAL;
1004
1005 addr = snd_pcm_sgbuf_get_addr(sgbuf, ofs);
1006 /* program the address field of the BDL entry */
1007 bdl[0] = cpu_to_le32((u32)addr);
1008 bdl[1] = cpu_to_le32(upper_32bit(addr));
1009 /* program the size field of the BDL entry */
1010 chunk = PAGE_SIZE - (ofs % PAGE_SIZE);
1011 if (size < chunk)
1012 chunk = size;
1013 bdl[2] = cpu_to_le32(chunk);
1014 /* program the IOC to enable interrupt
1015 * only when the whole fragment is processed
1016 */
1017 size -= chunk;
1018 bdl[3] = (size || !with_ioc) ? 0 : cpu_to_le32(0x01);
1019 bdl += 4;
1020 azx_dev->frags++;
1021 ofs += chunk;
1022 }
1023 *bdlp = bdl;
1024 return ofs;
1025}
1026
1027/*
980 * set up BDL entries 1028 * set up BDL entries
981 */ 1029 */
982static int azx_setup_periods(struct snd_pcm_substream *substream, 1030static int azx_setup_periods(struct snd_pcm_substream *substream,
983 struct azx_dev *azx_dev) 1031 struct azx_dev *azx_dev)
984{ 1032{
985 struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);
986 u32 *bdl; 1033 u32 *bdl;
987 int i, ofs, periods, period_bytes; 1034 int i, ofs, periods, period_bytes;
1035 int pos_adj = 0;
988 1036
989 /* reset BDL address */ 1037 /* reset BDL address */
990 azx_sd_writel(azx_dev, SD_BDLPL, 0); 1038 azx_sd_writel(azx_dev, SD_BDLPL, 0);
@@ -998,39 +1046,44 @@ static int azx_setup_periods(struct snd_pcm_substream *substream,
998 bdl = (u32 *)azx_dev->bdl.area; 1046 bdl = (u32 *)azx_dev->bdl.area;
999 ofs = 0; 1047 ofs = 0;
1000 azx_dev->frags = 0; 1048 azx_dev->frags = 0;
1001 for (i = 0; i < periods; i++) { 1049 azx_dev->irq_ignore = 0;
1002 int size, rest; 1050 if (bdl_pos_adj > 0) {
1003 if (i >= AZX_MAX_BDL_ENTRIES) { 1051 struct snd_pcm_runtime *runtime = substream->runtime;
1004 snd_printk(KERN_ERR "Too many BDL entries: " 1052 pos_adj = (bdl_pos_adj * runtime->rate + 47999) / 48000;
1005 "buffer=%d, period=%d\n", 1053 if (!pos_adj)
1006 azx_dev->bufsize, period_bytes); 1054 pos_adj = 1;
1007 /* reset */ 1055 pos_adj = frames_to_bytes(runtime, pos_adj);
1008 azx_sd_writel(azx_dev, SD_BDLPL, 0); 1056 if (pos_adj >= period_bytes) {
1009 azx_sd_writel(azx_dev, SD_BDLPU, 0); 1057 snd_printk(KERN_WARNING "Too big adjustment %d\n",
1010 return -EINVAL; 1058 bdl_pos_adj);
1059 pos_adj = 0;
1060 } else {
1061 ofs = setup_bdle(substream, azx_dev,
1062 &bdl, ofs, pos_adj, 1);
1063 if (ofs < 0)
1064 goto error;
1065 azx_dev->irq_ignore = 1;
1011 } 1066 }
1012 rest = period_bytes; 1067 }
1013 do { 1068 for (i = 0; i < periods; i++) {
1014 dma_addr_t addr = snd_pcm_sgbuf_get_addr(sgbuf, ofs); 1069 if (i == periods - 1 && pos_adj)
1015 /* program the address field of the BDL entry */ 1070 ofs = setup_bdle(substream, azx_dev, &bdl, ofs,
1016 bdl[0] = cpu_to_le32((u32)addr); 1071 period_bytes - pos_adj, 0);
1017 bdl[1] = cpu_to_le32(upper_32bit(addr)); 1072 else
1018 /* program the size field of the BDL entry */ 1073 ofs = setup_bdle(substream, azx_dev, &bdl, ofs,
1019 size = PAGE_SIZE - (ofs % PAGE_SIZE); 1074 period_bytes, 1);
1020 if (rest < size) 1075 if (ofs < 0)
1021 size = rest; 1076 goto error;
1022 bdl[2] = cpu_to_le32(size);
1023 /* program the IOC to enable interrupt
1024 * only when the whole fragment is processed
1025 */
1026 rest -= size;
1027 bdl[3] = rest ? 0 : cpu_to_le32(0x01);
1028 bdl += 4;
1029 azx_dev->frags++;
1030 ofs += size;
1031 } while (rest > 0);
1032 } 1077 }
1033 return 0; 1078 return 0;
1079
1080 error:
1081 snd_printk(KERN_ERR "Too many BDL entries: buffer=%d, period=%d\n",
1082 azx_dev->bufsize, period_bytes);
1083 /* reset */
1084 azx_sd_writel(azx_dev, SD_BDLPL, 0);
1085 azx_sd_writel(azx_dev, SD_BDLPU, 0);
1086 return -EINVAL;
1034} 1087}
1035 1088
1036/* 1089/*