// SPDX-License-Identifier: GPL-2.0-or-later /* * ALSA modem driver for Intel ICH (i8x0) chipsets * * Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz> * * This is modified (by Sasha Khapyorsky <sashak@alsa-project.org>) version * of ALSA ICH sound driver intel8x0.c .
*/
#define DEFINE_REGSET(name,base) \ enum { \
ICH_REG_##name##_BDBAR = base + 0x0, /* dword - buffer descriptor list base address */ \
ICH_REG_##name##_CIV = base + 0x04, /* byte - current index value */ \
ICH_REG_##name##_LVI = base + 0x05, /* byte - last valid index */ \
ICH_REG_##name##_SR = base + 0x06, /* byte - status register */ \
ICH_REG_##name##_PICB = base + 0x08, /* word - position in current buffer */ \
ICH_REG_##name##_PIV = base + 0x0a, /* byte - prefetched index value */ \
ICH_REG_##name##_CR = base + 0x0b, /* byte - control register */ \
}
/* Anyone holding a semaphore for 1 msec should be shot... */
time = 100; do { if (!(igetbyte(chip, ICHREG(ACC_SEMA)) & ICH_CAS)) return 0;
udelay(10);
} while (time--);
/* access to some forbidden (non existent) ac97 registers will not * reset the semaphore. So even if you don't get the semaphore, still
* continue the access. We don't need the semaphore anyway. */
dev_err(chip->card->dev, "codec_semaphore: semaphore is not ready [0x%x][0x%x]\n",
igetbyte(chip, ICHREG(ACC_SEMA)), igetdword(chip, ICHREG(GLOB_STA)));
iagetword(chip, 0); /* clear semaphore flag */ /* I don't care about the semaphore */ return -EBUSY;
}
staticinlinevoid snd_intel8x0m_update(struct intel8x0m *chip, struct ichdev *ichdev)
{ unsignedlong port = ichdev->reg_offset; int civ, i, step; int ack = 0;
spin_lock(&chip->reg_lock);
status = igetdword(chip, chip->int_sta_reg); if (status == 0xffffffff) { /* we are not yet resumed */
spin_unlock(&chip->reg_lock); return IRQ_NONE;
} if ((status & chip->int_sta_mask) == 0) { if (status)
iputdword(chip, chip->int_sta_reg, status);
spin_unlock(&chip->reg_lock); return IRQ_NONE;
}
for (i = 0; i < chip->bdbars_count; i++) {
ichdev = &chip->ichd[i]; if (status & ichdev->int_sta_mask)
snd_intel8x0m_update(chip, ichdev);
}
/* ack them */
iputdword(chip, chip->int_sta_reg, status & chip->int_sta_mask);
spin_unlock(&chip->reg_lock);
return IRQ_HANDLED;
}
/* * PCM part
*/
staticint snd_intel8x0m_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{ struct intel8x0m *chip = snd_pcm_substream_chip(substream); struct ichdev *ichdev = get_ichdev(substream); unsignedchar val = 0; unsignedlong port = ichdev->reg_offset;
switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME:
val = ICH_IOCE | ICH_STARTBM; break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND:
val = 0; break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
val = ICH_IOCE; break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
val = ICH_IOCE | ICH_STARTBM; break; default: return -EINVAL;
}
iputbyte(chip, port + ICH_REG_OFF_CR, val); if (cmd == SNDRV_PCM_TRIGGER_STOP) { /* wait until DMA stopped */ while (!(igetbyte(chip, port + ichdev->roff_sr) & ICH_DCH)) ; /* reset whole DMA things */
iputbyte(chip, port + ICH_REG_OFF_CR, ICH_RESETREGS);
} return 0;
}
if (rec->playback_ops)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, rec->playback_ops); if (rec->capture_ops)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, rec->capture_ops);
__err: /* clear the cold-reset bit for the next chance */ if (chip->device_type != DEVICE_ALI)
iputdword(chip, ICHREG(GLOB_CNT),
igetdword(chip, ICHREG(GLOB_CNT)) & ~ICH_AC97COLD); return err;
}
/* put logic to right state */ /* first clear status bits */
status = ICH_RCS | ICH_MIINT | ICH_MOINT;
cnt = igetdword(chip, ICHREG(GLOB_STA));
iputdword(chip, ICHREG(GLOB_STA), cnt & status);
/* ACLink on, 2 channels */
cnt = igetdword(chip, ICHREG(GLOB_CNT));
cnt &= ~(ICH_ACLINK); /* finish cold or do warm reset */
cnt |= (cnt & ICH_AC97COLD) == 0 ? ICH_AC97COLD : ICH_AC97WARM;
iputdword(chip, ICHREG(GLOB_CNT), cnt);
usleep_range(500, 1000); /* give warm reset some time */
end_time = jiffies + HZ / 4; do { if ((igetdword(chip, ICHREG(GLOB_CNT)) & ICH_AC97WARM) == 0) goto __ok;
schedule_timeout_uninterruptible(1);
} while (time_after_eq(end_time, jiffies));
dev_err(chip->card->dev, "AC'97 warm reset still in progress? [0x%x]\n",
igetdword(chip, ICHREG(GLOB_CNT))); return -EIO;
__ok: if (probing) { /* wait for any codec ready status. * Once it becomes ready it should remain ready * as long as we do not disable the ac97 link.
*/
end_time = jiffies + HZ; do {
status = igetdword(chip, ICHREG(GLOB_STA)) &
(ICH_PCR | ICH_SCR | ICH_TCR); if (status) break;
schedule_timeout_uninterruptible(1);
} while (time_after_eq(end_time, jiffies)); if (! status) { /* no codec is found */
dev_err(chip->card->dev, "codec_ready: codec is not ready [0x%x]\n",
igetdword(chip, ICHREG(GLOB_STA))); return -EIO;
}
/* up to two codecs (modem cannot be tertiary with ICH4) */
nstatus = ICH_PCR | ICH_SCR;
/* wait for other codecs ready status. */
end_time = jiffies + HZ / 4; while (status != nstatus && time_after_eq(end_time, jiffies)) {
schedule_timeout_uninterruptible(1);
status |= igetdword(chip, ICHREG(GLOB_STA)) & nstatus;
}
} else { /* resume phase */
status = 0; if (chip->ac97)
status |= get_ich_codec_bit(chip, chip->ac97->num); /* wait until all the probed codecs are ready */
end_time = jiffies + HZ; do {
nstatus = igetdword(chip, ICHREG(GLOB_STA)) &
(ICH_PCR | ICH_SCR | ICH_TCR); if (status == nstatus) break;
schedule_timeout_uninterruptible(1);
} while (time_after_eq(end_time, jiffies));
}
if (chip->device_type == DEVICE_SIS) { /* unmute the output on SIS7013 */
iputword(chip, 0x4c, igetword(chip, 0x4c) | 1);
}
return 0;
}
staticint snd_intel8x0m_chip_init(struct intel8x0m *chip, int probing)
{ unsignedint i; int err;
err = snd_intel8x0m_ich_chip_init(chip, probing); if (err < 0) return err;
iagetword(chip, 0); /* clear semaphore flag */
/* disable interrupts */ for (i = 0; i < chip->bdbars_count; i++)
iputbyte(chip, ICH_REG_OFF_CR + chip->ichd[i].reg_offset, 0x00); /* reset channels */ for (i = 0; i < chip->bdbars_count; i++)
iputbyte(chip, ICH_REG_OFF_CR + chip->ichd[i].reg_offset, ICH_RESETREGS); /* initialize Buffer Descriptor Lists */ for (i = 0; i < chip->bdbars_count; i++)
iputdword(chip, ICH_REG_OFF_BDBAR + chip->ichd[i].reg_offset, chip->ichd[i].bdbar_addr); return 0;
}
for (i = 0; i < chip->bdbars_count; i++) {
ichdev = &chip->ichd[i];
ichdev->ichd = i;
ichdev->reg_offset = tbl[i].offset;
ichdev->int_sta_mask = tbl[i].int_sta_mask; if (device_type == DEVICE_SIS) { /* SiS 7013 swaps the registers */
ichdev->roff_sr = ICH_REG_OFF_PICB;
ichdev->roff_picb = ICH_REG_OFF_SR;
} else {
ichdev->roff_sr = ICH_REG_OFF_SR;
ichdev->roff_picb = ICH_REG_OFF_PICB;
} if (device_type == DEVICE_ALI)
ichdev->ali_slot = (ichdev->reg_offset - 0x40) / 0x10;
} /* SIS7013 handles the pcm data in bytes, others are in words */
chip->pcm_pos_shift = (device_type == DEVICE_SIS) ? 0 : 1;
/* allocate buffer descriptor lists */ /* the start of each lists must be aligned to 8 bytes */
chip->bdbars = snd_devm_alloc_pages(&pci->dev, SNDRV_DMA_TYPE_DEV,
chip->bdbars_count * sizeof(u32) *
ICH_MAX_FRAGS * 2); if (!chip->bdbars) return -ENOMEM;
/* tables must be aligned to 8 bytes here, but the kernel pages
are much bigger, so we don't care (on i386) */
int_sta_masks = 0; for (i = 0; i < chip->bdbars_count; i++) {
ichdev = &chip->ichd[i];
ichdev->bdbar = ((__le32 *)chip->bdbars->area) + (i * ICH_MAX_FRAGS * 2);
ichdev->bdbar_addr = chip->bdbars->addr + (i * sizeof(u32) * ICH_MAX_FRAGS * 2);
int_sta_masks |= ichdev->int_sta_mask;
}
chip->int_sta_reg = ICH_REG_GLOB_STA;
chip->int_sta_mask = int_sta_masks;
pci_set_master(pci);
err = snd_intel8x0m_chip_init(chip, 1); if (err < 0) return err;
/* NOTE: we don't use devm version here since it's released / * re-acquired in PM callbacks. * It's released explicitly in snd_intel8x0m_free(), too.
*/ if (request_irq(pci->irq, snd_intel8x0m_interrupt, IRQF_SHARED,
KBUILD_MODNAME, chip)) {
dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq); return -EBUSY;
}
chip->irq = pci->irq;
card->sync_irq = chip->irq;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.