bc5774497e
- hpc1_hdd_{ctl,bufptr} and hpc3_hdd_{ctl,bufptr} are established in hpcreg.h. All references to these fields are updated with the exception of if_sq: (haltwo, hpcdma). This makes reading the code and spotting bugs easier. - hpcdma.c was applying EOCHAIN to the wrong descriptor word for the hpc1 case. - I added scsi_max_xfer to the abstraction layer, and it allows some crusty #defines in wdsc.c to be removed. hpc1 now doesn't waste descriptors as it once did. - hpcreg.h was updated to reflect the lack of XMITDONE bit in hpc1. HPC1_REV15 added for a test in hpc.c - hpc.c now verifies HPC1 revisions (1.0 vs 1.5) and prints the output a little prettier ;) - power interrupts shouldn't establish on non-IP22 platforms.
803 lines
20 KiB
C
803 lines
20 KiB
C
/* $NetBSD: haltwo.c,v 1.3 2003/12/29 06:33:57 sekiya Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 2003 Ilpo Ruotsalainen
|
|
* 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.
|
|
* 3. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
|
|
*
|
|
* <<Id: LICENSE_GC,v 1.1 2001/10/01 23:24:05 cgd Exp>>
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__KERNEL_RCSID(0, "$NetBSD: haltwo.c,v 1.3 2003/12/29 06:33:57 sekiya Exp $");
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/device.h>
|
|
#include <sys/audioio.h>
|
|
#include <sys/malloc.h>
|
|
#include <dev/audio_if.h>
|
|
#include <dev/auconv.h>
|
|
#include <dev/mulaw.h>
|
|
|
|
#include <uvm/uvm_extern.h>
|
|
|
|
#include <machine/bus.h>
|
|
|
|
#include <sgimips/hpc/hpcvar.h>
|
|
#include <sgimips/hpc/hpcreg.h>
|
|
|
|
#include <sgimips/hpc/haltworeg.h>
|
|
#include <sgimips/hpc/haltwovar.h>
|
|
|
|
#ifdef AUDIO_DEBUG
|
|
#define DPRINTF(x) printf x
|
|
#else
|
|
#define DPRINTF(x)
|
|
#endif
|
|
|
|
static int haltwo_open(void *, int);
|
|
static void haltwo_close(void *);
|
|
static int haltwo_query_encoding(void *, struct audio_encoding *);
|
|
static int haltwo_set_params(void *, int, int, struct audio_params *,
|
|
struct audio_params *);
|
|
static int haltwo_round_blocksize(void *, int);
|
|
static int haltwo_halt_output(void *);
|
|
static int haltwo_halt_input(void *);
|
|
static int haltwo_getdev(void *, struct audio_device *);
|
|
static int haltwo_set_port(void *, mixer_ctrl_t *);
|
|
static int haltwo_get_port(void *, mixer_ctrl_t *);
|
|
static int haltwo_query_devinfo(void *, mixer_devinfo_t *);
|
|
static void *haltwo_malloc(void *, int, size_t, struct malloc_type *, int);
|
|
static void haltwo_free(void *, void *, struct malloc_type *);
|
|
static int haltwo_get_props(void *);
|
|
static int haltwo_trigger_output(void *, void *, void *, int, void (*)(void *),
|
|
void *, struct audio_params *);
|
|
static int haltwo_trigger_input(void *, void *, void *, int, void (*)(void *),
|
|
void *, struct audio_params *);
|
|
|
|
static struct audio_hw_if haltwo_hw_if = {
|
|
haltwo_open,
|
|
haltwo_close,
|
|
NULL, /* drain */
|
|
haltwo_query_encoding,
|
|
haltwo_set_params,
|
|
haltwo_round_blocksize,
|
|
NULL, /* commit_settings */
|
|
NULL, /* init_output */
|
|
NULL, /* init_input */
|
|
NULL, /* start_output */
|
|
NULL, /* start_input */
|
|
haltwo_halt_output,
|
|
haltwo_halt_input,
|
|
NULL, /* speaker_ctl */
|
|
haltwo_getdev,
|
|
NULL, /* setfd */
|
|
haltwo_set_port,
|
|
haltwo_get_port,
|
|
haltwo_query_devinfo,
|
|
haltwo_malloc,
|
|
haltwo_free,
|
|
NULL, /* round_buffersize */
|
|
NULL, /* mappage */
|
|
haltwo_get_props,
|
|
haltwo_trigger_output,
|
|
haltwo_trigger_input,
|
|
NULL /* dev_ioctl */
|
|
};
|
|
|
|
static const struct audio_device haltwo_device = {
|
|
"HAL2",
|
|
"",
|
|
"haltwo"
|
|
};
|
|
|
|
static int haltwo_match(struct device *, struct cfdata *, void *);
|
|
static void haltwo_attach(struct device *, struct device *, void *);
|
|
static int haltwo_intr(void *);
|
|
|
|
CFATTACH_DECL(haltwo, sizeof(struct haltwo_softc),
|
|
haltwo_match, haltwo_attach, NULL, NULL);
|
|
|
|
#define haltwo_write(sc,type,off,val) \
|
|
bus_space_write_4(sc->sc_st, sc->sc_##type##_sh, off, val)
|
|
|
|
#define haltwo_read(sc,type,off) \
|
|
bus_space_read_4(sc->sc_st, sc->sc_##type##_sh, off)
|
|
|
|
static void
|
|
haltwo_write_indirect(struct haltwo_softc *sc, uint32_t ireg, uint16_t low,
|
|
uint16_t high)
|
|
{
|
|
|
|
haltwo_write(sc, ctl, HAL2_REG_CTL_IDR0, low);
|
|
haltwo_write(sc, ctl, HAL2_REG_CTL_IDR1, high);
|
|
haltwo_write(sc, ctl, HAL2_REG_CTL_IDR2, 0);
|
|
haltwo_write(sc, ctl, HAL2_REG_CTL_IDR3, 0);
|
|
haltwo_write(sc, ctl, HAL2_REG_CTL_IAR, ireg);
|
|
|
|
while (haltwo_read(sc, ctl, HAL2_REG_CTL_ISR) & HAL2_ISR_TSTATUS)
|
|
;
|
|
}
|
|
|
|
static void
|
|
haltwo_read_indirect(struct haltwo_softc *sc, uint32_t ireg, uint16_t *low,
|
|
uint16_t *high)
|
|
{
|
|
|
|
haltwo_write(sc, ctl, HAL2_REG_CTL_IAR,
|
|
ireg | HAL2_IAR_READ);
|
|
|
|
while (haltwo_read(sc, ctl, HAL2_REG_CTL_ISR) & HAL2_ISR_TSTATUS)
|
|
;
|
|
|
|
if (low)
|
|
*low = haltwo_read(sc, ctl, HAL2_REG_CTL_IDR0);
|
|
|
|
if (high)
|
|
*high = haltwo_read(sc, ctl, HAL2_REG_CTL_IDR1);
|
|
}
|
|
|
|
static int
|
|
haltwo_init_codec(struct haltwo_softc *sc, struct haltwo_codec *codec)
|
|
{
|
|
int err;
|
|
int rseg;
|
|
size_t allocsz = sizeof(struct hpc_dma_desc) * HALTWO_MAX_DMASEGS;
|
|
|
|
KASSERT(allocsz <= PAGE_SIZE);
|
|
|
|
err = bus_dmamem_alloc(sc->sc_dma_tag, allocsz, 0, 0, &codec->dma_seg,
|
|
1, &rseg, BUS_DMA_NOWAIT);
|
|
if (err)
|
|
goto out;
|
|
|
|
err = bus_dmamem_map(sc->sc_dma_tag, &codec->dma_seg, rseg, allocsz,
|
|
(caddr_t *)&codec->dma_descs, BUS_DMA_NOWAIT);
|
|
if (err)
|
|
goto out_free;
|
|
|
|
err = bus_dmamap_create(sc->sc_dma_tag, allocsz, 1, PAGE_SIZE, 0,
|
|
BUS_DMA_NOWAIT, &codec->dma_map);
|
|
if (err)
|
|
goto out_free;
|
|
|
|
err = bus_dmamap_load(sc->sc_dma_tag, codec->dma_map, codec->dma_descs,
|
|
allocsz, NULL, BUS_DMA_NOWAIT);
|
|
if (err)
|
|
goto out_destroy;
|
|
|
|
DPRINTF(("haltwo_init_codec: allocated %d descriptors (%d bytes)"
|
|
" at %p\n", HALTWO_MAX_DMASEGS, allocsz, codec->dma_descs));
|
|
|
|
memset(codec->dma_descs, 0, allocsz);
|
|
|
|
return (0);
|
|
|
|
out_destroy:
|
|
bus_dmamap_destroy(sc->sc_dma_tag, codec->dma_map);
|
|
out_free:
|
|
bus_dmamem_free(sc->sc_dma_tag, &codec->dma_seg, rseg);
|
|
out:
|
|
DPRINTF(("haltwo_init_codec failed: %d\n",err));
|
|
|
|
return (err);
|
|
}
|
|
|
|
static void
|
|
haltwo_setup_dma(struct haltwo_softc *sc, struct haltwo_codec *codec,
|
|
struct haltwo_dmabuf *dmabuf, size_t len, int blksize,
|
|
void (*intr)(void *), void *intrarg)
|
|
{
|
|
int i;
|
|
bus_dma_segment_t *segp;
|
|
struct hpc_dma_desc *descp;
|
|
int next_intr = blksize;
|
|
|
|
KASSERT(len % blksize == 0);
|
|
|
|
codec->intr = intr;
|
|
codec->intr_arg = intrarg;
|
|
|
|
segp = dmabuf->dma_map->dm_segs;
|
|
descp = codec->dma_descs;
|
|
|
|
/* Build descriptor chain for looping DMA, triggering interrupt every
|
|
* blksize bytes */
|
|
for (i = 0; i < dmabuf->dma_map->dm_nsegs; i++) {
|
|
descp->hpc3_hdd_bufptr = segp->ds_addr;
|
|
descp->hpc3_hdd_ctl = segp->ds_len;
|
|
|
|
KASSERT(next_intr >= segp->ds_len);
|
|
|
|
if (next_intr == segp->ds_len) {
|
|
/* Generate intr after this DMA buffer */
|
|
descp->hpc3_hdd_ctl |= HDD_CTL_INTR;
|
|
next_intr = blksize;
|
|
} else
|
|
next_intr -= segp->ds_len;
|
|
|
|
if (i < dmabuf->dma_map->dm_nsegs - 1)
|
|
descp->hdd_descptr = codec->dma_seg.ds_addr +
|
|
sizeof(struct hpc_dma_desc) * (i + 1);
|
|
else
|
|
descp->hdd_descptr = codec->dma_seg.ds_addr;
|
|
|
|
DPRINTF(("haltwo_setup_dma: hdd_bufptr = %x hdd_ctl = %x"
|
|
" hdd_descptr = %x\n", descp->hpc3_hdd_bufptr,
|
|
descp->hpc3_hdd_ctl, descp->hdd_descptr));
|
|
|
|
segp++;
|
|
descp++;
|
|
}
|
|
|
|
bus_dmamap_sync(sc->sc_dma_tag, codec->dma_map, 0,
|
|
codec->dma_map->dm_mapsize, BUS_DMASYNC_PREWRITE);
|
|
}
|
|
|
|
static int
|
|
haltwo_match(struct device *parent, struct cfdata *cf, void *aux)
|
|
{
|
|
struct hpc_attach_args *haa = aux;
|
|
|
|
if (strcmp(haa->ha_name, cf->cf_name) == 0)
|
|
return (1);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
haltwo_attach(struct device *parent, struct device *self, void *aux)
|
|
{
|
|
struct haltwo_softc *sc = (void *)self;
|
|
struct hpc_attach_args *haa = aux;
|
|
uint32_t rev;
|
|
|
|
sc->sc_st = haa->ha_st;
|
|
sc->sc_dma_tag = haa->ha_dmat;
|
|
|
|
if (bus_space_subregion(haa->ha_st, haa->ha_sh, haa->ha_devoff,
|
|
HPC_PBUS_CH0_DEVREGS_SIZE, &sc->sc_ctl_sh)) {
|
|
aprint_error(": unable to map control registers\n");
|
|
return;
|
|
}
|
|
|
|
if (bus_space_subregion(haa->ha_st, haa->ha_sh, HPC_PBUS_CH2_DEVREGS,
|
|
HPC_PBUS_CH2_DEVREGS_SIZE, &sc->sc_vol_sh)) {
|
|
aprint_error(": unable to map volume registers\n");
|
|
return;
|
|
}
|
|
|
|
if (bus_space_subregion(haa->ha_st, haa->ha_sh, haa->ha_dmaoff,
|
|
HPC_PBUS_DMAREGS_SIZE, &sc->sc_dma_sh)) {
|
|
aprint_error(": unable to map DMA registers\n");
|
|
return;
|
|
}
|
|
|
|
haltwo_write(sc, ctl, HAL2_REG_CTL_ISR, 0);
|
|
haltwo_write(sc, ctl, HAL2_REG_CTL_ISR,
|
|
HAL2_ISR_GLOBAL_RESET_N | HAL2_ISR_CODEC_RESET_N);
|
|
haltwo_write_indirect(sc, HAL2_IREG_RELAY_C, HAL2_RELAY_C_STATE, 0);
|
|
|
|
rev = haltwo_read(sc, ctl, HAL2_REG_CTL_REV);
|
|
|
|
/* This bit is inverted, the test is correct */
|
|
if (rev & HAL2_REV_AUDIO_PRESENT_N) {
|
|
aprint_error(": Audio hardware not present (revision %x)\n",
|
|
rev);
|
|
return;
|
|
}
|
|
|
|
if (cpu_intr_establish(haa->ha_irq, IPL_AUDIO, haltwo_intr, sc)
|
|
== NULL) {
|
|
aprint_error(": unable to establish interrupt\n");
|
|
return;
|
|
}
|
|
|
|
aprint_naive(": Audio controller\n");
|
|
|
|
aprint_normal(": HAL2 revision %d.%d.%d\n", (rev & 0x7000) >> 12,
|
|
(rev & 0x00F0) >> 4, rev & 0x000F);
|
|
|
|
if (haltwo_init_codec(sc, &sc->sc_dac)) {
|
|
aprint_error(
|
|
"haltwo_attach: unable to create DMA descriptor list\n");
|
|
return;
|
|
}
|
|
|
|
/* XXX Magic PBUS CFGDMA values from Linux HAL2 driver XXX */
|
|
bus_space_write_4(haa->ha_st, haa->ha_sh, HPC_PBUS_CH0_CFGDMA,
|
|
0x8208844);
|
|
bus_space_write_4(haa->ha_st, haa->ha_sh, HPC_PBUS_CH1_CFGDMA,
|
|
0x8208844);
|
|
|
|
/* Unmute output */
|
|
/* XXX Add mute/unmute support to mixer ops? XXX */
|
|
haltwo_write_indirect(sc, HAL2_IREG_DAC_C2, 0, 0);
|
|
|
|
/* Set master volume to zero */
|
|
sc->sc_vol_left = sc->sc_vol_right = 0;
|
|
haltwo_write(sc, vol, HAL2_REG_VOL_LEFT, sc->sc_vol_left);
|
|
haltwo_write(sc, vol, HAL2_REG_VOL_RIGHT, sc->sc_vol_right);
|
|
|
|
audio_attach_mi(&haltwo_hw_if, sc, &sc->sc_dev);
|
|
}
|
|
|
|
static int
|
|
haltwo_intr(void *v)
|
|
{
|
|
struct haltwo_softc *sc = v;
|
|
int ret = 0;
|
|
|
|
if (bus_space_read_4(sc->sc_st, sc->sc_dma_sh, HPC_PBUS_CH0_CTL)
|
|
& HPC_PBUS_DMACTL_IRQ) {
|
|
sc->sc_dac.intr(sc->sc_dac.intr_arg);
|
|
|
|
ret = 1;
|
|
} else
|
|
DPRINTF(("haltwo_intr: Huh?\n"));
|
|
|
|
return (ret);
|
|
}
|
|
|
|
static int
|
|
haltwo_open(void *v, int flags)
|
|
{
|
|
|
|
DPRINTF(("haltwo_open flags = %x\n", flags));
|
|
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
haltwo_close(void *v)
|
|
{
|
|
}
|
|
|
|
static int
|
|
haltwo_query_encoding(void *v, struct audio_encoding *e)
|
|
{
|
|
|
|
switch (e->index) {
|
|
case 0:
|
|
strcpy(e->name, AudioEslinear_le);
|
|
e->encoding = AUDIO_ENCODING_SLINEAR_LE;
|
|
e->precision = 16;
|
|
e->flags = 0;
|
|
break;
|
|
|
|
case 1:
|
|
strcpy(e->name, AudioEslinear_be);
|
|
e->encoding = AUDIO_ENCODING_SLINEAR_BE;
|
|
e->precision = 16;
|
|
e->flags = 0;
|
|
break;
|
|
|
|
case 2:
|
|
strcpy(e->name, AudioEmulaw);
|
|
e->encoding = AUDIO_ENCODING_ULAW;
|
|
e->precision = 8;
|
|
e->flags = AUDIO_ENCODINGFLAG_EMULATED;
|
|
break;
|
|
|
|
default:
|
|
return (EINVAL);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
haltwo_set_params(void *v, int setmode, int usemode, struct audio_params *play,
|
|
struct audio_params *rec)
|
|
{
|
|
struct haltwo_softc *sc = v;
|
|
int master, inc, mod;
|
|
uint16_t tmp;
|
|
|
|
if (play->hw_sample_rate < 4000)
|
|
play->hw_sample_rate = 4000;
|
|
if (play->hw_sample_rate > 48000)
|
|
play->hw_sample_rate = 48000;
|
|
|
|
play->sw_code = NULL;
|
|
play->factor = 1;
|
|
play->factor_denom = 1;
|
|
|
|
switch (play->encoding) {
|
|
case AUDIO_ENCODING_ULAW:
|
|
if (play->precision != 8)
|
|
return (EINVAL);
|
|
|
|
play->sw_code = mulaw_to_slinear16_le;
|
|
play->factor = 2;
|
|
play->hw_encoding = AUDIO_ENCODING_SLINEAR_LE;
|
|
break;
|
|
case AUDIO_ENCODING_SLINEAR_BE:
|
|
case AUDIO_ENCODING_SLINEAR_LE:
|
|
break;
|
|
|
|
default:
|
|
return (EINVAL);
|
|
}
|
|
|
|
if (44100 % play->hw_sample_rate < 48000 % play->hw_sample_rate)
|
|
master = 44100;
|
|
else
|
|
master = 48000;
|
|
|
|
/* HAL2 specification 3.1.2.21: Codecs should be driven with INC/MOD
|
|
* fractions equivalent to 4/N, where N is a positive integer. */
|
|
inc = 4;
|
|
mod = master * inc / play->hw_sample_rate;
|
|
|
|
/* Fixup upper layers idea of HW sample rate to the actual final rate */
|
|
play->hw_sample_rate = master * inc / mod;
|
|
|
|
DPRINTF(("haltwo_set_params: master = %d inc = %d mod = %d"
|
|
" hw_sample_rate = %ld\n", master, inc, mod,
|
|
play->hw_sample_rate));
|
|
|
|
/* Setup samplerate to HW */
|
|
haltwo_write_indirect(sc, HAL2_IREG_BRES1_C1,
|
|
master == 44100 ? 1 : 0, 0);
|
|
/* XXX Documentation disagrees but this seems to work XXX */
|
|
haltwo_write_indirect(sc, HAL2_IREG_BRES1_C2,
|
|
inc, 0xFFFF & (inc - mod - 1));
|
|
|
|
/* Setup endianness to HW */
|
|
haltwo_read_indirect(sc, HAL2_IREG_DMA_END, &tmp, NULL);
|
|
if (play->hw_encoding == AUDIO_ENCODING_SLINEAR_LE)
|
|
tmp |= HAL2_DMA_END_CODECTX;
|
|
else
|
|
tmp &= ~HAL2_DMA_END_CODECTX;
|
|
haltwo_write_indirect(sc, HAL2_IREG_DMA_END, tmp, 0);
|
|
|
|
/* Set PBUS channel, Bresenham clock source, number of channels to HW */
|
|
haltwo_write_indirect(sc, HAL2_IREG_DAC_C1,
|
|
(0 << HAL2_C1_DMA_SHIFT) |
|
|
(1 << HAL2_C1_CLKID_SHIFT) |
|
|
(play->hw_channels << HAL2_C1_DATAT_SHIFT), 0);
|
|
|
|
DPRINTF(("haltwo_set_params: hw_encoding = %d hw_channels = %d\n",
|
|
play->hw_encoding, play->hw_channels));
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
haltwo_round_blocksize(void *v,int blocksize)
|
|
{
|
|
|
|
/* XXX Make this smarter and support DMA descriptor chaining XXX */
|
|
/* XXX Rounding to nearest PAGE_SIZE might work? XXX */
|
|
return PAGE_SIZE;
|
|
}
|
|
|
|
static int
|
|
haltwo_halt_output(void *v)
|
|
{
|
|
struct haltwo_softc *sc = v;
|
|
|
|
/* Disable PBUS DMA */
|
|
bus_space_write_4(sc->sc_st, sc->sc_dma_sh, HPC_PBUS_CH0_CTL,
|
|
HPC_PBUS_DMACTL_ACT_LD);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
haltwo_halt_input(void *v)
|
|
{
|
|
|
|
return (ENXIO);
|
|
}
|
|
|
|
static int
|
|
haltwo_getdev(void *v, struct audio_device *dev)
|
|
{
|
|
|
|
*dev = haltwo_device;
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
haltwo_set_port(void *v, mixer_ctrl_t *mc)
|
|
{
|
|
struct haltwo_softc *sc = v;
|
|
int lval, rval;
|
|
|
|
if (mc->type != AUDIO_MIXER_VALUE)
|
|
return (EINVAL);
|
|
|
|
if (mc->un.value.num_channels == 1)
|
|
lval = rval = mc->un.value.level[AUDIO_MIXER_LEVEL_MONO];
|
|
else if (mc->un.value.num_channels == 2) {
|
|
lval = mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
|
|
rval = mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
|
|
} else
|
|
return (EINVAL);
|
|
|
|
switch (mc->dev) {
|
|
case HALTWO_MASTER_VOL:
|
|
sc->sc_vol_left = lval;
|
|
sc->sc_vol_right = rval;
|
|
|
|
haltwo_write(sc, vol, HAL2_REG_VOL_LEFT,
|
|
sc->sc_vol_left);
|
|
haltwo_write(sc, vol, HAL2_REG_VOL_RIGHT,
|
|
sc->sc_vol_right);
|
|
break;
|
|
|
|
default:
|
|
return (EINVAL);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
haltwo_get_port(void *v, mixer_ctrl_t *mc)
|
|
{
|
|
struct haltwo_softc *sc = v;
|
|
int l, r;
|
|
|
|
switch (mc->dev) {
|
|
case HALTWO_MASTER_VOL:
|
|
l = sc->sc_vol_left;
|
|
r = sc->sc_vol_right;
|
|
break;
|
|
|
|
default:
|
|
return (EINVAL);
|
|
}
|
|
|
|
if (mc->un.value.num_channels == 1)
|
|
mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] = (l+r) / 2;
|
|
else if (mc->un.value.num_channels == 2) {
|
|
mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l;
|
|
mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r;
|
|
} else
|
|
return (EINVAL);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
haltwo_query_devinfo(void *v, mixer_devinfo_t *dev)
|
|
{
|
|
|
|
switch (dev->index) {
|
|
/* Mixer values */
|
|
case HALTWO_MASTER_VOL:
|
|
dev->type = AUDIO_MIXER_VALUE;
|
|
dev->mixer_class = HALTWO_OUTPUT_CLASS;
|
|
dev->prev = dev->next = AUDIO_MIXER_LAST;
|
|
strcpy(dev->label.name, AudioNmaster);
|
|
dev->un.v.num_channels = 2;
|
|
strcpy(dev->un.v.units.name, AudioNvolume);
|
|
break;
|
|
|
|
/* Mixer classes */
|
|
case HALTWO_OUTPUT_CLASS:
|
|
dev->type = AUDIO_MIXER_CLASS;
|
|
dev->mixer_class = HALTWO_OUTPUT_CLASS;
|
|
dev->next = dev->prev = AUDIO_MIXER_LAST;
|
|
strcpy(dev->label.name, AudioCoutputs);
|
|
break;
|
|
|
|
default:
|
|
return (EINVAL);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
haltwo_alloc_dmamem(struct haltwo_softc *sc, size_t size,
|
|
struct haltwo_dmabuf *p)
|
|
{
|
|
int err;
|
|
|
|
p->size = size;
|
|
|
|
/* XXX Check align/boundary XXX */
|
|
/* XXX Pass flags and use them instead BUS_DMA_NOWAIT? XXX */
|
|
err = bus_dmamem_alloc(sc->sc_dma_tag, p->size, 0, 0, p->dma_segs,
|
|
HALTWO_MAX_DMASEGS, &p->dma_segcount, BUS_DMA_NOWAIT);
|
|
if (err)
|
|
goto out;
|
|
|
|
/* XXX BUS_DMA_COHERENT? XXX */
|
|
err = bus_dmamem_map(sc->sc_dma_tag, p->dma_segs, p->dma_segcount,
|
|
p->size, &p->kern_addr, BUS_DMA_NOWAIT | BUS_DMA_COHERENT);
|
|
if (err)
|
|
goto out_free;
|
|
|
|
/* XXX Just guessing ... XXX */
|
|
err = bus_dmamap_create(sc->sc_dma_tag, p->size, HALTWO_MAX_DMASEGS,
|
|
PAGE_SIZE, 0, BUS_DMA_NOWAIT, &p->dma_map);
|
|
if (err)
|
|
goto out_free;
|
|
|
|
err = bus_dmamap_load(sc->sc_dma_tag, p->dma_map, p->kern_addr,
|
|
p->size, NULL, BUS_DMA_NOWAIT);
|
|
if (err)
|
|
goto out_destroy;
|
|
|
|
return 0;
|
|
|
|
out_destroy:
|
|
bus_dmamap_destroy(sc->sc_dma_tag, p->dma_map);
|
|
out_free:
|
|
bus_dmamem_free(sc->sc_dma_tag, p->dma_segs, p->dma_segcount);
|
|
out:
|
|
DPRINTF(("haltwo_alloc_dmamem failed: %d\n",err));
|
|
|
|
return err;
|
|
}
|
|
|
|
static void *
|
|
haltwo_malloc(void *v, int direction, size_t size, struct malloc_type *type,
|
|
int flags)
|
|
{
|
|
struct haltwo_softc *sc = v;
|
|
struct haltwo_dmabuf *p;
|
|
|
|
DPRINTF(("haltwo_malloc size = %d\n", size));
|
|
|
|
p = malloc(sizeof(struct haltwo_dmabuf), type, flags);
|
|
if (!p)
|
|
return 0;
|
|
|
|
if (haltwo_alloc_dmamem(sc, size, p)) {
|
|
free(p, type);
|
|
return 0;
|
|
}
|
|
|
|
p->next = sc->sc_dma_bufs;
|
|
sc->sc_dma_bufs = p;
|
|
|
|
return p->kern_addr;
|
|
}
|
|
|
|
static void
|
|
haltwo_free(void *v, void *addr, struct malloc_type *type)
|
|
{
|
|
struct haltwo_softc *sc = v;
|
|
struct haltwo_dmabuf *p,**pp;
|
|
|
|
for (pp = &sc->sc_dma_bufs; (p = *pp) != NULL; pp = &p->next) {
|
|
if (p->kern_addr == addr) {
|
|
*pp = p->next;
|
|
free(p, type);
|
|
return;
|
|
}
|
|
}
|
|
|
|
panic("haltwo_free: buffer not in list");
|
|
}
|
|
|
|
static int
|
|
haltwo_get_props(void *v)
|
|
{
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
haltwo_trigger_output(void *v, void *start, void *end, int blksize,
|
|
void (*intr)(void *), void *intrarg, struct audio_params *param)
|
|
{
|
|
struct haltwo_softc *sc = v;
|
|
struct haltwo_dmabuf *p;
|
|
uint16_t tmp;
|
|
uint32_t ctrl;
|
|
unsigned int fifobeg, fifoend, highwater;
|
|
|
|
DPRINTF(("haltwo_trigger_output start = %p end = %p blksize = %d"
|
|
" param = %p\n", start, end, blksize, param));
|
|
|
|
for (p = sc->sc_dma_bufs; p != NULL; p = p->next)
|
|
if (p->kern_addr == start)
|
|
break;
|
|
|
|
if (p == NULL) {
|
|
printf("haltwo_trigger_output: buffer not in list\n");
|
|
|
|
return (EINVAL);
|
|
}
|
|
|
|
/* Disable PBUS DMA */
|
|
bus_space_write_4(sc->sc_st, sc->sc_dma_sh, HPC_PBUS_CH0_CTL,
|
|
HPC_PBUS_DMACTL_ACT_LD);
|
|
|
|
/* Disable HAL2 codec DMA */
|
|
haltwo_read_indirect(sc, HAL2_IREG_DMA_PORT_EN, &tmp, NULL);
|
|
haltwo_write_indirect(sc, HAL2_IREG_DMA_PORT_EN,
|
|
tmp & ~HAL2_DMA_PORT_EN_CODECTX, 0);
|
|
|
|
haltwo_setup_dma(sc, &sc->sc_dac, p, (char *)end - (char *)start,
|
|
blksize, intr, intrarg);
|
|
|
|
highwater = (param->hw_channels * 4) >> 1;
|
|
fifobeg = 0;
|
|
fifoend = (param->hw_channels * 8) >> 3;
|
|
|
|
DPRINTF(("haltwo_trigger_output: hw_channels = %d highwater = %d"
|
|
" fifobeg = %d fifoend = %d\n", param->hw_channels, highwater,
|
|
fifobeg, fifoend));
|
|
|
|
ctrl = HPC_PBUS_DMACTL_RT
|
|
| HPC_PBUS_DMACTL_ACT_LD
|
|
| (highwater << HPC_PBUS_DMACTL_HIGHWATER_SHIFT)
|
|
| (fifobeg << HPC_PBUS_DMACTL_FIFOBEG_SHIFT)
|
|
| (fifoend << HPC_PBUS_DMACTL_FIFOEND_SHIFT);
|
|
|
|
/* Using PBUS CH0 for DAC DMA */
|
|
haltwo_write_indirect(sc, HAL2_IREG_DMA_DRV, 1, 0);
|
|
|
|
/* HAL2 is ready for action, now setup PBUS for DMA transfer */
|
|
bus_space_write_4(sc->sc_st, sc->sc_dma_sh, HPC_PBUS_CH0_DP,
|
|
sc->sc_dac.dma_seg.ds_addr);
|
|
bus_space_write_4(sc->sc_st, sc->sc_dma_sh, HPC_PBUS_CH0_CTL,
|
|
ctrl | HPC_PBUS_DMACTL_ACT);
|
|
|
|
/* Both HAL2 and PBUS have been setup, now start it up */
|
|
haltwo_read_indirect(sc, HAL2_IREG_DMA_PORT_EN, &tmp, NULL);
|
|
haltwo_write_indirect(sc, HAL2_IREG_DMA_PORT_EN,
|
|
tmp | HAL2_DMA_PORT_EN_CODECTX, 0);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
haltwo_trigger_input(void *v, void *start, void *end, int blksize,
|
|
void (*intr)(void *), void *intrarg, struct audio_params *param)
|
|
{
|
|
struct haltwo_softc *sc = v;
|
|
struct haltwo_dmabuf *p;
|
|
|
|
DPRINTF(("haltwo_trigger_input start = %p end = %p blksize = %d\n",
|
|
start, end, blksize));
|
|
|
|
for (p = sc->sc_dma_bufs; p != NULL; p = p->next)
|
|
if (p->kern_addr == start)
|
|
break;
|
|
|
|
if (p == NULL) {
|
|
printf("haltwo_trigger_input: buffer not in list\n");
|
|
|
|
return (EINVAL);
|
|
}
|
|
|
|
#if 0
|
|
haltwo_setup_dma(sc, &sc->sc_adc, p, (char *)end - (char *)start,
|
|
blksize, intr, intrarg);
|
|
#endif
|
|
|
|
return (ENXIO);
|
|
}
|