- Introduce the bridge drivers for Sound Blaser, GUS and Crystal

Semiconductor CS461x/428x.
- Add support for GUS and CS461x/428x pcm.

Bridges reviewed by:			dfr, cg
GUS non-PnP support submitted by:	Ville-Pertti Keinonen <will@iki.fi>
GUS PnP support tested by:		Michiru Saito <mich@mtci.ne.jp>
This commit is contained in:
tanimura 1999-11-22 06:07:49 +00:00
parent 913c1ce734
commit 05e9b4af3e
12 changed files with 8620 additions and 64 deletions

46
sys/dev/sound/chip.h Normal file
View File

@ -0,0 +1,46 @@
/*-
* Copyright (c) 1999 Seigo Tanimura
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
/*
* These are the function codes assigned to the children of
* sound cards.
*/
enum {
SCF_PCM,
SCF_MIDI,
SCF_SYNTH,
};
/*
* This is the device information struct, used by
* sndcard device to pass the device function code
* to the driver.
*/
struct sndcard_func {
int func;
};

647
sys/dev/sound/isa/gusc.c Normal file
View File

@ -0,0 +1,647 @@
/*-
* Copyright (c) 1999 Seigo Tanimura
* Copyright (c) 1999 Ville-Pertti Keinonen
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include "gusc.h"
#include "isa.h"
#include "pnp.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <machine/resource.h>
#include <machine/bus.h>
#include <machine/clock.h>
#include <sys/rman.h>
#include <sys/soundcard.h>
#include <dev/sound/pcm/sound.h>
#include <dev/sound/chip.h>
#include "bus_if.h"
#if NISA > 0
#include <isa/isavar.h>
#include <isa/isa_common.h>
#ifdef __alpha__ /* XXX workaround a stupid warning */
#include <alpha/isa/isavar.h>
#endif
#endif /* NISA > 0 */
#if NGUSC > 0
#define LOGICALID_PCM 0x0000561e
#define LOGICALID_OPL 0x0300561e
#define LOGICALID_MIDI 0x0400561e
/* Interrupt handler. */
struct gusc_ihandler {
void (*intr)(void *);
void *arg;
};
/* Here is the parameter structure per a device. */
struct gusc_softc {
device_t dev; /* device */
int io_rid[3]; /* io port rids */
struct resource *io[3]; /* io port resources */
int io_alloced[3]; /* io port alloc flag */
int irq_rid; /* irq rids */
struct resource *irq; /* irq resources */
int irq_alloced; /* irq alloc flag */
int drq_rid[2]; /* drq rids */
struct resource *drq[2]; /* drq resources */
int drq_alloced[2]; /* drq alloc flag */
/* Interrupts are shared (XXX non-PnP only?) */
struct gusc_ihandler midi_intr;
struct gusc_ihandler pcm_intr;
};
typedef struct gusc_softc *sc_p;
#if NISA > 0
static int gusc_probe(device_t dev);
static int gusc_attach(device_t dev);
static int gusisa_probe(device_t dev);
static void gusc_intr(void *);
#endif /* NISA > 0 */
static struct resource *gusc_alloc_resource(device_t bus, device_t child, int type, int *rid,
u_long start, u_long end, u_long count, u_int flags);
static int gusc_release_resource(device_t bus, device_t child, int type, int rid,
struct resource *r);
#if notyet
static device_t find_masterdev(sc_p scp);
#endif /* notyet */
static int alloc_resource(sc_p scp);
static int release_resource(sc_p scp);
static devclass_t gusc_devclass;
#if NISA > 0
static int
gusc_probe(device_t dev)
{
u_int32_t vend_id, logical_id;
char *s;
struct sndcard_func *func;
vend_id = isa_get_vendorid(dev);
if (vend_id == 0)
return gusisa_probe(dev);
#if NPNP > 0
logical_id = isa_get_logicalid(dev);
s = NULL;
if (vend_id == 0x0100561e) { /* Gravis */
switch (logical_id) {
case LOGICALID_PCM:
s = "Gravis UltraSound Plug & Play PCM";
func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT);
if (func == NULL)
return (ENOMEM);
bzero(func, sizeof(*func));
func->func = SCF_PCM;
device_add_child(dev, "pcm", -1, func);
break;
#if notyet
case LOGICALID_OPL:
s = "Gravis UltraSound Plug & Play OPL";
func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT);
if (func == NULL)
return (ENOMEM);
bzero(func, sizeof(*func));
func->func = SCF_SYNTH;
device_add_child(dev, "midi", -1, func);
break;
case LOGICALID_MIDI:
s = "Gravis UltraSound Plug & Play MIDI";
func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT);
if (func == NULL)
return (ENOMEM);
bzero(func, sizeof(*func));
func->func = SCF_MIDI;
device_add_child(dev, "midi", -1, func);
break;
#endif /* notyet */
}
}
if (s != NULL) {
device_set_desc(dev, s);
return (0);
}
#endif /* NPNP > 0 */
return (ENXIO);
}
static void
port_wr(struct resource *r, int i, unsigned char v)
{
bus_space_write_1(rman_get_bustag(r), rman_get_bushandle(r), i, v);
}
static int
port_rd(struct resource *r, int i)
{
return bus_space_read_1(rman_get_bustag(r), rman_get_bushandle(r), i);
}
/*
* Probe for an old (non-PnP) GUS card on the ISA bus.
*/
static int
gusisa_probe(device_t dev)
{
struct resource *res, *res2;
int base, rid, rid2, s, flags;
unsigned char val;
base = isa_get_port(dev);
flags = device_get_flags(dev);
rid = 1;
res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, base + 0x100,
base + 0x107, 8, RF_ACTIVE);
if (res == NULL)
return ENXIO;
res2 = NULL;
/*
* Check for the presence of some GUS card. Reset the card,
* then see if we can access the memory on it.
*/
port_wr(res, 3, 0x4c);
port_wr(res, 5, 0);
DELAY(30 * 1000);
port_wr(res, 3, 0x4c);
port_wr(res, 5, 1);
DELAY(30 * 1000);
s = splhigh();
/* Write to DRAM. */
port_wr(res, 3, 0x43); /* Register select */
port_wr(res, 4, 0); /* Low addr */
port_wr(res, 5, 0); /* Med addr */
port_wr(res, 3, 0x44); /* Register select */
port_wr(res, 4, 0); /* High addr */
port_wr(res, 7, 0x55); /* DRAM */
/* Read from DRAM. */
port_wr(res, 3, 0x43); /* Register select */
port_wr(res, 4, 0); /* Low addr */
port_wr(res, 5, 0); /* Med addr */
port_wr(res, 3, 0x44); /* Register select */
port_wr(res, 4, 0); /* High addr */
val = port_rd(res, 7); /* DRAM */
splx(s);
if (val != 0x55)
goto fail;
rid2 = 0;
res2 = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid2, base, base, 1,
RF_ACTIVE);
if (res2 == NULL)
goto fail;
s = splhigh();
port_wr(res2, 0x0f, 0x20);
val = port_rd(res2, 0x0f);
splx(s);
if (val == 0xff || (val & 0x06) == 0)
val = 0;
else {
val = port_rd(res2, 0x506); /* XXX Out of range. */
if (val == 0xff)
val = 0;
}
bus_release_resource(dev, SYS_RES_IOPORT, rid2, res2);
bus_release_resource(dev, SYS_RES_IOPORT, rid, res);
if (val >= 10) {
struct sndcard_func *func;
/* Looks like a GUS MAX. Set the rest of the resources. */
bus_set_resource(dev, SYS_RES_IOPORT, 2, base + 0x10c, 8);
if (flags & DV_F_DUAL_DMA)
bus_set_resource(dev, SYS_RES_DRQ, 1,
flags & DV_F_DRQ_MASK, 1);
#if notyet
/* We can support the CS4231 and MIDI devices. */
func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT);
if (func == NULL)
return ENOMEM;
bzero(func, sizeof *func);
func->func = SCF_MIDI;
device_add_child(dev, "midi", -1, func);
#endif /* notyet */
func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT);
if (func == NULL)
printf("xxx: gus pcm not attached, out of memory\n");
else {
bzero(func, sizeof *func);
func->func = SCF_PCM;
device_add_child(dev, "pcm", -1, func);
}
device_set_desc(dev, "Gravis UltraSound MAX");
return 0;
} else {
/*
* TODO: Support even older GUS cards. MIDI should work on
* all models.
*/
return ENXIO;
}
fail:
bus_release_resource(dev, SYS_RES_IOPORT, rid, res);
return ENXIO;
}
static int
gusc_attach(device_t dev)
{
sc_p scp;
int unit;
void *ih;
scp = device_get_softc(dev);
unit = device_get_unit(dev);
bzero(scp, sizeof(*scp));
scp->dev = dev;
if (alloc_resource(scp)) {
release_resource(scp);
return (ENXIO);
}
bus_setup_intr(dev, scp->irq, INTR_TYPE_TTY, gusc_intr, scp, &ih);
bus_generic_attach(dev);
return (0);
}
/*
* Handle interrupts on GUS devices until there aren't any left.
*/
static void
gusc_intr(void *arg)
{
sc_p scp = (sc_p)arg;
int did_something;
do {
did_something = 0;
if (scp->pcm_intr.intr != NULL &&
(port_rd(scp->io[2], 2) & 1)) {
(*scp->pcm_intr.intr)(scp->pcm_intr.arg);
did_something = 1;
}
#if notyet
if (scp->midi_intr.intr != NULL &&
(port_rd(scp->io[1], 0) & 0x80)) {
(*scp->midi_intr.intr)(scp->midi_intr.arg);
did_something = 1;
}
#endif /* notyet */
} while (did_something != 0);
}
#endif /* NISA > 0 */
static struct resource *
gusc_alloc_resource(device_t bus, device_t child, int type, int *rid,
u_long start, u_long end, u_long count, u_int flags)
{
sc_p scp;
int *alloced, rid_max, alloced_max;
struct resource **res;
scp = device_get_softc(bus);
switch (type) {
case SYS_RES_IOPORT:
alloced = scp->io_alloced;
res = scp->io;
rid_max = 2;
alloced_max = 2; /* pcm + midi (more to include synth) */
break;
case SYS_RES_IRQ:
alloced = &scp->irq_alloced;
res = &scp->irq;
rid_max = 0;
alloced_max = 2; /* pcm and midi share the single irq. */
break;
case SYS_RES_DRQ:
alloced = scp->drq_alloced;
res = scp->drq;
rid_max = 1;
alloced_max = 1;
break;
default:
return (NULL);
}
if (*rid > rid_max || alloced[*rid] == alloced_max)
return (NULL);
alloced[*rid]++;
return (res[*rid]);
}
static int
gusc_release_resource(device_t bus, device_t child, int type, int rid,
struct resource *r)
{
sc_p scp;
int *alloced, rid_max;
scp = device_get_softc(bus);
switch (type) {
case SYS_RES_IOPORT:
alloced = scp->io_alloced;
rid_max = 2;
break;
case SYS_RES_IRQ:
alloced = &scp->irq_alloced;
rid_max = 0;
break;
case SYS_RES_DRQ:
alloced = scp->drq_alloced;
rid_max = 1;
break;
default:
return (1);
}
if (rid > rid_max || alloced[rid] == 0)
return (1);
alloced[rid]--;
return (0);
}
static int
gusc_setup_intr(device_t dev, device_t child, struct resource *irq,
int flags, driver_intr_t *intr, void *arg, void **cookiep)
{
sc_p scp = (sc_p)device_get_softc(dev);
devclass_t devclass;
devclass = device_get_devclass(child);
if (strcmp(devclass_get_name(devclass), "midi") == 0) {
scp->midi_intr.intr = intr;
scp->midi_intr.arg = arg;
return 0;
} else if (strcmp(devclass_get_name(devclass), "pcm") == 0) {
scp->pcm_intr.intr = intr;
scp->pcm_intr.arg = arg;
return 0;
}
return bus_generic_setup_intr(dev, child, irq, flags, intr,
arg, cookiep);
}
#if notyet
static device_t
find_masterdev(sc_p scp)
{
int i, units;
devclass_t devclass;
device_t dev;
devclass = device_get_devclass(scp->dev);
units = devclass_get_maxunit(devclass);
dev = NULL;
for (i = 0 ; i < units ; i++) {
dev = devclass_get_device(devclass, i);
if (isa_get_vendorid(dev) == isa_get_vendorid(scp->dev)
&& isa_get_logicalid(dev) == LOGICALID_PCM
&& isa_get_serial(dev) == isa_get_serial(scp->dev))
break;
}
if (i == units)
return (NULL);
return (dev);
}
#endif /* notyet */
static int io_range[3] = {0x10, 0x4, 0x4};
static int
alloc_resource(sc_p scp)
{
int i;
#if notyet
device_t dev;
#endif /* notyet */
switch(isa_get_logicalid(scp->dev)) {
case LOGICALID_PCM:
default: /* XXX Non-PnP */
for (i = 0 ; i < sizeof(scp->io) / sizeof(*scp->io) ; i++) {
if (scp->io[i] == NULL) {
scp->io_rid[i] = i;
scp->io[i] = bus_alloc_resource(scp->dev, SYS_RES_IOPORT, &scp->io_rid[i],
0, ~0, io_range[i], RF_ACTIVE);
if (scp->io[i] == NULL)
return (1);
scp->io_alloced[i] = 0;
}
}
if (scp->irq == NULL) {
scp->irq_rid = 0;
scp->irq = bus_alloc_resource(scp->dev, SYS_RES_IRQ, &scp->irq_rid,
0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
if (scp->irq == NULL)
return (1);
scp->irq_alloced = 0;
}
for (i = 0 ; i < sizeof(scp->drq) / sizeof(*scp->drq) ; i++) {
if (scp->drq[i] == NULL) {
scp->drq_rid[i] = i;
scp->drq[i] = bus_alloc_resource(scp->dev, SYS_RES_DRQ, &scp->drq_rid[i],
0, ~0, 1, RF_ACTIVE);
if (scp->drq[i] == NULL)
return (1);
scp->drq_alloced[i] = 0;
}
}
break;
#if notyet
case LOGICALID_OPL:
if (scp->io[0] == NULL) {
scp->io_rid[0] = 0;
scp->io[0] = bus_alloc_resource(scp->dev, SYS_RES_IOPORT, &scp->io_rid[0],
0, ~0, io_range[0], RF_ACTIVE);
if (scp->io[0] == NULL)
return (1);
scp->io_alloced[0] = 0;
}
break;
case LOGICALID_MIDI:
if (scp->io[0] == NULL) {
scp->io_rid[0] = 0;
scp->io[0] = bus_alloc_resource(scp->dev, SYS_RES_IOPORT, &scp->io_rid[0],
0, ~0, io_range[0], RF_ACTIVE);
if (scp->io[0] == NULL)
return (1);
scp->io_alloced[0] = 0;
}
if (scp->irq == NULL) {
/* The irq is shared with pcm audio. */
dev = find_masterdev(scp);
if (dev == NULL)
return (1);
scp->irq_rid = 0;
scp->irq = BUS_ALLOC_RESOURCE(dev, NULL, SYS_RES_IRQ, &scp->irq_rid,
0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
if (scp->irq == NULL)
return (1);
scp->irq_alloced = 0;
}
break;
#endif /* notyet */
}
return (0);
}
static int
release_resource(sc_p scp)
{
int i;
#if notyet
device_t dev;
#endif /* notyet */
switch(isa_get_logicalid(scp->dev)) {
case LOGICALID_PCM:
default: /* XXX Non-PnP */
for (i = 0 ; i < sizeof(scp->io) / sizeof(*scp->io) ; i++) {
if (scp->io[i] != NULL) {
bus_release_resource(scp->dev, SYS_RES_IOPORT, scp->io_rid[i], scp->io[i]);
scp->io[i] = NULL;
}
}
if (scp->irq != NULL) {
bus_release_resource(scp->dev, SYS_RES_IRQ, scp->irq_rid, scp->irq);
scp->irq = NULL;
}
for (i = 0 ; i < sizeof(scp->drq) / sizeof(*scp->drq) ; i++) {
if (scp->drq[i] != NULL) {
bus_release_resource(scp->dev, SYS_RES_DRQ, scp->drq_rid[i], scp->drq[i]);
scp->drq[i] = NULL;
}
}
break;
#if notyet
case LOGICALID_OPL:
if (scp->io[0] != NULL) {
bus_release_resource(scp->dev, SYS_RES_IOPORT, scp->io_rid[0], scp->io[0]);
scp->io[0] = NULL;
}
break;
case LOGICALID_MIDI:
if (scp->io[0] != NULL) {
bus_release_resource(scp->dev, SYS_RES_IOPORT, scp->io_rid[0], scp->io[0]);
scp->io[0] = NULL;
}
if (scp->irq != NULL) {
/* The irq is shared with pcm audio. */
dev = find_masterdev(scp);
if (dev == NULL)
return (1);
BUS_RELEASE_RESOURCE(dev, NULL, SYS_RES_IOPORT, scp->irq_rid, scp->irq);
scp->irq = NULL;
}
break;
#endif /* notyet */
}
return (0);
}
#if NISA > 0
static device_method_t gusc_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, gusc_probe),
DEVMETHOD(device_attach, gusc_attach),
DEVMETHOD(device_detach, bus_generic_detach),
DEVMETHOD(device_shutdown, bus_generic_shutdown),
DEVMETHOD(device_suspend, bus_generic_suspend),
DEVMETHOD(device_resume, bus_generic_resume),
/* Bus interface */
DEVMETHOD(bus_print_child, bus_generic_print_child),
DEVMETHOD(bus_alloc_resource, gusc_alloc_resource),
DEVMETHOD(bus_release_resource, gusc_release_resource),
DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
DEVMETHOD(bus_setup_intr, gusc_setup_intr),
DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
{ 0, 0 }
};
static driver_t gusc_driver = {
"gusc",
gusc_methods,
sizeof(struct gusc_softc),
};
/*
* gusc can be attached to an isa bus.
*/
DRIVER_MODULE(gusc, isa, gusc_driver, gusc_devclass, 0, 0);
#endif /* NISA > 0 */
#endif /* NGUSC > 0 */

View File

@ -34,6 +34,12 @@
/* board-specific include files */
#include <dev/sound/isa/mss.h>
#include <dev/sound/chip.h>
#include "gusc.h"
#if notyet
#include "midi.h"
#endif /* notyet */
#define abs(x) (((x) < 0) ? -(x) : (x))
@ -172,6 +178,7 @@ static pcm_channel mss_chantemplate = {
#define MD_OPTI931 0xB1
#define MD_OPTI925 0xB2
#define MD_GUSPNP 0xB8
#define MD_GUSMAX 0xB9
#define MD_YM0020 0xC1
#define MD_VIVO 0xD1
@ -241,7 +248,7 @@ opti_rd(struct mss_info *mss, u_char reg)
return port_rd(mss->conf_base, mss->opti_offset + 1);
}
#if NPNP > 0
#if NPNP > 0 || NGUSC > 0
static void
gus_wr(struct mss_info *mss, u_char reg, u_char value)
{
@ -255,7 +262,7 @@ gus_rd(struct mss_info *mss, u_char reg)
port_wr(mss->conf_base, 3, reg);
return port_rd(mss->conf_base, 5);
}
#endif
#endif /* NPNP > 0 || NGUSC > 0 */
static void
mss_release_resources(struct mss_info *mss, device_t dev)
@ -329,6 +336,64 @@ mss_alloc_resources(struct mss_info *mss, device_t dev)
return ok;
}
#if NGUSC > 0
/*
* XXX This might be better off in the gusc driver.
*/
static void
gusmax_setup(struct mss_info *mss, device_t dev, struct resource *alt)
{
static const unsigned char irq_bits[16] = {
0, 0, 0, 3, 0, 2, 0, 4, 0, 1, 0, 5, 6, 0, 0, 7
};
static const unsigned char dma_bits[8] = {
0, 1, 0, 2, 0, 3, 4, 5
};
device_t parent = device_get_parent(dev);
unsigned char irqctl, dmactl;
int s;
s = splhigh();
port_wr(alt, 0x0f, 0x05);
port_wr(alt, 0x00, 0x0c);
port_wr(alt, 0x0b, 0x00);
port_wr(alt, 0x0f, 0x00);
irqctl = irq_bits[isa_get_irq(parent)];
#if notyet
#if NMIDI > 0
/* Share the IRQ with the MIDI driver. */
irqctl |= 0x40;
#endif /* NMIDI > 0 */
#endif /* notyet */
dmactl = dma_bits[isa_get_drq(parent)];
if (device_get_flags(parent) & DV_F_DUAL_DMA)
dmactl |= dma_bits[device_get_flags(parent) & DV_F_DRQ_MASK]
<< 3;
/*
* Set the DMA and IRQ control latches.
*/
port_wr(alt, 0x00, 0x0c);
port_wr(alt, 0x0b, dmactl | 0x80);
port_wr(alt, 0x00, 0x4c);
port_wr(alt, 0x0b, irqctl);
port_wr(alt, 0x00, 0x0c);
port_wr(alt, 0x0b, dmactl);
port_wr(alt, 0x00, 0x4c);
port_wr(alt, 0x0b, irqctl);
port_wr(mss->conf_base, 2, 0);
port_wr(alt, 0x00, 0x0c);
port_wr(mss->conf_base, 2, 0);
splx(s);
}
#endif /* NGUSC > 0 */
static int
mss_init(struct mss_info *mss, device_t dev)
{
@ -356,8 +421,11 @@ mss_init(struct mss_info *mss, device_t dev)
opti_wr(mss, 6, 2); /* MCIR6: mss enable, sb disable */
opti_wr(mss, 5, 0x28); /* MCIR5: codec in exp. mode,fifo */
break;
#endif /* NPNP > 0 */
#if NPNP > 0 || NGUSC > 0
case MD_GUSPNP:
case MD_GUSMAX:
gus_wr(mss, 0x4c /* _URSTI */, 0);/* Pull reset */
DELAY(1000 * 30);
/* release reset and enable DAC */
@ -368,7 +436,15 @@ mss_init(struct mss_info *mss, device_t dev)
rid = 0;
alt = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
0, ~0, 1, RF_ACTIVE);
if (alt == NULL) {
printf("XXX couldn't init GUS PnP/MAX\n");
break;
}
port_wr(alt, 0, 0xC); /* enable int and dma */
#if NGUSC > 0
if (mss->bd_id == MD_GUSMAX)
gusmax_setup(mss, dev, alt);
#endif
bus_release_resource(dev, SYS_RES_IOPORT, rid, alt);
/*
@ -389,7 +465,8 @@ mss_init(struct mss_info *mss, device_t dev)
gus_wr(mss, 0x5b, tmp | 1);
BVDDB(printf("GUS: silicon rev %c\n", 'A' + ((tmp & 0xf) >> 4)));
break;
#endif
#endif /* NPNP > 0 || NGUSC > 0 */
case MD_YM0020:
conf_wr(mss, OPL3SAx_DMACONF, 0xa9); /* dma-b rec, dma-a play */
r6 = conf_rd(mss, OPL3SAx_DMACONF);
@ -1015,7 +1092,22 @@ wait_for_calibration(struct mss_info *mss)
n = ad_wait_init(mss, 1000);
if (n & MSS_IDXBUSY) printf("mss: Auto calibration timed out(1).\n");
for (t = 100; t > 0 && (ad_read(mss, 11) & 0x20) == 0; t--) DELAY(100);
/*
* There is no guarantee that we'll ever see ACI go on,
* calibration may finish before we get here.
*
* XXX Are there docs that even state that it might ever be
* visible off before calibration starts using any chip?
*/
if (mss->bd_id == MD_GUSMAX) {
/* 10 ms of busy-waiting is not reasonable normal behavior */
for (t = 100; t > 0 && (ad_read(mss, 11) & 0x20) == 0; t--)
;
if (t > 0 && t != 100)
printf("debug: ACI turned on: t = %d\n", t);
} else {
for (t = 100; t > 0 && (ad_read(mss, 11) & 0x20) == 0; t--) DELAY(100);
}
for (t = 100; t > 0 && ad_read(mss, 11) & 0x20; t--) DELAY(100);
}
@ -1290,9 +1382,11 @@ pnpmss_probe(device_t dev)
s = "OPTi925";
break;
#if 0
case 0x0000561e:
s = "GusPnP";
break;
#endif
case 0x01000000:
if (vend_id == 0x0100a90d) s = "CMI8330";
@ -1361,6 +1455,7 @@ pnpmss_attach(device_t dev)
mss->bd_id = MD_OPTI925;
break;
#if 0
case 0x0100561e: /* guspnp */
mss->bd_flags |= BD_F_MSS_OFFSET;
mss->io_rid = 2;
@ -1369,6 +1464,7 @@ pnpmss_attach(device_t dev)
mss->drq2_rid = 0;
mss->bd_id = MD_GUSPNP;
break;
#endif
default:
mss->bd_flags |= BD_F_MSS_OFFSET;
@ -1453,6 +1549,91 @@ opti931_intr(void *arg)
#endif /* NPNP > 0 */
#if NGUSC > 0
static int
guspcm_probe(device_t dev)
{
struct sndcard_func *func;
func = device_get_ivars(dev);
if (func == NULL || func->func != SCF_PCM)
return ENXIO;
device_set_desc(dev, "GUS CS4231");
return 0;
}
static int
guspcm_attach(device_t dev)
{
device_t parent = device_get_parent(dev);
struct mss_info *mss;
int base, flags;
unsigned char ctl;
mss = (struct mss_info *)malloc(sizeof *mss, M_DEVBUF, M_NOWAIT);
if (mss == NULL)
return ENOMEM;
bzero(mss, sizeof *mss);
mss->bd_flags = BD_F_MSS_OFFSET;
mss->io_rid = 2;
mss->conf_rid = 1;
mss->irq_rid = 0;
mss->drq1_rid = 1;
mss->drq2_rid = -1;
if (isa_get_vendorid(parent) == 0)
mss->bd_id = MD_GUSMAX;
else {
mss->bd_id = MD_GUSPNP;
mss->drq2_rid = 0;
goto skip_setup;
}
flags = device_get_flags(parent);
if (flags & DV_F_DUAL_DMA)
mss->drq2_rid = 0;
mss->conf_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &mss->conf_rid,
0, ~0, 8, RF_ACTIVE);
if (mss->conf_base == NULL) {
mss_release_resources(mss, dev);
return ENXIO;
}
base = isa_get_port(parent);
ctl = 0x40; /* CS4231 enable */
if (isa_get_drq(dev) > 3)
ctl |= 0x10; /* 16-bit dma channel 1 */
if ((flags & DV_F_DUAL_DMA) != 0 && (flags & DV_F_DRQ_MASK) > 3)
ctl |= 0x20; /* 16-bit dma channel 2 */
ctl |= (base >> 4) & 0x0f; /* 2X0 -> 3XC */
port_wr(mss->conf_base, 6, ctl);
skip_setup:
return mss_doattach(dev, mss);
}
static device_method_t guspcm_methods[] = {
DEVMETHOD(device_probe, guspcm_probe),
DEVMETHOD(device_attach, guspcm_attach),
{ 0, 0 }
};
static driver_t guspcm_driver = {
"pcm",
guspcm_methods,
sizeof(snddev_info),
};
DRIVER_MODULE(guspcm, gusc, guspcm_driver, pcm_devclass, 0, 0);
#endif /* NGUSC > 0 */
static int
mssmix_init(snd_mixer *m)
{
@ -1472,6 +1653,7 @@ mssmix_init(snd_mixer *m)
break;
case MD_GUSPNP:
case MD_GUSMAX:
/* this is only necessary in mode 3 ... */
ad_write(mss, 22, 0x88);
ad_write(mss, 23, 0x88);
@ -1631,6 +1813,7 @@ msschan_getcaps(void *data)
break;
case MD_GUSPNP:
case MD_GUSMAX:
return &guspnp_caps;
break;

View File

@ -34,8 +34,11 @@
#include <dev/sound/pcm/sound.h>
#if NPCM > 0
#include "sbc.h"
#define __SB_MIXER_C__ /* XXX warning... */
#include <dev/sound/isa/sb.h>
#include <dev/sound/chip.h>
/* channel interface */
static void *sbchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir);
@ -1258,25 +1261,6 @@ sbpnp_probe(device_t dev)
u_int32_t logical_id = isa_get_logicalid(dev);
switch(logical_id) {
case 0x43008c0e: /* CTL0043 */
case 0x01008c0e: /* CTL0001 */
s = "Vibra16X";
break;
case 0x31008c0e: /* CTL0031 */
case 0x41008c0e: /* CTL0041 */
case 0x42008c0e: /* CTL0042 */
s = "SB16 PnP";
break;
case 0x44008c0e: /* CTL0044 */
s = "Creative SB AWE64 Gold";
break;
case 0x45008c0e: /* CTL0045 */
s = "Creative AWE64 PnP";
break;
case 0x01100000: /* @@@1001 */
s = "Avance Asound 110";
break;
@ -1299,7 +1283,7 @@ sbpnp_probe(device_t dev)
}
if (s) {
device_set_desc(dev, s);
return 0;
return (0);
}
return ENXIO;
}
@ -1342,6 +1326,67 @@ DRIVER_MODULE(sbpnp, isa, sbpnp_driver, pcm_devclass, 0, 0);
#endif /* NPNP > 0 */
#if NSBC > 0
#define DESCSTR " PCM Audio"
static int
sbsbc_probe(device_t dev)
{
char *s = NULL;
struct sndcard_func *func;
/* The parent device has already been probed. */
func = device_get_ivars(dev);
if (func == NULL || func->func != SCF_PCM)
return (ENXIO);
s = "SB PCM Audio";
device_set_desc(dev, s);
return 0;
}
static int
sbsbc_attach(device_t dev)
{
struct sb_info *sb;
u_int32_t vend_id;
device_t sbc;
sbc = device_get_parent(dev);
vend_id = isa_get_vendorid(sbc);
sb = (struct sb_info *)malloc(sizeof *sb, M_DEVBUF, M_NOWAIT);
if (!sb) return ENXIO;
bzero(sb, sizeof *sb);
switch(vend_id) {
case 0xf0008c0e:
case 0x10019305:
case 0x20019305:
/* XXX add here the vend_id for other vibra16X cards... */
sb->bd_flags = BD_F_SB16X;
}
return sb_doattach(dev, sb);
}
static device_method_t sbsbc_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, sbsbc_probe),
DEVMETHOD(device_attach, sbsbc_attach),
{ 0, 0 }
};
static driver_t sbsbc_driver = {
"pcm",
sbsbc_methods,
sizeof(snddev_info),
};
DRIVER_MODULE(sbsbc, sbc, sbsbc_driver, pcm_devclass, 0, 0);
#endif /* NSBC > 0 */
#endif /* NPCM > 0 */

View File

@ -34,8 +34,11 @@
#include <dev/sound/pcm/sound.h>
#if NPCM > 0
#include "sbc.h"
#define __SB_MIXER_C__ /* XXX warning... */
#include <dev/sound/isa/sb.h>
#include <dev/sound/chip.h>
/* channel interface */
static void *sbchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir);
@ -1258,25 +1261,6 @@ sbpnp_probe(device_t dev)
u_int32_t logical_id = isa_get_logicalid(dev);
switch(logical_id) {
case 0x43008c0e: /* CTL0043 */
case 0x01008c0e: /* CTL0001 */
s = "Vibra16X";
break;
case 0x31008c0e: /* CTL0031 */
case 0x41008c0e: /* CTL0041 */
case 0x42008c0e: /* CTL0042 */
s = "SB16 PnP";
break;
case 0x44008c0e: /* CTL0044 */
s = "Creative SB AWE64 Gold";
break;
case 0x45008c0e: /* CTL0045 */
s = "Creative AWE64 PnP";
break;
case 0x01100000: /* @@@1001 */
s = "Avance Asound 110";
break;
@ -1299,7 +1283,7 @@ sbpnp_probe(device_t dev)
}
if (s) {
device_set_desc(dev, s);
return 0;
return (0);
}
return ENXIO;
}
@ -1342,6 +1326,67 @@ DRIVER_MODULE(sbpnp, isa, sbpnp_driver, pcm_devclass, 0, 0);
#endif /* NPNP > 0 */
#if NSBC > 0
#define DESCSTR " PCM Audio"
static int
sbsbc_probe(device_t dev)
{
char *s = NULL;
struct sndcard_func *func;
/* The parent device has already been probed. */
func = device_get_ivars(dev);
if (func == NULL || func->func != SCF_PCM)
return (ENXIO);
s = "SB PCM Audio";
device_set_desc(dev, s);
return 0;
}
static int
sbsbc_attach(device_t dev)
{
struct sb_info *sb;
u_int32_t vend_id;
device_t sbc;
sbc = device_get_parent(dev);
vend_id = isa_get_vendorid(sbc);
sb = (struct sb_info *)malloc(sizeof *sb, M_DEVBUF, M_NOWAIT);
if (!sb) return ENXIO;
bzero(sb, sizeof *sb);
switch(vend_id) {
case 0xf0008c0e:
case 0x10019305:
case 0x20019305:
/* XXX add here the vend_id for other vibra16X cards... */
sb->bd_flags = BD_F_SB16X;
}
return sb_doattach(dev, sb);
}
static device_method_t sbsbc_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, sbsbc_probe),
DEVMETHOD(device_attach, sbsbc_attach),
{ 0, 0 }
};
static driver_t sbsbc_driver = {
"pcm",
sbsbc_methods,
sizeof(snddev_info),
};
DRIVER_MODULE(sbsbc, sbc, sbsbc_driver, pcm_devclass, 0, 0);
#endif /* NSBC > 0 */
#endif /* NPCM > 0 */

View File

@ -34,8 +34,11 @@
#include <dev/sound/pcm/sound.h>
#if NPCM > 0
#include "sbc.h"
#define __SB_MIXER_C__ /* XXX warning... */
#include <dev/sound/isa/sb.h>
#include <dev/sound/chip.h>
/* channel interface */
static void *sbchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir);
@ -1258,25 +1261,6 @@ sbpnp_probe(device_t dev)
u_int32_t logical_id = isa_get_logicalid(dev);
switch(logical_id) {
case 0x43008c0e: /* CTL0043 */
case 0x01008c0e: /* CTL0001 */
s = "Vibra16X";
break;
case 0x31008c0e: /* CTL0031 */
case 0x41008c0e: /* CTL0041 */
case 0x42008c0e: /* CTL0042 */
s = "SB16 PnP";
break;
case 0x44008c0e: /* CTL0044 */
s = "Creative SB AWE64 Gold";
break;
case 0x45008c0e: /* CTL0045 */
s = "Creative AWE64 PnP";
break;
case 0x01100000: /* @@@1001 */
s = "Avance Asound 110";
break;
@ -1299,7 +1283,7 @@ sbpnp_probe(device_t dev)
}
if (s) {
device_set_desc(dev, s);
return 0;
return (0);
}
return ENXIO;
}
@ -1342,6 +1326,67 @@ DRIVER_MODULE(sbpnp, isa, sbpnp_driver, pcm_devclass, 0, 0);
#endif /* NPNP > 0 */
#if NSBC > 0
#define DESCSTR " PCM Audio"
static int
sbsbc_probe(device_t dev)
{
char *s = NULL;
struct sndcard_func *func;
/* The parent device has already been probed. */
func = device_get_ivars(dev);
if (func == NULL || func->func != SCF_PCM)
return (ENXIO);
s = "SB PCM Audio";
device_set_desc(dev, s);
return 0;
}
static int
sbsbc_attach(device_t dev)
{
struct sb_info *sb;
u_int32_t vend_id;
device_t sbc;
sbc = device_get_parent(dev);
vend_id = isa_get_vendorid(sbc);
sb = (struct sb_info *)malloc(sizeof *sb, M_DEVBUF, M_NOWAIT);
if (!sb) return ENXIO;
bzero(sb, sizeof *sb);
switch(vend_id) {
case 0xf0008c0e:
case 0x10019305:
case 0x20019305:
/* XXX add here the vend_id for other vibra16X cards... */
sb->bd_flags = BD_F_SB16X;
}
return sb_doattach(dev, sb);
}
static device_method_t sbsbc_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, sbsbc_probe),
DEVMETHOD(device_attach, sbsbc_attach),
{ 0, 0 }
};
static driver_t sbsbc_driver = {
"pcm",
sbsbc_methods,
sizeof(snddev_info),
};
DRIVER_MODULE(sbsbc, sbc, sbsbc_driver, pcm_devclass, 0, 0);
#endif /* NSBC > 0 */
#endif /* NPCM > 0 */

393
sys/dev/sound/isa/sbc.c Normal file
View File

@ -0,0 +1,393 @@
/*-
* Copyright (c) 1999 Seigo Tanimura
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include "sbc.h"
#include "isa.h"
#include "pnp.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <machine/resource.h>
#include <machine/bus.h>
#include <sys/rman.h>
#include <sys/soundcard.h>
#include <dev/sound/chip.h>
#if NISA > 0
#include <isa/isavar.h>
#include <isa/isa_common.h>
#ifdef __alpha__ /* XXX workaround a stupid warning */
#include <alpha/isa/isavar.h>
#endif
#endif /* NISA > 0 */
#if NSBC > 0
/* Here is the parameter structure per a device. */
struct sbc_softc {
device_t dev; /* device */
int io_rid[3]; /* io port rids */
struct resource *io[3]; /* io port resources */
int io_alloced[3]; /* io port alloc flag */
int irq_rid; /* irq rids */
struct resource *irq; /* irq resources */
int irq_alloced; /* irq alloc flag */
int drq_rid[2]; /* drq rids */
struct resource *drq[2]; /* drq resources */
int drq_alloced[2]; /* drq alloc flag */
};
typedef struct sbc_softc *sc_p;
#if NISA > 0 && NPNP > 0
static int sbc_probe(device_t dev);
static int sbc_attach(device_t dev);
#endif /* NISA > 0 && NPNP > 0 */
static struct resource *sbc_alloc_resource(device_t bus, device_t child, int type, int *rid,
u_long start, u_long end, u_long count, u_int flags);
static int sbc_release_resource(device_t bus, device_t child, int type, int rid,
struct resource *r);
static int alloc_resource(sc_p scp);
static int release_resource(sc_p scp);
static devclass_t sbc_devclass;
#if NISA > 0 && NPNP > 0
static int
sbc_probe(device_t dev)
{
u_int32_t vend_id, logical_id, vend_id2;
char *s;
struct sndcard_func *func;
vend_id = isa_get_vendorid(dev);
vend_id2 = vend_id & 0xff00ffff;
logical_id = isa_get_logicalid(dev);
s = NULL;
switch (logical_id) {
#if notdef
case 0x0000630e: /* Crystal Semiconductor */
if (vend_id2 ==0x3600630e) /* CS4236 */
s = "CS4236";
else if (vend_id2 ==0x3200630e) /* CS4232 */
s = "CS4232";
else if (vend_id2 ==0x3500630e) /* CS4236B */
s = "CS4236B";
break;
#endif /* notdef */
case 0x01008c0e: /* Creative ViBRA16C */
if (vend_id2 == 0x70008c0e)
s = "Creative ViBRA16C PnP";
break;
case 0x43008c0e: /* Creative ViBRA16X */
if (vend_id2 == 0xf0008c0e)
s = "Creative ViBRA16C PnP";
break;
case 0x31008c0e: /* Creative SB */
if (vend_id2 == 0x26008c0e)
s = "Creative SB16 PnP";
else if (vend_id2 == 0x42008c0e)
s = "Creative SB32 (CTL0042)";
else if (vend_id2 == 0x44008c0e)
s = "Creative SB32 (CTL0044)";
else if (vend_id2 == 0x49008c0e)
s = "Creative SB32 (CTL0049)";
else if (vend_id2 == 0xf1008c0e)
s = "Creative SB32 (CTL00f1)";
break;
case 0x42008c0e: /* Creative SB AWE64 (CTL00c1) */
if (vend_id2 == 0xc1008c0e)
s = "Creative SB AWE64 (CTL00c1)";
break;
case 0x45008c0e: /* Creative SB AWE64 (CTL0045) */
if (vend_id2 == 0xe4008c0e)
s = "Creative SB AWE64 (CTL0045)";
break;
#if notdef
case 0x01200001: /* Avance Logic */
if (vend_id2 == 0x20009305)
s = "Avance Logic ALS120";
break;
case 0x01100001: /* Avance Asound */
if (vend_id2 == 0x10009305)
s = "Avance Asound 110";
break;
case 0x68187316: /* ESS1868 */
if (vend_id2 == 0x68007316)
s = "ESS ES1868 Plug and Play AudioDrive";
break;
case 0x79187316: /* ESS1879 */
if (vend_id2 == 0x79007316)
s = "ESS ES1879 Plug and Play AudioDrive";
break;
case 0x2100a865: /* Yamaha */
if (vend_id2 == 0x2000a865)
s = "Yamaha OPL3-SA2/SAX Sound Board";
break;
case 0x80719304: /* Terratec */
if (vend_id2 == 0x1114b250)
s = "Terratec Soundsystem Base 1";
break;
case 0x0300561e: /* Gravis */
if (vend_id2 == 0x0100561e)
s = "Gravis UltraSound Plug & Play";
break;
#endif /* notdef */
}
if (s != NULL) {
device_set_desc(dev, s);
/* PCM Audio */
func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT);
if (func == NULL)
return (ENOMEM);
bzero(func, sizeof(*func));
func->func = SCF_PCM;
device_add_child(dev, "pcm", -1, func);
#if notyet
/* Midi Interface */
func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT);
if (func == NULL)
return (ENOMEM);
bzero(func, sizeof(*func));
func->func = SCF_MIDI;
device_add_child(dev, "midi", -1, func);
/* OPL FM Synthesizer */
func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT);
if (func == NULL)
return (ENOMEM);
bzero(func, sizeof(*func));
func->func = SCF_SYNTH;
device_add_child(dev, "midi", -1, func);
#endif /* notyet */
return (0);
}
return (ENXIO);
}
static int
sbc_attach(device_t dev)
{
sc_p scp;
int unit;
scp = device_get_softc(dev);
unit = device_get_unit(dev);
bzero(scp, sizeof(*scp));
scp->dev = dev;
if (alloc_resource(scp)) {
release_resource(scp);
return (ENXIO);
}
bus_generic_attach(dev);
return (0);
}
#endif /* NISA > 0 && NPNP > 0 */
static struct resource *
sbc_alloc_resource(device_t bus, device_t child, int type, int *rid,
u_long start, u_long end, u_long count, u_int flags)
{
sc_p scp;
int *alloced, rid_max, alloced_max;
struct resource **res;
scp = device_get_softc(bus);
switch (type) {
case SYS_RES_IOPORT:
alloced = scp->io_alloced;
res = scp->io;
rid_max = 2;
alloced_max = 1;
break;
case SYS_RES_IRQ:
alloced = &scp->irq_alloced;
res = &scp->irq;
rid_max = 0;
alloced_max = 2; /* pcm and mpu may share the irq. */
break;
case SYS_RES_DRQ:
alloced = scp->drq_alloced;
res = scp->drq;
rid_max = 1;
alloced_max = 1;
break;
default:
return (NULL);
}
if (*rid > rid_max || alloced[*rid] == alloced_max)
return (NULL);
alloced[*rid]++;
return (res[*rid]);
}
static int
sbc_release_resource(device_t bus, device_t child, int type, int rid,
struct resource *r)
{
sc_p scp;
int *alloced, rid_max;
scp = device_get_softc(bus);
switch (type) {
case SYS_RES_IOPORT:
alloced = scp->io_alloced;
rid_max = 2;
break;
case SYS_RES_IRQ:
alloced = &scp->irq_alloced;
rid_max = 0;
break;
case SYS_RES_DRQ:
alloced = scp->drq_alloced;
rid_max = 1;
break;
default:
return (1);
}
if (rid > rid_max || alloced[rid] == 0)
return (1);
alloced[rid]--;
return (0);
}
static int io_range[3] = {0x10, 0x4, 0x4};
static int
alloc_resource(sc_p scp)
{
int i;
for (i = 0 ; i < sizeof(scp->io) / sizeof(*scp->io) ; i++) {
if (scp->io[i] == NULL) {
scp->io_rid[i] = i;
scp->io[i] = bus_alloc_resource(scp->dev, SYS_RES_IOPORT, &scp->io_rid[i],
0, ~0, io_range[i], RF_ACTIVE);
if (scp->io[i] == NULL)
return (1);
scp->io_alloced[i] = 0;
}
}
if (scp->irq == NULL) {
scp->irq_rid = 0;
scp->irq = bus_alloc_resource(scp->dev, SYS_RES_IRQ, &scp->irq_rid,
0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
if (scp->irq == NULL)
return (1);
scp->irq_alloced = 0;
}
for (i = 0 ; i < sizeof(scp->drq) / sizeof(*scp->drq) ; i++) {
if (scp->drq[i] == NULL) {
scp->drq_rid[i] = i;
scp->drq[i] = bus_alloc_resource(scp->dev, SYS_RES_DRQ, &scp->drq_rid[i],
0, ~0, 1, RF_ACTIVE);
if (scp->drq[i] == NULL)
return (1);
scp->drq_alloced[i] = 0;
}
}
return (0);
}
static int
release_resource(sc_p scp)
{
int i;
for (i = 0 ; i < sizeof(scp->io) / sizeof(*scp->io) ; i++) {
if (scp->io[i] != NULL) {
bus_release_resource(scp->dev, SYS_RES_IOPORT, scp->io_rid[i], scp->io[i]);
scp->io[i] = NULL;
}
}
if (scp->irq != NULL) {
bus_release_resource(scp->dev, SYS_RES_IRQ, scp->irq_rid, scp->irq);
scp->irq = NULL;
}
for (i = 0 ; i < sizeof(scp->drq) / sizeof(*scp->drq) ; i++) {
if (scp->drq[i] != NULL) {
bus_release_resource(scp->dev, SYS_RES_DRQ, scp->drq_rid[i], scp->drq[i]);
scp->drq[i] = NULL;
}
}
return (0);
}
#if NISA > 0 && NPNP > 0
static device_method_t sbc_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, sbc_probe),
DEVMETHOD(device_attach, sbc_attach),
DEVMETHOD(device_detach, bus_generic_detach),
DEVMETHOD(device_shutdown, bus_generic_shutdown),
DEVMETHOD(device_suspend, bus_generic_suspend),
DEVMETHOD(device_resume, bus_generic_resume),
/* Bus interface */
DEVMETHOD(bus_print_child, bus_generic_print_child),
DEVMETHOD(bus_alloc_resource, sbc_alloc_resource),
DEVMETHOD(bus_release_resource, sbc_release_resource),
DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
{ 0, 0 }
};
static driver_t sbc_driver = {
"sbc",
sbc_methods,
sizeof(struct sbc_softc),
};
/*
* sbc can be attached to an isa bus.
*/
DRIVER_MODULE(sbc, isa, sbc_driver, sbc_devclass, 0, 0);
#endif /* NISA > 0 && NPNP > 0 */
#endif /* NSBC > 0 */

789
sys/dev/sound/pci/csa.c Normal file
View File

@ -0,0 +1,789 @@
/*-
* Copyright (c) 1999 Seigo Tanimura
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include "csa.h"
#include "pci.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <machine/resource.h>
#include <machine/bus.h>
#include <machine/clock.h>
#include <sys/rman.h>
#include <sys/soundcard.h>
#include <dev/sound/chip.h>
#include <dev/sound/pci/csareg.h>
#include <dev/sound/pci/csavar.h>
#if NPCI > 0
#include <pci/pcireg.h>
#include <pci/pcivar.h>
#endif /* NPCI > 0 */
#if NCSA > 0
#include <dev/sound/pci/csaimg.h>
/* Here is the parameter structure per a device. */
struct csa_softc {
device_t dev; /* device */
csa_res res; /* resources */
device_t pcm; /* pcm device */
driver_intr_t* pcmintr; /* pcm intr */
void *pcmintr_arg; /* pcm intr arg */
device_t midi; /* midi device */
driver_intr_t* midiintr; /* midi intr */
void *midiintr_arg; /* midi intr arg */
void *ih; /* cookie */
};
typedef struct csa_softc *sc_p;
#if NPCI > 0
static int csa_probe(device_t dev);
static int csa_attach(device_t dev);
#endif /* NPCI > 0 */
static struct resource *csa_alloc_resource(device_t bus, device_t child, int type, int *rid,
u_long start, u_long end, u_long count, u_int flags);
static int csa_release_resource(device_t bus, device_t child, int type, int rid,
struct resource *r);
static int csa_initialize(sc_p scp);
static void csa_clearserialfifos(csa_res *resp);
static void csa_resetdsp(csa_res *resp);
static int csa_downloadimage(csa_res *resp);
static int csa_transferimage(csa_res *resp, u_long *src, u_long dest, u_long len);
static devclass_t csa_devclass;
#if NPCI > 0
static int
csa_probe(device_t dev)
{
char *s;
struct sndcard_func *func;
s = NULL;
switch (pci_get_devid(dev)) {
case CS4610_PCI_ID:
s = "Crystal Semiconductor CS4610/4611 Audio accelerator";
break;
case CS4614_PCI_ID:
s = "Crystal Semiconductor CS4614/4622/4624 Audio accelerator/4280 Audio controller";
break;
case CS4615_PCI_ID:
s = "Crystal Semiconductor CS4615 Audio accelerator";
break;
case CS4281_PCI_ID:
s = "Crystal Semiconductor CS4281 Audio controller";
break;
}
if (s != NULL) {
device_set_desc(dev, s);
/* PCM Audio */
func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT);
if (func == NULL)
return (ENOMEM);
bzero(func, sizeof(*func));
func->func = SCF_PCM;
device_add_child(dev, "pcm", -1, func);
/* Midi Interface */
func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT);
if (func == NULL)
return (ENOMEM);
bzero(func, sizeof(*func));
func->func = SCF_MIDI;
device_add_child(dev, "midi", -1, func);
return (0);
}
return (ENXIO);
}
static int
csa_attach(device_t dev)
{
u_int32_t stcmd;
sc_p scp;
csa_res *resp;
scp = device_get_softc(dev);
/* Fill in the softc. */
bzero(scp, sizeof(*scp));
scp->dev = dev;
/* Wake up the device. */
stcmd = pci_read_config(dev, PCIR_COMMAND, 4);
if ((stcmd & PCIM_CMD_MEMEN) == 0 || (stcmd & PCIM_CMD_BUSMASTEREN) == 0) {
stcmd |= (PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN);
pci_write_config(dev, PCIR_COMMAND, 4, stcmd);
}
stcmd = pci_read_config(dev, PCIR_LATTIMER, 4);
if (stcmd < 32)
stcmd = 32;
pci_write_config(dev, PCIR_LATTIMER, 4, stcmd);
/* Allocate the resources. */
resp = &scp->res;
resp->io_rid = CS461x_IO_OFFSET;
resp->io = bus_alloc_resource(dev, SYS_RES_MEMORY, &resp->io_rid, 0, ~0, CS461x_IO_SIZE, RF_ACTIVE);
if (resp->io == NULL)
return (ENXIO);
resp->mem_rid = CS461x_MEM_OFFSET;
resp->mem = bus_alloc_resource(dev, SYS_RES_MEMORY, &resp->mem_rid, 0, ~0, CS461x_MEM_SIZE, RF_ACTIVE);
if (resp->mem == NULL) {
bus_release_resource(dev, SYS_RES_MEMORY, resp->io_rid, resp->io);
return (ENXIO);
}
resp->irq_rid = 0;
resp->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &resp->irq_rid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
if (resp->irq == NULL) {
bus_release_resource(dev, SYS_RES_MEMORY, resp->io_rid, resp->io);
bus_release_resource(dev, SYS_RES_MEMORY, resp->mem_rid, resp->mem);
return (ENXIO);
}
/* Initialize the chip. */
if (csa_initialize(scp)) {
bus_release_resource(dev, SYS_RES_MEMORY, resp->io_rid, resp->io);
bus_release_resource(dev, SYS_RES_MEMORY, resp->mem_rid, resp->mem);
bus_release_resource(dev, SYS_RES_IRQ, resp->irq_rid, resp->irq);
return (ENXIO);
}
/* Reset the Processor. */
csa_resetdsp(resp);
/* Download the Processor Image to the processor. */
if (csa_downloadimage(resp)) {
bus_release_resource(dev, SYS_RES_MEMORY, resp->io_rid, resp->io);
bus_release_resource(dev, SYS_RES_MEMORY, resp->mem_rid, resp->mem);
bus_release_resource(dev, SYS_RES_IRQ, resp->irq_rid, resp->irq);
return (ENXIO);
}
bus_generic_attach(dev);
return (0);
}
#endif /* NPCI > 0 */
static struct resource *
csa_alloc_resource(device_t bus, device_t child, int type, int *rid,
u_long start, u_long end, u_long count, u_int flags)
{
sc_p scp;
csa_res *resp;
struct resource *res;
scp = device_get_softc(bus);
resp = &scp->res;
switch (type) {
case SYS_RES_IRQ:
if (*rid != 0)
return (NULL);
res = resp->irq;
break;
case SYS_RES_MEMORY:
switch (*rid) {
case CS461x_IO_OFFSET:
res = resp->io;
break;
case CS461x_MEM_OFFSET:
res = resp->mem;
break;
default:
return (NULL);
}
break;
default:
return (NULL);
}
return res;
}
static int
csa_release_resource(device_t bus, device_t child, int type, int rid,
struct resource *r)
{
return (0);
}
static int
csa_initialize(sc_p scp)
{
int i;
u_int32_t acsts, acisv;
csa_res *resp;
resp = &scp->res;
/*
* First, blast the clock control register to zero so that the PLL starts
* out in a known state, and blast the master serial port control register
* to zero so that the serial ports also start out in a known state.
*/
csa_writeio(resp, BA0_CLKCR1, 0);
csa_writeio(resp, BA0_SERMC1, 0);
/*
* If we are in AC97 mode, then we must set the part to a host controlled
* AC-link. Otherwise, we won't be able to bring up the link.
*/
#if 1
csa_writeio(resp, BA0_SERACC, SERACC_HSP | SERACC_CODEC_TYPE_1_03); /* 1.03 codec */
#else
csa_writeio(resp, BA0_SERACC, SERACC_HSP | SERACC_CODEC_TYPE_2_0); /* 2.0 codec */
#endif /* 1 */
/*
* Drive the ARST# pin low for a minimum of 1uS (as defined in the AC97
* spec) and then drive it high. This is done for non AC97 modes since
* there might be logic external to the CS461x that uses the ARST# line
* for a reset.
*/
csa_writeio(resp, BA0_ACCTL, 0);
DELAY(250);
csa_writeio(resp, BA0_ACCTL, ACCTL_RSTN);
/*
* The first thing we do here is to enable sync generation. As soon
* as we start receiving bit clock, we'll start producing the SYNC
* signal.
*/
csa_writeio(resp, BA0_ACCTL, ACCTL_ESYN | ACCTL_RSTN);
/*
* Now wait for a short while to allow the AC97 part to start
* generating bit clock (so we don't try to start the PLL without an
* input clock).
*/
DELAY(50000);
/*
* Set the serial port timing configuration, so that
* the clock control circuit gets its clock from the correct place.
*/
csa_writeio(resp, BA0_SERMC1, SERMC1_PTC_AC97);
/*
* Write the selected clock control setup to the hardware. Do not turn on
* SWCE yet (if requested), so that the devices clocked by the output of
* PLL are not clocked until the PLL is stable.
*/
csa_writeio(resp, BA0_PLLCC, PLLCC_LPF_1050_2780_KHZ | PLLCC_CDR_73_104_MHZ);
csa_writeio(resp, BA0_PLLM, 0x3a);
csa_writeio(resp, BA0_CLKCR2, CLKCR2_PDIVS_8);
/*
* Power up the PLL.
*/
csa_writeio(resp, BA0_CLKCR1, CLKCR1_PLLP);
/*
* Wait until the PLL has stabilized.
*/
DELAY(50000);
/*
* Turn on clocking of the core so that we can setup the serial ports.
*/
csa_writeio(resp, BA0_CLKCR1, csa_readio(resp, BA0_CLKCR1) | CLKCR1_SWCE);
/*
* Fill the serial port FIFOs with silence.
*/
csa_clearserialfifos(resp);
/*
* Set the serial port FIFO pointer to the first sample in the FIFO.
*/
#if notdef
csa_writeio(resp, BA0_SERBSP, 0);
#endif /* notdef */
/*
* Write the serial port configuration to the part. The master
* enable bit is not set until all other values have been written.
*/
csa_writeio(resp, BA0_SERC1, SERC1_SO1F_AC97 | SERC1_SO1EN);
csa_writeio(resp, BA0_SERC2, SERC2_SI1F_AC97 | SERC1_SO1EN);
csa_writeio(resp, BA0_SERMC1, SERMC1_PTC_AC97 | SERMC1_MSPE);
/*
* Wait for the codec ready signal from the AC97 codec.
*/
acsts = 0;
for (i = 0 ; i < 1000 ; i++) {
/*
* First, lets wait a short while to let things settle out a bit,
* and to prevent retrying the read too quickly.
*/
DELAY(250);
/*
* Read the AC97 status register to see if we've seen a CODEC READY
* signal from the AC97 codec.
*/
acsts = csa_readio(resp, BA0_ACSTS);
if ((acsts & ACSTS_CRDY) != 0)
break;
}
/*
* Make sure we sampled CODEC READY.
*/
if ((acsts & ACSTS_CRDY) == 0)
return (ENXIO);
/*
* Assert the vaid frame signal so that we can start sending commands
* to the AC97 codec.
*/
csa_writeio(resp, BA0_ACCTL, ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN);
/*
* Wait until we've sampled input slots 3 and 4 as valid, meaning that
* the codec is pumping ADC data across the AC-link.
*/
acisv = 0;
for (i = 0 ; i < 1000 ; i++) {
/*
* First, lets wait a short while to let things settle out a bit,
* and to prevent retrying the read too quickly.
*/
#if notdef
DELAY(10000000L); /* clw */
#else
DELAY(2500);
#endif /* notdef */
/*
* Read the input slot valid register and see if input slots 3 and
* 4 are valid yet.
*/
acisv = csa_readio(resp, BA0_ACISV);
if ((acisv & (ACISV_ISV3 | ACISV_ISV4)) == (ACISV_ISV3 | ACISV_ISV4))
break;
}
/*
* Make sure we sampled valid input slots 3 and 4. If not, then return
* an error.
*/
if ((acisv & (ACISV_ISV3 | ACISV_ISV4)) != (ACISV_ISV3 | ACISV_ISV4))
return (ENXIO);
/*
* Now, assert valid frame and the slot 3 and 4 valid bits. This will
* commense the transfer of digital audio data to the AC97 codec.
*/
csa_writeio(resp, BA0_ACOSV, ACOSV_SLV3 | ACOSV_SLV4);
/*
* Power down the DAC and ADC. We will power them up (if) when we need
* them.
*/
#if notdef
csa_writeio(resp, BA0_AC97_POWERDOWN, 0x300);
#endif /* notdef */
/*
* Turn off the Processor by turning off the software clock enable flag in
* the clock control register.
*/
#if notdef
clkcr1 = csa_readio(resp, BA0_CLKCR1) & ~CLKCR1_SWCE;
csa_writeio(resp, BA0_CLKCR1, clkcr1);
#endif /* notdef */
/*
* Enable interrupts on the part.
*/
#if notdef
csa_writeio(resp, BA0_HICR, HICR_IEV | HICR_CHGM);
#endif /* notdef */
return (0);
}
static void
csa_clearserialfifos(csa_res *resp)
{
int i, j, pwr;
u_int8_t clkcr1, serbst;
/*
* See if the devices are powered down. If so, we must power them up first
* or they will not respond.
*/
pwr = 1;
clkcr1 = csa_readio(resp, BA0_CLKCR1);
if ((clkcr1 & CLKCR1_SWCE) == 0) {
csa_writeio(resp, BA0_CLKCR1, clkcr1 | CLKCR1_SWCE);
pwr = 0;
}
/*
* We want to clear out the serial port FIFOs so we don't end up playing
* whatever random garbage happens to be in them. We fill the sample FIFOs
* with zero (silence).
*/
csa_writeio(resp, BA0_SERBWP, 0);
/* Fill all 256 sample FIFO locations. */
serbst = 0;
for (i = 0 ; i < 256 ; i++) {
/* Make sure the previous FIFO write operation has completed. */
for (j = 0 ; j < 5 ; j++) {
DELAY(250);
serbst = csa_readio(resp, BA0_SERBST);
if ((serbst & SERBST_WBSY) == 0)
break;
}
if ((serbst & SERBST_WBSY) != 0) {
if (!pwr)
csa_writeio(resp, BA0_CLKCR1, clkcr1);
}
/* Write the serial port FIFO index. */
csa_writeio(resp, BA0_SERBAD, i);
/* Tell the serial port to load the new value into the FIFO location. */
csa_writeio(resp, BA0_SERBCM, SERBCM_WRC);
}
/*
* Now, if we powered up the devices, then power them back down again.
* This is kinda ugly, but should never happen.
*/
if (!pwr)
csa_writeio(resp, BA0_CLKCR1, clkcr1);
}
static void
csa_resetdsp(csa_res *resp)
{
int i;
/*
* Write the reset bit of the SP control register.
*/
csa_writemem(resp, BA1_SPCR, SPCR_RSTSP);
/*
* Write the control register.
*/
csa_writemem(resp, BA1_SPCR, SPCR_DRQEN);
/*
* Clear the trap registers.
*/
for (i = 0 ; i < 8 ; i++) {
csa_writemem(resp, BA1_DREG, DREG_REGID_TRAP_SELECT + i);
csa_writemem(resp, BA1_TWPR, 0xffff);
}
csa_writemem(resp, BA1_DREG, 0);
/*
* Set the frame timer to reflect the number of cycles per frame.
*/
csa_writemem(resp, BA1_FRMT, 0xadf);
}
static int
csa_downloadimage(csa_res *resp)
{
int ret;
u_long ul, offset;
for (ul = 0, offset = 0 ; ul < INKY_MEMORY_COUNT ; ul++) {
/*
* DMA this block from host memory to the appropriate
* memory on the CSDevice.
*/
ret = csa_transferimage(
resp,
BA1Struct.BA1Array + offset,
BA1Struct.MemoryStat[ul].ulDestByteOffset,
BA1Struct.MemoryStat[ul].ulSourceByteSize);
if (ret)
return (ret);
offset += BA1Struct.MemoryStat[ul].ulSourceByteSize >> 2;
}
return (0);
}
static int
csa_transferimage(csa_res *resp, u_long *src, u_long dest, u_long len)
{
u_long ul;
/*
* We do not allow DMAs from host memory to host memory (although the DMA
* can do it) and we do not allow DMAs which are not a multiple of 4 bytes
* in size (because that DMA can not do that). Return an error if either
* of these conditions exist.
*/
if ((len & 0x3) != 0)
return (EINVAL);
/* Check the destination address that it is a multiple of 4 */
if ((dest & 0x3) != 0)
return (EINVAL);
/* Write the buffer out. */
for (ul = 0 ; ul < len ; ul += 4)
csa_writemem(resp, dest + ul, src[ul >> 2]);
return (0);
}
int
csa_readcodec(csa_res *resp, u_long offset, u_int32_t *data)
{
int i;
u_int32_t acsda, acctl, acsts;
/*
* Make sure that there is not data sitting around from a previous
* uncompleted access. ACSDA = Status Data Register = 47Ch
*/
acsda = csa_readio(resp, BA0_ACSDA);
/*
* Setup the AC97 control registers on the CS461x to send the
* appropriate command to the AC97 to perform the read.
* ACCAD = Command Address Register = 46Ch
* ACCDA = Command Data Register = 470h
* ACCTL = Control Register = 460h
* set DCV - will clear when process completed
* set CRW - Read command
* set VFRM - valid frame enabled
* set ESYN - ASYNC generation enabled
* set RSTN - ARST# inactive, AC97 codec not reset
*/
/*
* Get the actual AC97 register from the offset
*/
csa_writeio(resp, BA0_ACCAD, offset - BA0_AC97_RESET);
csa_writeio(resp, BA0_ACCDA, 0);
csa_writeio(resp, BA0_ACCTL, ACCTL_DCV | ACCTL_CRW | ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN);
/*
* Wait for the read to occur.
*/
acctl = 0;
for (i = 0 ; i < 10 ; i++) {
/*
* First, we want to wait for a short time.
*/
DELAY(25);
/*
* Now, check to see if the read has completed.
* ACCTL = 460h, DCV should be reset by now and 460h = 17h
*/
acctl = csa_readio(resp, BA0_ACCTL);
if ((acctl & ACCTL_DCV) == 0)
break;
}
/*
* Make sure the read completed.
*/
if ((acctl & ACCTL_DCV) != 0)
return (EAGAIN);
/*
* Wait for the valid status bit to go active.
*/
acsts = 0;
for (i = 0 ; i < 10 ; i++) {
/*
* Read the AC97 status register.
* ACSTS = Status Register = 464h
*/
acsts = csa_readio(resp, BA0_ACSTS);
/*
* See if we have valid status.
* VSTS - Valid Status
*/
if ((acsts & ACSTS_VSTS) != 0)
break;
/*
* Wait for a short while.
*/
DELAY(25);
}
/*
* Make sure we got valid status.
*/
if ((acsts & ACSTS_VSTS) == 0)
return (EAGAIN);
/*
* Read the data returned from the AC97 register.
* ACSDA = Status Data Register = 474h
*/
*data = csa_readio(resp, BA0_ACSDA);
return (0);
}
int
csa_writecodec(csa_res *resp, u_long offset, u_int32_t data)
{
int i;
u_int32_t acctl;
/*
* Setup the AC97 control registers on the CS461x to send the
* appropriate command to the AC97 to perform the write.
* ACCAD = Command Address Register = 46Ch
* ACCDA = Command Data Register = 470h
* ACCTL = Control Register = 460h
* set DCV - will clear when process completed
* set VFRM - valid frame enabled
* set ESYN - ASYNC generation enabled
* set RSTN - ARST# inactive, AC97 codec not reset
*/
/*
* Get the actual AC97 register from the offset
*/
csa_writeio(resp, BA0_ACCAD, offset - BA0_AC97_RESET);
csa_writeio(resp, BA0_ACCDA, data);
csa_writeio(resp, BA0_ACCTL, ACCTL_DCV | ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN);
/*
* Wait for the write to occur.
*/
acctl = 0;
for (i = 0 ; i < 10 ; i++) {
/*
* First, we want to wait for a short time.
*/
DELAY(25);
/*
* Now, check to see if the read has completed.
* ACCTL = 460h, DCV should be reset by now and 460h = 17h
*/
acctl = csa_readio(resp, BA0_ACCTL);
if ((acctl & ACCTL_DCV) == 0)
break;
}
/*
* Make sure the write completed.
*/
if ((acctl & ACCTL_DCV) != 0)
return (EAGAIN);
return (0);
}
u_int32_t
csa_readio(csa_res *resp, u_long offset)
{
u_int32_t ul;
if (offset < BA0_AC97_RESET)
return bus_space_read_4(rman_get_bustag(resp->io), rman_get_bushandle(resp->io), offset) & 0xffffffff;
else {
if (csa_readcodec(resp, offset, &ul))
ul = 0;
return (ul);
}
}
void
csa_writeio(csa_res *resp, u_long offset, u_int32_t data)
{
if (offset < BA0_AC97_RESET)
bus_space_write_4(rman_get_bustag(resp->io), rman_get_bushandle(resp->io), offset, data);
else
csa_writecodec(resp, offset, data);
}
u_int32_t
csa_readmem(csa_res *resp, u_long offset)
{
return bus_space_read_4(rman_get_bustag(resp->mem), rman_get_bushandle(resp->mem), offset) & 0xffffffff;
}
void
csa_writemem(csa_res *resp, u_long offset, u_int32_t data)
{
bus_space_write_4(rman_get_bustag(resp->mem), rman_get_bushandle(resp->mem), offset, data);
}
#if NPCI > 0
static device_method_t csa_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, csa_probe),
DEVMETHOD(device_attach, csa_attach),
DEVMETHOD(device_detach, bus_generic_detach),
DEVMETHOD(device_shutdown, bus_generic_shutdown),
DEVMETHOD(device_suspend, bus_generic_suspend),
DEVMETHOD(device_resume, bus_generic_resume),
/* Bus interface */
DEVMETHOD(bus_print_child, bus_generic_print_child),
DEVMETHOD(bus_alloc_resource, csa_alloc_resource),
DEVMETHOD(bus_release_resource, csa_release_resource),
DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
{ 0, 0 }
};
static driver_t csa_driver = {
"csa",
csa_methods,
sizeof(struct csa_softc),
};
/*
* csa can be attached to a pci bus.
*/
DRIVER_MODULE(csa, pci, csa_driver, csa_devclass, 0, 0);
#endif /* NPCI > 0 */
#endif /* NCSA > 0 */

3493
sys/dev/sound/pci/csaimg.h Normal file

File diff suppressed because it is too large Load Diff

851
sys/dev/sound/pci/csapcm.c Normal file
View File

@ -0,0 +1,851 @@
/*-
* Copyright (c) 1999 Seigo Tanimura
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include "opt_devfs.h"
#include "pci.h"
#include "csa.h"
#include "pcm.h"
#include <sys/soundcard.h>
#include <dev/sound/pcm/sound.h>
#include <dev/sound/pcm/ac97.h>
#include <dev/sound/chip.h>
#include <dev/sound/pci/csareg.h>
#include <dev/sound/pci/csavar.h>
#include <pci/pcireg.h>
#include <pci/pcivar.h>
#if NCSA > 0
/* device private data */
struct csa_info;
struct csa_chinfo {
struct csa_info *parent;
pcm_channel *channel;
snd_dbuf *buffer;
int dir;
u_int32_t fmt;
};
struct csa_info {
csa_res res; /* resource */
void *ih; /* Interrupt cookie */
bus_dma_tag_t parent_dmat; /* DMA tag */
/* Contents of board's registers */
u_long pfie;
u_long pctl;
u_long cctl;
struct csa_chinfo pch, rch;
};
/* -------------------------------------------------------------------- */
/* prototypes */
static int csa_init(struct csa_info *);
static void csa_intr(void *);
static void csa_setplaysamplerate(csa_res *resp, u_long ulInRate);
static void csa_setcapturesamplerate(csa_res *resp, u_long ulOutRate);
static void csa_startplaydma(struct csa_info *csa);
static void csa_startcapturedma(struct csa_info *csa);
static void csa_stopplaydma(struct csa_info *csa);
static void csa_stopcapturedma(struct csa_info *csa);
static void csa_powerupadc(csa_res *resp);
static void csa_powerupdac(csa_res *resp);
static int csa_startdsp(csa_res *resp);
static int csa_allocres(struct csa_info *scp, device_t dev);
static void csa_releaseres(struct csa_info *scp, device_t dev);
/* talk to the codec - called from ac97.c */
static u_int32_t csa_rdcd(void *, int);
static void csa_wrcd(void *, int, u_int32_t);
/* channel interface */
static void *csachan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir);
static int csachan_setdir(void *data, int dir);
static int csachan_setformat(void *data, u_int32_t format);
static int csachan_setspeed(void *data, u_int32_t speed);
static int csachan_setblocksize(void *data, u_int32_t blocksize);
static int csachan_trigger(void *data, int go);
static int csachan_getptr(void *data);
static pcmchan_caps *csachan_getcaps(void *data);
static pcmchan_caps csa_playcaps = {
8000, 48000,
AFMT_STEREO | AFMT_U8 | AFMT_S8 | AFMT_S16_LE | AFMT_S16_BE,
AFMT_STEREO | AFMT_S16_LE
};
static pcmchan_caps csa_reccaps = {
11025, 48000,
AFMT_STEREO | AFMT_S16_LE,
AFMT_STEREO | AFMT_S16_LE
};
static pcm_channel csa_chantemplate = {
csachan_init,
csachan_setdir,
csachan_setformat,
csachan_setspeed,
csachan_setblocksize,
csachan_trigger,
csachan_getptr,
csachan_getcaps,
};
/* -------------------------------------------------------------------- */
/* channel interface */
static void *
csachan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir)
{
struct csa_info *csa = devinfo;
struct csa_chinfo *ch = (dir == PCMDIR_PLAY)? &csa->pch : &csa->rch;
ch->parent = csa;
ch->channel = c;
ch->buffer = b;
ch->buffer->bufsize = CS461x_BUFFSIZE;
if (chn_allocbuf(ch->buffer, csa->parent_dmat) == -1) return NULL;
return ch;
}
static int
csachan_setdir(void *data, int dir)
{
struct csa_chinfo *ch = data;
struct csa_info *csa = ch->parent;
csa_res *resp;
resp = &csa->res;
if (dir == PCMDIR_PLAY)
csa_writemem(resp, BA1_PBA, vtophys(ch->buffer->buf));
else
csa_writemem(resp, BA1_CBA, vtophys(ch->buffer->buf));
ch->dir = dir;
return 0;
}
static int
csachan_setformat(void *data, u_int32_t format)
{
struct csa_chinfo *ch = data;
struct csa_info *csa = ch->parent;
u_long pdtc;
csa_res *resp;
resp = &csa->res;
if (ch->dir == PCMDIR_REC)
csa_writemem(resp, BA1_CIE, (csa_readmem(resp, BA1_CIE) & ~0x0000003f) | 0x00000001);
else {
csa->pfie = csa_readmem(resp, BA1_PFIE) & ~0x0000f03f;
if (format & AFMT_U8 || format & AFMT_U16_LE || format & AFMT_U16_BE)
csa->pfie |= 0x8000;
if (format & AFMT_S16_BE || format & AFMT_U16_BE)
csa->pfie |= 0x4000;
if (!(format & AFMT_STEREO))
csa->pfie |= 0x2000;
if (format & AFMT_U8 || format & AFMT_S8)
csa->pfie |= 0x1000;
csa_writemem(resp, BA1_PFIE, csa->pfie);
pdtc = csa_readmem(resp, BA1_PDTC) & ~0x000003ff;
if ((format & AFMT_S16_BE || format & AFMT_U16_BE || format & AFMT_S16_LE || format & AFMT_U16_LE) && (format & AFMT_STEREO))
pdtc |= 0x00f;
else if ((format & AFMT_S16_BE || format & AFMT_U16_BE || format & AFMT_S16_LE || format & AFMT_U16_LE) || (format & AFMT_STEREO))
pdtc |= 0x007;
else
pdtc |= 0x003;
csa_writemem(resp, BA1_PDTC, pdtc);
}
ch->fmt = format;
return 0;
}
static int
csachan_setspeed(void *data, u_int32_t speed)
{
struct csa_chinfo *ch = data;
struct csa_info *csa = ch->parent;
csa_res *resp;
resp = &csa->res;
if (ch->dir == PCMDIR_PLAY)
csa_setplaysamplerate(resp, speed);
else if (ch->dir == PCMDIR_REC)
csa_setcapturesamplerate(resp, speed);
/* rec/play speeds locked together - should indicate in flags */
#if 0
if (ch->direction == PCMDIR_PLAY) d->rec[0].speed = speed;
else d->play[0].speed = speed;
#endif
return speed; /* XXX calc real speed */
}
static void
csa_setplaysamplerate(csa_res *resp, u_long ulInRate)
{
u_long ulTemp1, ulTemp2;
u_long ulPhiIncr;
u_long ulCorrectionPerGOF, ulCorrectionPerSec;
u_long ulOutRate;
ulOutRate = 48000;
/*
* Compute the values used to drive the actual sample rate conversion.
* The following formulas are being computed, using inline assembly
* since we need to use 64 bit arithmetic to compute the values:
*
* ulPhiIncr = floor((Fs,in * 2^26) / Fs,out)
* ulCorrectionPerGOF = floor((Fs,in * 2^26 - Fs,out * ulPhiIncr) /
* GOF_PER_SEC)
* ulCorrectionPerSec = Fs,in * 2^26 - Fs,out * phiIncr -
* GOF_PER_SEC * ulCorrectionPerGOF
*
* i.e.
*
* ulPhiIncr:ulOther = dividend:remainder((Fs,in * 2^26) / Fs,out)
* ulCorrectionPerGOF:ulCorrectionPerSec =
* dividend:remainder(ulOther / GOF_PER_SEC)
*/
ulTemp1 = ulInRate << 16;
ulPhiIncr = ulTemp1 / ulOutRate;
ulTemp1 -= ulPhiIncr * ulOutRate;
ulTemp1 <<= 10;
ulPhiIncr <<= 10;
ulTemp2 = ulTemp1 / ulOutRate;
ulPhiIncr += ulTemp2;
ulTemp1 -= ulTemp2 * ulOutRate;
ulCorrectionPerGOF = ulTemp1 / GOF_PER_SEC;
ulTemp1 -= ulCorrectionPerGOF * GOF_PER_SEC;
ulCorrectionPerSec = ulTemp1;
/*
* Fill in the SampleRateConverter control block.
*/
csa_writemem(resp, BA1_PSRC, ((ulCorrectionPerSec << 16) & 0xFFFF0000) | (ulCorrectionPerGOF & 0xFFFF));
csa_writemem(resp, BA1_PPI, ulPhiIncr);
}
static void
csa_setcapturesamplerate(csa_res *resp, u_long ulOutRate)
{
u_long ulPhiIncr, ulCoeffIncr, ulTemp1, ulTemp2;
u_long ulCorrectionPerGOF, ulCorrectionPerSec, ulInitialDelay;
u_long dwFrameGroupLength, dwCnt;
u_long ulInRate;
ulInRate = 48000;
/*
* We can only decimate by up to a factor of 1/9th the hardware rate.
* Return an error if an attempt is made to stray outside that limit.
*/
if((ulOutRate * 9) < ulInRate)
return;
/*
* We can not capture at at rate greater than the Input Rate (48000).
* Return an error if an attempt is made to stray outside that limit.
*/
if(ulOutRate > ulInRate)
return;
/*
* Compute the values used to drive the actual sample rate conversion.
* The following formulas are being computed, using inline assembly
* since we need to use 64 bit arithmetic to compute the values:
*
* ulCoeffIncr = -floor((Fs,out * 2^23) / Fs,in)
* ulPhiIncr = floor((Fs,in * 2^26) / Fs,out)
* ulCorrectionPerGOF = floor((Fs,in * 2^26 - Fs,out * ulPhiIncr) /
* GOF_PER_SEC)
* ulCorrectionPerSec = Fs,in * 2^26 - Fs,out * phiIncr -
* GOF_PER_SEC * ulCorrectionPerGOF
* ulInitialDelay = ceil((24 * Fs,in) / Fs,out)
*
* i.e.
*
* ulCoeffIncr = neg(dividend((Fs,out * 2^23) / Fs,in))
* ulPhiIncr:ulOther = dividend:remainder((Fs,in * 2^26) / Fs,out)
* ulCorrectionPerGOF:ulCorrectionPerSec =
* dividend:remainder(ulOther / GOF_PER_SEC)
* ulInitialDelay = dividend(((24 * Fs,in) + Fs,out - 1) / Fs,out)
*/
ulTemp1 = ulOutRate << 16;
ulCoeffIncr = ulTemp1 / ulInRate;
ulTemp1 -= ulCoeffIncr * ulInRate;
ulTemp1 <<= 7;
ulCoeffIncr <<= 7;
ulCoeffIncr += ulTemp1 / ulInRate;
ulCoeffIncr ^= 0xFFFFFFFF;
ulCoeffIncr++;
ulTemp1 = ulInRate << 16;
ulPhiIncr = ulTemp1 / ulOutRate;
ulTemp1 -= ulPhiIncr * ulOutRate;
ulTemp1 <<= 10;
ulPhiIncr <<= 10;
ulTemp2 = ulTemp1 / ulOutRate;
ulPhiIncr += ulTemp2;
ulTemp1 -= ulTemp2 * ulOutRate;
ulCorrectionPerGOF = ulTemp1 / GOF_PER_SEC;
ulTemp1 -= ulCorrectionPerGOF * GOF_PER_SEC;
ulCorrectionPerSec = ulTemp1;
ulInitialDelay = ((ulInRate * 24) + ulOutRate - 1) / ulOutRate;
/*
* Fill in the VariDecimate control block.
*/
csa_writemem(resp, BA1_CSRC,
((ulCorrectionPerSec << 16) & 0xFFFF0000) | (ulCorrectionPerGOF & 0xFFFF));
csa_writemem(resp, BA1_CCI, ulCoeffIncr);
csa_writemem(resp, BA1_CD,
(((BA1_VARIDEC_BUF_1 + (ulInitialDelay << 2)) << 16) & 0xFFFF0000) | 0x80);
csa_writemem(resp, BA1_CPI, ulPhiIncr);
/*
* Figure out the frame group length for the write back task. Basically,
* this is just the factors of 24000 (2^6*3*5^3) that are not present in
* the output sample rate.
*/
dwFrameGroupLength = 1;
for(dwCnt = 2; dwCnt <= 64; dwCnt *= 2)
{
if(((ulOutRate / dwCnt) * dwCnt) !=
ulOutRate)
{
dwFrameGroupLength *= 2;
}
}
if(((ulOutRate / 3) * 3) !=
ulOutRate)
{
dwFrameGroupLength *= 3;
}
for(dwCnt = 5; dwCnt <= 125; dwCnt *= 5)
{
if(((ulOutRate / dwCnt) * dwCnt) !=
ulOutRate)
{
dwFrameGroupLength *= 5;
}
}
/*
* Fill in the WriteBack control block.
*/
csa_writemem(resp, BA1_CFG1, dwFrameGroupLength);
csa_writemem(resp, BA1_CFG2, (0x00800000 | dwFrameGroupLength));
csa_writemem(resp, BA1_CCST, 0x0000FFFF);
csa_writemem(resp, BA1_CSPB, ((65536 * ulOutRate) / 24000));
csa_writemem(resp, (BA1_CSPB + 4), 0x0000FFFF);
}
static int
csachan_setblocksize(void *data, u_int32_t blocksize)
{
#if notdef
return blocksize;
#else
struct csa_chinfo *ch = data;
return ch->buffer->bufsize / 2;
#endif /* notdef */
}
static int
csachan_trigger(void *data, int go)
{
struct csa_chinfo *ch = data;
struct csa_info *csa = ch->parent;
if (ch->dir == PCMDIR_PLAY) {
if (go == PCMTRIG_START)
csa_startplaydma(csa);
else
csa_stopplaydma(csa);
} else {
if (go == PCMTRIG_START)
csa_startcapturedma(csa);
else
csa_stopcapturedma(csa);
}
return 0;
}
static void
csa_startplaydma(struct csa_info *csa)
{
csa_res *resp;
u_long ul;
resp = &csa->res;
ul = csa_readmem(resp, BA1_PCTL);
ul &= 0x0000ffff;
csa_writemem(resp, BA1_PCTL, ul | csa->pctl);
csa_writemem(resp, BA1_PVOL, 0x80008000);
}
static void
csa_startcapturedma(struct csa_info *csa)
{
csa_res *resp;
u_long ul;
resp = &csa->res;
ul = csa_readmem(resp, BA1_CCTL);
ul &= 0xffff0000;
csa_writemem(resp, BA1_CCTL, ul | csa->cctl);
csa_writemem(resp, BA1_CVOL, 0x80008000);
}
static void
csa_stopplaydma(struct csa_info *csa)
{
csa_res *resp;
u_long ul;
resp = &csa->res;
ul = csa_readmem(resp, BA1_PCTL);
csa->pctl = ul & 0xffff0000;
csa_writemem(resp, BA1_PCTL, ul & 0x0000ffff);
csa_writemem(resp, BA1_PVOL, 0xffffffff);
}
static void
csa_stopcapturedma(struct csa_info *csa)
{
csa_res *resp;
u_long ul;
resp = &csa->res;
ul = csa_readmem(resp, BA1_CCTL);
csa->cctl = ul & 0x0000ffff;
csa_writemem(resp, BA1_CCTL, ul & 0xffff0000);
csa_writemem(resp, BA1_CVOL, 0xffffffff);
}
static void
csa_powerupdac(csa_res *resp)
{
int i;
u_long ul;
/*
* Power on the DACs on the AC97 codec. We turn off the DAC
* powerdown bit and write the new value of the power control
* register.
*/
ul = csa_readio(resp, BA0_AC97_POWERDOWN);
ul &= 0xfdff;
csa_writeio(resp, BA0_AC97_POWERDOWN, ul);
/*
* Now, we wait until we sample a DAC ready state.
*/
for (i = 0 ; i < 32 ; i++) {
/*
* First, lets wait a short while to let things settle out a
* bit, and to prevent retrying the read too quickly.
*/
DELAY(125);
/*
* Read the current state of the power control register.
*/
ul = csa_readio(resp, BA0_AC97_POWERDOWN);
/*
* If the DAC ready state bit is set, then stop waiting.
*/
if ((ul & 0x2) != 0)
break;
}
/*
* The DACs are now calibrated, so we can unmute the DAC output.
*/
csa_writeio(resp, BA0_AC97_PCM_OUT_VOLUME, 0x0808);
}
static void
csa_powerupadc(csa_res *resp)
{
int i;
u_long ul;
/*
* Power on the ADCs on the AC97 codec. We turn off the ADC
* powerdown bit and write the new value of the power control
* register.
*/
ul = csa_readio(resp, BA0_AC97_POWERDOWN);
ul &= 0xfeff;
csa_writeio(resp, BA0_AC97_POWERDOWN, ul);
/*
* Now, we wait until we sample a ADC ready state.
*/
for (i = 0 ; i < 32 ; i++) {
/*
* First, lets wait a short while to let things settle out a
* bit, and to prevent retrying the read too quickly.
*/
DELAY(125);
/*
* Read the current state of the power control register.
*/
ul = csa_readio(resp, BA0_AC97_POWERDOWN);
/*
* If the ADC ready state bit is set, then stop waiting.
*/
if ((ul & 0x1) != 0)
break;
}
}
static int
csa_startdsp(csa_res *resp)
{
int i;
u_long ul;
/*
* Set the frame timer to reflect the number of cycles per frame.
*/
csa_writemem(resp, BA1_FRMT, 0xadf);
/*
* Turn on the run, run at frame, and DMA enable bits in the local copy of
* the SP control register.
*/
csa_writemem(resp, BA1_SPCR, SPCR_RUN | SPCR_RUNFR | SPCR_DRQEN);
/*
* Wait until the run at frame bit resets itself in the SP control
* register.
*/
ul = 0;
for (i = 0 ; i < 25 ; i++) {
/*
* Wait a little bit, so we don't issue PCI reads too frequently.
*/
#if notdef
DELAY(1000);
#else
DELAY(125);
#endif /* notdef */
/*
* Fetch the current value of the SP status register.
*/
ul = csa_readmem(resp, BA1_SPCR);
/*
* If the run at frame bit has reset, then stop waiting.
*/
if((ul & SPCR_RUNFR) == 0)
break;
}
/*
* If the run at frame bit never reset, then return an error.
*/
if((ul & SPCR_RUNFR) != 0)
return (EAGAIN);
return (0);
}
static int
csachan_getptr(void *data)
{
struct csa_chinfo *ch = data;
struct csa_info *csa = ch->parent;
csa_res *resp;
int ptr;
resp = &csa->res;
if (ch->dir == PCMDIR_PLAY) {
ptr = csa_readmem(resp, BA1_PBA) - vtophys(ch->buffer->buf);
if ((ch->fmt & AFMT_U8) != 0 || (ch->fmt & AFMT_S8) != 0)
ptr >>= 1;
} else {
ptr = csa_readmem(resp, BA1_CBA) - vtophys(ch->buffer->buf);
if ((ch->fmt & AFMT_U8) != 0 || (ch->fmt & AFMT_S8) != 0)
ptr >>= 1;
}
return (ptr);
}
static pcmchan_caps *
csachan_getcaps(void *data)
{
struct csa_chinfo *ch = data;
return (ch->dir == PCMDIR_PLAY)? &csa_playcaps : &csa_reccaps;
}
/* The interrupt handler */
static void
csa_intr (void *p)
{
struct csa_info *csa = p;
csa_res *resp;
u_int hisr;
resp = &csa->res;
/* Is this interrupt for us? */
/* XXX The parent device should handle this. */
hisr = csa_readio(resp, BA0_HISR);
if ((hisr & ~HISR_INTENA) == 0) {
/* Throw an eoi. */
csa_writeio(resp, BA0_HICR, HICR_IEV | HICR_CHGM);
return;
}
if ((hisr & HISR_VC0) != 0)
chn_intr(csa->pch.channel);
if ((hisr & HISR_VC1) != 0)
chn_intr(csa->rch.channel);
/* Throw an eoi. */
csa_writeio(resp, BA0_HICR, HICR_IEV | HICR_CHGM);
}
/* -------------------------------------------------------------------- */
/*
* Probe and attach the card
*/
static int
csa_init(struct csa_info *csa)
{
csa_res *resp;
resp = &csa->res;
csa->pfie = 0;
csa_stopplaydma(csa);
csa_stopcapturedma(csa);
/* Crank up the power on the DAC and ADC. */
csa_powerupadc(resp);
csa_powerupdac(resp);
csa_setplaysamplerate(resp, 8000);
csa_setcapturesamplerate(resp, 8000);
if (csa_startdsp(resp))
return (1);
return 0;
}
/* Allocates resources. */
static int
csa_allocres(struct csa_info *csa, device_t dev)
{
csa_res *resp;
resp = &csa->res;
if (resp->io == NULL) {
resp->io = bus_alloc_resource(dev, SYS_RES_MEMORY, &resp->io_rid, 0, ~0, CS461x_IO_SIZE, RF_ACTIVE);
if (resp->io == NULL)
return (1);
}
if (resp->mem == NULL) {
resp->mem = bus_alloc_resource(dev, SYS_RES_MEMORY, &resp->mem_rid, 0, ~0, CS461x_MEM_SIZE, RF_ACTIVE);
if (resp->mem == NULL)
return (1);
}
if (resp->irq == NULL) {
resp->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &resp->irq_rid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
if (resp->irq == NULL)
return (1);
}
if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/CS461x_BUFFSIZE, /*boundary*/CS461x_BUFFSIZE,
/*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
/*highaddr*/BUS_SPACE_MAXADDR,
/*filter*/NULL, /*filterarg*/NULL,
/*maxsize*/CS461x_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff,
/*flags*/0, &csa->parent_dmat) != 0)
return (1);
return (0);
}
/* Releases resources. */
static void
csa_releaseres(struct csa_info *csa, device_t dev)
{
csa_res *resp;
resp = &csa->res;
if (resp->irq != NULL) {
bus_release_resource(dev, SYS_RES_IRQ, resp->irq_rid, resp->irq);
resp->irq = NULL;
}
if (resp->io != NULL) {
bus_release_resource(dev, SYS_RES_MEMORY, resp->io_rid, resp->io);
resp->io = NULL;
}
if (resp->mem != NULL) {
bus_release_resource(dev, SYS_RES_MEMORY, resp->mem_rid, resp->mem);
resp->mem = NULL;
}
}
static int pcmcsa_probe(device_t dev);
static int pcmcsa_attach(device_t dev);
static int
pcmcsa_probe(device_t dev)
{
char *s;
struct sndcard_func *func;
/* The parent device has already been probed. */
func = device_get_ivars(dev);
if (func == NULL || func->func != SCF_PCM)
return (ENXIO);
s = "CS461x PCM Audio";
device_set_desc(dev, s);
return (0);
}
static int
pcmcsa_attach(device_t dev)
{
snddev_info *devinfo;
struct csa_info *csa;
csa_res *resp;
int unit;
char status[SND_STATUSLEN];
struct ac97_info *codec;
devinfo = device_get_softc(dev);
csa = malloc(sizeof(*csa), M_DEVBUF, M_NOWAIT);
if (csa == NULL)
return (ENOMEM);
bzero(csa, sizeof(*csa));
unit = device_get_unit(dev);
/* Allocate the resources. */
resp = &csa->res;
resp->io_rid = CS461x_IO_OFFSET;
resp->mem_rid = CS461x_MEM_OFFSET;
resp->irq_rid = 0;
if (csa_allocres(csa, dev)) {
csa_releaseres(csa, dev);
return (ENXIO);
}
if (csa_init(csa)) {
csa_releaseres(csa, dev);
return (ENXIO);
}
codec = ac97_create(csa, csa_rdcd, csa_wrcd);
if (codec == NULL)
return (ENXIO);
mixer_init(devinfo, &ac97_mixer, codec);
snprintf(status, SND_STATUSLEN, "at irq %ld", rman_get_start(resp->irq));
/* Enable interrupt. */
if (bus_setup_intr(dev, resp->irq, INTR_TYPE_TTY, csa_intr, csa, &csa->ih)) {
csa_releaseres(csa, dev);
return (ENXIO);
}
if ((csa_readio(resp, BA0_HISR) & HISR_INTENA) == 0)
csa_writeio(resp, BA0_HICR, HICR_IEV | HICR_CHGM);
csa_writemem(resp, BA1_PFIE, csa_readmem(resp, BA1_PFIE) & ~0x0000f03f);
csa_writemem(resp, BA1_CIE, (csa_readmem(resp, BA1_CIE) & ~0x0000003f) | 0x00000001);
if (pcm_register(dev, csa, 1, 1)) {
csa_releaseres(csa, dev);
return (ENXIO);
}
pcm_addchan(dev, PCMDIR_REC, &csa_chantemplate, csa);
pcm_addchan(dev, PCMDIR_PLAY, &csa_chantemplate, csa);
pcm_setstatus(dev, status);
return (0);
}
/* ac97 codec */
static u_int32_t
csa_rdcd(void *devinfo, int regno)
{
u_int32_t data;
struct csa_info *csa = (struct csa_info *)devinfo;
if (csa_readcodec(&csa->res, regno + BA0_AC97_RESET, &data))
data = 0;
return data;
}
static void
csa_wrcd(void *devinfo, int regno, u_int32_t data)
{
struct csa_info *csa = (struct csa_info *)devinfo;
csa_writecodec(&csa->res, regno + BA0_AC97_RESET, data);
}
static device_method_t pcmcsa_methods[] = {
/* Device interface */
DEVMETHOD(device_probe , pcmcsa_probe ),
DEVMETHOD(device_attach, pcmcsa_attach),
{ 0, 0 },
};
static driver_t pcmcsa_driver = {
"pcm",
pcmcsa_methods,
sizeof(snddev_info),
};
static devclass_t pcm_devclass;
DRIVER_MODULE(pcmcsa, csa, pcmcsa_driver, pcm_devclass, 0, 0);
#endif /* NCSA > 0 */

1967
sys/dev/sound/pci/csareg.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,52 @@
/*-
* Copyright (c) 1999 Seigo Tanimura
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _CSA_VAR_H
#define _CSA_VAR_H
/* Resources. */
struct csa_res {
int io_rid; /* io rid */
struct resource *io; /* io */
int mem_rid; /* memory rid */
struct resource *mem; /* memory */
int irq_rid; /* irq rid */
struct resource *irq; /* irq */
};
typedef struct csa_res csa_res;
/* Common functions for csa. */
int csa_readcodec(csa_res *resp, u_long offset, u_int32_t *data);
int csa_writecodec(csa_res *resp, u_long offset, u_int32_t data);
u_int32_t csa_readio(csa_res *resp, u_long offset);
void csa_writeio(csa_res *resp, u_long offset, u_int32_t data);
u_int32_t csa_readmem(csa_res *resp, u_long offset);
void csa_writemem(csa_res *resp, u_long offset, u_int32_t data);
#endif /* _CSA_VAR_H */