aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci/hda/hda_controller.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/pci/hda/hda_controller.c')
-rw-r--r--sound/pci/hda/hda_controller.c200
1 files changed, 70 insertions, 130 deletions
diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c
index 875e0ebf56eb..06f39c126ba4 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 */
@@ -673,125 +674,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 +725,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 +975,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 +998,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 +1011,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 +1062,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 +1080,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 +1101,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 +1112,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 +1220,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 +1241,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 +1265,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 +1500,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 +1514,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 +1555,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 +1572,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 +1593,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 +1614,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 +1946,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");