Mercurial > illumos > illumos-gate
changeset 10657:468e99e6f9ea
6874994 audiocmi could support surround sound
author | Garrett D'Amore <gdamore@opensolaris.org> |
---|---|
date | Sat, 26 Sep 2009 18:12:09 -0700 |
parents | 217544b3cf73 |
children | b6d8a8316c41 |
files | usr/src/uts/common/io/audio/drv/audiocmi/audiocmi.c usr/src/uts/common/io/audio/drv/audiocmi/audiocmi.h |
diffstat | 2 files changed, 216 insertions(+), 62 deletions(-) [+] |
line wrap: on
line diff
--- a/usr/src/uts/common/io/audio/drv/audiocmi/audiocmi.c Sat Sep 26 09:41:57 2009 -0700 +++ b/usr/src/uts/common/io/audio/drv/audiocmi/audiocmi.c Sat Sep 26 18:12:09 2009 -0700 @@ -29,22 +29,28 @@ * This file is part of Open Sound System * * Copyright (C) 4Front Technologies 1996-2008. - * - * This software is released under CDDL 1.0 source license. - * See the COPYING file included in the main directory of this source - * distribution for the license terms and conditions. */ #include <sys/audio/audio_driver.h> #include <sys/note.h> #include <sys/pci.h> +#include <sys/sysmacros.h> #include "audiocmi.h" /* - * Note: The original 4Front driver had support SPDIF, dual dac, and - * multichannel surround options. However, we haven't got any cards - * with these advanced features available on them for testing, so - * we're just going to support basic analog stereo for now. + * Note: The original 4Front driver had support SPDIF and dual dac + * options. Dual dac support is probably not terribly useful. SPDIF + * on the other hand might be quite useful, we just don't have a card + * that supports it at present. Some variants of the chip are also + * capable of jack retasking, but we're electing to punt on supporting + * that as well, for now (we don't have any cards that would benefit + * from this feature.) + * + * Note that surround support requires the use of the second DMA + * engine, and that the same second DMA engine is the only way one can + * capture from SPDIF. Rather than support a lot more complexity in + * the driver, we we will probably just punt on ever supporting + * capture of SPDIF. (SPDIF playback should be doable, however.) * * Adding back support for the advanced features would be an * interesting project for someone with access to suitable hardware. @@ -156,9 +162,67 @@ SET32(dev, REG_FUNCTRL1, port->fc1_rate_mask); SET32(dev, REG_CHFORMAT, port->chformat_mask); + if ((port->num == 1) && (dev->maxch > 2)) { + CLR32(dev, REG_LEGACY, LEGACY_NXCHG); + + if (port->nchan > 2) { + SET32(dev, REG_MISC, MISC_XCHGDAC); + CLR32(dev, REG_MISC, MISC_N4SPK3D); + } else { + CLR32(dev, REG_MISC, MISC_XCHGDAC); + SET32(dev, REG_MISC, MISC_N4SPK3D); + } + + switch (port->nchan) { + case 2: + if (dev->maxch >= 8) { + CLR8(dev, REG_MISC2, MISC2_CHB3D8C); + } + if (dev->maxch >= 6) { + CLR32(dev, REG_CHFORMAT, CHFORMAT_CHB3D5C); + CLR32(dev, REG_LEGACY, LEGACY_CHB3D6C); + } + if (dev->maxch >= 4) { + CLR32(dev, REG_CHFORMAT, CHFORMAT_CHB3D); + } + break; + case 4: + if (dev->maxch >= 8) { + CLR8(dev, REG_MISC2, MISC2_CHB3D8C); + } + if (dev->maxch >= 6) { + CLR32(dev, REG_CHFORMAT, CHFORMAT_CHB3D5C); + CLR32(dev, REG_LEGACY, LEGACY_CHB3D6C); + CLR32(dev, REG_MISC, MISC_ENCENTER); + CLR32(dev, REG_LEGACY, LEGACY_EXBASSEN); + } + SET32(dev, REG_CHFORMAT, CHFORMAT_CHB3D); + break; + case 6: + if (dev->maxch >= 8) { + CLR8(dev, REG_MISC2, MISC2_CHB3D8C); + } + SET32(dev, REG_CHFORMAT, CHFORMAT_CHB3D5C); + SET32(dev, REG_LEGACY, LEGACY_CHB3D6C); + CLR32(dev, REG_MISC, MISC_ENCENTER); + CLR32(dev, REG_LEGACY, LEGACY_EXBASSEN); + CLR32(dev, REG_CHFORMAT, CHFORMAT_CHB3D); + break; + + case 8: + SET8(dev, REG_MISC2, MISC2_CHB3D8C); + CLR32(dev, REG_MISC, MISC_ENCENTER); + CLR32(dev, REG_LEGACY, LEGACY_EXBASSEN); + CLR32(dev, REG_CHFORMAT, CHFORMAT_CHB3D5C); + CLR32(dev, REG_LEGACY, LEGACY_CHB3D6C); + CLR32(dev, REG_CHFORMAT, CHFORMAT_CHB3D); + break; + } + } + PUT32(dev, port->reg_paddr, port->paddr); - PUT16(dev, port->reg_bufsz, port->nframes - 1); - PUT16(dev, port->reg_fragsz, port->fragfr - 1); + PUT16(dev, port->reg_bufsz, (port->bufsz / 4) - 1); + PUT16(dev, port->reg_fragsz, (port->fragfr * port->nchan / 2) - 1); /* Analog output */ if (port->capture) { @@ -198,36 +262,16 @@ { cmpci_port_t *port = arg; cmpci_dev_t *dev = port->dev; - int intrs; + + _NOTE(ARGUNUSED(flag)); mutex_enter(&dev->mutex); - if (flag & ENGINE_INPUT) { - intrs = dev->rintrs; - } else { - intrs = dev->pintrs; - } - - /* - * Calculate fragfr, nfrags, buf. - * - * 48 as minimum is chosen to ensure that we will have at - * least 4 fragments. 512 is just an arbitrary limit, and at - * the smallest frame size will result in no more than 176 - * fragments. - */ - intrs = min(512, max(48, intrs)); - - port->fragfr = (48000 / intrs); - port->nfrags = CMPCI_BUF_LEN / (port->fragfr * 4); - port->nframes = port->nfrags * port->fragfr; - port->count = 0; - port->bufsz = port->nframes * 4; - *fragfrp = port->fragfr; *nfp = port->nfrags; *bufp = port->kaddr; + port->count = 0; port->open = B_TRUE; cmpci_reset_port(port); @@ -259,11 +303,12 @@ if ((dev->suspended) || (!port->open)) return; - offset = GET32(dev, port->reg_paddr) - port->paddr; + /* this gives us the offset in dwords */ + offset = (port->bufsz / 4) - (GET16(dev, port->reg_bufsz) + 1); - /* check for wrap */ + /* check for wrap - note that the count is given in dwords */ if (offset < port->offset) { - count = (port->bufsz - port->offset) + offset; + count = ((port->bufsz / 4) - port->offset) + offset; } else { count = offset - port->offset; } @@ -281,12 +326,16 @@ mutex_enter(&dev->mutex); cmpci_update_port(port); - /* 4 is from 16-bit stereo */ - count = port->count / 4; + /* the count is in dwords */ + count = port->count; + mutex_exit(&dev->mutex); - /* NB: 2 because each sample is 2 bytes wide */ - return (count); + /* + * convert dwords to frames - unfortunately this requires a + * divide + */ + return (count / (port->nchan / 2)); } @@ -718,10 +767,26 @@ } static int -cmpci_channels(void *unused) +cmpci_channels(void *arg) +{ + cmpci_port_t *port = arg; + + return (port->nchan); +} + +static void +cmpci_chinfo(void *arg, int chan, unsigned *offset, unsigned *incr) { - _NOTE(ARGUNUSED(unused)); - return (2); + cmpci_port_t *port = arg; + static const int map8ch[] = { 0, 1, 4, 5, 2, 3, 6, 7 }; + static const int map4ch[] = { 0, 1, 2, 3 }; + + if (port->nchan <= 4) { + *offset = map4ch[chan]; + } else { + *offset = map8ch[chan]; + } + *incr = port->nchan; } static int @@ -761,13 +826,15 @@ cmpci_rate, cmpci_sync, cmpci_qlen, - NULL, + cmpci_chinfo, }; static int cmpci_init(cmpci_dev_t *dev) { audio_dev_t *adev = dev->adev; + int playch; + int intrs; dev->pintrs = ddi_prop_get_int(DDI_DEV_T_ANY, dev->dip, DDI_PROP_DONTPASS, "play-interrupts", DEFINTS); @@ -775,6 +842,16 @@ dev->rintrs = ddi_prop_get_int(DDI_DEV_T_ANY, dev->dip, DDI_PROP_DONTPASS, "record-interrupts", DEFINTS); + playch = ddi_prop_get_int(DDI_DEV_T_ANY, dev->dip, + DDI_PROP_DONTPASS, "channels", dev->maxch); + + if ((playch % 2) || (playch < 2) || (playch > dev->maxch)) { + audio_dev_warn(adev, + "Invalid channels property (%d), resetting to %d", + playch, dev->maxch); + playch = dev->maxch; + } + for (int i = 0; i < PORT_MAX; i++) { cmpci_port_t *port; @@ -815,6 +892,8 @@ port->capture = B_TRUE; port->fc1_rate_mask = FUNCTRL1_ADC_RATE_48K; port->chformat_mask = CHFORMAT_CH0_16ST; + port->nchan = 2; + intrs = dev->rintrs; break; case 1: @@ -832,15 +911,45 @@ port->capture = B_FALSE; port->fc1_rate_mask = FUNCTRL1_DAC_RATE_48K; port->chformat_mask = CHFORMAT_CH1_16ST; + port->nchan = playch; + intrs = dev->pintrs; break; } + /* + * Calculate fragfr, nfrags, buf. + * + * 48 as minimum is chosen to ensure that we will have + * at least 4 fragments. 512 is just an arbitrary + * limit, and at the smallest frame size will result + * in no more than 176 fragments. + */ + intrs = min(512, max(48, intrs)); + + /* + * Two fragments are enough to get ping-pong buffers. + * The hardware could support considerably more than + * this, but it just wastes memory. + */ + port->nfrags = 2; + + /* + * For efficiency, we'd like to have the fragments + * evenly divisble by 64 bytes. Since frames are + * already evenly divisble by 4 (16-bit stereo), this + * is adequate. For a typical configuration (175 Hz + * requested) this will translate to 166 Hz. + */ + port->fragfr = P2ROUNDUP((48000 / intrs), 16); + port->nframes = port->nfrags * port->fragfr; + port->bufsz = port->nframes * port->nchan * 2; + if (ddi_dma_alloc_handle(dev->dip, &dma_attr, DDI_DMA_DONTWAIT, NULL, &port->dmah) != DDI_SUCCESS) { audio_dev_warn(adev, "ch%d: dma hdl alloc failed", i); return (DDI_FAILURE); } - if (ddi_dma_mem_alloc(port->dmah, CMPCI_BUF_LEN, &buf_attr, + if (ddi_dma_mem_alloc(port->dmah, port->bufsz, &buf_attr, DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT, NULL, &port->kaddr, &rlen, &port->acch) != DDI_SUCCESS) { audio_dev_warn(adev, "ch%d: dma mem allcoc failed", i); @@ -978,35 +1087,48 @@ } /* setup some initial values */ + dev->maxch = 2; audio_dev_set_description(adev, "C-Media PCI Audio"); switch (device) { case CMEDIA_CM8738: - /* Crazy 8738 detection scheme */ - val = GET32(dev, REG_INTCTRL) & 0x1F000000; + /* + * Crazy 8738 detection scheme. Reviewing multiple + * different open sources gives multiple different + * answers here. Its unclear how accurate this is. + * The approach taken here is a bit conservative in + * assigning multiple channel support, but for users + * with newer 8768 cards should offer the best + * capability. + */ + val = GET32(dev, REG_INTCTRL) & INTCTRL_MDL_MASK; if (val == 0) { - if (GET32(dev, REG_CHFORMAT & 0x1F000000)) { - audio_dev_set_version(adev, "CM8738-037"); + if (GET32(dev, REG_CHFORMAT & CHFORMAT_VER_MASK)) { + audio_dev_set_version(adev, "CMI-8738-037"); + dev->maxch = 4; } else { - audio_dev_set_version(adev, "CM8738-033"); + audio_dev_set_version(adev, "CMI-8738-033"); } - } else if (val & 0x0C000000) { - audio_dev_set_version(adev, "CMI8768"); - } else if (val & 0x08000000) { - audio_dev_set_version(adev, "CMI8738-055"); - } else if (val & 0x04000000) { - audio_dev_set_version(adev, "CMI8738-039"); + } else if ((val & INTCTRL_MDL_068) == INTCTRL_MDL_068) { + audio_dev_set_version(adev, "CMI-8768"); + dev->maxch = 8; + } else if ((val & INTCTRL_MDL_055) == INTCTRL_MDL_055) { + audio_dev_set_version(adev, "CMI-8738-055"); + dev->maxch = 6; + } else if ((val & INTCTRL_MDL_039) == INTCTRL_MDL_039) { + audio_dev_set_version(adev, "CMI-8738-039"); + dev->maxch = 4; } else { - audio_dev_set_version(adev, "CMI8738"); + audio_dev_set_version(adev, "CMI-8738"); } break; case CMEDIA_CM8338A: - audio_dev_set_version(dev->adev, "CM8338A"); + audio_dev_set_version(dev->adev, "CMI-8338"); break; case CMEDIA_CM8338B: - audio_dev_set_version(dev->adev, "CM8338B"); + audio_dev_set_version(dev->adev, "CMI-8338B"); break; }
--- a/usr/src/uts/common/io/audio/drv/audiocmi/audiocmi.h Sat Sep 26 09:41:57 2009 -0700 +++ b/usr/src/uts/common/io/audio/drv/audiocmi/audiocmi.h Sat Sep 26 18:12:09 2009 -0700 @@ -67,6 +67,8 @@ #define REG_CH1_PADDR 0x88 #define REG_CH1_BUFSZ 0x8C #define REG_CH1_FRAGSZ 0x8E +#define REG_SPDIF_STAT 0x90 +#define REG_MISC2 0x92 #define FUNCTRL0_CH1_RST BIT(19) #define FUNCTRL0_CH0_RST BIT(18) @@ -101,6 +103,11 @@ #define FUNCTRL1_UART_EN BIT(2) #define FUNCTRL1_JYSTK_EN BIT(1) +#define CHFORMAT_CHB3D5C BIT(31) /* 5 channel surround */ +#define CHFORMAT_CHB3D BIT(29) /* 4 channel surround */ +#define CHFORMAT_VER_MASK (0x1f << 24) +#define CHFORMAT_VER_033 0 +#define CHFORMAT_VER_037 1 #define CHFORMAT_CH1_MASK (0x3 << 2) #define CHFORMAT_CH1_16ST (0x3 << 2) #define CHFORMAT_CH1_16MO (0x2 << 2) @@ -112,6 +119,10 @@ #define CHFORMAT_CH0_8ST (0x1 << 0) #define CHFORMAT_CH0_8MO (0x0 << 0) +#define INTCTRL_MDL_MASK (0xffU << 24) +#define INTCTRL_MDL_068 (0x28 << 24) +#define INTCTRL_MDL_055 (0x8 << 24) +#define INTCTRL_MDL_039 (0x4 << 24) #define INTCTRL_TDMA_EN BIT(18) #define INTCTRL_CH1_EN BIT(17) #define INTCTRL_CH0_EN BIT(16) @@ -130,27 +141,48 @@ #define INTSTAT_CH1_INT BIT(1) #define INTSTAT_CH0_INT BIT(0) +#define LEGACY_NXCHG BIT(31) +#define LEGACY_CHB3D6C BIT(15) /* 6 channel surround */ +#define LEGACY_CENTR2LN BIT(14) /* line in as center out */ +#define LEGACY_BASS2LN BIT(13) /* line in as lfe */ +#define LEGACY_EXBASSEN BIT(12) /* external bass input enable */ + #define MISC_PWD BIT(31) /* power down */ #define MISC_RESET BIT(30) +#define MISC_N4SPK3D BIT(26) /* 4 channel emulation */ #define MISC_ENDBDAC BIT(23) /* dual dac */ #define MISC_XCHGDAC BIT(22) /* swap front/rear dacs */ +#define MISC_SPD32SEL BIT(21) /* 32-bit SPDIF (default 16-bit) */ #define MISC_FM_EN BIT(19) /* enable legacy FM */ +#define MISC_SPDF_AC97 BIT(15) /* spdif out 44.1k (0), 48 k (1) */ +#define MISC_ENCENTER BIT(7) /* enable center */ +#define MISC_REAR2LN BIT(6) /* send rear to line in */ #define MIX2_FMMUTE BIT(7) #define MIX2_WSMUTE BIT(6) +#define MIX2_SPK4 BIT(5) /* line-in is rear out */ +#define MIX2_REAR2FRONT BIT(4) /* swap front and rear */ #define MIX2_WAVEIN_L BIT(3) /* for recording wave out */ #define MIX2_WAVEIN_R BIT(2) /* for recording wave out */ +#define MIX2_X3DEN BIT(1) /* 3D surround enable */ +#define MIX2_CDPLAY BIT(0) /* spdif-in PCM to DAC */ #define MIX3_RAUXREN BIT(7) #define MIX3_RAUXLEN BIT(6) #define MIX3_VAUXRM BIT(5) /* r-aux mute */ #define MIX3_VAUXLM BIT(4) /* l-aux mute */ #define MIX3_VADCMIC_MASK (0x7 << 1) /* rec mic volume */ +#define MIX3_CEN2MIC BIT(2) #define MIX3_MICGAINZ BIT(0) /* mic gain */ #define VAUX_L_MASK 0xf0 #define VAUX_R_MASK 0x0f +#define MISC2_CHB3D8C BIT(5) /* 8 channel surround */ +#define MISC2_SPD32FMT BIT(4) /* spdif at 32 kHz */ +#define MISC2_ADC2SPDIF BIT(3) /* send adc to spdif out */ +#define MISC2_SHAREADC BIT(2) /* use adc for cen/lfe */ + /* Indexes via SBINDEX */ #define IDX_MASTER_LEFT 0x30 #define IDX_MASTER_RIGHT 0x31 @@ -237,6 +269,7 @@ unsigned nfrags; unsigned nframes; unsigned bufsz; + unsigned nchan; boolean_t capture; boolean_t open; @@ -279,8 +312,8 @@ #define MDL_CM8338A 2 #define MDL_CM8338B 3 #define MDL_CM8768 4 - char *chip_name; - int chiprev; + + int maxch; boolean_t suspended; @@ -294,7 +327,6 @@ * giving a total address space of 256K. Note, however, that we will restrict * this further when we do fragment and memory allocation. */ -#define CMPCI_BUF_LEN (65536) #define DEFINTS 175 #define GET8(dev, offset) \