audiocmi.c revision 08045defdf65ee890fef6e20510a093a17feb8fe
/*
* 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
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Purpose: Driver for CMEDIA CM8738 PCI audio controller.
*/
/*
* 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 "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.
*
* 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.
*/
static ddi_device_acc_attr_t acc_attr = {
};
static ddi_device_acc_attr_t buf_attr = {
};
static ddi_dma_attr_t dma_attr = {
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 uint_t
{
return (rv);
}
intclear = 0;
}
}
/* toggle the bits that we are going to handle */
if (intclear) {
}
if (cb0) {
}
if (cb1) {
}
return (rv);
}
static void
{
return;
/* reset channel */
drv_usecwait(10);
drv_usecwait(10);
/* Set 48k 16-bit stereo -- these are just with all bits set. */
/* Analog output */
/* Analog capture */
} else {
}
}
static void
{
return;
}
static void
{
return;
}
static int
{
int intrs;
if (flag & ENGINE_INPUT) {
} else {
}
/*
* 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.
*/
return (0);
}
static void
cmpci_close(void *arg)
{
}
static void
{
return;
/* check for wrap */
} else {
}
}
static uint64_t
cmpci_count(void *arg)
{
/* 4 is from 16-bit stereo */
/* NB: 2 because each sample is 2 bytes wide */
return (count);
}
static int
{
int actual;
(actual != 1)) {
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
NULL) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static void
{
}
static uint8_t
{
}
static void
{
return;
/* 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
{
}
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
cmpci_format(void *unused)
{
return (AUDIO_FORMAT_S16_LE);
}
static int
cmpci_channels(void *unused)
{
return (2);
}
static int
cmpci_rate(void *unused)
{
return (48000);
}
static void
{
}
static size_t
cmpci_qlen(void *unused)
{
return (0);
}
AUDIO_ENGINE_VERSION, /* version number */
NULL, /* start */
NULL, /* stop */
NULL,
};
static int
{
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;
}
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 */
if (val == 0) {
} else {
}
} else if (val & 0x0C000000) {
} else if (val & 0x08000000) {
} else if (val & 0x04000000) {
} else {
}
break;
case CMEDIA_CM8338A:
break;
case CMEDIA_CM8338B:
break;
}
goto err_exit;
}
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);
for (int i = 0; i < PORT_MAX; i++) {
}
}
return (DDI_SUCCESS);
}
static int
{
return (DDI_FAILURE);
/* disable interrupts */
/* disable channels */
return (DDI_SUCCESS);
}
static int
{
return (DDI_SUCCESS);
}
static int
{
return (DDI_FAILURE);
}
/* disable interrupts */
/* 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 (cmpci_suspend(dev));
default:
return (DDI_FAILURE);
}
}
static struct dev_ops cmpci_dev_ops = {
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 */
};
static struct modldrv cmpci_modldrv = {
&mod_driverops, /* drv_modops */
"C-Media PCI Audio", /* linkinfo */
&cmpci_dev_ops, /* dev_ops */
};
static struct modlinkage modlinkage = {
{ &cmpci_modldrv, NULL }
};
int
_init(void)
{
int rv;
}
return (rv);
}
int
_fini(void)
{
int rv;
}
return (rv);
}
int
{
}