static void
auich_attach(device_t parent, device_t self, void *aux)
{
	struct auich_softc *sc = device_private(self);
	struct pci_attach_args *pa;
	pcireg_t v, subdev;
	const char *intrstr;
	const struct auich_devtype *d;
	const struct sysctlnode *node, *node_ac97clock;
	int err, node_mib, i;

	sc->sc_dev = self;
	pa = aux;

	if ((d = auich_lookup(pa, auich_modem_devices)) != NULL) {
		sc->sc_modem_offset = 0x10;
		sc->sc_codectype = AC97_CODEC_TYPE_MODEM;
	} else if ((d = auich_lookup(pa, auich_audio_devices)) != NULL) {
		sc->sc_modem_offset = 0;
		sc->sc_codectype = AC97_CODEC_TYPE_AUDIO;
	} else
		panic("auich_attach: impossible");

	if (sc->sc_codectype == AC97_CODEC_TYPE_AUDIO)
		aprint_naive(": Audio controller\n");
	else
		aprint_naive(": Modem controller\n");

	sc->sc_pc = pa->pa_pc;
	sc->sc_pt = pa->pa_tag;

	aprint_normal(": %s\n", d->name);

	if (d->id == PCIID_ICH4 || d->id == PCIID_ICH5 || d->id == PCIID_ICH6
	    || d->id == PCIID_ICH7 || d->id == PCIID_I6300ESB
	    || d->id == PCIID_ICH4MODEM) {
		/*
		 * Use native mode for Intel 6300ESB and ICH4/ICH5/ICH6/ICH7
		 */

		if (pci_mapreg_map(pa, ICH_MMBAR, PCI_MAPREG_TYPE_MEM, 0,
		    &sc->iot, &sc->mix_ioh, NULL, &sc->mix_size)) {
			goto retry_map;
		}
		if (pci_mapreg_map(pa, ICH_MBBAR, PCI_MAPREG_TYPE_MEM, 0,
		    &sc->iot, &sc->aud_ioh, NULL, &sc->aud_size)) {
			goto retry_map;
		}
		goto map_done;
	} else
		goto non_native_map;

retry_map:
	sc->sc_iose = 1;
	v = pci_conf_read(pa->pa_pc, pa->pa_tag, ICH_CFG);
	pci_conf_write(pa->pa_pc, pa->pa_tag, ICH_CFG,
		       v | ICH_CFG_IOSE);

non_native_map:
	if (pci_mapreg_map(pa, ICH_NAMBAR, PCI_MAPREG_TYPE_IO, 0,
			   &sc->iot, &sc->mix_ioh, NULL, &sc->mix_size)) {
		aprint_error_dev(self, "can't map codec i/o space\n");
		return;
	}
	if (pci_mapreg_map(pa, ICH_NABMBAR, PCI_MAPREG_TYPE_IO, 0,
			   &sc->iot, &sc->aud_ioh, NULL, &sc->aud_size)) {
		aprint_error_dev(self, "can't map device i/o space\n");
		return;
	}

map_done:
	sc->dmat = pa->pa_dmat;

	/* enable bus mastering */
	v = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG);
	pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG,
	    v | PCI_COMMAND_MASTER_ENABLE | PCI_COMMAND_BACKTOBACK_ENABLE);

	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
	mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_AUDIO);

	/* Map and establish the interrupt. */
	if (pci_intr_map(pa, &sc->intrh)) {
		aprint_error_dev(self, "can't map interrupt\n");
		return;
	}
	intrstr = pci_intr_string(pa->pa_pc, sc->intrh);
	sc->sc_ih = pci_intr_establish(pa->pa_pc, sc->intrh, IPL_AUDIO,
	    auich_intr, sc);
	if (sc->sc_ih == NULL) {
		aprint_error_dev(self, "can't establish interrupt");
		if (intrstr != NULL)
			aprint_error(" at %s", intrstr);
		aprint_error("\n");
		return;
	}
	aprint_normal_dev(self, "interrupting at %s\n", intrstr);

	snprintf(sc->sc_audev.name, MAX_AUDIO_DEV_LEN, "%s AC97", d->shortname);
	snprintf(sc->sc_audev.version, MAX_AUDIO_DEV_LEN,
		 "0x%02x", PCI_REVISION(pa->pa_class));
	strlcpy(sc->sc_audev.config, device_xname(self), MAX_AUDIO_DEV_LEN);

	/* SiS 7012 needs special handling */
	if (d->id == PCIID_SIS7012) {
		sc->sc_sts_reg = ICH_PICB;
		sc->sc_sample_shift = 0;
		sc->sc_pcm246_mask = ICH_SIS_PCM246_MASK;
		sc->sc_pcm2 = ICH_SIS_PCM2;
		sc->sc_pcm4 = ICH_SIS_PCM4;
		sc->sc_pcm6 = ICH_SIS_PCM6;
		/* Un-mute output. From Linux. */
		bus_space_write_4(sc->iot, sc->aud_ioh, ICH_SIS_NV_CTL,
		    bus_space_read_4(sc->iot, sc->aud_ioh, ICH_SIS_NV_CTL) |
		    ICH_SIS_CTL_UNMUTE);
	} else {
		sc->sc_sts_reg = ICH_STS;
		sc->sc_sample_shift = 1;
		sc->sc_pcm246_mask = ICH_PCM246_MASK;
		sc->sc_pcm2 = ICH_PCM2;
		sc->sc_pcm4 = ICH_PCM4;
		sc->sc_pcm6 = ICH_PCM6;
	}

	/* Workaround for a 440MX B-stepping erratum */
	sc->sc_dmamap_flags = BUS_DMA_COHERENT;
	if (d->id == PCIID_440MX) {
		sc->sc_dmamap_flags |= BUS_DMA_NOCACHE;
		aprint_normal_dev(self, "DMA bug workaround enabled\n");
	}

	/* Set up DMA lists. */
	sc->pcmo.qptr = sc->pcmi.qptr = sc->mici.qptr = 0;
	auich_alloc_cdata(sc);

	DPRINTF(ICH_DEBUG_DMA, ("auich_attach: lists %p %p %p\n",
	    sc->pcmo.dmalist, sc->pcmi.dmalist, sc->mici.dmalist));

	/* Modem codecs are always the secondary codec on ICH */
	sc->sc_codecnum = sc->sc_codectype == AC97_CODEC_TYPE_MODEM ? 1 : 0;

	sc->host_if.arg = sc;
	sc->host_if.attach = auich_attach_codec;
	sc->host_if.read = auich_read_codec;
	sc->host_if.write = auich_write_codec;
	sc->host_if.reset = auich_reset_codec;
	sc->host_if.flags = auich_flags_codec;
	sc->host_if.spdif_event = auich_spdif_event;

	subdev = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_SUBSYS_ID_REG);
	switch (subdev) {
	case 0x202f161f:	/* Gateway 7326GZ */
	case 0x203a161f:	/* Gateway 4028GZ */
	case 0x204c161f:	/* Kvazar-Micro Senator 3592XT */
	case 0x8144104d:	/* Sony VAIO PCG-TR* */
	case 0x8197104d:	/* Sony S1XP */
	case 0x81c0104d:	/* Sony VAIO type T */
	case 0x81c5104d:	/* Sony VAIO VGN-B1XP */
		sc->sc_codecflags = AC97_HOST_INVERTED_EAMP;
		break;
	default:
		sc->sc_codecflags = 0;
		break;
	}

	if (ac97_attach_type(&sc->host_if, self, sc->sc_codectype,
	    &sc->sc_lock) != 0)
		return;

	mutex_enter(&sc->sc_lock);
	sc->codec_if->vtbl->unlock(sc->codec_if);
	sc->sc_fixedrate = AC97_IS_FIXED_RATE(sc->codec_if);

	/* setup audio_format */
	if (sc->sc_codectype == AC97_CODEC_TYPE_AUDIO) {
		memcpy(sc->sc_audio_formats, auich_audio_formats, sizeof(auich_audio_formats));
		if (!AC97_IS_4CH(sc->codec_if))
			AUFMT_INVALIDATE(&sc->sc_audio_formats[AUICH_FORMATS_4CH]);
		if (!AC97_IS_6CH(sc->codec_if))
			AUFMT_INVALIDATE(&sc->sc_audio_formats[AUICH_FORMATS_6CH]);
		if (AC97_IS_FIXED_RATE(sc->codec_if)) {
			for (i = 0; i < AUICH_AUDIO_NFORMATS; i++) {
				sc->sc_audio_formats[i].frequency_type = 1;
				sc->sc_audio_formats[i].frequency[0] = 48000;
			}
		}
		mutex_exit(&sc->sc_lock);
		if (0 != auconv_create_encodings(sc->sc_audio_formats, AUICH_AUDIO_NFORMATS,
						 &sc->sc_encodings))
			return;
		if (0 != auconv_create_encodings(auich_spdif_formats, AUICH_SPDIF_NFORMATS,
						 &sc->sc_spdif_encodings))
			return;
	} else {
		mutex_exit(&sc->sc_lock);
		memcpy(sc->sc_modem_formats, auich_modem_formats, sizeof(auich_modem_formats));
		if (0 != auconv_create_encodings(sc->sc_modem_formats, AUICH_MODEM_NFORMATS,
						 &sc->sc_encodings))
			return;
	}

	/* Watch for power change */
	if (!pmf_device_register(self, NULL, auich_resume))
		aprint_error_dev(self, "couldn't establish power handler\n");

	config_interrupts(self, auich_finish_attach);

	/* sysctl setup */
	if (sc->sc_fixedrate && sc->sc_codectype == AC97_CODEC_TYPE_AUDIO)
		return;

	err = sysctl_createv(&sc->sc_log, 0, NULL, NULL, 0,
			     CTLTYPE_NODE, "hw", NULL, NULL, 0, NULL, 0,
			     CTL_HW, CTL_EOL);
	if (err != 0)
		goto sysctl_err;
	err = sysctl_createv(&sc->sc_log, 0, NULL, &node, 0,
			     CTLTYPE_NODE, device_xname(self), NULL, NULL, 0,
			     NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL);
	if (err != 0)
		goto sysctl_err;
	node_mib = node->sysctl_num;

	if (!sc->sc_fixedrate) {
		/* passing the sc address instead of &sc->sc_ac97_clock */
		err = sysctl_createv(&sc->sc_log, 0, NULL, &node_ac97clock,
				     CTLFLAG_READWRITE,
				     CTLTYPE_INT, "ac97rate",
				     SYSCTL_DESCR("AC'97 codec link rate"),
				     auich_sysctl_verify, 0, (void *)sc, 0,
				     CTL_HW, node_mib, CTL_CREATE, CTL_EOL);
		if (err != 0)
			goto sysctl_err;
		sc->sc_ac97_clock_mib = node_ac97clock->sysctl_num;
	}

	return;

 sysctl_err:
	printf("%s: failed to add sysctl nodes. (%d)\n",
	       device_xname(self), err);
	return;			/* failure of sysctl is not fatal. */
}
static void
auacer_attach(struct device *parent, struct device *self, void *aux)
{
	struct auacer_softc *sc;
	struct pci_attach_args *pa;
	pci_intr_handle_t ih;
	bus_size_t aud_size;
	pcireg_t v;
	const char *intrstr;
	int i;

	sc = (struct auacer_softc *)self;
	pa = aux;
	aprint_normal(": Acer Labs M5455 Audio controller\n");

	if (pci_mapreg_map(pa, 0x10, PCI_MAPREG_TYPE_IO, 0, &sc->iot,
		&sc->aud_ioh, NULL, &aud_size)) {
		aprint_error(": can't map i/o space\n");
		return;
	}

	sc->sc_pc = pa->pa_pc;
	sc->sc_pt = pa->pa_tag;
	sc->dmat = pa->pa_dmat;

	sc->sc_dmamap_flags = BUS_DMA_COHERENT;	/* XXX remove */

	/* enable bus mastering */
	v = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG);
	pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG,
	    v | PCI_COMMAND_MASTER_ENABLE);

	/* Map and establish the interrupt. */
	if (pci_intr_map(pa, &ih)) {
		aprint_error_dev(&sc->sc_dev, "can't map interrupt\n");
		return;
	}
	intrstr = pci_intr_string(pa->pa_pc, ih);
	sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_AUDIO,
	    auacer_intr, sc);
	if (sc->sc_ih == NULL) {
		aprint_error_dev(&sc->sc_dev, "can't establish interrupt");
		if (intrstr != NULL)
			aprint_normal(" at %s", intrstr);
		aprint_normal("\n");
		return;
	}
	aprint_normal_dev(&sc->sc_dev, "interrupting at %s\n", intrstr);

	strlcpy(sc->sc_audev.name, "M5455 AC97", MAX_AUDIO_DEV_LEN);
	snprintf(sc->sc_audev.version, MAX_AUDIO_DEV_LEN,
		 "0x%02x", PCI_REVISION(pa->pa_class));
	strlcpy(sc->sc_audev.config, device_xname(&sc->sc_dev), MAX_AUDIO_DEV_LEN);

	/* Set up DMA lists. */
	auacer_alloc_cdata(sc);
	sc->sc_pcmo.dmalist = sc->sc_cdata->ic_dmalist_pcmo;
	sc->sc_pcmo.ptr = 0;
	sc->sc_pcmo.port = ALI_BASE_PO;

	DPRINTF(ALI_DEBUG_DMA, ("auacer_attach: lists %p\n",
	    sc->sc_pcmo.dmalist));

	sc->host_if.arg = sc;
	sc->host_if.attach = auacer_attach_codec;
	sc->host_if.read = auacer_read_codec;
	sc->host_if.write = auacer_write_codec;
	sc->host_if.reset = auacer_reset_codec;

	if (ac97_attach(&sc->host_if, self) != 0)
		return;

	/* setup audio_format */
	memcpy(sc->sc_formats, auacer_formats, sizeof(auacer_formats));
	if (!AC97_IS_4CH(sc->codec_if))
		AUFMT_INVALIDATE(&sc->sc_formats[AUACER_FORMATS_4CH]);
	if (!AC97_IS_6CH(sc->codec_if))
		AUFMT_INVALIDATE(&sc->sc_formats[AUACER_FORMATS_6CH]);
	if (AC97_IS_FIXED_RATE(sc->codec_if)) {
		for (i = 0; i < AUACER_NFORMATS; i++) {
			sc->sc_formats[i].frequency_type = 1;
			sc->sc_formats[i].frequency[0] = 48000;
		}
	}

	if (0 != auconv_create_encodings(sc->sc_formats, AUACER_NFORMATS,
					 &sc->sc_encodings)) {
		return;
	}

	audio_attach_mi(&auacer_hw_if, sc, &sc->sc_dev);

	auacer_reset(sc);

	if (!pmf_device_register(self, NULL, auacer_resume))
		aprint_error_dev(self, "couldn't establish power handler\n");
}
Exemple #3
0
static void
auvia_attach(device_t parent, device_t self, void *aux)
{
	struct pci_attach_args *pa;
	struct auvia_softc *sc;
	const char *intrstr;
	pci_chipset_tag_t pc;
	pcitag_t pt;
	pci_intr_handle_t ih;
	pcireg_t pr;
	int r;
	const char *revnum;	/* VT823xx revision number */
	char intrbuf[PCI_INTRSTR_LEN];

	pa = aux;
	sc = device_private(self);
	sc->sc_dev = self;
	intrstr = NULL;
	pc = pa->pa_pc;
	pt = pa->pa_tag;
	revnum = NULL;

	aprint_naive(": Audio controller\n");

	sc->sc_play.sc_base = AUVIA_PLAY_BASE;
	sc->sc_record.sc_base = AUVIA_RECORD_BASE;
	if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_VIATECH_VT8233_AC97) {
		sc->sc_flags |= AUVIA_FLAGS_VT8233;
		sc->sc_play.sc_base = VIA8233_MP_BASE;
		sc->sc_record.sc_base = VIA8233_WR_BASE;
	}

	if (pci_mapreg_map(pa, 0x10, PCI_MAPREG_TYPE_IO, 0, &sc->sc_iot,
		&sc->sc_ioh, NULL, &sc->sc_iosize)) {
		aprint_error(": can't map i/o space\n");
		return;
	}

	sc->sc_dmat = pa->pa_dmat;
	sc->sc_pc = pc;
	sc->sc_pt = pt;

	r = PCI_REVISION(pa->pa_class);
	if (sc->sc_flags & AUVIA_FLAGS_VT8233) {
		snprintf(sc->sc_revision, sizeof(sc->sc_revision), "0x%02X", r);
		switch(r) {
		case VIA_REV_8233PRE:
			/* same as 8233, but should not be in the market */
			revnum = "3-Pre";
			break;
		case VIA_REV_8233C:
			/* 2 rec, 4 pb, 1 multi-pb */
			revnum = "3C";
			break;
		case VIA_REV_8233:
			/* 2 rec, 4 pb, 1 multi-pb, spdif */
			revnum = "3";
			break;
		case VIA_REV_8233A:
			/* 1 rec, 1 multi-pb, spdif */
			revnum = "3A";
			break;
		default:
			break;
		}
		if (r >= VIA_REV_8237)
			revnum = "7";
		else if (r >= VIA_REV_8235) /* 2 rec, 4 pb, 1 multi-pb, spdif */
			revnum = "5";
		aprint_normal(": VIA Technologies VT823%s AC'97 Audio "
		    "(rev %s)\n", revnum, sc->sc_revision);
	} else {
		sc->sc_revision[1] = '\0';
		if (r == 0x20) {
			sc->sc_revision[0] = 'H';
		} else if ((r >= 0x10) && (r <= 0x14)) {
			sc->sc_revision[0] = 'A' + (r - 0x10);
		} else {
			snprintf(sc->sc_revision, sizeof(sc->sc_revision),
			    "0x%02X", r);
		}

		aprint_normal(": VIA Technologies VT82C686A AC'97 Audio "
		    "(rev %s)\n", sc->sc_revision);
	}

	if (pci_intr_map(pa, &ih)) {
		aprint_error(": couldn't map interrupt\n");
		bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_iosize);
		return;
	}
	intrstr = pci_intr_string(pc, ih, intrbuf, sizeof(intrbuf));

	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
	mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_AUDIO);

	sc->sc_ih = pci_intr_establish(pc, ih, IPL_AUDIO, auvia_intr, sc);
	if (sc->sc_ih == NULL) {
		aprint_error_dev(sc->sc_dev, "couldn't establish interrupt");
		if (intrstr != NULL)
			aprint_error(" at %s", intrstr);
		aprint_error("\n");
		bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_iosize);
		mutex_destroy(&sc->sc_lock);
		mutex_destroy(&sc->sc_intr_lock);
		return;
	}

	aprint_normal_dev(sc->sc_dev, "interrupting at %s\n", intrstr);

	/* disable SBPro compat & others */
	pr = pci_conf_read(pc, pt, AUVIA_PCICONF_JUNK);

	pr &= ~AUVIA_PCICONF_ENABLES; /* clear compat function enables */
	/* XXX what to do about MIDI, FM, joystick? */

	pr |= (AUVIA_PCICONF_ACLINKENAB | AUVIA_PCICONF_ACNOTRST
		| AUVIA_PCICONF_ACVSR | AUVIA_PCICONF_ACSGD);

	pr &= ~(AUVIA_PCICONF_ACFM | AUVIA_PCICONF_ACSB);

	pci_conf_write(pc, pt, AUVIA_PCICONF_JUNK, pr);

	sc->host_if.arg = sc;
	sc->host_if.attach = auvia_attach_codec;
	sc->host_if.read = auvia_read_codec;
	sc->host_if.write = auvia_write_codec;
	sc->host_if.reset = auvia_reset_codec;
	sc->host_if.spdif_event = auvia_spdif_event;

	if ((r = ac97_attach(&sc->host_if, self, &sc->sc_lock)) != 0) {
		aprint_error_dev(sc->sc_dev, "can't attach codec (error 0x%X)\n", r);
		pci_intr_disestablish(pc, sc->sc_ih);
		bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_iosize);
		mutex_destroy(&sc->sc_lock);
		mutex_destroy(&sc->sc_intr_lock);
		return;
	}

	/* setup audio_format */
	memcpy(sc->sc_formats, auvia_formats, sizeof(auvia_formats));
	mutex_enter(&sc->sc_lock);
	if (sc->sc_play.sc_base != VIA8233_MP_BASE || !AC97_IS_4CH(sc->codec_if)) {
		AUFMT_INVALIDATE(&sc->sc_formats[AUVIA_FORMATS_4CH_8]);
		AUFMT_INVALIDATE(&sc->sc_formats[AUVIA_FORMATS_4CH_16]);
	}
	if (sc->sc_play.sc_base != VIA8233_MP_BASE || !AC97_IS_6CH(sc->codec_if)) {
		AUFMT_INVALIDATE(&sc->sc_formats[AUVIA_FORMATS_6CH_8]);
		AUFMT_INVALIDATE(&sc->sc_formats[AUVIA_FORMATS_6CH_16]);
	}
	if (AC97_IS_FIXED_RATE(sc->codec_if)) {
		for (r = 0; r < AUVIA_NFORMATS; r++) {
			sc->sc_formats[r].frequency_type = 1;
			sc->sc_formats[r].frequency[0] = 48000;
		}
	}
	mutex_exit(&sc->sc_lock);

	if (0 != auconv_create_encodings(sc->sc_formats, AUVIA_NFORMATS,
					 &sc->sc_encodings)) {
		mutex_enter(&sc->sc_lock);
		sc->codec_if->vtbl->detach(sc->codec_if);
		mutex_exit(&sc->sc_lock);
		pci_intr_disestablish(pc, sc->sc_ih);
		bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_iosize);
		mutex_destroy(&sc->sc_lock);
		mutex_destroy(&sc->sc_intr_lock);
		aprint_error_dev(sc->sc_dev, "can't create encodings\n");
		return;
	}
	if (0 != auconv_create_encodings(auvia_spdif_formats,
	    AUVIA_SPDIF_NFORMATS, &sc->sc_spdif_encodings)) {
		mutex_enter(&sc->sc_lock);
		sc->codec_if->vtbl->detach(sc->codec_if);
		mutex_exit(&sc->sc_lock);
		pci_intr_disestablish(pc, sc->sc_ih);
		bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_iosize);
		mutex_destroy(&sc->sc_lock);
		mutex_destroy(&sc->sc_intr_lock);
		aprint_error_dev(sc->sc_dev, "can't create spdif encodings\n");
		return;
	}

	if (!pmf_device_register(self, NULL, auvia_resume))
		aprint_error_dev(self, "couldn't establish power handler\n");

	audio_attach_mi(&auvia_hw_if, sc, sc->sc_dev);
	mutex_enter(&sc->sc_lock);
	sc->codec_if->vtbl->unlock(sc->codec_if);
	mutex_exit(&sc->sc_lock);
	return;
}