diff options
Diffstat (limited to 'sound/pci/hda/hda_intel.c')
-rw-r--r-- | sound/pci/hda/hda_intel.c | 117 |
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]; | |||
58 | static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1}; | 58 | static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1}; |
59 | static int single_cmd; | 59 | static int single_cmd; |
60 | static int enable_msi; | 60 | static int enable_msi; |
61 | static int bdl_pos_adj = 1; | ||
61 | 62 | ||
62 | module_param_array(index, int, NULL, 0444); | 63 | module_param_array(index, int, NULL, 0444); |
63 | MODULE_PARM_DESC(index, "Index value for Intel HD audio interface."); | 64 | MODULE_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)."); |
78 | module_param(enable_msi, int, 0444); | 79 | module_param(enable_msi, int, 0444); |
79 | MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)"); | 80 | MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)"); |
81 | module_param(bdl_pos_adj, int, 0644); | ||
82 | MODULE_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 | */ | ||
991 | static 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 | */ |
982 | static int azx_setup_periods(struct snd_pcm_substream *substream, | 1030 | static 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 | /* |