diff options
-rw-r--r-- | sound/pci/hda/hda_controller.c | 127 | ||||
-rw-r--r-- | sound/pci/hda/hda_controller.h | 6 | ||||
-rw-r--r-- | sound/pci/hda/hda_intel.c | 151 | ||||
-rw-r--r-- | sound/pci/hda/hda_priv.h | 17 | ||||
-rw-r--r-- | sound/pci/hda/hda_tegra.c | 2 |
5 files changed, 174 insertions, 129 deletions
diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index 875e0ebf56eb..a562d86c02fb 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -152,8 +152,8 @@ static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev) upper_32_bits(azx_dev->bdl.addr)); /* enable the position buffer */ - if (chip->position_fix[0] != POS_FIX_LPIB || - chip->position_fix[1] != POS_FIX_LPIB) { + if (chip->get_position[0] != azx_get_pos_lpib || + chip->get_position[1] != azx_get_pos_lpib) { if (!(azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE)) azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr | ICH6_DPLBASE_ENABLE); @@ -673,125 +673,40 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) return 0; } -/* get the current DMA position with correction on VIA chips */ -static unsigned int azx_via_get_position(struct azx *chip, - struct azx_dev *azx_dev) +unsigned int azx_get_pos_lpib(struct azx *chip, struct azx_dev *azx_dev) { - unsigned int link_pos, mini_pos, bound_pos; - unsigned int mod_link_pos, mod_dma_pos, mod_mini_pos; - unsigned int fifo_size; - - link_pos = azx_sd_readl(chip, azx_dev, SD_LPIB); - if (azx_dev->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - /* Playback, no problem using link position */ - return link_pos; - } - - /* Capture */ - /* For new chipset, - * use mod to get the DMA position just like old chipset - */ - mod_dma_pos = le32_to_cpu(*azx_dev->posbuf); - mod_dma_pos %= azx_dev->period_bytes; - - /* azx_dev->fifo_size can't get FIFO size of in stream. - * Get from base address + offset. - */ - fifo_size = readw(chip->remap_addr + VIA_IN_STREAM0_FIFO_SIZE_OFFSET); - - if (azx_dev->insufficient) { - /* Link position never gather than FIFO size */ - if (link_pos <= fifo_size) - return 0; - - azx_dev->insufficient = 0; - } - - if (link_pos <= fifo_size) - mini_pos = azx_dev->bufsize + link_pos - fifo_size; - else - mini_pos = link_pos - fifo_size; - - /* Find nearest previous boudary */ - mod_mini_pos = mini_pos % azx_dev->period_bytes; - mod_link_pos = link_pos % azx_dev->period_bytes; - if (mod_link_pos >= fifo_size) - bound_pos = link_pos - mod_link_pos; - else if (mod_dma_pos >= mod_mini_pos) - bound_pos = mini_pos - mod_mini_pos; - else { - bound_pos = mini_pos - mod_mini_pos + azx_dev->period_bytes; - if (bound_pos >= azx_dev->bufsize) - bound_pos = 0; - } + return azx_sd_readl(chip, azx_dev, SD_LPIB); +} +EXPORT_SYMBOL_GPL(azx_get_pos_lpib); - /* Calculate real DMA position we want */ - return bound_pos + mod_dma_pos; +unsigned int azx_get_pos_posbuf(struct azx *chip, struct azx_dev *azx_dev) +{ + return le32_to_cpu(*azx_dev->posbuf); } +EXPORT_SYMBOL_GPL(azx_get_pos_posbuf); unsigned int azx_get_position(struct azx *chip, - struct azx_dev *azx_dev, - bool with_check) + struct azx_dev *azx_dev) { struct snd_pcm_substream *substream = azx_dev->substream; - struct azx_pcm *apcm = snd_pcm_substream_chip(substream); unsigned int pos; int stream = substream->stream; - struct hda_pcm_stream *hinfo = apcm->hinfo[stream]; int delay = 0; - switch (chip->position_fix[stream]) { - case POS_FIX_LPIB: - /* read LPIB */ - pos = azx_sd_readl(chip, azx_dev, SD_LPIB); - break; - case POS_FIX_VIACOMBO: - pos = azx_via_get_position(chip, azx_dev); - break; - default: - /* use the position buffer */ - pos = le32_to_cpu(*azx_dev->posbuf); - if (with_check && chip->position_fix[stream] == POS_FIX_AUTO) { - if (!pos || pos == (u32)-1) { - dev_info(chip->card->dev, - "Invalid position buffer, using LPIB read method instead.\n"); - chip->position_fix[stream] = POS_FIX_LPIB; - pos = azx_sd_readl(chip, azx_dev, SD_LPIB); - } else - chip->position_fix[stream] = POS_FIX_POSBUF; - } - break; - } + if (chip->get_position[stream]) + pos = chip->get_position[stream](chip, azx_dev); + else /* use the position buffer as default */ + pos = azx_get_pos_posbuf(chip, azx_dev); if (pos >= azx_dev->bufsize) pos = 0; - /* calculate runtime delay from LPIB */ - if (substream->runtime && - chip->position_fix[stream] == POS_FIX_POSBUF && - (chip->driver_caps & AZX_DCAPS_COUNT_LPIB_DELAY)) { - unsigned int lpib_pos = azx_sd_readl(chip, azx_dev, SD_LPIB); - if (stream == SNDRV_PCM_STREAM_PLAYBACK) - delay = pos - lpib_pos; - else - delay = lpib_pos - pos; - if (delay < 0) { - if (delay >= azx_dev->delay_negative_threshold) - delay = 0; - else - delay += azx_dev->bufsize; - } - if (delay >= azx_dev->period_bytes) { - dev_info(chip->card->dev, - "Unstable LPIB (%d >= %d); disabling LPIB delay counting\n", - delay, azx_dev->period_bytes); - delay = 0; - chip->driver_caps &= ~AZX_DCAPS_COUNT_LPIB_DELAY; - } - delay = bytes_to_frames(substream->runtime, delay); - } - if (substream->runtime) { + struct azx_pcm *apcm = snd_pcm_substream_chip(substream); + struct hda_pcm_stream *hinfo = apcm->hinfo[stream]; + + if (chip->get_delay[stream]) + delay += chip->get_delay[stream](chip, azx_dev, pos); if (hinfo->ops.get_delay) delay += hinfo->ops.get_delay(hinfo, apcm->codec, substream); @@ -809,7 +724,7 @@ static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream) struct azx *chip = apcm->chip; struct azx_dev *azx_dev = get_azx_dev(substream); return bytes_to_frames(substream->runtime, - azx_get_position(chip, azx_dev, false)); + azx_get_position(chip, azx_dev)); } static int azx_get_wallclock_tstamp(struct snd_pcm_substream *substream, diff --git a/sound/pci/hda/hda_controller.h b/sound/pci/hda/hda_controller.h index baf0e77330af..91f04958e2b0 100644 --- a/sound/pci/hda/hda_controller.h +++ b/sound/pci/hda/hda_controller.h @@ -25,9 +25,9 @@ static inline struct azx_dev *get_azx_dev(struct snd_pcm_substream *substream) { return substream->runtime->private_data; } -unsigned int azx_get_position(struct azx *chip, - struct azx_dev *azx_dev, - bool with_check); +unsigned int azx_get_position(struct azx *chip, struct azx_dev *azx_dev); +unsigned int azx_get_pos_lpib(struct azx *chip, struct azx_dev *azx_dev); +unsigned int azx_get_pos_posbuf(struct azx *chip, struct azx_dev *azx_dev); /* Stream control. */ void azx_stream_stop(struct azx *chip, struct azx_dev *azx_dev); diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 25753db97071..cb5d807bbd0c 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -66,6 +66,15 @@ #include "hda_controller.h" #include "hda_priv.h" +/* position fix mode */ +enum { + POS_FIX_AUTO, + POS_FIX_LPIB, + POS_FIX_POSBUF, + POS_FIX_VIACOMBO, + POS_FIX_COMBO, +}; + static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; @@ -434,6 +443,38 @@ static void azx_init_pci(struct azx *chip) } } +/* calculate runtime delay from LPIB */ +static int azx_get_delay_from_lpib(struct azx *chip, struct azx_dev *azx_dev, + unsigned int pos) +{ + struct snd_pcm_substream *substream = azx_dev->substream; + int stream = substream->stream; + unsigned int lpib_pos = azx_get_pos_lpib(chip, azx_dev); + int delay; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + delay = pos - lpib_pos; + else + delay = lpib_pos - pos; + if (delay < 0) { + if (delay >= azx_dev->delay_negative_threshold) + delay = 0; + else + delay += azx_dev->bufsize; + } + + if (delay >= azx_dev->period_bytes) { + dev_info(chip->card->dev, + "Unstable LPIB (%d >= %d); disabling LPIB delay counting\n", + delay, azx_dev->period_bytes); + delay = 0; + chip->driver_caps &= ~AZX_DCAPS_COUNT_LPIB_DELAY; + chip->get_delay[stream] = NULL; + } + + return bytes_to_frames(substream->runtime, delay); +} + static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev); /* called from IRQ */ @@ -464,6 +505,8 @@ static int azx_position_check(struct azx *chip, struct azx_dev *azx_dev) */ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev) { + struct snd_pcm_substream *substream = azx_dev->substream; + int stream = substream->stream; u32 wallclk; unsigned int pos; @@ -471,7 +514,25 @@ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev) if (wallclk < (azx_dev->period_wallclk * 2) / 3) return -1; /* bogus (too early) interrupt */ - pos = azx_get_position(chip, azx_dev, true); + if (chip->get_position[stream]) + pos = chip->get_position[stream](chip, azx_dev); + else { /* use the position buffer as default */ + pos = azx_get_pos_posbuf(chip, azx_dev); + if (!pos || pos == (u32)-1) { + dev_info(chip->card->dev, + "Invalid position buffer, using LPIB read method instead.\n"); + chip->get_position[stream] = azx_get_pos_lpib; + pos = azx_get_pos_lpib(chip, azx_dev); + chip->get_delay[stream] = NULL; + } else { + chip->get_position[stream] = azx_get_pos_posbuf; + if (chip->driver_caps & AZX_DCAPS_COUNT_LPIB_DELAY) + chip->get_delay[stream] = azx_get_delay_from_lpib; + } + } + + if (pos >= azx_dev->bufsize) + pos = 0; if (WARN_ONCE(!azx_dev->period_bytes, "hda-intel: zero azx_dev->period_bytes")) @@ -554,6 +615,62 @@ static int azx_acquire_irq(struct azx *chip, int do_disconnect) return 0; } +/* get the current DMA position with correction on VIA chips */ +static unsigned int azx_via_get_position(struct azx *chip, + struct azx_dev *azx_dev) +{ + unsigned int link_pos, mini_pos, bound_pos; + unsigned int mod_link_pos, mod_dma_pos, mod_mini_pos; + unsigned int fifo_size; + + link_pos = azx_sd_readl(chip, azx_dev, SD_LPIB); + if (azx_dev->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + /* Playback, no problem using link position */ + return link_pos; + } + + /* Capture */ + /* For new chipset, + * use mod to get the DMA position just like old chipset + */ + mod_dma_pos = le32_to_cpu(*azx_dev->posbuf); + mod_dma_pos %= azx_dev->period_bytes; + + /* azx_dev->fifo_size can't get FIFO size of in stream. + * Get from base address + offset. + */ + fifo_size = readw(chip->remap_addr + VIA_IN_STREAM0_FIFO_SIZE_OFFSET); + + if (azx_dev->insufficient) { + /* Link position never gather than FIFO size */ + if (link_pos <= fifo_size) + return 0; + + azx_dev->insufficient = 0; + } + + if (link_pos <= fifo_size) + mini_pos = azx_dev->bufsize + link_pos - fifo_size; + else + mini_pos = link_pos - fifo_size; + + /* Find nearest previous boudary */ + mod_mini_pos = mini_pos % azx_dev->period_bytes; + mod_link_pos = link_pos % azx_dev->period_bytes; + if (mod_link_pos >= fifo_size) + bound_pos = link_pos - mod_link_pos; + else if (mod_dma_pos >= mod_mini_pos) + bound_pos = mini_pos - mod_mini_pos; + else { + bound_pos = mini_pos - mod_mini_pos + azx_dev->period_bytes; + if (bound_pos >= azx_dev->bufsize) + bound_pos = 0; + } + + /* Calculate real DMA position we want */ + return bound_pos + mod_dma_pos; +} + #ifdef CONFIG_PM static DEFINE_MUTEX(card_list_lock); static LIST_HEAD(card_list); @@ -1084,6 +1201,30 @@ static int check_position_fix(struct azx *chip, int fix) return POS_FIX_AUTO; } +static void assign_position_fix(struct azx *chip, int fix) +{ + static azx_get_pos_callback_t callbacks[] = { + [POS_FIX_AUTO] = NULL, + [POS_FIX_LPIB] = azx_get_pos_lpib, + [POS_FIX_POSBUF] = azx_get_pos_posbuf, + [POS_FIX_VIACOMBO] = azx_via_get_position, + [POS_FIX_COMBO] = azx_get_pos_lpib, + }; + + chip->get_position[0] = chip->get_position[1] = callbacks[fix]; + + /* combo mode uses LPIB only for playback */ + if (fix == POS_FIX_COMBO) + chip->get_position[1] = NULL; + + if (fix == POS_FIX_POSBUF && + (chip->driver_caps & AZX_DCAPS_COUNT_LPIB_DELAY)) { + chip->get_delay[0] = chip->get_delay[1] = + azx_get_delay_from_lpib; + } + +} + /* * black-lists for probe_mask */ @@ -1258,13 +1399,7 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci, init_vga_switcheroo(chip); init_completion(&chip->probe_wait); - chip->position_fix[0] = chip->position_fix[1] = - check_position_fix(chip, position_fix[dev]); - /* combo mode uses LPIB for playback */ - if (chip->position_fix[0] == POS_FIX_COMBO) { - chip->position_fix[0] = POS_FIX_LPIB; - chip->position_fix[1] = POS_FIX_AUTO; - } + assign_position_fix(chip, check_position_fix(chip, position_fix[dev])); check_probe_mask(chip, dev); diff --git a/sound/pci/hda/hda_priv.h b/sound/pci/hda/hda_priv.h index da12d10b778f..43dd8a19cc09 100644 --- a/sound/pci/hda/hda_priv.h +++ b/sound/pci/hda/hda_priv.h @@ -188,15 +188,6 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; #define AZX_DCAPS_I915_POWERWELL (1 << 27) /* HSW i915 powerwell support */ #define AZX_DCAPS_CORBRP_SELF_CLEAR (1 << 28) /* CORBRP clears itself after reset */ -/* position fix mode */ -enum { - POS_FIX_AUTO, - POS_FIX_LPIB, - POS_FIX_POSBUF, - POS_FIX_VIACOMBO, - POS_FIX_COMBO, -}; - /* Defines for ATI HD Audio support in SB450 south bridge */ #define ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR 0x42 #define ATI_SB450_HDAUDIO_ENABLE_SNOOP 0x02 @@ -321,6 +312,9 @@ struct azx_pcm { struct list_head list; }; +typedef unsigned int (*azx_get_pos_callback_t)(struct azx *, struct azx_dev *); +typedef int (*azx_get_delay_callback_t)(struct azx *, struct azx_dev *, unsigned int pos); + struct azx { struct snd_card *card; struct pci_dev *pci; @@ -339,6 +333,10 @@ struct azx { /* Register interaction. */ const struct hda_controller_ops *ops; + /* position adjustment callbacks */ + azx_get_pos_callback_t get_position[2]; + azx_get_delay_callback_t get_delay[2]; + /* pci resources */ unsigned long addr; void __iomem *remap_addr; @@ -374,7 +372,6 @@ struct azx { #endif /* flags */ - int position_fix[2]; /* for both playback/capture streams */ const int *bdl_pos_adj; int poll_count; unsigned int running:1; diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c index a366ba9293a8..12920b99d238 100644 --- a/sound/pci/hda/hda_tegra.c +++ b/sound/pci/hda/hda_tegra.c @@ -480,8 +480,6 @@ static int hda_tegra_create(struct snd_card *card, INIT_LIST_HEAD(&chip->pcm_list); INIT_LIST_HEAD(&chip->list); - chip->position_fix[0] = POS_FIX_AUTO; - chip->position_fix[1] = POS_FIX_AUTO; chip->codec_probe_mask = -1; chip->single_cmd = false; |