2529 lines
71 KiB
C
2529 lines
71 KiB
C
#ident "@(#) dbri_mmcodec.c 1.1@(#) Copyright (c) 1991-92 Sun Microsystems, Inc."
|
|
|
|
#include <sys/errno.h>
|
|
#include <sys/types.h>
|
|
#include <sys/param.h>
|
|
#include <sys/limits.h>
|
|
#include <sys/stream.h>
|
|
#include <sys/stropts.h>
|
|
#include <sys/ioccom.h>
|
|
#include <sys/time.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/file.h>
|
|
|
|
#include <sun/audioio.h>
|
|
#include <sun/isdnio.h>
|
|
#include <sun/dbriio.h>
|
|
#include <sbusdev/audiovar.h>
|
|
#include <sbusdev/dbri_reg.h>
|
|
#include <sbusdev/mmcodec_reg.h>
|
|
#include <sbusdev/dbrivar.h>
|
|
#include "audiodebug.h"
|
|
|
|
|
|
/* Global variables */
|
|
extern int Audio_debug;
|
|
|
|
int Dbri_recv_bsize = DBRI_RECV_BSIZE;
|
|
|
|
|
|
|
|
/* Some useful defines - see the self documenting code */
|
|
|
|
#define SB_PAL_LEN 8
|
|
#define ULAW_ZERO 0xffffffff
|
|
#define ALAW_ZERO 0xd5d5d5d5
|
|
#define LINEAR_ZERO 0x00000000
|
|
|
|
#define DUMMYDATA_LEN (32)
|
|
#define DUMMYDATA_CYCLE (driver->ser_sts.pal_present ? driver->ser_sts.mmcodec_ts_start:CHI_DATA_MODE_LEN)
|
|
#define DM_T_5_8_LEN (32)
|
|
#define DM_T_5_8_CYCLE (driver->ser_sts.mmcodec_ts_start + 32)
|
|
#define DM_R_5_6_LEN (10)
|
|
#define DM_R_5_6_CYCLE (driver->ser_sts.mmcodec_ts_start + 32)
|
|
#define DM_R_7_8_LEN (16)
|
|
#define DM_R_7_8_CYCLE (driver->ser_sts.mmcodec_ts_start + 48)
|
|
|
|
/* XMIT cycles can not start at zero */
|
|
#define CM_T_1_4_LEN (32)
|
|
#define CM_T_1_4_CYCLE (driver->ser_sts.pal_present ? driver->ser_sts.mmcodec_ts_start:CHI_CTRL_MODE_LEN)
|
|
#define CM_R_1_2_LEN (16)
|
|
#define CM_R_1_2_CYCLE (driver->ser_sts.mmcodec_ts_start + 0)
|
|
#define CM_R_3_4_LEN (16)
|
|
#define CM_R_3_4_CYCLE (driver->ser_sts.mmcodec_ts_start + 16)
|
|
#define CM_R_7_LEN (8)
|
|
#define CM_R_7_CYCLE (driver->ser_sts.mmcodec_ts_start + 48)
|
|
|
|
/* Masks for reading Control Mode data back from the MMCODEC */
|
|
#define CM_R_1_2_MASK 0x063f
|
|
#define CM_R_3_4_MASK 0x3f03
|
|
#define CM_R_7_MANU(a) ((a & 0xf0) >> 4)
|
|
#define CM_R_7_REV(a) (a & 0x0f)
|
|
|
|
/* parameters to mmcodec_setup_chi */
|
|
#define SETUPCHI_CTRL_MODE 1
|
|
#define SETUPCHI_CODEC_MASTER 2
|
|
#define SETUPCHI_DBRI_MASTER 3
|
|
|
|
/* parameters to mmcodec_set_spkrmute */
|
|
#define SPKRMUTE_SET 1
|
|
#define SPKRMUTE_GET 2
|
|
#define SPKRMUTE_CLEAR 3
|
|
#define SPKRMUTE_TOGGLE 4
|
|
|
|
#define MMCODEC_SLEEPPRI (PZERO-1)
|
|
#define MMCODEC_TIMEOUT (1 * hz) /* 3 secs must be too long */
|
|
#define SB_BUTTON_REPEAT (hz / 6) /* 6 times / second repeat */
|
|
#define SB_BUTTON_START (hz) /* repeat after 1 second */
|
|
int sb_button_repeat = 0;
|
|
|
|
#define MMCODEC_NOFS_THRESHOLD (10)
|
|
int audio_error_debug = 0;
|
|
#define eprintf if (audio_error_debug) printf
|
|
|
|
|
|
/* SB PAL bit definitions */
|
|
#define VMINUS_BUTTON 0x01
|
|
#define VPLUS_BUTTON 0x02
|
|
#define MUTE_BUTTON 0x04
|
|
#define HPHONE_IN 0x08
|
|
#define MIKE_IN 0x10
|
|
#define ID_MASK 0xe0
|
|
#define PAL_ID 0xa0
|
|
|
|
void
|
|
mmcodec_reset(driver)
|
|
dbri_dev_t *driver;
|
|
{
|
|
dbri_reg_t *chip = driver->chip;
|
|
u_char temp;
|
|
|
|
/*
|
|
* After DBRI has been reset, and before we have done any
|
|
* write to the pio pins, we can read them to find out the
|
|
* status of the CODEC's currently "on the system". If there
|
|
* is a Speakerbox plugged in, then the external power down
|
|
* pin will be pulled high. If there is an onboard MMCODEC,
|
|
* then the internal power down pin will be pulled high. If
|
|
* there is both, the Speakerbox will take precedence.
|
|
*/
|
|
temp = chip->n.pio;
|
|
aprintf("mmcodec_reset: reg2 initially 0x%x\n", (unsigned int)temp);
|
|
if (temp & SCHI_SET_PDN) {
|
|
driver->ser_sts.pal_present = TRUE;
|
|
driver->ser_sts.mmcodec_ts_start = SB_PAL_LEN;
|
|
} else if (temp & SCHI_SET_INT_PDN) {
|
|
driver->ser_sts.pal_present = FALSE;
|
|
driver->ser_sts.mmcodec_ts_start = 0;
|
|
} else {
|
|
(void) printf("Audio: no Audio device found\n");
|
|
driver->ser_sts.chi_state = CHI_NO_DEVICES;
|
|
driver->chip->n.pio = 0;
|
|
/*
|
|
* You can't unplug an onboard CODEC, so we know
|
|
* that if there's no device, it *should* be an SB
|
|
*/
|
|
driver->ser_sts.pal_present = TRUE;
|
|
driver->ser_sts.mmcodec_ts_start = SB_PAL_LEN;
|
|
|
|
return;
|
|
}
|
|
driver->chip->n.pio = SCHI_ENA_ALL | SCHI_SET_DATA_MODE |
|
|
SCHI_SET_INT_PDN | SCHI_CLR_RESET | SCHI_SET_PDN;
|
|
driver->ser_sts.chi_state = CHI_POWERED_DOWN;
|
|
|
|
return;
|
|
} /* mmcodec_reset */
|
|
|
|
|
|
unsigned int
|
|
reverse_bit_order(word, len)
|
|
unsigned int word;
|
|
int len;
|
|
{
|
|
int bit;
|
|
int count;
|
|
unsigned int new_word = 0;
|
|
|
|
len--;
|
|
for (count = 0; count <= len; count++) {
|
|
bit = ((word >> count) & 0x1);
|
|
new_word += (bit << (len - count));
|
|
}
|
|
|
|
return (new_word);
|
|
} /* reverse_bit_order */
|
|
|
|
int
|
|
mmcodec_getdev(driver)
|
|
dbri_dev_t *driver;
|
|
{
|
|
if (driver->ser_sts.pal_present) {
|
|
return (AUDIO_DEV_SPEAKERBOX);
|
|
} else {
|
|
return (AUDIO_DEV_CODEC);
|
|
}
|
|
} /* mmcodec_getdev */
|
|
|
|
|
|
void
|
|
mmcodec_init_pipes(driver)
|
|
dbri_dev_t *driver;
|
|
{
|
|
int tmp;
|
|
unsigned int pipe;
|
|
pipe_tab_t *pipep;
|
|
dbri_chip_cmd_t cmd;
|
|
int pal_value;
|
|
mmcodec_data_t mmdata;
|
|
mmcodec_ctrl_t mmctrl;
|
|
|
|
aprintf("mmcodec_init_pipes: driver=0x%x\n", (unsigned int)driver);
|
|
driver->chip->n.sts |= DBRI_STS_C; /* Enable CHI intrfce */
|
|
|
|
/* Setup CHI base pipe */
|
|
pipep = &driver->ptab[DBRI_PIPE_CHI];
|
|
pipep->dts.word32 = 0;
|
|
pipep->in_tsd.word32 = 0;
|
|
pipep->out_tsd.word32 = 0;
|
|
|
|
/* Setup dts command words */
|
|
pipep->dts.r.cmd = DBRI_OPCODE_DTS; /* DTS command */
|
|
pipep->dts.r.vi = DBRI_DTS_VI; /* input tsd */
|
|
pipep->dts.r.vo = DBRI_DTS_VO; /* output tsd */
|
|
pipep->dts.r.id = DBRI_DTS_ID; /* Add timeslot */
|
|
pipep->dts.r.oldin = DBRI_PIPE_CHI; /* old input pipe */
|
|
pipep->dts.r.oldout = DBRI_PIPE_CHI; /* old output pipe */
|
|
pipep->dts.r.pipe = DBRI_PIPE_CHI; /* pipe 16 */
|
|
|
|
pipep->in_tsd.chi.mode = DBRI_DTS_ALTERNATE; /* alternate */
|
|
pipep->in_tsd.chi.next = DBRI_PIPE_CHI; /* next is 16 */
|
|
|
|
pipep->out_tsd.chi.mode = DBRI_DTS_ALTERNATE; /* alternamte */
|
|
pipep->out_tsd.chi.next = DBRI_PIPE_CHI; /* next pipe 16 */
|
|
|
|
cmd.cmd_wd1 = pipep->dts.word32; /* DTS command */
|
|
cmd.cmd_wd2 = pipep->in_tsd.word32;
|
|
cmd.cmd_wd3 = pipep->out_tsd.word32;
|
|
dbri_command(driver, cmd);
|
|
|
|
/*
|
|
* Issue SDP's for pipes 17, 18, and 19 - Data Mode pipes for, transmit
|
|
* timeslots 5-8 (32 bits), receive timeslots 5-6 (16 bits), and
|
|
* receive timeslots 7-8 (16 bits) respectively
|
|
*/
|
|
pipep = &driver->ptab[DBRI_PIPE_DM_T_5_8];
|
|
pipep->sdp = DBRI_CMD_SDP | /* SDP Command */
|
|
DBRI_SDP_CHNG | /* report any change */
|
|
DBRI_SDP_FIXED | /* fixed data */
|
|
DBRI_SDP_D | /* transmit pipe */
|
|
DBRI_SDP_PTR | /* ptr to TMD */
|
|
DBRI_SDP_CLR | /* clear pipe */
|
|
DBRI_PIPE_DM_T_5_8; /* pipe number */
|
|
cmd.cmd_wd1 = pipep->sdp;
|
|
cmd.cmd_wd2 = 0;
|
|
dbri_command(driver, cmd);
|
|
|
|
pipep = &driver->ptab[DBRI_PIPE_DM_R_5_6];
|
|
pipep->sdp = DBRI_CMD_SDP | /* SDP Command */
|
|
DBRI_SDP_CHNG | /* report any change */
|
|
DBRI_SDP_FIXED | /* fixed data */
|
|
DBRI_SDP_PTR | /* ptr to RMD */
|
|
DBRI_SDP_CLR | /* clear pipe */
|
|
DBRI_PIPE_DM_R_5_6; /* pipe number */
|
|
cmd.cmd_wd1 = pipep->sdp;
|
|
cmd.cmd_wd2 = 0;
|
|
dbri_command(driver, cmd);
|
|
|
|
pipep = &driver->ptab[DBRI_PIPE_DM_R_7_8];
|
|
pipep->sdp = DBRI_CMD_SDP | /* SDP Command */
|
|
DBRI_SDP_CHNG | /* report any change */
|
|
DBRI_SDP_FIXED | /* fixed data */
|
|
DBRI_SDP_PTR | /* ptr to RMD */
|
|
DBRI_SDP_CLR | /* clear pipe */
|
|
DBRI_PIPE_DM_R_7_8; /* pipe number */
|
|
cmd.cmd_wd1 = pipep->sdp;
|
|
cmd.cmd_wd2 = 0;
|
|
dbri_command(driver, cmd);
|
|
|
|
/*
|
|
* Now issue SDP's for pipes 20, 21, and 22 - Control Mode pipes for,
|
|
* Transmit Timeslots 1-4 (32 bits), Receive timeslots 1-2 (16 bits),
|
|
* and Receive timeslots 3-4 (16 bits) respectively
|
|
*/
|
|
pipep = &driver->ptab[DBRI_PIPE_CM_T_1_4];
|
|
pipep->sdp = DBRI_CMD_SDP | /* SDP Command */
|
|
DBRI_SDP_CHNG | /* report any change */
|
|
DBRI_SDP_FIXED | /* fixed data */
|
|
DBRI_SDP_D | /* transmit pipe */
|
|
DBRI_SDP_PTR | /* ptr to TMD */
|
|
DBRI_SDP_CLR | /* clear pipe */
|
|
DBRI_PIPE_CM_T_1_4; /* pipe number */
|
|
cmd.cmd_wd1 = pipep->sdp;
|
|
cmd.cmd_wd2 = 0;
|
|
dbri_command(driver, cmd);
|
|
|
|
pipep = &driver->ptab[DBRI_PIPE_CM_R_1_2];
|
|
pipep->sdp = DBRI_CMD_SDP | /* SDP Command */
|
|
DBRI_SDP_CHNG | /* report any change */
|
|
DBRI_SDP_FIXED | /* fixed data */
|
|
DBRI_SDP_PTR | /* ptr to RMD */
|
|
DBRI_SDP_CLR | /* clear pipe */
|
|
DBRI_PIPE_CM_R_1_2; /* pipe number */
|
|
cmd.cmd_wd1 = pipep->sdp;
|
|
cmd.cmd_wd2 = 0;
|
|
dbri_command(driver, cmd);
|
|
|
|
pipep = &driver->ptab[DBRI_PIPE_CM_R_3_4];
|
|
pipep->sdp = DBRI_CMD_SDP | /* SDP Command */
|
|
DBRI_SDP_CHNG | /* report any change */
|
|
DBRI_SDP_FIXED | /* fixed data */
|
|
DBRI_SDP_PTR | /* ptr to RMD */
|
|
DBRI_SDP_CLR | /* clear pipe */
|
|
DBRI_PIPE_CM_R_3_4; /* pipe number */
|
|
cmd.cmd_wd1 = pipep->sdp;
|
|
cmd.cmd_wd2 = 0;
|
|
dbri_command(driver, cmd);
|
|
|
|
pipep = &driver->ptab[DBRI_PIPE_CM_R_7];
|
|
pipep->sdp = DBRI_CMD_SDP | /* SDP Command */
|
|
DBRI_SDP_CHNG | /* report any change */
|
|
DBRI_SDP_FIXED | /* fixed data */
|
|
DBRI_SDP_PTR | /* ptr to RMD */
|
|
DBRI_SDP_CLR | /* clear pipe */
|
|
DBRI_PIPE_CM_R_7; /* pipe number */
|
|
cmd.cmd_wd1 = pipep->sdp;
|
|
cmd.cmd_wd2 = 0;
|
|
dbri_command(driver, cmd);
|
|
|
|
/*
|
|
* Set up pipe to transmit dummy data, when we're not transmitting
|
|
* real data. Since DBRI drives the CHIDX line low (zero) when
|
|
* there is no pipe defined for a time slot, it looks like
|
|
* zeroes to the CODEC. In ulaw, zero is actually all ones so
|
|
* the zeroes sent by DBRI causes popping and clicking
|
|
*/
|
|
pipe = DBRI_PIPE_DUMMYDATA;
|
|
pipep = &driver->ptab[pipe];
|
|
pipep->sdp = DBRI_CMD_SDP | /* SDP Command */
|
|
DBRI_SDP_CHNG | /* report any change */
|
|
DBRI_SDP_FIXED | /* fixed data */
|
|
DBRI_SDP_D | /* transmit pipe */
|
|
DBRI_SDP_PTR | /* ptr to TMD */
|
|
DBRI_SDP_CLR | /* clear pipe */
|
|
DBRI_PIPE_DUMMYDATA; /* pipe number */
|
|
cmd.cmd_wd1 = pipep->sdp;
|
|
cmd.cmd_wd2 = 0;
|
|
dbri_command(driver, cmd);
|
|
|
|
/*
|
|
* Set up data mode control information
|
|
*/
|
|
|
|
mmdata.word32 = 0;
|
|
|
|
/*
|
|
* Whatever we initialize the MMCODEC to here must match with
|
|
* what dbri_attach set's up. Default is DBRI_DEFAULT_GAIN, with
|
|
* speaker and microphones as ports
|
|
*/
|
|
mmdata.r.om0 = ~MMCODEC_OM0_ENABLE & 1;
|
|
mmdata.r.om1 = ~MMCODEC_OM1_ENABLE & 1;
|
|
mmdata.r.sm = MMCODEC_SM; /* enable speaker */
|
|
mmdata.r.ovr = MMCODEC_OVR_CLR; /* clear overflow conditions */
|
|
mmdata.r.is = MMCODEC_IS_MIC;
|
|
|
|
/* Calculate default output attenuation */
|
|
tmp = MMCODEC_MAX_ATEN -
|
|
(DBRI_DEFAULT_GAIN * (MMCODEC_MAX_ATEN+1) / (AUDIO_MAX_GAIN+1));
|
|
mmdata.r.lo = tmp;
|
|
mmdata.r.ro = tmp;
|
|
|
|
/* Calculate default input gain */
|
|
tmp = DBRI_DEFAULT_GAIN * (MMCODEC_MAX_GAIN+1) / (AUDIO_MAX_GAIN+1);
|
|
mmdata.r.lg = tmp;
|
|
mmdata.r.rg = tmp;
|
|
|
|
/* Calculate default monitor gain */
|
|
mmdata.r.ma = MMCODEC_MA_MAX_ATEN; /* monitor attenuation */
|
|
|
|
/*
|
|
* We create a standard for handing data for fixed pipes; the
|
|
* data found in the pipe table will be "normal" so we can look
|
|
* at it and manipulate it without hurting our brains, however
|
|
* the data sent to the dbri_command routine will be reversed so
|
|
* it doesn't have to know about it.
|
|
*/
|
|
pipe = DBRI_PIPE_DM_T_5_8;
|
|
pipep = &driver->ptab[pipe];
|
|
cmd.cmd_wd1 = DBRI_CMD_SSP | DBRI_SSP_PIPE(pipe); /* SSP command */
|
|
pipep->ssp = mmdata.word32;
|
|
cmd.cmd_wd2 = reverse_bit_order(pipep->ssp, DM_T_5_8_LEN);
|
|
dbri_command(driver, cmd);
|
|
|
|
/*
|
|
* Set up control mode initial values
|
|
*/
|
|
|
|
mmctrl.word32[0] = 0;
|
|
|
|
mmctrl.r.dcb = 0;
|
|
mmctrl.r.sre = ~MMCODEC_SRE & 1;
|
|
mmctrl.r.vs0 = MMCODEC_VS0;
|
|
mmctrl.r.vs1 = MMCODEC_VS1;
|
|
mmctrl.r.dfr = MMCODEC_DFR_8000; /* 8 KHz */
|
|
mmctrl.r.st = MMCODEC_ST_MONO;
|
|
mmctrl.r.df = MMCODEC_DF_ULAW; /* uLAW encoding */
|
|
mmctrl.r.mck = MMCODEC_MCK_XTAL1; /* Clock is XTAL1 */
|
|
#if CHI_DATA_MODE_LEN == 64
|
|
mmctrl.r.bsel = MMCODEC_BSEL_64;
|
|
#elif CHI_DATA_MODE_LEN == 128
|
|
mmctrl.r.bsel = MMCODEC_BSEL_128;
|
|
#elif CHI_DATA_MODE_LEN == 256
|
|
mmctrl.r.bsel = MMCODEC_BSEL_256;
|
|
#else
|
|
print error... illegal CHI_DATA_MODE_LEN value;
|
|
#endif
|
|
mmctrl.r.xclk = MMCODEC_XCLK; /* CODEC is CHI master */
|
|
mmctrl.r.xen = MMCODEC_XEN; /* enable serial xmit */
|
|
mmctrl.r.enl = ~MMCODEC_ENL & 1; /* disable loopback */
|
|
|
|
/* We currently don't use Time Slots 5, 6, or 8, in control mode */
|
|
|
|
pipe = DBRI_PIPE_CM_T_1_4;
|
|
pipep = &driver->ptab[pipe];
|
|
cmd.cmd_wd1 = DBRI_CMD_SSP | DBRI_SSP_PIPE(pipe); /* SSP command */
|
|
pipep->ssp = mmctrl.word32[0];
|
|
cmd.cmd_wd2 = reverse_bit_order(pipep->ssp, CM_T_1_4_LEN);
|
|
dbri_command(driver, cmd);
|
|
|
|
if (driver->ser_sts.pal_present) {
|
|
|
|
/* Issue SDP's for ID Pal pipes here */
|
|
|
|
pipep = &driver->ptab[DBRI_PIPE_SB_PAL_T];
|
|
pipep->sdp = DBRI_CMD_SDP | /* SDP Command */
|
|
DBRI_SDP_CHNG | /* report any change */
|
|
DBRI_SDP_FIXED | /* fixed data */
|
|
DBRI_SDP_D | /* transmit pipe */
|
|
DBRI_SDP_PTR | /* ptr to TMD */
|
|
DBRI_SDP_CLR | /* clear pipe */
|
|
DBRI_PIPE_SB_PAL_T; /* pipe number */
|
|
cmd.cmd_wd1 = pipep->sdp;
|
|
cmd.cmd_wd2 = 0;
|
|
dbri_command(driver, cmd);
|
|
|
|
pal_value = 0; /* LED off initially */
|
|
cmd.cmd_wd1 = DBRI_CMD_SSP | DBRI_SSP_PIPE(DBRI_PIPE_SB_PAL_T);
|
|
pipep->ssp = pal_value;
|
|
cmd.cmd_wd2 = reverse_bit_order(pipep->ssp, SB_PAL_LEN);
|
|
dbri_command(driver, cmd);
|
|
|
|
pipep = &driver->ptab[DBRI_PIPE_SB_PAL_R];
|
|
pipep->sdp = DBRI_CMD_SDP | /* SDP Command */
|
|
DBRI_SDP_CHNG | /* report any change */
|
|
DBRI_SDP_FIXED | /* fixed data */
|
|
DBRI_SDP_PTR | /* ptr to RMD */
|
|
DBRI_SDP_CLR | /* clear pipe */
|
|
DBRI_PIPE_SB_PAL_R; /* pipe number */
|
|
cmd.cmd_wd1 = pipep->sdp;
|
|
cmd.cmd_wd2 = 0;
|
|
dbri_command(driver, cmd);
|
|
|
|
/*
|
|
* Set initial value to compare against; assume the
|
|
* headphones and mike aren't plugged in so we get
|
|
* avail_ports set properly
|
|
*/
|
|
pal_value = PAL_ID;
|
|
pipep->ssp = pal_value;
|
|
}
|
|
|
|
/*
|
|
* XXX - Once we find out whether or not we're a Speakerbox
|
|
* or running on Sunergy, fix the avail_ports field here
|
|
*/
|
|
|
|
cmd.cmd_wd1 = DBRI_CMD_PAUSE; /* PAUSE command */
|
|
dbri_command(driver, cmd);
|
|
|
|
if (sb_button_repeat == 0)
|
|
sb_button_repeat = SB_BUTTON_REPEAT;
|
|
|
|
return;
|
|
} /* mmcodec_init_pipes */
|
|
|
|
|
|
/*
|
|
* The dbri_setup_dts command won't work for regular overhead kind
|
|
* of pipes since it wants to get all the information from the channel
|
|
* table, and we don't want to add these to it. This is a much simpler
|
|
* version of the routine.
|
|
*/
|
|
void
|
|
mmcodec_do_dts(driver, pipe, oldpipe, nextpipe, dir, cycle, length)
|
|
dbri_dev_t *driver;
|
|
unsigned int pipe, oldpipe, nextpipe;
|
|
int dir, cycle, length;
|
|
{
|
|
dbri_chip_cmd_t cmd;
|
|
pipe_tab_t *pp, *op, *np;
|
|
|
|
pp = &driver->ptab[pipe];
|
|
np = &driver->ptab[nextpipe];
|
|
op = &driver->ptab[oldpipe];
|
|
|
|
pp->dts.r.cmd = DBRI_OPCODE_DTS; /* DTS command */
|
|
pp->dts.r.id = DBRI_DTS_ID; /* Add timeslot */
|
|
pp->dts.r.pipe = pipe; /* pipe # */
|
|
pp->in_tsd.word32 = 0;
|
|
pp->out_tsd.word32 = 0;
|
|
|
|
if (dir == DBRI_IN) {
|
|
op->in_tsd.r.next = pipe;
|
|
np->dts.r.oldin = pipe;
|
|
|
|
pp->dts.r.vi = DBRI_DTS_VI;
|
|
pp->dts.r.oldin = oldpipe;
|
|
pp->in_tsd.r.len = length;
|
|
pp->in_tsd.r.cycle = cycle;
|
|
pp->in_tsd.r.mode = DBRI_DTS_SINGLE;
|
|
pp->in_tsd.r.next = nextpipe;
|
|
} else {
|
|
op->out_tsd.r.next = pipe;
|
|
np->dts.r.oldout = pipe;
|
|
|
|
pp->dts.r.vo = DBRI_DTS_VO;
|
|
pp->dts.r.oldout = oldpipe;
|
|
pp->out_tsd.r.len = length;
|
|
pp->out_tsd.r.cycle = cycle;
|
|
pp->out_tsd.r.mode = DBRI_DTS_SINGLE;
|
|
pp->out_tsd.r.next = nextpipe;
|
|
}
|
|
|
|
cmd.cmd_wd1 = pp->dts.word32;
|
|
cmd.cmd_wd2 = pp->in_tsd.word32;
|
|
cmd.cmd_wd3 = pp->out_tsd.word32;
|
|
dbri_command(driver, cmd);
|
|
|
|
} /* mmcodec_do_dts */
|
|
|
|
|
|
void
|
|
mmcodec_dts_datamode_pipes(driver)
|
|
dbri_dev_t *driver;
|
|
{
|
|
pipe_tab_t *pipep;
|
|
|
|
aprintf("mmcodec_dts_datamode_pipes: 0x%x\n", (unsigned int)driver);
|
|
/*
|
|
* This set's up the pipes so that the linked list is the
|
|
* same all the time. Transmit side looks like 16-[23]-17-16, and
|
|
* the receive side looks like 16-[24]-18-19-16
|
|
* We can't use the dbri_setup_dts routine since these don't/can't
|
|
* show up in the Channel table, but code is stolen from there
|
|
* It still needs to reflect the list in the pipe table though
|
|
* so that the above routine (and others) continue to work
|
|
*
|
|
* The SBPal pipes are DTS's both here and in mmcodec_dts_cntlmode.
|
|
* In theory, they could be done once in the beginning, like
|
|
* in init_pipes and then never touched again, but that would mean
|
|
* that the datamode and control mode frame lengths were the same.
|
|
* (The only difference between this code and the cntlmode code
|
|
* is the Transmit pipe cycle value)
|
|
*/
|
|
if (driver->ser_sts.pal_present) {
|
|
mmcodec_do_dts(driver, DBRI_PIPE_SB_PAL_T,
|
|
/* old, next */ DBRI_PIPE_CHI, DBRI_PIPE_CHI,
|
|
/* dir, c, l */ DBRI_OUT, CHI_DATA_MODE_LEN, SB_PAL_LEN);
|
|
|
|
mmcodec_do_dts(driver, DBRI_PIPE_SB_PAL_R,
|
|
/* old, next */ DBRI_PIPE_CHI, DBRI_PIPE_CHI,
|
|
/* dir, c, l */ DBRI_IN, 0, SB_PAL_LEN);
|
|
} else {
|
|
/*
|
|
* Setup CHI base pipe
|
|
*/
|
|
pipep = &driver->ptab[DBRI_PIPE_CHI];
|
|
|
|
pipep->dts.word32 = 0;
|
|
pipep->in_tsd.word32 = 0;
|
|
pipep->out_tsd.word32 = 0;
|
|
|
|
/*
|
|
* Setup dts command words
|
|
*/
|
|
pipep->dts.r.cmd = DBRI_OPCODE_DTS;
|
|
pipep->dts.r.vi = DBRI_DTS_VI; /* input tsd */
|
|
pipep->dts.r.vo = DBRI_DTS_VO; /* output tsd */
|
|
pipep->dts.r.id = DBRI_DTS_ID; /* Add timeslot */
|
|
pipep->dts.r.oldin = DBRI_PIPE_CHI;
|
|
pipep->dts.r.oldout = DBRI_PIPE_CHI;
|
|
pipep->dts.r.pipe = DBRI_PIPE_CHI;
|
|
|
|
pipep->in_tsd.chi.mode = DBRI_DTS_ALTERNATE;
|
|
pipep->in_tsd.chi.next = DBRI_PIPE_CHI;
|
|
|
|
pipep->out_tsd.chi.mode = DBRI_DTS_ALTERNATE;
|
|
pipep->out_tsd.chi.next = DBRI_PIPE_CHI;
|
|
|
|
{
|
|
dbri_chip_cmd_t cmd;
|
|
|
|
cmd.cmd_wd1 = pipep->dts.word32;
|
|
cmd.cmd_wd2 = pipep->in_tsd.word32;
|
|
cmd.cmd_wd3 = pipep->out_tsd.word32;
|
|
|
|
dbri_command(driver, cmd);
|
|
}
|
|
}
|
|
|
|
mmcodec_do_dts(driver, DBRI_PIPE_DM_T_5_8,
|
|
((unsigned int) (driver->ser_sts.pal_present ?
|
|
DBRI_PIPE_SB_PAL_T : DBRI_PIPE_CHI)), /* old */
|
|
DBRI_PIPE_CHI, /* next */
|
|
DBRI_OUT, DM_T_5_8_CYCLE, DM_T_5_8_LEN); /* dir, c, l */
|
|
|
|
#ifdef notdef
|
|
/*
|
|
* XXX5 6/15/92 - not using this pipe continuously reduces the
|
|
* load on DBRI and hopefully less receiver overflow errors
|
|
*/
|
|
mmcodec_do_dts(driver, DBRI_PIPE_DM_R_5_6,
|
|
((unsigned int) (driver->ser_sts.pal_present ?
|
|
DBRI_PIPE_SB_PAL_R : DBRI_PIPE_CHI)), /* old */
|
|
DBRI_PIPE_CHI, /* next */
|
|
DBRI_IN, DM_R_5_6_CYCLE, DM_R_5_6_LEN); /* dir, c, l */
|
|
|
|
/*
|
|
* XXX5 5/7/92 - not using this pipe causes machines with a 16.67Mhz
|
|
* SBus to *not* hang on CHI receiver errors; revisit later
|
|
*/
|
|
mmcodec_do_dts(driver, DBRI_PIPE_DM_R_7_8,
|
|
DBRI_PIPE_DM_R_5_6, DBRI_PIPE_CHI, /* old, next */
|
|
DBRI_IN, DM_R_7_8_CYCLE, DM_R_7_8_LEN); /* dir, c, l */
|
|
#endif notdef
|
|
|
|
return;
|
|
} /* mmcodec_dts_datamode_pipes */
|
|
|
|
|
|
void
|
|
mmcodec_dts_cntlmode_pipes(driver)
|
|
dbri_dev_t *driver;
|
|
{
|
|
aprintf("mmcodec_dts_cntlmode_pipes: 0x%x\n", (unsigned int)driver);
|
|
/*
|
|
* This set's up the pipes so that the linked list is the
|
|
* same all the time. Transmit side looks like 16-[23]-20-16, and
|
|
* the receive side looks like 16-[24]-21-22-16
|
|
* We can't use the dbri_setup_dts routine since these don't/can't
|
|
* show up in the Channel table, but code is stolen from there
|
|
* It still needs to reflect the list in the pipe table though
|
|
* so that the above routine (and others) continue to work
|
|
*/
|
|
|
|
if (driver->ser_sts.pal_present) {
|
|
mmcodec_do_dts(driver, DBRI_PIPE_SB_PAL_T,
|
|
/* old, next */ DBRI_PIPE_CHI, DBRI_PIPE_CHI,
|
|
/* dir, c, l */ DBRI_OUT, CHI_CTRL_MODE_LEN, SB_PAL_LEN);
|
|
|
|
mmcodec_do_dts(driver, DBRI_PIPE_SB_PAL_R,
|
|
/* old, next */ DBRI_PIPE_CHI, DBRI_PIPE_CHI,
|
|
/* dir, c, l */ DBRI_IN, 0, SB_PAL_LEN);
|
|
}
|
|
|
|
mmcodec_do_dts(driver, DBRI_PIPE_CM_T_1_4,
|
|
((unsigned int) (driver->ser_sts.pal_present ?
|
|
DBRI_PIPE_SB_PAL_T : DBRI_PIPE_CHI)), /* old */
|
|
DBRI_PIPE_CHI, /* next */
|
|
DBRI_OUT, CM_T_1_4_CYCLE, CM_T_1_4_LEN); /* dir, c, l */
|
|
|
|
|
|
mmcodec_do_dts(driver, DBRI_PIPE_CM_R_1_2,
|
|
((unsigned int) (driver->ser_sts.pal_present ?
|
|
DBRI_PIPE_SB_PAL_R : DBRI_PIPE_CHI)), /* old */
|
|
DBRI_PIPE_CHI, /* next */
|
|
DBRI_IN, CM_R_1_2_CYCLE, CM_R_1_2_LEN); /* dir, c, l */
|
|
|
|
mmcodec_do_dts(driver, DBRI_PIPE_CM_R_3_4,
|
|
/* old, next */ DBRI_PIPE_CM_R_1_2, DBRI_PIPE_CHI,
|
|
/* dir, c, l */ DBRI_IN, CM_R_3_4_CYCLE, CM_R_3_4_LEN);
|
|
|
|
mmcodec_do_dts(driver, DBRI_PIPE_CM_R_7,
|
|
/* old, next */ DBRI_PIPE_CM_R_3_4, DBRI_PIPE_CHI,
|
|
/* dir, c, l */ DBRI_IN, CM_R_7_CYCLE, CM_R_7_LEN);
|
|
|
|
return;
|
|
} /* mmcodec_dts_cntlmode_pipes */
|
|
|
|
|
|
/*
|
|
* mmcodec_setup_chi - Setup CHI bus appropriately; this is hardcoded for
|
|
* a single MMCODEC on the CHI bus, with or without a Speakerbox PAL.
|
|
*/
|
|
void
|
|
mmcodec_setup_chi(driver, how)
|
|
dbri_dev_t *driver;
|
|
int how;
|
|
{
|
|
dbri_chip_cmd_t cmd;
|
|
serial_status_t *serialp;
|
|
unsigned int chicm;
|
|
|
|
/*
|
|
* The first thing to do when changing clock parameters (according
|
|
* to Harry French) is to disable CHI so that everything is reset.
|
|
* Spec says "does not drive CHIDX or CHIDR when XEN is 0".
|
|
*/
|
|
serialp = &driver->ser_sts;
|
|
serialp->chi_cdm = (DBRI_CMD_CDM |
|
|
DBRI_CDM_XCE | /* transmit on rising edge */
|
|
DBRI_CDM_REN); /* receive enable */
|
|
|
|
cmd.cmd_wd1 = serialp->chi_cdm; /* CDM command */
|
|
dbri_command(driver, cmd);
|
|
|
|
/* Set up parameters for CHI and CDM commands */
|
|
/*
|
|
* XXX4 - bug#15, bpf must always be zero; shouldn't hurt V5?
|
|
*/
|
|
switch (how) {
|
|
case SETUPCHI_CTRL_MODE:
|
|
case SETUPCHI_DBRI_MASTER:
|
|
chicm = (256*6)/CHI_CTRL_MODE_LEN;
|
|
break;
|
|
|
|
case SETUPCHI_CODEC_MASTER:
|
|
chicm = 1; /* CHICK is an input NOT 8kHz */
|
|
break;
|
|
|
|
default:
|
|
(void) printf("audio: invalid setup!\n");
|
|
break;
|
|
}
|
|
|
|
/* issue CHI command */
|
|
serialp->chi_cmd = DBRI_CMD_CHI | /* CHI cmd */
|
|
DBRI_CHI_CHICM(chicm) | /* clock mode */
|
|
DBRI_CHI_INT | /* report now */
|
|
DBRI_CHI_CHIL | /* gen CHIL ints */
|
|
/* no open drain */
|
|
/* FS sampled falling */
|
|
DBRI_CHI_FD | /* FS driven rising */
|
|
DBRI_CHI_BPF(0); /* bpf always 0 */
|
|
|
|
|
|
cmd.cmd_wd1 = serialp->chi_cmd; /* CHI command */
|
|
dbri_command(driver, cmd);
|
|
|
|
cmd.cmd_wd1 = DBRI_CMD_PAUSE; /* PAUSE command */
|
|
dbri_command(driver, cmd);
|
|
|
|
return;
|
|
} /* mmcodec_setup_chi */
|
|
|
|
void
|
|
mmcodec_enable_chi(driver)
|
|
dbri_dev_t *driver;
|
|
{
|
|
dbri_chip_cmd_t cmd;
|
|
serial_status_t *serialp;
|
|
|
|
serialp = &driver->ser_sts;
|
|
serialp->chi_cdm = (DBRI_CMD_CDM | /* CDM command */
|
|
/* xmit on CHIDX */
|
|
/* rcv data on CHIDR */
|
|
/* falling recv data */
|
|
/* recv one clock per bit */
|
|
/* strobe on even clk */
|
|
/* xmit one clock per bit */
|
|
DBRI_CDM_XCE | /* transmit on rising edge */
|
|
DBRI_CDM_XEN | /* transmit enable */
|
|
DBRI_CDM_REN); /* receive enable */
|
|
|
|
cmd.cmd_wd1 = serialp->chi_cdm; /* CDM command */
|
|
dbri_command(driver, cmd);
|
|
|
|
return;
|
|
} /* mmcodec_enable_chi */
|
|
|
|
void
|
|
mmcodec_watchdog(driver)
|
|
dbri_dev_t *driver;
|
|
{
|
|
dbri_stream_t *dsp;
|
|
pipe_tab_t *pipep;
|
|
struct iocblk *iocp;
|
|
int s;
|
|
|
|
s = splr(driver->intrlevel);
|
|
printf("audio %d: Unable to communicate with speakerbox.\n",
|
|
driver->ser_sts.chi_state);
|
|
driver->ser_sts.chi_state = CHI_NO_DEVICES;
|
|
driver->chip->n.pio = SCHI_ENA_ALL | SCHI_SET_DATA_MODE |
|
|
SCHI_SET_INT_PDN | SCHI_CLR_RESET | SCHI_SET_PDN;
|
|
|
|
dsp = &driver->ioc[(int)DBRI_AUDIO_IN];
|
|
pipep = &driver->ptab[dsp->pipe];
|
|
if ((dsp->pipe != DBRI_BAD_PIPE) && ISPIPEINUSE(pipep)) {
|
|
if (dsp->mp != NULL) {
|
|
dsp->mp->b_datap->db_type = M_IOCNAK;
|
|
iocp = (struct iocblk *)dsp->mp->b_rptr;
|
|
iocp->ioc_rval = -1;
|
|
iocp->ioc_error = EIO;
|
|
qreply(dsp->as.writeq, dsp->mp);
|
|
dsp->mp = NULL;
|
|
}
|
|
}
|
|
dsp = &driver->ioc[(int)DBRI_AUDIO_OUT];
|
|
pipep = &driver->ptab[dsp->pipe];
|
|
if ((dsp->pipe != DBRI_BAD_PIPE) && ISPIPEINUSE(pipep)) {
|
|
if (dsp->mp != NULL) {
|
|
dsp->mp->b_datap->db_type = M_IOCNAK;
|
|
iocp = (struct iocblk *)dsp->mp->b_rptr;
|
|
iocp->ioc_rval = -1;
|
|
iocp->ioc_error = EIO;
|
|
qreply(dsp->as.writeq, dsp->mp);
|
|
dsp->mp = NULL;
|
|
}
|
|
}
|
|
|
|
#ifndef NOTYET
|
|
/*
|
|
* Only wakeup opens, not initial setup (from startup audio) or
|
|
* ioctls. If you "wakeup too early", dbri will panic a C2
|
|
* (see bugid# 1092706)
|
|
*/
|
|
if (driver->ser_sts.chi_flags & CHI_FLAG_OPEN_SLEEP) {
|
|
driver->ser_sts.chi_flags &= ~(CHI_FLAG_OPEN_SLEEP);
|
|
wakeup((caddr_t)driver);
|
|
}
|
|
#endif !NOTYET
|
|
(void) splx(s);
|
|
return;
|
|
} /* mmcodec_watchdog */
|
|
|
|
|
|
/*
|
|
* Start the process of putting the MMCODEC into Control Mode; we have to
|
|
* wait for interrupts from DBRI before things can finish, so the rest of
|
|
* this is driven by the state machine at interrupt level. Check out
|
|
* dbri_fxdt_intr, to see how it works
|
|
*/
|
|
void
|
|
mmcodec_setup_ctrl_mode(driver)
|
|
dbri_dev_t *driver;
|
|
{
|
|
int s;
|
|
u_char temp;
|
|
unsigned int pipe;
|
|
pipe_tab_t *pipep;
|
|
dbri_chip_cmd_t cmd;
|
|
mmcodec_data_t mmdata;
|
|
mmcodec_ctrl_t mmctrl;
|
|
|
|
aprintf("mmcodec_setup_ctrl_mode %d:\n",
|
|
driver->ser_sts.chi_state);
|
|
|
|
/*
|
|
* 1) Volume attenuation should be increased to Max; mute
|
|
* all outputs (if CODEC is already powered down (or
|
|
* previously not there), or all outputs are already
|
|
* muted, we don't need to do this)
|
|
*/
|
|
pipe = DBRI_PIPE_DM_T_5_8;
|
|
pipep = &driver->ptab[pipe];
|
|
mmdata.word32 = pipep->ssp;
|
|
if ((driver->ser_sts.chi_state == CHI_POWERED_DOWN) ||
|
|
(driver->ser_sts.chi_state == CHI_NO_DEVICES) ||
|
|
(!mmdata.r.om1 && !mmdata.r.om0 && !mmdata.r.sm)) {
|
|
|
|
timeout((int(*)())mmcodec_watchdog, (caddr_t)driver,
|
|
MMCODEC_TIMEOUT);
|
|
} else if (driver->ser_sts.chi_state != CHI_GOING_TO_CTRL_MODE) {
|
|
|
|
mmcodec_do_dts(driver, DBRI_PIPE_DM_R_5_6,
|
|
((unsigned int) (driver->ser_sts.pal_present ?
|
|
DBRI_PIPE_SB_PAL_R : DBRI_PIPE_CHI)), /* old */
|
|
DBRI_PIPE_CHI, /* next */
|
|
DBRI_IN, DM_R_5_6_CYCLE, DM_R_5_6_LEN); /* dir, c, l */
|
|
|
|
mmdata.r.lo = MMCODEC_MAX_DEV_ATEN;
|
|
mmdata.r.ro = MMCODEC_MAX_DEV_ATEN;
|
|
mmdata.r.sm = ~MMCODEC_SM & 1;
|
|
mmdata.r.om0 = ~MMCODEC_OM0_ENABLE & 1;
|
|
mmdata.r.om1 = ~MMCODEC_OM1_ENABLE & 1;
|
|
/*
|
|
* Be sure not to save these new values in pipep->ssp so
|
|
* that we can restore them to their original values later
|
|
*/
|
|
driver->ser_sts.chi_state = CHI_GOING_TO_CTRL_MODE;
|
|
cmd.cmd_wd1 = DBRI_CMD_SSP | DBRI_SSP_PIPE(pipe);
|
|
cmd.cmd_wd2 = reverse_bit_order(mmdata.word32, DM_T_5_8_LEN);
|
|
ATRACEI(mmcodec_setup_ctrl_mode, 'SSP ', cmd.cmd_wd2);
|
|
dbri_command(driver, cmd);
|
|
cmd.cmd_wd1 = DBRI_CMD_PAUSE;
|
|
dbri_command(driver, cmd);
|
|
timeout((int(*)())mmcodec_watchdog, (caddr_t)driver,
|
|
MMCODEC_TIMEOUT);
|
|
return;
|
|
}
|
|
/*
|
|
* 2) Stop receiving audio data
|
|
* SDP NULL has been issued previously to now
|
|
*/
|
|
/*
|
|
* 3) Set D/C low to indicate control mode
|
|
*/
|
|
temp = driver->chip->n.pio;
|
|
aprintf("mmcodec_setup_ctrl_mode: reg2 initially 0x%x\n",
|
|
(unsigned int)temp);
|
|
if (driver->ser_sts.pal_present) {
|
|
driver->chip->n.pio = SCHI_ENA_ALL | SCHI_SET_CTRL_MODE |
|
|
SCHI_SET_INT_PDN | SCHI_CLR_RESET | SCHI_CLR_PDN;
|
|
} else {
|
|
driver->chip->n.pio = SCHI_ENA_ALL | SCHI_SET_CTRL_MODE |
|
|
SCHI_CLR_INT_PDN | SCHI_CLR_RESET | SCHI_SET_PDN;
|
|
}
|
|
|
|
/*
|
|
* 4) Wait 12 SCLKS or 12/(5.5125*64) ms
|
|
*/
|
|
DELAY(34); /* param is an int */
|
|
driver->ser_sts.chi_state = CHI_IN_CTRL_MODE;
|
|
temp = driver->chip->n.pio;
|
|
aprintf("mmcodec_setup_ctrl_mode: reg2 finally 0x%x\n",
|
|
(unsigned int)temp);
|
|
|
|
/*
|
|
* 6) Send Control information to MMCODEC w/ DCB bit low
|
|
*/
|
|
pipe = DBRI_PIPE_CM_T_1_4;
|
|
pipep = &driver->ptab[pipe];
|
|
mmctrl.word32[0] = pipep->ssp;
|
|
mmctrl.r.dcb = 0;
|
|
cmd.cmd_wd1 = DBRI_CMD_SSP | DBRI_SSP_PIPE(pipe); /* SSP command */
|
|
pipep->ssp = mmctrl.word32[0];
|
|
cmd.cmd_wd2 = reverse_bit_order(pipep->ssp, CM_T_1_4_LEN);
|
|
ATRACEI(mmcodec_setup_ctrl_mode, 'SSP ', cmd.cmd_wd2);
|
|
dbri_command(driver, cmd);
|
|
cmd.cmd_wd1 = DBRI_CMD_PAUSE; /* PAUSE command */
|
|
dbri_command(driver, cmd);
|
|
|
|
s = splr(driver->intrlevel);
|
|
mmcodec_dts_cntlmode_pipes(driver);
|
|
|
|
/*
|
|
* 5) Start driving FSYNC and SCLK with DBRI
|
|
*/
|
|
mmcodec_setup_chi(driver, SETUPCHI_CTRL_MODE);
|
|
driver->ser_sts.chi_state = CHI_WAIT_DCB_LOW;
|
|
mmcodec_enable_chi(driver);
|
|
(void) splx(s);
|
|
|
|
return;
|
|
} /* mmcodec_setup_ctrl_mode */
|
|
|
|
/*
|
|
* The first 194 frames of data mode is when the CODEC does it's
|
|
* auto-calibration, so data isn't valid for recording and the CODEC
|
|
* won't play any data, until these are over. This routine gets
|
|
* called after auto cal to send or receive data
|
|
*/
|
|
void
|
|
mmcodec_data_mode_ok(driver)
|
|
dbri_dev_t *driver;
|
|
{
|
|
unsigned int pipe;
|
|
pipe_tab_t *pipep;
|
|
dbri_chip_cmd_t cmd;
|
|
dbri_stream_t *dsp;
|
|
union dbri_dtscmd dts;
|
|
union dbri_dtstsd out_tsd;
|
|
int s;
|
|
|
|
/*
|
|
* One of the few routines called from timeout, need to block intrs
|
|
*/
|
|
s = splr(driver->intrlevel);
|
|
aprintf("mmcodec_data_mode_ok: state=%d\n", driver->ser_sts.chi_state);
|
|
if (driver->ser_sts.chi_state != CHI_CODEC_CALIBRATION) {
|
|
return;
|
|
}
|
|
|
|
/* Now we can unmute outputs */
|
|
pipe = DBRI_PIPE_DM_T_5_8;
|
|
pipep = &driver->ptab[pipe]; /* ctrl in data stream */
|
|
cmd.cmd_wd1 = DBRI_CMD_SSP | DBRI_SSP_PIPE(pipe);
|
|
cmd.cmd_wd2 = reverse_bit_order(pipep->ssp, DM_T_5_8_LEN);
|
|
ATRACEI(mmcodec_data_mode_ok, 'SSP ', cmd.cmd_wd2);
|
|
dbri_command(driver, cmd); /* Set short pipe data */
|
|
|
|
cmd.cmd_wd1 = DBRI_CMD_PAUSE; /* PAUSE command */
|
|
dbri_command(driver, cmd);
|
|
|
|
/*
|
|
* NOW restart any pipes that were stopped while we reconfig'd
|
|
* the codec.
|
|
*/
|
|
dsp = &driver->ioc[(int)DBRI_AUDIO_IN];
|
|
pipep = &driver->ptab[dsp->pipe];
|
|
if ((dsp->pipe != DBRI_BAD_PIPE) && ISPIPEINUSE(pipep)) {
|
|
aprintf("mmcodec_data_mode_ok: dts recv pipe %d\n", dsp->pipe);
|
|
(void) dbri_setup_dts(pipep->as, dsp->pipe, DBRI_AUDIO_IN);
|
|
cmd.cmd_wd1 = pipep->dts.word32;
|
|
cmd.cmd_wd2 = pipep->in_tsd.word32;
|
|
cmd.cmd_wd3 = pipep->out_tsd.word32;
|
|
dbri_command(driver, cmd);
|
|
|
|
/* XXX - call dbri_start? call before DTS? */
|
|
|
|
if (dsp->mp != NULL) {
|
|
ATRACEI(mmcodec_data_mode_ok, 'Rrpy', dsp->pipe);
|
|
dsp->mp->b_datap->db_type = M_IOCACK;
|
|
qreply(dsp->as.writeq, dsp->mp);
|
|
dsp->mp = NULL;
|
|
audio_sendsig(dsp->as.control_as);
|
|
}
|
|
}
|
|
|
|
dsp = &driver->ioc[(int)DBRI_AUDIO_OUT];
|
|
pipep = &driver->ptab[dsp->pipe];
|
|
if ((dsp->pipe != DBRI_BAD_PIPE) && ISPIPEINUSE(pipep)) {
|
|
aprintf("mmcodec_data_mode_ok: dts xmit pipe %d\n", dsp->pipe);
|
|
(void) dbri_setup_dts(pipep->as, dsp->pipe, DBRI_AUDIO_OUT);
|
|
cmd.cmd_wd1 = pipep->dts.word32;
|
|
cmd.cmd_wd2 = pipep->in_tsd.word32;
|
|
cmd.cmd_wd3 = pipep->out_tsd.word32;
|
|
dbri_command(driver, cmd);
|
|
|
|
/* XXX - call dbri_start? call before DTS? */
|
|
|
|
if (dsp->mp != NULL) {
|
|
ATRACEI(mmcodec_data_mode_ok, 'Trpy', dsp->pipe);
|
|
dsp->mp->b_datap->db_type = M_IOCACK;
|
|
qreply(dsp->as.writeq, dsp->mp);
|
|
dsp->mp = NULL;
|
|
audio_sendsig(dsp->as.control_as);
|
|
}
|
|
} else {
|
|
/*
|
|
* Set up dummy data pipe to send zeroes; (XXX - this
|
|
* just screws up 8bit stereo even more.) The way we
|
|
* get away with this is by not putting any values in
|
|
* the pipe table, thereby hiding pipe 26 from the rest
|
|
* of the driver. When a *real* data pipe get's defined
|
|
* in dbri_setup_dts, it will just get plopped in the
|
|
* linked list where 26 was, and 26 will be out
|
|
*
|
|
* Dummy data is always 32 bits, regardless of current
|
|
* MMCODEC format, for simplicity
|
|
*
|
|
* BTW, I *really* don't like this - this really sucks
|
|
* but got any other ideas? (Besides rewriting from scratch)
|
|
*/
|
|
aprintf("mmcodec_data_mode_ok: dts dummy pipe\n");
|
|
dts.word32 = 0;
|
|
out_tsd.word32 = 0;
|
|
dts.r.cmd = DBRI_OPCODE_DTS;
|
|
dts.r.id = DBRI_DTS_ID;
|
|
dts.r.pipe = DBRI_PIPE_DUMMYDATA;
|
|
dts.r.vo = DBRI_DTS_VO;
|
|
dts.r.oldout = ((unsigned int)
|
|
(driver->ser_sts.pal_present ?
|
|
DBRI_PIPE_SB_PAL_T : DBRI_PIPE_CHI));
|
|
out_tsd.r.len = DUMMYDATA_LEN;
|
|
out_tsd.r.cycle = DUMMYDATA_CYCLE;
|
|
out_tsd.r.mode = DBRI_DTS_SINGLE;
|
|
out_tsd.r.next = DBRI_PIPE_DM_T_5_8;
|
|
|
|
cmd.cmd_wd1 = DBRI_CMD_SSP | DBRI_SSP_PIPE(DBRI_PIPE_DUMMYDATA);
|
|
cmd.cmd_wd2 = reverse_bit_order(driver->ser_sts.chi_dd_val,
|
|
DUMMYDATA_LEN);
|
|
dbri_command(driver, cmd);
|
|
|
|
cmd.cmd_wd1 = dts.word32;
|
|
cmd.cmd_wd2 = 0;
|
|
cmd.cmd_wd3 = out_tsd.word32;
|
|
dbri_command(driver, cmd);
|
|
}
|
|
|
|
/*
|
|
* Only wakeup opens, not initial setup (from startup audio) or
|
|
* ioctls. If you "wakeup too early", dbri will panic a C2
|
|
* (see bugid# 1092706)
|
|
*/
|
|
if (driver->ser_sts.chi_flags & CHI_FLAG_OPEN_SLEEP) {
|
|
driver->ser_sts.chi_flags &= ~(CHI_FLAG_OPEN_SLEEP);
|
|
wakeup((caddr_t)driver);
|
|
aprintf("mmcodec_data_mode_ok: wakeup!\n");
|
|
}
|
|
|
|
driver->ser_sts.chi_state = CHI_IN_DATA_MODE;
|
|
|
|
(void) splx(s);
|
|
return;
|
|
|
|
} /* mmcodec_data_mode_ok */
|
|
|
|
void
|
|
mmcodec_setup_data_mode(driver)
|
|
dbri_dev_t *driver;
|
|
{
|
|
u_char temp;
|
|
dbri_stream_t *dsp;
|
|
int mute_time;
|
|
|
|
untimeout((int(*)())mmcodec_watchdog, (caddr_t)driver);
|
|
|
|
aprintf("mmcodec_setup_data_mode: driver=0x%x\n", (unsigned int)driver);
|
|
|
|
/*
|
|
* 13) Start sending data
|
|
*/
|
|
mmcodec_dts_datamode_pipes(driver);
|
|
|
|
mmcodec_enable_chi(driver);
|
|
|
|
/*
|
|
* 14) Set D/C pin high to indicate data mode
|
|
*/
|
|
temp = driver->chip->n.pio;
|
|
aprintf("mmcodec_setup_data_mode: reg2 initially 0x%x\n",
|
|
(unsigned int)temp);
|
|
driver->chip->n.pio |= SCHI_SET_DATA_MODE;
|
|
temp = driver->chip->n.pio;
|
|
aprintf("mmcodec_setup_data_mode: reg2 finally 0x%x\n",
|
|
(unsigned int)temp);
|
|
|
|
/*
|
|
* Calculate how long auto-calibration is suppoosed to take
|
|
* calc is 1/<sample_rate>*194 frames which turns out to
|
|
* either be 1, 2, or 3 clock ticks - pick which one it is
|
|
*
|
|
* XXX - fix this - it assumes 10ms clock ticks
|
|
*/
|
|
dsp = &driver->ioc[(int)DBRI_AUDIO_IN];
|
|
if (dsp->as.info.sample_rate >= 22050)
|
|
mute_time = 1;
|
|
else if (dsp->as.info.sample_rate >= 11025)
|
|
mute_time = 2;
|
|
else
|
|
mute_time = 3;
|
|
aprintf("mmcodec_setup_data_mode: mute time is %d\n", mute_time);
|
|
timeout((int(*)())mmcodec_data_mode_ok, (caddr_t)driver, mute_time);
|
|
|
|
/* reset back to zero */
|
|
driver->ser_sts.chi_nofs = 0;
|
|
return;
|
|
} /* mmcodec_setup_data_mode */
|
|
|
|
|
|
void
|
|
mmcodec_startup_audio(driver)
|
|
dbri_dev_t *driver;
|
|
{
|
|
pipe_tab_t *pipep;
|
|
aud_stream_t *as;
|
|
|
|
pipep = &driver->ptab[DBRI_PIPE_CHI];
|
|
if (!ISPIPEINUSE(pipep)) {
|
|
mmcodec_init_pipes(driver);
|
|
}
|
|
/*
|
|
* Always start up as ulaw; AUDIO_IN is as good an "as" as any
|
|
*/
|
|
as = &driver->ioc[(int)DBRI_AUDIO_IN].as;
|
|
if (driver->ser_sts.chi_state == CHI_POWERED_DOWN) {
|
|
mmcodec_set_audio_config(driver, as,
|
|
8000, 1, 8, AUDIO_ENCODING_ULAW);
|
|
}
|
|
} /* mmcodec_startup_audio */
|
|
|
|
|
|
/*
|
|
* audio_con_stream - connect an audio stream; could be play or record
|
|
*/
|
|
int
|
|
audio_con_stream(driver, as, error)
|
|
dbri_dev_t *driver;
|
|
aud_stream_t *as;
|
|
int *error;
|
|
#ifdef NOTYET
|
|
{
|
|
#ifdef lint
|
|
as = as;
|
|
#endif lint
|
|
|
|
/* See if we timed out */
|
|
if (driver->ser_sts.chi_state == CHI_NO_DEVICES) {
|
|
/*
|
|
* Start things back up, but still return an error;
|
|
*/
|
|
mmcodec_set_audio_config(driver, as,
|
|
8000, 1, 8, AUDIO_ENCODING_ULAW);
|
|
*error = ENODEV;
|
|
return (DBRI_BADCONN);
|
|
} else {
|
|
return (DBRI_GOODCONN);
|
|
}
|
|
|
|
} /* audio_con_stream */
|
|
#else
|
|
{
|
|
int skip = FALSE;
|
|
|
|
aprintf("audio_con_stream:\n");
|
|
/*
|
|
* If both a reader and a writer open at the same time, we
|
|
* need to make sure the following is only done once ... eg: if
|
|
* reader is sleeping, waiting for completion, then the writer
|
|
* should not call "set_audio_config", but should go to sleep
|
|
* waiting for the reader's sleep to wake up and then they can
|
|
* both continue.
|
|
*/
|
|
if ((as->play_as->info.open && as->record_as->info.open) &&
|
|
(as->play_as->readq != as->record_as->readq)) {
|
|
skip = TRUE;
|
|
}
|
|
if (!skip) {
|
|
mmcodec_set_audio_config(driver, as,
|
|
8000, 1, 8, AUDIO_ENCODING_ULAW);
|
|
|
|
driver->ser_sts.chi_flags |= CHI_FLAG_OPEN_SLEEP;
|
|
(void) sleep((caddr_t)driver, MMCODEC_SLEEPPRI);
|
|
aprintf("audio_con_stream:sleep WOKEUP!\n");
|
|
} else {
|
|
aprintf("audio_con_stream: skipping config\n");
|
|
}
|
|
|
|
/* See if we timed out */
|
|
if (driver->ser_sts.chi_state == CHI_NO_DEVICES) {
|
|
*error = ENODEV;
|
|
return (DBRI_BADCONN);
|
|
} else {
|
|
return (DBRI_GOODCONN);
|
|
}
|
|
|
|
} /* audio_con_stream */
|
|
#endif NOTYET
|
|
|
|
|
|
/*
|
|
* audio_xcon_stream - disconnect an audio stream; dbri_disconnect has
|
|
* already issued SDP to stop and deleted the pipe - we just cleanup for audio
|
|
* if this is the "last close"
|
|
*/
|
|
/*ARGSUSED*/
|
|
void
|
|
audio_xcon_stream(driver, as)
|
|
dbri_dev_t *driver;
|
|
aud_stream_t *as;
|
|
{
|
|
#ifdef NOTYET
|
|
unsigned int pipe;
|
|
pipe_tab_t *ipipep, *opipep;
|
|
|
|
|
|
/*
|
|
* XXX - should not reset if there is a serial to serial connection
|
|
* left (except that'll be mulaw anyway?)
|
|
*/
|
|
/*
|
|
* The "open" flag doesn't get reset until after audio_close
|
|
* has finished so we can't check that to determine if both
|
|
* play and record are now closed.
|
|
*/
|
|
pipe = driver->ioc[(int)DBRI_AUDIO_IN].pipe;
|
|
ipipep = &driver->ptab[pipe];
|
|
pipe = driver->ioc[(int)DBRI_AUDIO_OUT].pipe;
|
|
opipep = &driver->ptab[pipe];
|
|
if (!ISPIPEINUSE(ipipep) && !ISPIPEINUSE(opipep)) {
|
|
/*
|
|
* XXX - We could put a check here to not do this if already
|
|
* mulaw, but this is what puts things back together if
|
|
* the speakerbox gets unplugged;
|
|
*/
|
|
mmcodec_set_audio_config(driver, as,
|
|
8000, 1, 8, AUDIO_ENCODING_ULAW);
|
|
}
|
|
#endif NOTYET
|
|
dbri_stream_t *dsp;
|
|
dbri_chip_cmd_t cmd;
|
|
union dbri_dtscmd dts;
|
|
union dbri_dtstsd out_tsd;
|
|
|
|
/*
|
|
* XXX - should not reset if there is a serial to serial connection
|
|
* left (except that'll be mulaw anyway?)
|
|
*/
|
|
dsp = &driver->ioc[(int)DBRI_AUDIO_OUT];
|
|
if (dsp == (dbri_stream_t *)as) {
|
|
/*
|
|
* Closing the audio out stream; I wonder if we want to
|
|
* do this for B Channels as well?
|
|
*/
|
|
/*
|
|
* Set up dummy data pipe to send zeroes; (XXX - this
|
|
* just screws up 8bit stereo even more.) The way we
|
|
* get away with this is by not putting any values in
|
|
* the pipe table, thereby hiding pipe 26 from the rest
|
|
* of the driver. When a *real* data pipe get's defined
|
|
* in dbri_setup_dts, it will just get plopped in the
|
|
* linked list where 26 was, and 26 will be out
|
|
*
|
|
* Dummy data is always 32 bits, regardless of current
|
|
* MMCODEC format, for simplicity
|
|
*
|
|
* BTW, I *really* don't like this - this really sucks
|
|
* but got any other ideas? (Besides rewriting from scratch)
|
|
*/
|
|
dts.word32 = 0;
|
|
out_tsd.word32 = 0;
|
|
dts.r.cmd = DBRI_OPCODE_DTS;
|
|
dts.r.id = DBRI_DTS_ID;
|
|
dts.r.pipe = DBRI_PIPE_DUMMYDATA;
|
|
dts.r.vo = DBRI_DTS_VO;
|
|
dts.r.oldout = ((unsigned int)
|
|
(driver->ser_sts.pal_present ?
|
|
DBRI_PIPE_SB_PAL_T : DBRI_PIPE_CHI));
|
|
out_tsd.r.len = DUMMYDATA_LEN;
|
|
out_tsd.r.cycle = DUMMYDATA_CYCLE;
|
|
out_tsd.r.mode = DBRI_DTS_SINGLE;
|
|
out_tsd.r.next = DBRI_PIPE_DM_T_5_8;
|
|
|
|
cmd.cmd_wd1 = DBRI_CMD_SSP | DBRI_SSP_PIPE(DBRI_PIPE_DUMMYDATA);
|
|
cmd.cmd_wd2 = reverse_bit_order(driver->ser_sts.chi_dd_val,
|
|
DUMMYDATA_LEN);
|
|
dbri_command(driver, cmd);
|
|
|
|
cmd.cmd_wd1 = dts.word32;
|
|
cmd.cmd_wd2 = 0;
|
|
cmd.cmd_wd3 = out_tsd.word32;
|
|
dbri_command(driver, cmd);
|
|
}
|
|
|
|
return;
|
|
} /* audio_xcon_stream */
|
|
|
|
|
|
/*
|
|
* mmcodec_check_audio_config - returns error if invalid configuration
|
|
*/
|
|
int
|
|
mmcodec_check_audio_config(driver, sample_rate, channels, precision, encoding)
|
|
dbri_dev_t *driver;
|
|
unsigned int sample_rate;
|
|
unsigned int channels;
|
|
unsigned int precision;
|
|
unsigned int encoding;
|
|
{
|
|
mmcodec_ctrl_t mmctrl;
|
|
|
|
aprintf("mmcodec_check_audio_config: ");
|
|
aprintf("sample_rate to %u ", sample_rate);
|
|
aprintf("channels to %u ", channels);
|
|
aprintf("precision to %u ", precision);
|
|
aprintf("encoding to %u ", encoding);
|
|
aprintf("\n");
|
|
|
|
mmctrl.word32[0] = driver->ptab[DBRI_PIPE_CM_T_1_4].ssp;
|
|
if (!Modify(encoding)) {
|
|
if (mmctrl.r.df == MMCODEC_DF_ULAW)
|
|
encoding = AUDIO_ENCODING_ULAW;
|
|
else if (mmctrl.r.df == MMCODEC_DF_ALAW)
|
|
encoding = AUDIO_ENCODING_ALAW;
|
|
else
|
|
encoding = AUDIO_ENCODING_LINEAR;
|
|
}
|
|
|
|
switch (encoding) {
|
|
case AUDIO_ENCODING_ULAW:
|
|
case AUDIO_ENCODING_ALAW:
|
|
if (Modify(channels) && (channels != 1))
|
|
return (EINVAL);
|
|
if (Modify(precision) && (precision != 8))
|
|
return (EINVAL);
|
|
if (Modify(sample_rate) && (sample_rate != 8000))
|
|
return (EINVAL);
|
|
break;
|
|
case AUDIO_ENCODING_LINEAR:
|
|
if (Modify(channels) && (channels != 2) && (channels != 1))
|
|
return (EINVAL);
|
|
if (Modify(precision) && (precision != 16))
|
|
return (EINVAL);
|
|
if (Modify(sample_rate)) {
|
|
/*
|
|
* Current thinking is that close only
|
|
* counts in horseshoes and handgrenades and
|
|
* audio API's - the driver wants it exact.
|
|
*/
|
|
switch (sample_rate) {
|
|
case 8000:
|
|
case 16000:
|
|
case 18900:
|
|
case 32000:
|
|
case 37800:
|
|
case 44100:
|
|
case 48000:
|
|
case 9600:
|
|
/* semi-supported now means supported */
|
|
case 5512:
|
|
case 11025:
|
|
case 27429:
|
|
case 22050:
|
|
case 33075:
|
|
case 6615:
|
|
break;
|
|
default:
|
|
return (EINVAL);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
return (EINVAL);
|
|
} /* switch */
|
|
|
|
return (0);
|
|
} /* mmcodec_check_audio_config */
|
|
|
|
|
|
/*
|
|
* mmcodec_set_audio_config - Set the configuration of the "audio device" to
|
|
* these parameters; although the encoding can imply the values for the rest
|
|
* of the parameters, they need to be valid as these are used to fill in the
|
|
* global values returned to user programs
|
|
*/
|
|
void
|
|
mmcodec_set_audio_config(driver, as, sample_rate, channels, precision, encoding)
|
|
dbri_dev_t *driver;
|
|
aud_stream_t *as;
|
|
unsigned int sample_rate;
|
|
unsigned int channels;
|
|
unsigned int precision;
|
|
unsigned int encoding;
|
|
{
|
|
mmcodec_ctrl_t mmctrl;
|
|
dbri_stream_t *iocp_in, *iocp_out;
|
|
|
|
aprintf("mmcodec_set_audio_config: ");
|
|
aprintf("sample_rate-%u ", sample_rate);
|
|
aprintf("channels-%u ", channels);
|
|
aprintf("precision-%u ", precision);
|
|
aprintf("encoding-%u ", encoding);
|
|
aprintf("\n");
|
|
|
|
iocp_out = AsToDs(as->play_as);
|
|
iocp_in = AsToDs(as->record_as);
|
|
|
|
mmctrl.word32[0] = driver->ptab[DBRI_PIPE_CM_T_1_4].ssp;
|
|
|
|
/*
|
|
* Change the input buffer size to reflect new format; for
|
|
* consistency, we try to keep it about around an 8th of a second
|
|
* but we can't go above DBRI_MAXPACKET
|
|
*/
|
|
if (encoding == AUDIO_ENCODING_ULAW) {
|
|
mmctrl.r.df = MMCODEC_DF_ULAW;
|
|
mmctrl.r.st = MMCODEC_ST_MONO;
|
|
mmctrl.r.dfr = MMCODEC_DFR_8000;
|
|
if (ISCONTROLSTREAM(as) &&
|
|
(driver->ser_sts.chi_flags & CHI_FLAG_SER_STS)) {
|
|
/* serial to serial connection setup */
|
|
mmctrl.r.xclk = ~MMCODEC_XCLK & 1; /* no xmit clk*/
|
|
mmctrl.r.bsel = MMCODEC_BSEL_256;
|
|
mmctrl.r.mck = MMCODEC_MCK_MSTR;
|
|
} else { /* direct audio connection */
|
|
mmctrl.r.xclk = MMCODEC_XCLK;
|
|
mmctrl.r.bsel = MMCODEC_BSEL_128;
|
|
mmctrl.r.mck = MMCODEC_MCK_XTAL1;
|
|
}
|
|
iocp_in->as.input_size = Dbri_recv_bsize;
|
|
driver->ser_sts.chi_dd_val = ULAW_ZERO;
|
|
|
|
} else if (encoding == AUDIO_ENCODING_ALAW) {
|
|
mmctrl.r.df = MMCODEC_DF_ALAW;
|
|
mmctrl.r.st = MMCODEC_ST_MONO;
|
|
mmctrl.r.dfr = MMCODEC_DFR_8000;
|
|
mmctrl.r.mck = MMCODEC_MCK_XTAL1;
|
|
iocp_in->as.input_size = Dbri_recv_bsize;
|
|
driver->ser_sts.chi_dd_val = ALAW_ZERO;
|
|
} else {
|
|
/* XXX - add 8 bit stereo support sometime later */
|
|
|
|
iocp_in->as.input_size =
|
|
MIN((sample_rate * 2 * channels / 8), DBRI_MAXPACKET);
|
|
iocp_in->as.input_size &= 0xfffffff0;
|
|
driver->ser_sts.chi_dd_val = LINEAR_ZERO;
|
|
|
|
mmctrl.r.df = MMCODEC_DF_16_BIT;
|
|
if (channels == 2)
|
|
mmctrl.r.st = MMCODEC_ST_STEREO;
|
|
else
|
|
mmctrl.r.st = MMCODEC_ST_MONO;
|
|
switch (sample_rate) {
|
|
case 16000: /* G_722 */
|
|
mmctrl.r.dfr = MMCODEC_DFR_16000;
|
|
mmctrl.r.mck = MMCODEC_MCK_XTAL1;
|
|
break;
|
|
case 18900: /* CDROM_XA_C */
|
|
mmctrl.r.dfr = MMCODEC_DFR_18900;
|
|
mmctrl.r.mck = MMCODEC_MCK_XTAL2;
|
|
break;
|
|
case 32000: /* DAT_32 */
|
|
mmctrl.r.dfr = MMCODEC_DFR_32000;
|
|
mmctrl.r.mck = MMCODEC_MCK_XTAL1;
|
|
break;
|
|
case 37800: /* CDROM_XA_AB */
|
|
mmctrl.r.dfr = MMCODEC_DFR_37800;
|
|
mmctrl.r.mck = MMCODEC_MCK_XTAL2;
|
|
break;
|
|
case 44100: /* CD_DA */
|
|
mmctrl.r.dfr = MMCODEC_DFR_44100;
|
|
mmctrl.r.mck = MMCODEC_MCK_XTAL2;
|
|
break;
|
|
case 48000: /* DAT_48 */
|
|
mmctrl.r.dfr = MMCODEC_DFR_48000;
|
|
mmctrl.r.mck = MMCODEC_MCK_XTAL1;
|
|
break;
|
|
case 9600: /* SPEECHIO */
|
|
mmctrl.r.dfr = MMCODEC_DFR_9600;
|
|
mmctrl.r.mck = MMCODEC_MCK_XTAL1;
|
|
break;
|
|
case 8000: /* ULAW and ALAW */
|
|
default:
|
|
mmctrl.r.dfr = MMCODEC_DFR_8000;
|
|
mmctrl.r.mck = MMCODEC_MCK_XTAL1;
|
|
break;
|
|
/* The above are the *real* ones, these are fake */
|
|
case 5513:
|
|
mmctrl.r.dfr = MMCODEC_DFR_5513;
|
|
mmctrl.r.mck = MMCODEC_MCK_XTAL2;
|
|
break;
|
|
case 11025:
|
|
mmctrl.r.dfr = MMCODEC_DFR_11025;
|
|
mmctrl.r.mck = MMCODEC_MCK_XTAL2;
|
|
break;
|
|
case 27429:
|
|
mmctrl.r.dfr = MMCODEC_DFR_27429;
|
|
mmctrl.r.mck = MMCODEC_MCK_XTAL1;
|
|
break;
|
|
case 22050:
|
|
mmctrl.r.dfr = MMCODEC_DFR_22050;
|
|
mmctrl.r.mck = MMCODEC_MCK_XTAL2;
|
|
break;
|
|
case 33075:
|
|
mmctrl.r.dfr = MMCODEC_DFR_33075;
|
|
mmctrl.r.mck = MMCODEC_MCK_XTAL2;
|
|
break;
|
|
case 6615:
|
|
mmctrl.r.dfr = MMCODEC_DFR_6615;
|
|
mmctrl.r.mck = MMCODEC_MCK_XTAL2;
|
|
break;
|
|
} /* switch */
|
|
}
|
|
aprintf("mmcodec_set_audio_config: mmctrl = 0x%x\n", mmctrl.word32[0]);
|
|
driver->ptab[DBRI_PIPE_CM_T_1_4].ssp = mmctrl.word32[0];
|
|
aprintf("changing input_size to %d\n", iocp_in->as.input_size);
|
|
|
|
/*
|
|
* Update the current configuration of the audio device passed
|
|
* to user programs
|
|
*/
|
|
iocp_in->as.info.sample_rate =
|
|
iocp_out->as.info.sample_rate = sample_rate;
|
|
iocp_in->as.info.channels =
|
|
iocp_out->as.info.channels = channels;
|
|
iocp_in->as.info.precision =
|
|
iocp_out->as.info.precision = precision;
|
|
iocp_in->as.info.encoding =
|
|
iocp_out->as.info.encoding = encoding;
|
|
mmcodec_setup_ctrl_mode(driver);
|
|
return;
|
|
} /* mmcodec_set_audio_config */
|
|
|
|
|
|
/*
|
|
* Speaker Mute essentially determines how to set the LED more than
|
|
* anything else. Since we have two (mutually exclusive) interests,
|
|
* Speaker Muted and Output to Speaker enabled or disabled, and only one
|
|
* bit (sm), this gets a litte tricky. So we use the hwi_state.output_muted
|
|
* as the former and play.ports as the latter. Notice that the value of
|
|
* 'sm' at any given time doesn't really tell us anything.
|
|
*/
|
|
int
|
|
mmcodec_set_spkrmute(driver, how)
|
|
dbri_dev_t *driver;
|
|
int how;
|
|
{
|
|
int pal_value;
|
|
pipe_tab_t *pipep;
|
|
mmcodec_data_t mmdata;
|
|
dbri_chip_cmd_t cmd;
|
|
aud_stream_t *as;
|
|
|
|
as = &driver->ioc[(int)DBRI_AUDIO_OUT].as;
|
|
pipep = &driver->ptab[DBRI_PIPE_DM_T_5_8];
|
|
mmdata.word32 = pipep->ssp;
|
|
/*
|
|
* We optimise a little bit here and if the "new" setting
|
|
* is the same as the existing one, don't issue the SSP
|
|
*/
|
|
switch (how) {
|
|
case SPKRMUTE_SET:
|
|
if (driver->hwi_state.output_muted == TRUE) {
|
|
return (-1); /* if already muted, do nothing */
|
|
}
|
|
mmdata.r.sm = ~MMCODEC_SM & 1;
|
|
mmdata.r.om0 = ~MMCODEC_OM0_ENABLE & 1;
|
|
mmdata.r.om1 = ~MMCODEC_OM1_ENABLE & 1;
|
|
driver->hwi_state.output_muted = TRUE;
|
|
break;
|
|
case SPKRMUTE_GET:
|
|
return ((int)driver->hwi_state.output_muted);
|
|
case SPKRMUTE_CLEAR:
|
|
if (driver->hwi_state.output_muted == FALSE) {
|
|
return (-1); /* if not muted, don't do anything */
|
|
}
|
|
if (as->info.port & AUDIO_SPEAKER) {
|
|
mmdata.r.sm = MMCODEC_SM;
|
|
}
|
|
if (as->info.port & AUDIO_HEADPHONE) {
|
|
mmdata.r.om1 = MMCODEC_OM1_ENABLE;
|
|
}
|
|
if (as->info.port & AUDIO_LINE_OUT) {
|
|
mmdata.r.om0 = MMCODEC_OM0_ENABLE;
|
|
}
|
|
driver->hwi_state.output_muted = FALSE;
|
|
break;
|
|
case SPKRMUTE_TOGGLE:
|
|
if (driver->hwi_state.output_muted == FALSE) {
|
|
mmdata.r.sm = ~MMCODEC_SM & 1;
|
|
mmdata.r.om0 = ~MMCODEC_OM0_ENABLE & 1;
|
|
mmdata.r.om1 = ~MMCODEC_OM1_ENABLE & 1;
|
|
driver->hwi_state.output_muted = TRUE;
|
|
} else {
|
|
if (as->info.port & AUDIO_SPEAKER) {
|
|
mmdata.r.sm = MMCODEC_SM;
|
|
}
|
|
if (as->info.port & AUDIO_HEADPHONE) {
|
|
mmdata.r.om1 = MMCODEC_OM1_ENABLE;
|
|
}
|
|
if (as->info.port & AUDIO_LINE_OUT) {
|
|
mmdata.r.om0 = MMCODEC_OM0_ENABLE;
|
|
}
|
|
driver->hwi_state.output_muted = FALSE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* XXX - technically, if sm didn't change we don't need to do this */
|
|
cmd.cmd_wd1 = DBRI_CMD_SSP | DBRI_SSP_PIPE(DBRI_PIPE_DM_T_5_8);
|
|
pipep->ssp = mmdata.word32;
|
|
cmd.cmd_wd2 = reverse_bit_order(pipep->ssp, DM_T_5_8_LEN);
|
|
ATRACEI(mmcodec_set_spkrmute, 'SSP ', cmd.cmd_wd2);
|
|
dbri_command(driver, cmd);
|
|
|
|
/*
|
|
* Set the mute LED appropriately; notice we never save the current
|
|
* setting in the ssp area of pipe_tab_t; I *think* this is good
|
|
*/
|
|
if (driver->hwi_state.output_muted == TRUE) {
|
|
pal_value = 1;
|
|
} else {
|
|
pal_value = 0;
|
|
}
|
|
|
|
cmd.cmd_wd1 = DBRI_CMD_SSP | DBRI_SSP_PIPE(DBRI_PIPE_SB_PAL_T);
|
|
cmd.cmd_wd2 = reverse_bit_order((unsigned int)pal_value, SB_PAL_LEN);
|
|
dbri_command(driver, cmd);
|
|
|
|
cmd.cmd_wd1 = DBRI_CMD_PAUSE;
|
|
dbri_command(driver, cmd);
|
|
|
|
return (-1); /* should not be used */
|
|
} /* mmcodec_set_spkrmute */
|
|
|
|
|
|
unsigned char
|
|
dbri_output_muted(driver, val)
|
|
dbri_dev_t *driver;
|
|
unsigned char val;
|
|
{
|
|
if (val) {
|
|
(void) mmcodec_set_spkrmute(driver, SPKRMUTE_SET);
|
|
} else {
|
|
(void) mmcodec_set_spkrmute(driver, SPKRMUTE_CLEAR);
|
|
}
|
|
return (driver->hwi_state.output_muted);
|
|
}
|
|
|
|
/*
|
|
* Set output port to external jack or built-in speaker
|
|
*
|
|
* Notice the way the "sm" field is being overloaded here - it's both
|
|
* a speaker muted bit, and an enable for the Speaker. If we call
|
|
* mmcodec_set_spkrmute to set this bit, it's muting the speaker
|
|
* and all the appropriate software things happen (LED turned on and
|
|
* field in aud_state_t set correctly). If we set it to enable or
|
|
* disable output to the speaker, none of this happens
|
|
*/
|
|
unsigned int
|
|
dbri_outport(driver, val)
|
|
dbri_dev_t *driver;
|
|
unsigned int val;
|
|
{
|
|
unsigned int pipe;
|
|
pipe_tab_t *pipep;
|
|
mmcodec_data_t mmdata;
|
|
dbri_chip_cmd_t cmd;
|
|
|
|
aprintf("dbri_outport: driver=0x%x, val=%u\n", (unsigned int)driver,
|
|
val);
|
|
|
|
/* Pipes need to be setup prior to calling this routine */
|
|
pipe = DBRI_PIPE_DM_T_5_8;
|
|
pipep = &driver->ptab[pipe]; /* ctrl in data stream */
|
|
mmdata.word32 = pipep->ssp;
|
|
|
|
/*
|
|
* On MMCODEC om0 is AUDIO_LINE_OUT, om1 is AUDIO_HEADPHONE,
|
|
* and sm is effectively AUDIO_SPEAKER
|
|
*/
|
|
if (driver->hwi_state.output_muted == FALSE) {
|
|
/* disable everything, then selectively enable */
|
|
mmdata.r.sm = ~MMCODEC_SM & 1;
|
|
mmdata.r.om0 = ~MMCODEC_OM0_ENABLE & 1;
|
|
mmdata.r.om1 = ~MMCODEC_OM1_ENABLE & 1;
|
|
if (val & AUDIO_SPEAKER)
|
|
mmdata.r.sm = MMCODEC_SM;
|
|
if (val & AUDIO_HEADPHONE)
|
|
mmdata.r.om1 = MMCODEC_OM1_ENABLE;
|
|
if (val & AUDIO_LINE_OUT)
|
|
mmdata.r.om0 = MMCODEC_OM0_ENABLE;
|
|
|
|
cmd.cmd_wd1 = DBRI_CMD_SSP | DBRI_SSP_PIPE(pipe);
|
|
pipep->ssp = mmdata.word32;
|
|
cmd.cmd_wd2 = reverse_bit_order(pipep->ssp, DM_T_5_8_LEN);
|
|
ATRACEI(dbri_outport, 'SSP ', cmd.cmd_wd2);
|
|
dbri_command(driver, cmd); /* Set short pipe data */
|
|
}
|
|
|
|
return (val);
|
|
} /* dbri_outport */
|
|
|
|
|
|
/* Set input port to line in jack or microphone */
|
|
unsigned int
|
|
dbri_inport(driver, val)
|
|
dbri_dev_t *driver;
|
|
unsigned int val;
|
|
{
|
|
unsigned int pipe;
|
|
pipe_tab_t *pipep;
|
|
mmcodec_data_t mmdata;
|
|
dbri_chip_cmd_t cmd;
|
|
|
|
aprintf("dbri_inport: driver=0x%x, val=%u\n", (unsigned int)driver,
|
|
val);
|
|
/* Pipes need to be setup prior to calling this routine */
|
|
pipe = DBRI_PIPE_DM_T_5_8;
|
|
pipep = &driver->ptab[pipe]; /* ctrl in data stream */
|
|
mmdata.word32 = pipep->ssp;
|
|
|
|
/*
|
|
* Again we have an either/or situation, but on input this
|
|
* makes more sense.
|
|
*/
|
|
mmdata.r.is = (val == AUDIO_MICROPHONE) ? MMCODEC_IS_MIC
|
|
: MMCODEC_IS_LINE;
|
|
|
|
cmd.cmd_wd1 = DBRI_CMD_SSP | DBRI_SSP_PIPE(pipe);
|
|
pipep->ssp = mmdata.word32;
|
|
cmd.cmd_wd2 = reverse_bit_order(pipep->ssp, DM_T_5_8_LEN);
|
|
ATRACEI(dbri_inport, 'SSP ', cmd.cmd_wd2);
|
|
dbri_command(driver, cmd); /* Set short pipe data */
|
|
|
|
return (val);
|
|
} /* dbri_inport */
|
|
|
|
/*
|
|
* XXX - in all the routines, dbri_play_gain, dbri_record_gain, and
|
|
* in mmcodec_change_volume, the code mistakenly uses 0 (zero) instead
|
|
* of using AUDIO_MIN_GAIN
|
|
*/
|
|
/*
|
|
* Convert play gain to chip values and load them. Return the closest
|
|
* appropriate gain value. Use the balance field to determine what
|
|
* to put in each of the left and right attenuation fields
|
|
*/
|
|
unsigned int
|
|
dbri_play_gain(driver, val, balance)
|
|
dbri_dev_t *driver;
|
|
unsigned int val;
|
|
unsigned char balance;
|
|
{
|
|
unsigned int pipe;
|
|
unsigned int la, ra, tmp;
|
|
int r, l;
|
|
pipe_tab_t *pipep;
|
|
mmcodec_data_t mmdata;
|
|
dbri_chip_cmd_t cmd;
|
|
|
|
pipe = DBRI_PIPE_DM_T_5_8;
|
|
pipep = &driver->ptab[pipe]; /* ctrl in data stream */
|
|
mmdata.word32 = pipep->ssp;
|
|
|
|
r = l = val;
|
|
if (balance < AUDIO_MID_BALANCE)
|
|
r = MAX(0, (int)(val - ((AUDIO_MID_BALANCE - balance) <<
|
|
AUDIO_BALANCE_SHIFT)));
|
|
else if (balance > AUDIO_MID_BALANCE)
|
|
l = MAX(0, (int)(val - ((balance - AUDIO_MID_BALANCE) <<
|
|
AUDIO_BALANCE_SHIFT)));
|
|
|
|
if (l == 0) {
|
|
la = MMCODEC_MAX_DEV_ATEN;
|
|
} else {
|
|
la = MMCODEC_MAX_ATEN -
|
|
(l * (MMCODEC_MAX_ATEN+1) / (AUDIO_MAX_GAIN+1));
|
|
}
|
|
if (r == 0) {
|
|
ra = MMCODEC_MAX_DEV_ATEN;
|
|
} else {
|
|
ra = MMCODEC_MAX_ATEN -
|
|
(r * (MMCODEC_MAX_ATEN+1) / (AUDIO_MAX_GAIN+1));
|
|
}
|
|
|
|
mmdata.r.lo = la;
|
|
mmdata.r.ro = ra;
|
|
|
|
cmd.cmd_wd1 = DBRI_CMD_SSP | DBRI_SSP_PIPE(pipe);
|
|
pipep->ssp = mmdata.word32;
|
|
cmd.cmd_wd2 = reverse_bit_order(pipep->ssp, DM_T_5_8_LEN);
|
|
ATRACEI(dbri_play_gain, 'SSP ', cmd.cmd_wd2);
|
|
dbri_command(driver, cmd); /* Set short pipe data */
|
|
|
|
cmd.cmd_wd1 = DBRI_CMD_PAUSE; /* PAUSE command */
|
|
dbri_command(driver, cmd);
|
|
|
|
/*
|
|
* We end up returning a value slightly different than the one
|
|
* passed in - *most* applications expect this; the tmp variable
|
|
* is only needed for the debugging printf
|
|
*/
|
|
if ((val == 0) || (val == AUDIO_MAX_GAIN)) {
|
|
tmp = val;
|
|
} else {
|
|
if (l == val) {
|
|
tmp = ((MMCODEC_MAX_ATEN - la) * (AUDIO_MAX_GAIN+1) /
|
|
(MMCODEC_MAX_ATEN+1));
|
|
} else if (r == val) {
|
|
tmp = ((MMCODEC_MAX_ATEN - ra) * (AUDIO_MAX_GAIN+1) /
|
|
(MMCODEC_MAX_ATEN+1));
|
|
}
|
|
}
|
|
aprintf("dbri_play_gain:v=%u, b=%u, l=%d, r=%d, la=%u, ra=%u, tmp=%u\n",
|
|
val, balance, l, r, la, ra, tmp);
|
|
return (tmp);
|
|
} /* dbri_play_gain */
|
|
|
|
/*
|
|
* Increase or Decrease the play volume, *ONE* *device* unit at a time
|
|
*/
|
|
void
|
|
mmcodec_change_volume(driver, dir)
|
|
dbri_dev_t *driver;
|
|
unsigned int dir;
|
|
{
|
|
unsigned int pipe;
|
|
pipe_tab_t *pipep;
|
|
mmcodec_data_t mmdata;
|
|
dbri_chip_cmd_t cmd;
|
|
aud_stream_t *as_output;
|
|
unsigned char balance;
|
|
|
|
as_output = &driver->ioc[(int)DBRI_AUDIO_OUT].as;
|
|
balance = as_output->info.balance;
|
|
pipe = DBRI_PIPE_DM_T_5_8;
|
|
pipep = &driver->ptab[pipe]; /* ctrl in data stream */
|
|
mmdata.word32 = pipep->ssp;
|
|
|
|
/*
|
|
* Volume up button pressed - decrease attenuation; Volume down
|
|
* button pressed - increase attenuation. Handle left and right
|
|
* attenuation individually since balance may have them a different
|
|
* values; find out which one is the "correct" one and which one is
|
|
* compensating for balance
|
|
*/
|
|
aprintf("IN: output now %u %u\n",
|
|
mmdata.r.lo, mmdata.r.ro);
|
|
if (balance <= AUDIO_MID_BALANCE) {
|
|
if (dir == VPLUS_BUTTON) {
|
|
if (mmdata.r.lo == 0)
|
|
return;
|
|
if (mmdata.r.lo == MMCODEC_MAX_DEV_ATEN)
|
|
mmdata.r.lo = MMCODEC_MAX_ATEN - 1;
|
|
else
|
|
mmdata.r.lo--;
|
|
mmdata.r.ro = MIN(MMCODEC_MAX_ATEN,
|
|
(int)(mmdata.r.lo + (AUDIO_MID_BALANCE - balance)));
|
|
|
|
} else if (dir == VMINUS_BUTTON) {
|
|
if (mmdata.r.lo == MMCODEC_MAX_DEV_ATEN)
|
|
return;
|
|
mmdata.r.lo++;
|
|
mmdata.r.ro = MIN(MMCODEC_MAX_ATEN,
|
|
(int)(mmdata.r.lo + (AUDIO_MID_BALANCE - balance)));
|
|
|
|
if (mmdata.r.lo == MMCODEC_MAX_ATEN)
|
|
mmdata.r.lo = MMCODEC_MAX_DEV_ATEN;
|
|
if (mmdata.r.ro == MMCODEC_MAX_ATEN)
|
|
mmdata.r.ro = MMCODEC_MAX_DEV_ATEN;
|
|
|
|
} else {
|
|
return;
|
|
}
|
|
if (mmdata.r.lo == 0) {
|
|
as_output->info.gain = AUDIO_MAX_GAIN;
|
|
} else if (mmdata.r.lo == MMCODEC_MAX_DEV_ATEN) {
|
|
as_output->info.gain = 0;
|
|
} else {
|
|
as_output->info.gain =
|
|
((MMCODEC_MAX_ATEN - mmdata.r.lo) *
|
|
(AUDIO_MAX_GAIN+1) / (MMCODEC_MAX_ATEN+1));
|
|
}
|
|
} else if (balance > AUDIO_MID_BALANCE) {
|
|
if (dir == VPLUS_BUTTON) {
|
|
if (mmdata.r.ro == 0)
|
|
return;
|
|
if (mmdata.r.ro == MMCODEC_MAX_DEV_ATEN)
|
|
mmdata.r.ro = MMCODEC_MAX_ATEN - 1;
|
|
else
|
|
mmdata.r.ro--;
|
|
mmdata.r.lo = MIN(MMCODEC_MAX_ATEN,
|
|
(int)(mmdata.r.ro + (balance - AUDIO_MID_BALANCE)));
|
|
|
|
} else if (dir == VMINUS_BUTTON) {
|
|
if (mmdata.r.ro == MMCODEC_MAX_DEV_ATEN)
|
|
return;
|
|
mmdata.r.ro++;
|
|
mmdata.r.lo = MIN(MMCODEC_MAX_ATEN,
|
|
(int)(mmdata.r.ro + (balance - AUDIO_MID_BALANCE)));
|
|
|
|
if (mmdata.r.ro == MMCODEC_MAX_ATEN)
|
|
mmdata.r.ro = MMCODEC_MAX_DEV_ATEN;
|
|
if (mmdata.r.lo == MMCODEC_MAX_ATEN)
|
|
mmdata.r.lo = MMCODEC_MAX_DEV_ATEN;
|
|
|
|
} else {
|
|
return;
|
|
}
|
|
if (mmdata.r.ro == 0) {
|
|
as_output->info.gain = AUDIO_MAX_GAIN;
|
|
} else if (mmdata.r.ro == MMCODEC_MAX_DEV_ATEN) {
|
|
as_output->info.gain = 0;
|
|
} else {
|
|
as_output->info.gain =
|
|
((MMCODEC_MAX_ATEN - mmdata.r.ro) *
|
|
(AUDIO_MAX_GAIN+1) / (MMCODEC_MAX_ATEN+1));
|
|
}
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
aprintf("mmcodec_change_volume: output now %u %u\n",
|
|
mmdata.r.lo, mmdata.r.ro);
|
|
cmd.cmd_wd1 = DBRI_CMD_SSP | DBRI_SSP_PIPE(pipe);
|
|
pipep->ssp = mmdata.word32;
|
|
cmd.cmd_wd2 = reverse_bit_order(pipep->ssp, DM_T_5_8_LEN);
|
|
ATRACEI(mmcodec_change_volume, 'SSP ', cmd.cmd_wd2);
|
|
dbri_command(driver, cmd); /* Set short pipe data */
|
|
|
|
cmd.cmd_wd1 = DBRI_CMD_PAUSE; /* PAUSE command */
|
|
dbri_command(driver, cmd);
|
|
|
|
return;
|
|
} /* mmcodec_change_volume */
|
|
|
|
/*
|
|
* Convert record gain to chip values and load them.
|
|
* Return the closest appropriate gain value.
|
|
*/
|
|
unsigned int
|
|
dbri_record_gain(driver, val, balance)
|
|
dbri_dev_t *driver;
|
|
unsigned int val;
|
|
unsigned char balance;
|
|
{
|
|
unsigned int pipe;
|
|
unsigned int lg, rg, tmp;
|
|
int l, r;
|
|
pipe_tab_t *pipep;
|
|
mmcodec_data_t mmdata;
|
|
dbri_chip_cmd_t cmd;
|
|
|
|
/* Pipes need to be setup prior to calling this routine */
|
|
pipe = DBRI_PIPE_DM_T_5_8;
|
|
pipep = &driver->ptab[pipe]; /* ctrl in data stream */
|
|
mmdata.word32 = pipep->ssp;
|
|
|
|
l = r = val;
|
|
if (balance < AUDIO_MID_BALANCE)
|
|
r = MAX(0, (int)(val - ((AUDIO_MID_BALANCE - balance) <<
|
|
AUDIO_BALANCE_SHIFT)));
|
|
else if (balance > AUDIO_MID_BALANCE)
|
|
l = MAX(0, (int)(val - ((balance - AUDIO_MID_BALANCE) <<
|
|
AUDIO_BALANCE_SHIFT)));
|
|
|
|
lg = l * (MMCODEC_MAX_GAIN+1) / (AUDIO_MAX_GAIN+1);
|
|
rg = r * (MMCODEC_MAX_GAIN+1) / (AUDIO_MAX_GAIN+1);
|
|
|
|
mmdata.r.lg = lg;
|
|
mmdata.r.rg = rg;
|
|
|
|
cmd.cmd_wd1 = DBRI_CMD_SSP | DBRI_SSP_PIPE(pipe);
|
|
pipep->ssp = mmdata.word32;
|
|
cmd.cmd_wd2 = reverse_bit_order(pipep->ssp, DM_T_5_8_LEN);
|
|
ATRACEI(dbri_record_gain, 'SSP ', cmd.cmd_wd2);
|
|
dbri_command(driver, cmd); /* Set short pipe data */
|
|
|
|
cmd.cmd_wd1 = DBRI_CMD_PAUSE; /* PAUSE command */
|
|
dbri_command(driver, cmd);
|
|
|
|
/*
|
|
* We end up returning a value slightly different than the one
|
|
* passed in - *most* applications expect this; the tmp variable
|
|
* is only needed for the debugging printf
|
|
*/
|
|
if (l == val) {
|
|
tmp = ((lg+1) * AUDIO_MAX_GAIN) / (MMCODEC_MAX_GAIN+1);
|
|
} else if (r == val) {
|
|
tmp = ((rg+1) * AUDIO_MAX_GAIN) / (MMCODEC_MAX_GAIN+1);
|
|
}
|
|
aprintf("dbri_record_gain: v=%u,b=%u,l=%d,r=%d,lg=%u,rg=%u,tmp=%u\n",
|
|
val, balance, l, r, lg, rg, tmp);
|
|
return (tmp);
|
|
} /* dbri_record_gain */
|
|
|
|
/*
|
|
* Convert monitor gain to chip values and load them.
|
|
* Return the closest appropriate gain value.
|
|
*/
|
|
unsigned int
|
|
dbri_monitor_gain(driver, val)
|
|
dbri_dev_t *driver;
|
|
unsigned int val;
|
|
{
|
|
unsigned int pipe;
|
|
int aten, tmp;
|
|
pipe_tab_t *pipep;
|
|
mmcodec_data_t mmdata;
|
|
dbri_chip_cmd_t cmd;
|
|
|
|
pipe = DBRI_PIPE_DM_T_5_8;
|
|
pipep = &driver->ptab[pipe]; /* ctrl in data stream */
|
|
mmdata.word32 = pipep->ssp;
|
|
|
|
aten = MMCODEC_MA_MAX_ATEN -
|
|
(val * (MMCODEC_MA_MAX_ATEN+1) / (AUDIO_MAX_GAIN+1));
|
|
aprintf("dbri_monitor_gain: val=%u, attenuation becomes %d\n", val,
|
|
aten);
|
|
mmdata.r.ma = aten;
|
|
|
|
cmd.cmd_wd1 = DBRI_CMD_SSP | DBRI_SSP_PIPE(pipe);
|
|
pipep->ssp = mmdata.word32;
|
|
cmd.cmd_wd2 = reverse_bit_order(pipep->ssp, DM_T_5_8_LEN);
|
|
ATRACEI(dbri_monitor_gain, 'SSP ', cmd.cmd_wd2);
|
|
dbri_command(driver, cmd); /* Set short pipe data */
|
|
|
|
cmd.cmd_wd1 = DBRI_CMD_PAUSE; /* PAUSE command */
|
|
dbri_command(driver, cmd);
|
|
|
|
/*
|
|
* We end up returning a value slightly different than the one
|
|
* passed in - *most* applications expect this; the tmp variable
|
|
* is only needed for the debugging printf
|
|
*/
|
|
if (val == AUDIO_MAX_GAIN) {
|
|
tmp = AUDIO_MAX_GAIN;
|
|
} else {
|
|
tmp = (MMCODEC_MAX_GAIN - aten) * (AUDIO_MAX_GAIN+1) /
|
|
(MMCODEC_MAX_GAIN+1);
|
|
}
|
|
aprintf("dbri_monitor_gain: val=%u, aten becomes %u, returns %u\n",
|
|
val, aten, tmp);
|
|
return (tmp);
|
|
} /* dbri_monitor_gain */
|
|
|
|
|
|
void
|
|
mmcodec_volume_timer(driver)
|
|
dbri_dev_t *driver;
|
|
{
|
|
int done;
|
|
pipe_tab_t *pipep;
|
|
aud_stream_t *ctrl_as;
|
|
|
|
pipep = &driver->ptab[DBRI_PIPE_SB_PAL_R];
|
|
done = FALSE;
|
|
if (pipep->ssp & VPLUS_BUTTON) {
|
|
mmcodec_change_volume(driver, VPLUS_BUTTON);
|
|
done = TRUE;
|
|
} else if (pipep->ssp & VMINUS_BUTTON) {
|
|
mmcodec_change_volume(driver, VMINUS_BUTTON);
|
|
done = TRUE;
|
|
}
|
|
if (done) {
|
|
ctrl_as = &driver->ioc[(int)DBRI_AUDIOCTL].as;
|
|
audio_sendsig(ctrl_as);
|
|
timeout((int(*)())mmcodec_volume_timer, (caddr_t)driver,
|
|
sb_button_repeat);
|
|
}
|
|
} /* mmcodec_volume_timer */
|
|
|
|
/*
|
|
* dbri_fxdt_intr - handle interrupts from fixed pipes
|
|
*/
|
|
void
|
|
dbri_fxdt_intr(driver, intr)
|
|
dbri_dev_t *driver;
|
|
dbri_intrq_ent_t intr;
|
|
{
|
|
unsigned int pipe, t14d, r12d, r34d, tmp;
|
|
int change;
|
|
pipe_tab_t *pipep;
|
|
mmcodec_ctrl_t mmctrl;
|
|
mmcodec_data_t mmdata;
|
|
dbri_chip_cmd_t cmd;
|
|
aud_stream_t *ctrl_as, *as_input, *as_output;
|
|
|
|
aprintf("dbri_fxdt_intr: channel %d, data 0x%x\n",
|
|
intr.f.channel, (unsigned int)intr.code_fxdt.changed_data);
|
|
switch (intr.f.channel) {
|
|
case DBRI_PIPE_SB_PAL_R:
|
|
pipe = intr.f.channel;
|
|
pipep = &driver->ptab[intr.f.channel];
|
|
tmp = reverse_bit_order(intr.code_fxdt.changed_data,
|
|
SB_PAL_LEN);
|
|
aprintf("PAL RECV: 0x%x\n", tmp);
|
|
/* Ignore changes to zero from CODEC handshake */
|
|
if (tmp == 0)
|
|
break;
|
|
/* Check for no change; DBRI-V1 bug #110 and V3 #7 and V5 #? */
|
|
if (pipep->ssp == tmp)
|
|
break;
|
|
if ((tmp & ID_MASK) != PAL_ID) {
|
|
/*
|
|
* XXX - we get constant changes on this pipe
|
|
* suggesting no speakerbox - why?
|
|
*/
|
|
aprintf("Audio: ID 0x%x, SpeakerBox disconnected?\n",
|
|
tmp);
|
|
break;
|
|
}
|
|
change = FALSE;
|
|
as_input = &driver->ioc[(int)DBRI_AUDIO_IN].as;
|
|
as_output = &driver->ioc[(int)DBRI_AUDIO_OUT].as;
|
|
ctrl_as = &driver->ioc[(int)DBRI_AUDIOCTL].as;
|
|
if ((tmp & MIKE_IN) != (pipep->ssp & MIKE_IN)) {
|
|
aprintf("mike status change\n");
|
|
if (tmp & MIKE_IN) {
|
|
as_input->info.avail_ports |= AUDIO_MICROPHONE;
|
|
} else {
|
|
as_input->info.avail_ports &= ~AUDIO_MICROPHONE;
|
|
}
|
|
change = TRUE;
|
|
}
|
|
if ((tmp & HPHONE_IN) != (pipep->ssp & HPHONE_IN)) {
|
|
aprintf("headphone status change\n");
|
|
if (tmp & HPHONE_IN) {
|
|
as_output->info.avail_ports |= AUDIO_HEADPHONE;
|
|
} else {
|
|
as_output->info.avail_ports &= ~AUDIO_HEADPHONE;
|
|
}
|
|
change = TRUE;
|
|
}
|
|
|
|
/*
|
|
* Current button policy is:
|
|
* Mute reduces volume alot. If you hit either
|
|
* volume button while muted, it will "un-mute" the speaker
|
|
* Leaving button pressed continues to affect volume
|
|
*
|
|
* Since pressing the buttons really fast can leave a bunch
|
|
* of outstanding timeout requests, everytime we see a new
|
|
* button press, turn off the one from before. It's a good
|
|
* thing we can have as many untimeouts as we want ....
|
|
*/
|
|
pipep->ssp = tmp;
|
|
if (pipep->ssp & VPLUS_BUTTON) {
|
|
mmcodec_change_volume(driver, VPLUS_BUTTON);
|
|
(void) mmcodec_set_spkrmute(driver, SPKRMUTE_CLEAR);
|
|
change = TRUE;
|
|
untimeout((int(*)())mmcodec_volume_timer,
|
|
(caddr_t)driver);
|
|
timeout((int(*)())mmcodec_volume_timer,
|
|
(caddr_t)driver, SB_BUTTON_START);
|
|
} else if (pipep->ssp & VMINUS_BUTTON) {
|
|
mmcodec_change_volume(driver, VMINUS_BUTTON);
|
|
(void) mmcodec_set_spkrmute(driver, SPKRMUTE_CLEAR);
|
|
change = TRUE;
|
|
untimeout((int(*)())mmcodec_volume_timer,
|
|
(caddr_t)driver);
|
|
timeout((int(*)())mmcodec_volume_timer,
|
|
(caddr_t)driver, SB_BUTTON_START);
|
|
} else if (pipep->ssp & MUTE_BUTTON) {
|
|
(void) mmcodec_set_spkrmute(driver, SPKRMUTE_TOGGLE);
|
|
change = TRUE;
|
|
}
|
|
/*
|
|
* change means two things - one is that a signal needs to
|
|
* be sent up the control device, and the other is that a
|
|
* button was pushed down; button UP events mean untimeout
|
|
*/
|
|
if (change) {
|
|
/*
|
|
* audio_sendsig takes care of the fact that
|
|
* the control device may not be open
|
|
*/
|
|
audio_sendsig(ctrl_as);
|
|
} else {
|
|
untimeout((int(*)())mmcodec_volume_timer,
|
|
(caddr_t)driver);
|
|
}
|
|
break;
|
|
|
|
case DBRI_PIPE_CM_R_7:
|
|
pipep = &driver->ptab[DBRI_PIPE_CM_R_7];
|
|
tmp = reverse_bit_order(intr.code_fxdt.changed_data,
|
|
CM_R_7_LEN);
|
|
/*
|
|
* We get transitions to zero when we first start up, and
|
|
* due to bug (that isn't a bug anymore) in the Analog CODEC,
|
|
* we have to ignore tranisitions when we're going to data mode
|
|
* from control mode (bit's after DCB are invalid)
|
|
*/
|
|
if ((tmp != 0) &&
|
|
(driver->ser_sts.chi_state == CHI_WAIT_DCB_LOW) &&
|
|
!(driver->ser_sts.chi_flags & CHI_FLAG_CODEC_RPTD)) {
|
|
|
|
pipep->ssp = tmp;
|
|
(void) printf("MMCODEC: manufacturer id %u, rev %u\n",
|
|
CM_R_7_MANU(pipep->ssp),
|
|
CM_R_7_REV(pipep->ssp));
|
|
driver->ser_sts.chi_flags |= CHI_FLAG_CODEC_RPTD;
|
|
}
|
|
break;
|
|
|
|
case DBRI_PIPE_CHI:
|
|
case DBRI_PIPE_DM_T_5_8:
|
|
case DBRI_PIPE_CM_T_1_4:
|
|
case DBRI_PIPE_SB_PAL_T:
|
|
aprintf("dbri_fxdt_intr: XMIT? pipe %d, data 0x%x\n",
|
|
intr.f.channel, (unsigned int)intr.f.field);
|
|
break;
|
|
|
|
case DBRI_PIPE_DM_R_5_6:
|
|
pipe = intr.f.channel;
|
|
pipep = &driver->ptab[intr.f.channel];
|
|
/*
|
|
* XX5 - non intuitive code alert
|
|
* the receive pipe length for this pipe is only 10, but
|
|
* we reverse 16 bits - this allows us to put the correct
|
|
* data into word16 - just don't look at the other 6 bits
|
|
* since they're not valid
|
|
*/
|
|
tmp = reverse_bit_order(intr.code_fxdt.changed_data, 16);
|
|
/* Check for no change; DBRI-V1 bug #110 and V3 #7 and V5 #? */
|
|
if (pipep->ssp == tmp)
|
|
break;
|
|
pipep->ssp = tmp;
|
|
/*
|
|
* Once at max attenuation and outputs have been muted,
|
|
* we can go into control mode. Note that the CODEC may
|
|
* not really be at max attenuation since they reflect
|
|
* it in the data stream before it actually happens, but
|
|
* we should at least be muted which is what we care about
|
|
*/
|
|
aprintf("fxdt_intr: DM_R %d, val=0x%x\n", pipe, tmp);
|
|
if (driver->ser_sts.chi_state == CHI_GOING_TO_CTRL_MODE) {
|
|
mmdata.word16[0] = tmp;
|
|
if (!mmdata.r.om1 && !mmdata.r.om0 && !mmdata.r.sm)
|
|
mmcodec_setup_ctrl_mode(driver);
|
|
}
|
|
break;
|
|
|
|
case DBRI_PIPE_DM_R_7_8:
|
|
/* This pipe is no longer used */
|
|
break;
|
|
|
|
case DBRI_PIPE_CM_R_1_2:
|
|
case DBRI_PIPE_CM_R_3_4:
|
|
pipe = intr.f.channel;
|
|
pipep = &driver->ptab[intr.f.channel];
|
|
pipep->ssp = reverse_bit_order(intr.code_fxdt.changed_data, 16);
|
|
if (pipe == DBRI_PIPE_CM_R_1_2)
|
|
pipep->ssp &= CM_R_1_2_MASK;
|
|
else if (pipe == DBRI_PIPE_CM_R_3_4)
|
|
pipep->ssp &= CM_R_3_4_MASK;
|
|
else
|
|
return;
|
|
|
|
t14d = driver->ptab[DBRI_PIPE_CM_T_1_4].ssp;
|
|
r12d = driver->ptab[DBRI_PIPE_CM_R_1_2].ssp;
|
|
r34d = driver->ptab[DBRI_PIPE_CM_R_3_4].ssp;
|
|
aprintf("t14d=0x%x, r12d=0x%x, r34d=0x%x\n", t14d, r12d, r34d);
|
|
|
|
/*
|
|
* 7) Wait for DCB from MMCODEC to go low
|
|
* Must check all bits as they come back to make sure
|
|
* they're *all* correct
|
|
*/
|
|
if (driver->ser_sts.chi_state == CHI_WAIT_DCB_LOW) {
|
|
if (((t14d & CM_R_3_4_MASK) != r34d) ||
|
|
(((t14d >> 16) & CM_R_1_2_MASK) != r12d)) {
|
|
aprintf("dcb not low yet\n");
|
|
return;
|
|
}
|
|
aprintf("dbri_fxdt_intr: have DCB low\n");
|
|
|
|
/*
|
|
* 8) Set DCB bit to MMCODEC high
|
|
*/
|
|
mmctrl.word32[0] = t14d;
|
|
mmctrl.r.dcb = MMCODEC_DCB;
|
|
|
|
pipe = DBRI_PIPE_CM_T_1_4;
|
|
pipep = &driver->ptab[pipe];
|
|
cmd.cmd_wd1 = DBRI_CMD_SSP | DBRI_SSP_PIPE(pipe);
|
|
pipep->ssp = mmctrl.word32[0];
|
|
cmd.cmd_wd2 = reverse_bit_order(pipep->ssp,
|
|
CM_T_1_4_LEN);
|
|
ATRACEI(dbri_fxdt_intr, 'SSP ', cmd.cmd_wd2);
|
|
dbri_command(driver, cmd);
|
|
|
|
driver->ser_sts.chi_state = CHI_WAIT_DCB_HIGH;
|
|
|
|
/*
|
|
* 9) Wait for DCB from MMCODEC to go high
|
|
* Due to Analog devices chip bug (which isn't a bug cause
|
|
* we changed the spec for them), all bits after DCB are
|
|
* invalid so we can't check for them - must isolate DCB bit.
|
|
*/
|
|
} else if (driver->ser_sts.chi_state == CHI_WAIT_DCB_HIGH) {
|
|
mmctrl.word16[0] = r12d;
|
|
if (!mmctrl.r.dcb) {
|
|
/*
|
|
* XXX - this is actually very bad, if
|
|
* we're going from low to high, we're
|
|
* only going to get one interrupt on
|
|
* #21, so if we're not straight now, we
|
|
* never will be; Unfortunately, DBRI
|
|
* randomly gives us extra fxdt interrupts
|
|
* so this is not true.
|
|
*/
|
|
aprintf("dcb not high yet!!\n");
|
|
return;
|
|
}
|
|
aprintf("dbri_fxdt_intr: have DCB high\n");
|
|
/*
|
|
* 10) Discontinue xmitting and receiving control info
|
|
*/
|
|
/*
|
|
* 11) Stop driving FSYNC and SCLK
|
|
*/
|
|
driver->ser_sts.chi_state = CHI_GOING_TO_DATA_MODE;
|
|
|
|
/*
|
|
* XXX This is not quite right for monitor pipes
|
|
* There could be a unidirectional audio stream as well
|
|
* as a bidirectional audio stream, thus the check
|
|
* should be modified.
|
|
*/
|
|
|
|
as_input = &driver->ioc[(int)DBRI_AUDIO_IN].as;
|
|
as_output = &driver->ioc[(int)DBRI_AUDIO_OUT].as;
|
|
if (driver->ser_sts.chi_flags & CHI_FLAG_SER_STS) {
|
|
mmcodec_setup_chi(driver, SETUPCHI_DBRI_MASTER);
|
|
} else { /* /dev/audio opened directly */
|
|
mmcodec_setup_chi(driver,SETUPCHI_CODEC_MASTER);
|
|
}
|
|
} else {
|
|
aprintf("dbri_fxdt_intr: CM R Intr state=%u\n",
|
|
driver->ser_sts.chi_state);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
(void) printf("dbri_fxdt_intr: DEFAULT! pipe=%d, data=0x%x\n",
|
|
intr.f.channel, (unsigned int)intr.f.field);
|
|
break;
|
|
|
|
} /* switch on channel */
|
|
|
|
return;
|
|
} /* dbri_fxdt_intr */
|
|
|
|
|
|
/*
|
|
* dbri_chil_intr - handle interrupts from the CHI serial interface; this
|
|
* routine only gets called if the channel indicates CHI (36)
|
|
*/
|
|
void
|
|
dbri_chil_intr(driver, intr)
|
|
dbri_dev_t *driver;
|
|
dbri_intrq_ent_t intr;
|
|
{
|
|
serial_status_t *serialp;
|
|
dbri_stream_t *dsp;
|
|
dbri_chip_cmd_t cmd;
|
|
|
|
serialp = &driver->ser_sts;
|
|
|
|
aprintf("dbri_chil_intr: state=%d, channel=%d, field=0x%x\n",
|
|
driver->ser_sts.chi_state, intr.f.channel, intr.f.field);
|
|
if (intr.code_chil.overflow) {
|
|
eprintf("CHI receiver overflow/error\n");
|
|
/*
|
|
* must disable and then reenable the CHI receiver
|
|
* to get things going again
|
|
*/
|
|
cmd.cmd_wd1 = serialp->chi_cdm;
|
|
cmd.cmd_wd1 &= ~(DBRI_CDM_REN);
|
|
dbri_command(driver, cmd);
|
|
cmd.cmd_wd1 = serialp->chi_cdm;
|
|
dbri_command(driver, cmd);
|
|
|
|
dsp = &driver->ioc[(int)DBRI_AUDIO_IN];
|
|
dsp->audio_uflow = TRUE;
|
|
}
|
|
switch (driver->ser_sts.chi_state) {
|
|
case CHI_GOING_TO_DATA_MODE:
|
|
/*
|
|
* Second CHIL - to setup data mode again
|
|
*/
|
|
driver->ser_sts.chi_state = CHI_CODEC_CALIBRATION;
|
|
mmcodec_setup_data_mode(driver);
|
|
break;
|
|
case CHI_WAIT_DCB_LOW:
|
|
/*
|
|
* First CHIL - to setup control mode
|
|
*/
|
|
break;
|
|
default:
|
|
/*
|
|
* XXX5 - V5 keeps issuing CHIL interrupts forever when
|
|
* the SB gets unplugged - must issue a CHI command to
|
|
* disable it; Since the CODEC handshake changes Clock
|
|
* masters twice (one to DBRI master and one to MMCODEC
|
|
* master), we could *potentially* get a "CHIL nofs"
|
|
* interrupt for each if each change between the 2 left
|
|
* no frame sync for 125us. So we don't know whether the
|
|
* SB is actually unplugged until we get the 3rd "CHIL nofs".
|
|
* This assumes a "quick" system - we have noticed that
|
|
* on a busy system, the hand shake can take a long time
|
|
* so we come up with some arbitrarily high number (like 10)
|
|
* to wait ....
|
|
* Issuing the CHI command with the interrupt bit off disables
|
|
* the interrupts ONCE dbri has actually executed it, hence
|
|
* the check for state.
|
|
*/
|
|
if ((intr.code_chil.nofs) &&
|
|
(driver->ser_sts.chi_state != CHI_NO_DEVICES)) {
|
|
|
|
driver->ser_sts.chi_nofs++;
|
|
if (driver->ser_sts.chi_nofs > MMCODEC_NOFS_THRESHOLD) {
|
|
(void) printf("Audio: speakerbox unplugged\n");
|
|
cmd.cmd_wd1 = serialp->chi_cmd;
|
|
cmd.cmd_wd1 &= ~(DBRI_CHI_CHIL);
|
|
dbri_command(driver, cmd);
|
|
driver->ser_sts.chi_state = CHI_NO_DEVICES;
|
|
/* Don't drive PIO lines - use defaults */
|
|
driver->chip->n.pio = 0;
|
|
/*
|
|
* XXX5 - if we're in the middle of a write,
|
|
* we should send up an M_ERROR
|
|
*/
|
|
}
|
|
}
|
|
break;
|
|
} /* switch */
|
|
|
|
return;
|
|
} /* dbri_chil_intr */
|
|
|
|
|
|
unsigned int
|
|
get_aud_pipe_cycle(as, pipe, channel)
|
|
aud_stream_t *as;
|
|
unsigned int pipe;
|
|
dbri_chnl_t channel;
|
|
{
|
|
unsigned int cycle;
|
|
dbri_dev_t *driver = (dbri_dev_t *)as->v->devdata;
|
|
|
|
#ifdef lint
|
|
pipe = pipe;
|
|
driver = driver;
|
|
#endif lint
|
|
|
|
cycle = -1; /* a very large number */
|
|
/*
|
|
* We must return different values depending on the presence of
|
|
* speakerbox and whether or not this is an xmit or recv pipe
|
|
*/
|
|
switch (channel) {
|
|
case DBRI_AUDIO_OUT:
|
|
cycle = ((unsigned int)
|
|
(driver->ser_sts.pal_present ?
|
|
SB_PAL_LEN : CHI_DATA_MODE_LEN));
|
|
break;
|
|
case DBRI_AUDIO_IN:
|
|
cycle = (unsigned int) driver->ser_sts.mmcodec_ts_start;
|
|
break;
|
|
default:
|
|
(void) printf("get_aud_pipe_cycle: bad channel!!\n");
|
|
break;
|
|
} /* switch */
|
|
return (cycle);
|
|
} /* get_aud_pipe_cycle */
|
|
|
|
|
|
unsigned int
|
|
get_aud_pipe_length(as, pipe, channel)
|
|
aud_stream_t *as;
|
|
unsigned int pipe;
|
|
dbri_chnl_t channel;
|
|
{
|
|
unsigned int length;
|
|
dbri_dev_t *driver = (dbri_dev_t *)as->v->devdata;
|
|
mmcodec_ctrl_t mmctrl;
|
|
|
|
#ifdef lint
|
|
pipe = pipe;
|
|
channel = channel;
|
|
#endif lint
|
|
|
|
length = -1; /* only to be compatible with cycle above */
|
|
mmctrl.word32[0] = driver->ptab[DBRI_PIPE_CM_T_1_4].ssp;
|
|
|
|
/*
|
|
* As I see it, we return either 8, 16, or 32 and nothing else
|
|
* Right? 8 bit stereo, is uninteresting as Ben says .... Notice
|
|
* length is *not* direction dependent.
|
|
*/
|
|
if ((mmctrl.r.df == MMCODEC_DF_ULAW) ||
|
|
(mmctrl.r.df == MMCODEC_DF_ALAW)) {
|
|
length = 8;
|
|
} else if (mmctrl.r.df == MMCODEC_DF_16_BIT) {
|
|
if (mmctrl.r.st == MMCODEC_ST_MONO)
|
|
length = 16;
|
|
else
|
|
length = 32;
|
|
}
|
|
|
|
return (length);
|
|
} /* get_aud_pipe_length */
|