- 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:
parent
913c1ce734
commit
05e9b4af3e
46
sys/dev/sound/chip.h
Normal file
46
sys/dev/sound/chip.h
Normal 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
647
sys/dev/sound/isa/gusc.c
Normal 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 */
|
@ -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;
|
||||
|
||||
|
@ -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 */
|
||||
|
||||
|
||||
|
@ -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 */
|
||||
|
||||
|
||||
|
@ -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
393
sys/dev/sound/isa/sbc.c
Normal 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
789
sys/dev/sound/pci/csa.c
Normal 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
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
851
sys/dev/sound/pci/csapcm.c
Normal 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
1967
sys/dev/sound/pci/csareg.h
Normal file
File diff suppressed because it is too large
Load Diff
52
sys/dev/sound/pci/csavar.h
Normal file
52
sys/dev/sound/pci/csavar.h
Normal 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 */
|
Loading…
Reference in New Issue
Block a user