diff options
Diffstat (limited to 'sound/pci/hda/hda_controller.c')
-rw-r--r-- | sound/pci/hda/hda_controller.c | 203 |
1 files changed, 72 insertions, 131 deletions
diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index 6df04d9..8337645 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -27,6 +27,7 @@ #include <linux/module.h> #include <linux/pm_runtime.h> #include <linux/slab.h> +#include <linux/reboot.h> #include <sound/core.h> #include <sound/initval.h> #include "hda_priv.h" @@ -152,11 +153,11 @@ 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 (!(azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE)) + if (chip->get_position[0] != azx_get_pos_lpib || + chip->get_position[1] != azx_get_pos_lpib) { + if (!(azx_readl(chip, DPLBASE) & AZX_DPLBASE_ENABLE)) azx_writel(chip, DPLBASE, - (u32)chip->posbuf.addr | ICH6_DPLBASE_ENABLE); + (u32)chip->posbuf.addr | AZX_DPLBASE_ENABLE); } /* set the interrupt enable bits in the descriptor control register */ @@ -482,7 +483,8 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream) } azx_stream_reset(chip, azx_dev); - format_val = snd_hda_calc_stream_format(runtime->rate, + format_val = snd_hda_calc_stream_format(apcm->codec, + runtime->rate, runtime->channels, runtime->format, hinfo->maxbps, @@ -673,125 +675,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 +726,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, @@ -1059,10 +976,10 @@ static void azx_init_cmd_io(struct azx *chip) azx_writew(chip, CORBWP, 0); /* reset the corb hw read pointer */ - azx_writew(chip, CORBRP, ICH6_CORBRP_RST); + azx_writew(chip, CORBRP, AZX_CORBRP_RST); if (!(chip->driver_caps & AZX_DCAPS_CORBRP_SELF_CLEAR)) { for (timeout = 1000; timeout > 0; timeout--) { - if ((azx_readw(chip, CORBRP) & ICH6_CORBRP_RST) == ICH6_CORBRP_RST) + if ((azx_readw(chip, CORBRP) & AZX_CORBRP_RST) == AZX_CORBRP_RST) break; udelay(1); } @@ -1082,7 +999,7 @@ static void azx_init_cmd_io(struct azx *chip) } /* enable corb dma */ - azx_writeb(chip, CORBCTL, ICH6_CORBCTL_RUN); + azx_writeb(chip, CORBCTL, AZX_CORBCTL_RUN); /* RIRB set up */ chip->rirb.addr = chip->rb.addr + 2048; @@ -1095,14 +1012,14 @@ static void azx_init_cmd_io(struct azx *chip) /* set the rirb size to 256 entries (ULI requires explicitly) */ azx_writeb(chip, RIRBSIZE, 0x02); /* reset the rirb hw write pointer */ - azx_writew(chip, RIRBWP, ICH6_RIRBWP_RST); + azx_writew(chip, RIRBWP, AZX_RIRBWP_RST); /* set N=1, get RIRB response interrupt for new entry */ if (chip->driver_caps & AZX_DCAPS_CTX_WORKAROUND) azx_writew(chip, RINTCNT, 0xc0); else azx_writew(chip, RINTCNT, 1); /* enable rirb dma and response irq */ - azx_writeb(chip, RIRBCTL, ICH6_RBCTL_DMA_EN | ICH6_RBCTL_IRQ_EN); + azx_writeb(chip, RIRBCTL, AZX_RBCTL_DMA_EN | AZX_RBCTL_IRQ_EN); spin_unlock_irq(&chip->reg_lock); } EXPORT_SYMBOL_GPL(azx_init_cmd_io); @@ -1146,7 +1063,7 @@ static int azx_corb_send_cmd(struct hda_bus *bus, u32 val) return -EIO; } wp++; - wp %= ICH6_MAX_CORB_ENTRIES; + wp %= AZX_MAX_CORB_ENTRIES; rp = azx_readw(chip, CORBRP); if (wp == rp) { @@ -1164,7 +1081,7 @@ static int azx_corb_send_cmd(struct hda_bus *bus, u32 val) return 0; } -#define ICH6_RIRB_EX_UNSOL_EV (1<<4) +#define AZX_RIRB_EX_UNSOL_EV (1<<4) /* retrieve RIRB entry - called from interrupt handler */ static void azx_update_rirb(struct azx *chip) @@ -1185,7 +1102,7 @@ static void azx_update_rirb(struct azx *chip) while (chip->rirb.rp != wp) { chip->rirb.rp++; - chip->rirb.rp %= ICH6_MAX_RIRB_ENTRIES; + chip->rirb.rp %= AZX_MAX_RIRB_ENTRIES; rp = chip->rirb.rp << 1; /* an RIRB entry is 8-bytes */ res_ex = le32_to_cpu(chip->rirb.buf[rp + 1]); @@ -1196,8 +1113,7 @@ static void azx_update_rirb(struct azx *chip) res, res_ex, chip->rirb.rp, wp); snd_BUG(); - } - else if (res_ex & ICH6_RIRB_EX_UNSOL_EV) + } else if (res_ex & AZX_RIRB_EX_UNSOL_EV) snd_hda_queue_unsol_event(chip->bus, res, res_ex); else if (chip->rirb.cmds[addr]) { chip->rirb.res[addr] = res; @@ -1305,7 +1221,7 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus, /* release CORB/RIRB */ azx_free_cmd_io(chip); /* disable unsolicited responses */ - azx_writel(chip, GCTL, azx_readl(chip, GCTL) & ~ICH6_GCTL_UNSOL); + azx_writel(chip, GCTL, azx_readl(chip, GCTL) & ~AZX_GCTL_UNSOL); return -1; } @@ -1326,7 +1242,7 @@ static int azx_single_wait_for_response(struct azx *chip, unsigned int addr) while (timeout--) { /* check IRV busy bit */ - if (azx_readw(chip, IRS) & ICH6_IRS_VALID) { + if (azx_readw(chip, IRS) & AZX_IRS_VALID) { /* reuse rirb.res as the response return value */ chip->rirb.res[addr] = azx_readl(chip, IR); return 0; @@ -1350,13 +1266,13 @@ static int azx_single_send_cmd(struct hda_bus *bus, u32 val) bus->rirb_error = 0; while (timeout--) { /* check ICB busy bit */ - if (!((azx_readw(chip, IRS) & ICH6_IRS_BUSY))) { + if (!((azx_readw(chip, IRS) & AZX_IRS_BUSY))) { /* Clear IRV valid bit */ azx_writew(chip, IRS, azx_readw(chip, IRS) | - ICH6_IRS_VALID); + AZX_IRS_VALID); azx_writel(chip, IC, val); azx_writew(chip, IRS, azx_readw(chip, IRS) | - ICH6_IRS_BUSY); + AZX_IRS_BUSY); return azx_single_wait_for_response(chip, addr); } udelay(1); @@ -1585,10 +1501,10 @@ void azx_enter_link_reset(struct azx *chip) unsigned long timeout; /* reset controller */ - azx_writel(chip, GCTL, azx_readl(chip, GCTL) & ~ICH6_GCTL_RESET); + azx_writel(chip, GCTL, azx_readl(chip, GCTL) & ~AZX_GCTL_RESET); timeout = jiffies + msecs_to_jiffies(100); - while ((azx_readb(chip, GCTL) & ICH6_GCTL_RESET) && + while ((azx_readb(chip, GCTL) & AZX_GCTL_RESET) && time_before(jiffies, timeout)) usleep_range(500, 1000); } @@ -1599,7 +1515,7 @@ static void azx_exit_link_reset(struct azx *chip) { unsigned long timeout; - azx_writeb(chip, GCTL, azx_readb(chip, GCTL) | ICH6_GCTL_RESET); + azx_writeb(chip, GCTL, azx_readb(chip, GCTL) | AZX_GCTL_RESET); timeout = jiffies + msecs_to_jiffies(100); while (!azx_readb(chip, GCTL) && @@ -1640,7 +1556,7 @@ static int azx_reset(struct azx *chip, bool full_reset) /* Accept unsolicited responses */ if (!chip->single_cmd) azx_writel(chip, GCTL, azx_readl(chip, GCTL) | - ICH6_GCTL_UNSOL); + AZX_GCTL_UNSOL); /* detect codecs */ if (!chip->codec_mask) { @@ -1657,7 +1573,7 @@ static void azx_int_enable(struct azx *chip) { /* enable controller CIE and GIE */ azx_writel(chip, INTCTL, azx_readl(chip, INTCTL) | - ICH6_INT_CTRL_EN | ICH6_INT_GLOBAL_EN); + AZX_INT_CTRL_EN | AZX_INT_GLOBAL_EN); } /* disable interrupts */ @@ -1678,7 +1594,7 @@ static void azx_int_disable(struct azx *chip) /* disable controller CIE and GIE */ azx_writel(chip, INTCTL, azx_readl(chip, INTCTL) & - ~(ICH6_INT_CTRL_EN | ICH6_INT_GLOBAL_EN)); + ~(AZX_INT_CTRL_EN | AZX_INT_GLOBAL_EN)); } /* clear interrupts */ @@ -1699,7 +1615,7 @@ static void azx_int_clear(struct azx *chip) azx_writeb(chip, RIRBSTS, RIRB_INT_MASK); /* clear int status */ - azx_writel(chip, INTSTS, ICH6_INT_CTRL_EN | ICH6_INT_ALL_STREAM); + azx_writel(chip, INTSTS, AZX_INT_CTRL_EN | AZX_INT_ALL_STREAM); } /* @@ -2031,5 +1947,30 @@ int azx_init_stream(struct azx *chip) } EXPORT_SYMBOL_GPL(azx_init_stream); +/* + * reboot notifier for hang-up problem at power-down + */ +static int azx_halt(struct notifier_block *nb, unsigned long event, void *buf) +{ + struct azx *chip = container_of(nb, struct azx, reboot_notifier); + snd_hda_bus_reboot_notify(chip->bus); + azx_stop_chip(chip); + return NOTIFY_OK; +} + +void azx_notifier_register(struct azx *chip) +{ + chip->reboot_notifier.notifier_call = azx_halt; + register_reboot_notifier(&chip->reboot_notifier); +} +EXPORT_SYMBOL_GPL(azx_notifier_register); + +void azx_notifier_unregister(struct azx *chip) +{ + if (chip->reboot_notifier.notifier_call) + unregister_reboot_notifier(&chip->reboot_notifier); +} +EXPORT_SYMBOL_GPL(azx_notifier_unregister); + MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Common HDA driver funcitons"); |