/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
/*
* Purpose: Driver for CMEDIA CM8738 PCI audio controller.
*/
/*
* This file is part of Open Sound System
*
* Copyright (C) 4Front Technologies 1996-2008.
*/
#include <sys/sysmacros.h>
#include "audiocmi.h"
/*
* 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.
*
* Note that each variant (CMI 8338, 8738-033, -037, -055, and 8768)
* seems to have significant differences in some of the registers.
* While programming these parts for basic stereo is pretty much the
* same on all parts, doing anything more than that can be
* sigificantly different for each part.
*/
};
};
DMA_ATTR_VERSION, /* dma_attr_version */
0x0, /* dma_attr_addr_lo */
0xffffffffU, /* dma_attr_addr_hi */
0x3ffff, /* dma_attr_count_max */
0x8, /* dma_attr_align */
0x7f, /* dma_attr_burstsizes */
0x1, /* dma_attr_minxfer */
0x3ffff, /* dma_attr_maxxfer */
0x3ffff, /* dma_attr_seg */
0x1, /* dma_attr_sgllen */
0x1, /* dma_attr_granular */
0 /* dma_attr_flags */
};
static int
{
return (0);
}
static void
{
}
static int
{
/* reset channel */
drv_usecwait(10);
drv_usecwait(10);
/* Set 48k 16-bit stereo -- these are just with all bits set. */
} else {
}
case 2:
}
}
}
break;
case 4:
}
}
break;
case 6:
}
break;
case 8:
break;
}
}
/* Analog output */
/* Analog capture */
} else {
}
return (0);
}
static void
{
}
static uint64_t
{
/* this gives us the offset in dwords */
/* check for wrap - note that the count is given in dwords */
} else {
}
/*
* convert dwords to frames - unfortunately this requires a
* divide
*/
}
static void
{
}
static uint8_t
{
}
static void
{
/* reset all mix values */
outmix = OUTMIX_MIC |
/* program PCM volume */
if (left) {
/* left and right are the same */
} else {
}
/* speaker volume - mute in extension register, but we don't use */
/* mic gain */
if (left) {
/* set record mic gain */
v &= ~(0x7 << 1);
} else {
outmix &= ~OUTMIX_MIC;
}
/* line in */
if (left) {
} else {
inmix[0] &= ~INMIX_LINE_L;
outmix &= ~OUTMIX_LINE_L;
}
if (right) {
} else {
outmix &= ~OUTMIX_LINE_R;
}
/* cd */
if (left) {
} else {
inmix[0] &= ~INMIX_CD_L;
outmix &= ~OUTMIX_CD_L;
}
if (right) {
} else {
outmix &= ~OUTMIX_CD_R;
}
/* aux - trickier because it doesn't use regular sbpro mixer */
/* maybe enable recording */
} else {
}
/* maybe enable monitoring */
} else {
}
/* now do the recsrcs */
}
inmix[0] &= ~INMIX_LINE_L;
}
inmix[0] &= ~INMIX_CD_L;
}
} else {
}
/* now the monsrcs */
outmix &= ~OUTMIX_MIC;
}
}
}
/* micboost */
} else {
}
}
static int
{
/*
* We don't bother to check for valid values - a bogus value
* will give incorrect volumes, but is otherwise harmless.
*/
return (0);
}
static int
{
return (0);
}
static void
{
switch (num) {
case CTL_VOLUME:
desc.acd_minvalue = 0;
break;
case CTL_LINEOUT:
desc.acd_minvalue = 0;
break;
case CTL_SPEAKER:
desc.acd_minvalue = 0;
break;
case CTL_MIC:
desc.acd_minvalue = 0;
break;
case CTL_LINEIN:
desc.acd_minvalue = 0;
break;
case CTL_CD:
desc.acd_minvalue = 0;
break;
case CTL_AUX:
desc.acd_minvalue = 0;
break;
case CTL_RECSRCS:
break;
case CTL_MONSRCS:
break;
case CTL_MICBOOST:
desc.acd_minvalue = 0;
break;
}
}
static void
{
} else {
}
}
static void
{
for (int i = 0; i < CTL_NUM; i++) {
}
}
}
static void
{
/* Full reset */
drv_usecwait(100);
/* reset all channels */
/* disable interrupts and such */
/* disable uart, joystick in Function Control Reg1 */
/*
* Set DAC and ADC rates to 48 kHz - note that both rates have
* all bits set in them, so we can do this with a simple "set".
*/
/* Set 16-bit stereo -- also these are just with all bits set. */
}
static int
{
return (AUDIO_FORMAT_S16_LE);
}
static int
{
}
static void
{
} else {
}
}
static int
{
return (48000);
}
static void
{
}
AUDIO_ENGINE_VERSION, /* version number */
NULL, /* qlen */
NULL, /* playahead */
};
static int
{
int playch;
"Invalid channels property (%d), resetting to %d",
}
for (int i = 0; i < PORT_MAX; i++) {
unsigned dmaflags;
unsigned caps;
unsigned ccnt;
/*
* Channel 0 is recording channel, unless we are in
* dual DAC mode. The reason for this is simple --
* only channel "B" (which I presume to mean channel
* 1) supports multichannel configuration.
*
* However, if we're going to use SPDIF recording,
* then recording *must* occur on channel 1. Yes, the
* hardware is "strange".
*/
switch (i) {
case 0:
break;
case 1:
break;
}
/*
* 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.
*/
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
}
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
void
{
/* free up ports, including DMA resources for ports */
for (int i = 0; i < PORT_MAX; i++) {
}
}
}
}
}
int
{
return (DDI_FAILURE);
}
if (vendor != CMEDIA_VENDOR_ID ||
(device != CMEDIA_CM8338B))) {
return (DDI_FAILURE);
}
/* enable IO and Master accesses */
goto err_exit;
}
goto err_exit;
}
/* setup some initial values */
switch (device) {
case CMEDIA_CM8738:
/*
* 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.
*/
if (val == 0) {
} else {
}
} else {
}
break;
case CMEDIA_CM8338A:
break;
case CMEDIA_CM8338B:
break;
}
goto err_exit;
}
return (DDI_SUCCESS);
return (DDI_FAILURE);
}
static int
{
/* wait one millisecond, to give reset a chance to get up */
drv_usecwait(1000);
return (DDI_SUCCESS);
}
static int
{
return (DDI_FAILURE);
/* disable channels */
return (DDI_SUCCESS);
}
static int
{
return (DDI_FAILURE);
}
/* disable channels */
return (DDI_SUCCESS);
}
static int
{
switch (cmd) {
case DDI_ATTACH:
return (cmpci_attach(dip));
case DDI_RESUME:
return (DDI_FAILURE);
}
return (cmpci_resume(dev));
default:
return (DDI_FAILURE);
}
}
static int
{
return (DDI_FAILURE);
}
switch (cmd) {
case DDI_DETACH:
return (cmpci_detach(dev));
case DDI_SUSPEND:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
DEVO_REV, /* rev */
0, /* refcnt */
NULL, /* getinfo */
nulldev, /* identify */
nulldev, /* probe */
cmpci_ddi_attach, /* attach */
cmpci_ddi_detach, /* detach */
nodev, /* reset */
NULL, /* cb_ops */
NULL, /* bus_ops */
NULL, /* power */
cmpci_quiesce, /* quiesce */
};
&mod_driverops, /* drv_modops */
"C-Media PCI Audio", /* linkinfo */
&cmpci_dev_ops, /* dev_ops */
};
{ &cmpci_modldrv, NULL }
};
int
_init(void)
{
int rv;
}
return (rv);
}
int
_fini(void)
{
int rv;
}
return (rv);
}
int
{
}