/*
* 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 the CMedia 8788 sound card
*/
/*
*
* Copyright (C) 4Front Technologies 1996-2011.
*
* 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/sysmacros.h>
#include "audiocmihd.h"
};
};
DMA_ATTR_V0, /* version number */
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 cmediahd_attach(dev_info_t *);
static int cmediahd_resume(dev_info_t *);
static int cmediahd_detach(cmediahd_devc_t *);
static int cmediahd_suspend(cmediahd_devc_t *);
static int cmediahd_open(void *, int, unsigned *, caddr_t *);
static void cmediahd_close(void *);
static int cmediahd_start(void *);
static void cmediahd_stop(void *);
static int cmediahd_format(void *);
static int cmediahd_channels(void *);
static int cmediahd_rate(void *);
static uint64_t cmediahd_count(void *);
static void cmediahd_sync(void *, unsigned);
static void cmediahd_chinfo(void *, int, unsigned *, unsigned *);
static int cmediahd_alloc_port(cmediahd_devc_t *, int);
static void cmediahd_reset_port(cmediahd_portc_t *);
static void cmediahd_destroy(cmediahd_devc_t *);
static void cmediahd_hwinit(cmediahd_devc_t *);
static void cmediahd_ac97_hwinit(cmediahd_devc_t *);
static void cmediahd_del_controls(cmediahd_devc_t *);
NULL, /* qlen */
NULL /* playahead */
};
0, 0, 3, 7, 10, 13, 16, 19,
21, 23, 26, 28, 30, 32, 34, 35,
37, 39, 40, 42, 43, 45, 46, 47,
49, 50, 51, 52, 53, 55, 56, 57,
58, 59, 60, 61, 62, 63, 64, 65,
65, 66, 67, 68, 69, 70, 70, 71,
72, 73, 73, 74, 75, 75, 76, 77,
77, 78, 79, 79, 80, 81, 81, 82,
82, 83, 84, 84, 85, 85, 86, 86,
87, 87, 88, 88, 89, 89, 90, 90,
91, 91, 92, 92, 93, 93, 94, 94,
95, 95, 96, 96, 96, 97, 97, 98,
98, 98, 99, 99, 100
};
static uint32_t
{
return (vol);
}
static uint16_t
{
val = 0L;
drv_usecwait(100);
return (data);
}
static void
{
val = 0L;
drv_usecwait(100);
}
#if 0 /* Front Panel AC'97 not supported yet */
static uint16_t
{
val = 0L;
drv_usecwait(100);
return (data);
}
static void
{
val = 0L;
drv_usecwait(100);
}
#endif
static void
{
unsigned int tmp;
/* check if SPI is busy */
count = 10;
drv_usecwait(10);
}
shift = 9;
latch = 0;
} else {
shift = 8;
latch = 0x80;
}
/* write 2-byte data values */
/* Latch high, clock=160, Len=2byte, mode=write */
/* now address which codec you want to send the data to */
/* send the command to write the data */
}
static void
unsigned char data)
{
/* Wait for it to stop being busy */
drv_usecwait(10);
count--;
}
if (count == 0) {
return;
}
/* first write the Register Address into the MAP register */
/* now write the data */
/* select the codec number to address */
}
static void
{
/* Fast Two-Wire. Reduces the wire ready time. */
/* Power down, enable control mode. */
/*
* Left justified PCM (DAC and 8788 support I2S, but doesn't work.
* Setting it introduces clipping like hell).
*/
/* remove the powerdown flag */
}
static void
{
/* Power down and enable control port. */
/* Left-justified PCM */
/* Ramp & Automute, re-set DAC defaults. */
/* Filter control, DAC defs. */
/* Invert control, DAC defs. */
/* Mixing control, DAC defs. */
/* Volume to -64dB. */
/* Power up. */
}
static void
{
}
static void
{
switch (codec_id) {
case 0:
CS4398_VOL(left));
CS4398_VOL(right));
break;
case 1:
CS4362A_VOL(left));
CS4362A_VOL(right));
break;
case 2:
CS4362A_VOL(left));
CS4362A_VOL(right));
break;
case 3:
CS4362A_VOL(left));
CS4362A_VOL(right));
break;
}
}
static void
{
}
static void
{
if (codec_id == 0) {
}
}
static void
{
switch (codec_id) {
case 0: /* front */
break;
case 1: /* side */
break;
case 2: /* rear */
break;
case 3: /* center */
break;
}
}
static void
{
if (left > 100)
left = 100;
if (right > 100)
right = 100;
}
static void
{
if (left > 100)
left = 100;
if (right > 100)
right = 100;
case SUBID_XONAR_D1:
case SUBID_XONAR_DX:
break;
case SUBID_XONAR_D2:
case SUBID_XONAR_D2X:
break;
case SUBID_XONAR_STX:
break;
case SUBID_XONAR_DS:
break;
default:
break;
}
}
/*
* Audio routines
*/
int
{
return (0);
}
void
{
}
int
{
case CMEDIAHD_PLAY:
/* enable the dma */
break;
case CMEDIAHD_REC:
/* enable the channel */
break;
}
return (0);
}
void
{
case CMEDIAHD_PLAY:
/* disable dma */
break;
case CMEDIAHD_REC:
/* disable dma */
break;
}
}
int
{
return (AUDIO_FORMAT_S16_LE);
}
int
{
}
int
{
return (48000);
}
void
{
}
static void
{
} else {
}
}
{
else
/* check for wrap */
} else {
}
/* convert from 16-bit stereo */
return (count);
}
/* private implementation bits */
void
{
int channels;
return;
case CMEDIAHD_PLAY:
/* reset channel */
drv_usecwait(10);
drv_usecwait(10);
case 2:
channels = 0;
break;
case 4:
channels = 1;
break;
case 6:
channels = 2;
break;
case 8:
channels = 3;
break;
}
/* set the format bits in play format register */
break;
case CMEDIAHD_REC:
drv_usecwait(10);
drv_usecwait(10);
case 2:
channels = 0x0;
break;
case 4:
channels = 0x1;
break;
case 6:
channels = 0x2;
break;
case 8:
channels = 0x4;
break;
default:
/* Stereo - boomer only supports stereo */
channels = 0x0;
break;
}
}
}
int
{
int dir;
unsigned caps;
switch (num) {
case CMEDIAHD_REC:
dir = DDI_DMA_READ;
break;
case CMEDIAHD_PLAY:
dir = DDI_DMA_WRITE;
break;
default:
return (DDI_FAILURE);
}
/*
* Calculate buffer size and frames
*/
/* Alloc buffers */
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
&count) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
void
{
for (int i = 0; i < CMEDIAHD_NUM_PORTC; i++) {
if (!portc)
continue;
}
}
}
}
}
}
}
}
}
}
void
{
/* GPIO #0 programmed as output, set CMI9780 Reg0x70 */
/* unmute Master Volume */
/* change PCBeep path, set Mix2FR on, option for quality issue */
/* mute PCBeep, option for quality issues */
/* Record Select Control Register (Index 1Ah) */
/* set Mic Volume Register 0x0Eh umute and enable micboost */
/* set Line in Volume Register 0x10h mute */
/* set CD Volume Register 0x12h mute */
/* set AUX Volume Register 0x16h max */
/* set record gain Register 0x1Ch to max */
/* GPIO status register enable GPO0 */
}
void
{
unsigned short sVal;
unsigned short i2s_fmt;
unsigned char bVal;
int i, count;
/* setup the default rec DMA engines to REC_A */
/* setup GPIOs to 0 */
devc->gpio_codec = 0;
/* Init CMI Controller */
if (!(sVal & 0x0008)) {
bVal |= 0x20;
}
/* Cold reset onboard AC97 */
count = 100;
drv_usecwait(100);
}
if (!count)
/* check if there's an onboard AC97 codec (CODEC 0) */
if (sVal & 0x10) {
/* disable CODEC0 OUTPUT */
/* enable CODEC0 INPUT */
}
/* check if there's an front panel AC97 codec (CODEC1) */
if (sVal & 0x20) {
/* enable CODEC1 OUTPUT */
/* enable CODEC1 INPUT */
}
/* Disable AC97 interrupts and initialize AC97 */
i2s_fmt = 0x011A;
/* Setup I2S to use 16bit instead of 24Bit */
/* setup Routing regs (default vals) */
/* Enable Xonar output */
case SUBID_XONAR_D1:
case SUBID_XONAR_DX:
/* GPIO0 = 0x001controls output */
/* GPIO2/3 = 0x00C codec output control */
/* disable AC97 mixer - not used */
/* setup for 2wire communication mode */
/* setup GPIO direction */
/* setup GPIO pins */
/* init the front and rear dacs */
break;
case SUBID_XONAR_D2:
case SUBID_XONAR_D2X:
/* GPIO8 = 0x0100 controls output */
/* GPIO2/3 = 0x000C codec output control */
/* disable the AC97 mixer - it's not useful */
/* setup for spi communication mode */
/* setup the GPIO direction */
/* setup GPIO Pins */
/* for all 4 codecs: unmute, set to 24Bit SPI */
for (i = 0; i < 4; ++i) {
/* left vol */
/* right vol */
}
break;
case SUBID_XONAR_STX:
/* disable the AC97 mixer - it's not useful */
/* setup for spi communication mode */
/* setup the GPIO direction */
/* setup GPIO Pins */
/* init front DAC */
/* left vol */
/* right vol */
break;
case SUBID_XONAR_DS:
/* GPIO 8 = 1 output enabled 0 mute */
/* GPIO 7 = 1 lineout enabled 0 mute */
/* GPIO 6 = 1 mic select 0 line-in select */
/* GPIO 4 = 1 FP Headphone plugged in */
/* GPIO 3 = 1 FP Mic plugged in */
/* disable the AC97 mixer - it's not useful */
/* setup for spi communication mode */
/* setup the GPIO direction */
/* setup GPIO Pins */
/* powerdown hp */
/* LJust/24bit */
break;
default:
/* SPI default for anything else, including the */
break;
}
/* only initialize AC97 if not defined */
}
static int
{
case CTL_VOLUME:
case CTL_FRONT:
break;
case CTL_REAR:
break;
case CTL_CENTER:
val &= 0xff;
break;
case CTL_LFE:
val &= 0xff;
val <<= 8;
break;
case CTL_SURROUND:
break;
case CTL_MONITOR:
/* enable recording monitor rec 1 and rec2 */
if (val)
else
break;
case CTL_RECSRC:
switch (val) {
case 1: /* Line */
break;
case 2: /* Mic */
/* Unmute Mic */
/* Mute AUX and Video */
break;
case 4: /* AUX */
/* Unmute AUX */
/* Mute CD and Mic */
break;
case 8: /* Video (CD) */
/* Unmute Video (CD) */
/* Mute AUX and Mic */
/* set input to video */
break;
}
break;
case CTL_LOOP:
if (val)
else
break;
case CTL_SPREAD:
if (val)
else
0xE400, PLAY_ROUTING);
break;
case CTL_RECGAIN:
break;
case CTL_MICVOL:
if (val)
else
break;
case CTL_AUXVOL:
if (val)
else
break;
case CTL_CDVOL:
if (val)
else
break;
}
return (0);
}
static int
{
return (0);
}
static void
{
switch (num) {
case CTL_VOLUME:
desc.acd_minvalue = 0;
break;
case CTL_FRONT:
desc.acd_minvalue = 0;
break;
case CTL_REAR:
desc.acd_minvalue = 0;
break;
case CTL_SURROUND:
desc.acd_minvalue = 0;
break;
case CTL_CENTER:
desc.acd_minvalue = 0;
break;
case CTL_LFE:
desc.acd_minvalue = 0;
break;
case CTL_MONITOR:
desc.acd_minvalue = 0;
break;
case CTL_RECSRC:
} else {
}
break;
case CTL_LOOP:
desc.acd_minvalue = 0;
break;
case CTL_SPREAD:
desc.acd_minvalue = 0;
break;
case CTL_RECGAIN:
desc.acd_minvalue = 0;
break;
case CTL_MICVOL:
desc.acd_minvalue = 0;
break;
case CTL_AUXVOL:
desc.acd_minvalue = 0;
break;
case CTL_CDVOL:
desc.acd_minvalue = 0;
break;
}
}
static void
{
int ctl;
continue;
}
}
static void
{
case SUBID_XONAR_DS:
break;
case SUBID_XONAR_D2:
case SUBID_XONAR_D2X:
break;
}
}
void
{
for (int i = 0; i < CTL_NUM; i++) {
}
}
}
int
{
goto error;
}
goto error;
}
if (vendor != PCI_VENDOR_ID_CMEDIA ||
device != PCI_DEVICE_ID_CMEDIAHD) {
goto error;
}
goto error;
}
/* Detect Xonar device */
if (subvendor == ASUS_VENDOR_ID) {
switch (subdevice) {
case SUBID_XONAR_D1:
"Asus Xonar D1 (AV100)");
break;
case SUBID_XONAR_DX:
"Asus Xonar DX (AV100)");
break;
case SUBID_XONAR_D2:
"Asus Xonar D2 (AV200)");
break;
case SUBID_XONAR_D2X:
"Asus Xonar D2X (AV200)");
break;
case SUBID_XONAR_STX:
"Asus Xonar STX (AV100)");
break;
case SUBID_XONAR_DS:
"Asus Xonar DS (AV66)");
break;
default:
"Asus Xonar Unknown Model");
break;
}
}
goto error;
goto error;
/* Add the AC97 Mixer if there is an onboard AC97 device */
goto error;
}
}
#if 0
/* Add the front panel AC97 device if one exists */
if (devc->has_fp_ac97) {
goto error;
}
}
#endif
/* Add the standard CMI8788 Mixer panel */
goto error;
}
return (DDI_SUCCESS);
return (DDI_FAILURE);
}
int
{
return (DDI_SUCCESS);
}
int
{
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
int
{
return (DDI_SUCCESS);
}
static int cmediahd_ddi_quiesce(dev_info_t *);
DEVO_REV, /* rev */
0, /* refcnt */
NULL, /* getinfo */
nulldev, /* identify */
nulldev, /* probe */
cmediahd_ddi_attach, /* attach */
cmediahd_ddi_detach, /* detach */
nodev, /* reset */
NULL, /* cb_ops */
NULL, /* bus_ops */
NULL, /* power */
cmediahd_ddi_quiesce, /* quiesce */
};
&mod_driverops, /* drv_modops */
"CMedia 8788", /* linkinfo */
&cmediahd_dev_ops, /* dev_ops */
};
{ &cmediahd_modldrv, NULL }
};
int
_init(void)
{
int rv;
}
return (rv);
}
int
_fini(void)
{
int rv;
}
return (rv);
}
int
{
}
int
{
switch (cmd) {
case DDI_ATTACH:
return (cmediahd_attach(dip));
case DDI_RESUME:
return (cmediahd_resume(dip));
default:
return (DDI_FAILURE);
}
}
int
{
switch (cmd) {
case DDI_DETACH:
return (cmediahd_detach(devc));
case DDI_SUSPEND:
return (cmediahd_suspend(devc));
default:
return (DDI_FAILURE);
}
}
int
{
/*
* Turn off the hardware
*/
return (DDI_SUCCESS);
}