Add the driver for CMI8738 sound card

Change-Id: I5a53e3b8652a014e105ca7c77c42a730f6824307
This commit is contained in:
Jia-Ju Bai 2017-01-04 12:37:31 +00:00 committed by David van Moolenbroek
parent 6dd801ffb8
commit 37e23b1cc7
12 changed files with 1160 additions and 0 deletions

View File

@ -8,6 +8,7 @@
./etc/system.conf.d/3c90x minix-base
./etc/system.conf.d/amddev minix-base
./etc/system.conf.d/atl2 minix-base
./etc/system.conf.d/cmi8738 minix-base
./etc/system.conf.d/dec21140A minix-base
./etc/system.conf.d/dp8390 minix-base
./etc/system.conf.d/dpeth minix-base
@ -31,6 +32,7 @@
./service/amddev minix-base
./service/at_wini minix-base
./service/atl2 minix-base
./service/cmi8738 minix-base
./service/dec21140A minix-base
./service/dp8390 minix-base
./service/dpeth minix-base

View File

@ -16,6 +16,7 @@
./usr/libdata/debug/service/amddev.debug minix-debug debug
./usr/libdata/debug/service/at_wini.debug minix-debug debug
./usr/libdata/debug/service/atl2.debug minix-debug debug
./usr/libdata/debug/service/cmi8738.debug minix-debug debug
./usr/libdata/debug/service/dec21140A.debug minix-debug debug
./usr/libdata/debug/service/dp8390.debug minix-debug debug
./usr/libdata/debug/service/dpeth.debug minix-debug debug

View File

@ -1,6 +1,7 @@
.include <bsd.own.mk>
.if ${MACHINE_ARCH} == "i386"
SUBDIR+= cmi8738
SUBDIR+= es1370
SUBDIR+= es1371
SUBDIR+= sb16

View File

@ -0,0 +1,12 @@
# Makefile for the CMI8738 driver
PROG= cmi8738
SRCS= cmi8738.c mixer.c
FILES=${PROG}.conf
FILESNAME=${PROG}
FILESDIR= /etc/system.conf.d
DPADD+= ${LIBAUDIODRIVER} ${LIBCHARDRIVER} ${LIBSYS}
LDADD+= -laudiodriver -lchardriver -lsys
.include <minix.service.mk>

View File

@ -0,0 +1,7 @@
The cmi8738 driver is for C-Media 8738/8768 sound card.
This driver is referred to Minix(3.4.0) es1371 driver,
Linux(4.2.1) snd_cmipci driver and CMI8738 Audio Specification(Rev 2.2).
Revision 1.0 2016/12/16
Authored by Jia-Ju Bai <baijiaju1990@163.com>

View File

@ -0,0 +1,595 @@
#include "cmi8738.h"
#include "mixer.h"
/* global value */
DEV_STRUCT dev;
aud_sub_dev_conf_t aud_conf[3];
sub_dev_t sub_dev[3];
special_file_t special_file[3];
drv_t drv;
/* internal function */
static int dev_probe(void);
static int set_sample_rate(u32_t rate, int num);
static int set_stereo(u32_t stereo, int num);
static int set_bits(u32_t bits, int sub_dev);
static int set_frag_size(u32_t frag_size, int num);
static int set_sign(u32_t val, int num);
static int get_frag_size(u32_t *val, int *len, int num);
static int free_buf(u32_t *val, int *len, int num);
/* developer interface */
static int dev_reset(u32_t *base);
static void dev_configure(u32_t *base);
static void dev_init_mixer(u32_t *base);
static void dev_set_sample_rate(u32_t *base, u16_t sample_rate);
static void dev_set_format(u32_t *base, u32_t bits, u32_t sign,
u32_t stereo, u32_t sample_count);
static void dev_start_channel(u32_t *base, int sub_dev);
static void dev_stop_channel(u32_t *base, int sub_dev);
static void dev_set_dma(u32_t *base, u32_t dma, u32_t len, int sub_dev);
static u32_t dev_read_dma_current(u32_t *base, int sub_dev);
static void dev_pause_dma(u32_t *base, int sub_dev);
static void dev_resume_dma(u32_t *base, int sub_dev);
static void dev_intr_other(u32_t *base, u32_t status);
static u32_t dev_read_clear_intr_status(u32_t *base);
static void dev_intr_enable(u32_t *base, int flag);
/* ======= Developer implemented function ======= */
/* ====== Self-defined function ====== */
void dev_io_set_clear(u32_t base, u32_t reg, u32_t val, int flag) {
u32_t data;
data = sdr_in32(base, reg);
if (flag == 0)
data &= ~val;
else if (flag == 1)
data |= val;
sdr_out32(base, reg, data);
}
/* ====== Mixer handling interface ======*/
/* Write the data to mixer register (### WRITE_MIXER_REG ###) */
void dev_mixer_write(u32_t *base, u32_t reg, u32_t val) {
u32_t base0 = base[0];
sdr_out8(base0, REG_SB_ADDR, reg);
sdr_out8(base0, REG_SB_DATA, val);
}
/* Read the data from mixer register (### READ_MIXER_REG ###) */
u32_t dev_mixer_read(u32_t *base, u32_t reg) {
u32_t base0 = base[0];
sdr_out8(base0, REG_SB_ADDR, reg);
return sdr_in8(base0, REG_SB_DATA);
}
/* ====== Developer interface ======*/
/* Reset the device (### RESET_HARDWARE_CAN_FAIL ###)
* -- Return OK means success, Others means failure */
static int dev_reset(u32_t *base) {
u32_t data, base0 = base[0];
dev_io_set_clear(base0, REG_MISC_CTRL, CMD_POWER_DOWN, 0);
dev_io_set_clear(base0, REG_MISC_CTRL, CMD_RESET, 1);
micro_delay(100);
dev_io_set_clear(base0, REG_MISC_CTRL, CMD_RESET, 0);
return OK;
}
/* Configure hardware registers (### CONF_HARDWARE ###) */
static void dev_configure(u32_t *base) {
u32_t data, base0 = base[0];
dev_io_set_clear(base0, REG_FUNC_CTRL, CMD_ADC_C0, 0);
dev_io_set_clear(base0, REG_FUNC_CTRL, CMD_ADC_C1, 1);
dev_io_set_clear(base0, REG_MISC_CTRL, CMD_N4SPK3D, 1);
dev_io_set_clear(base0, REG_FUNC_CTRL1, CMD_SPDIF_ENA, 0);
dev_io_set_clear(base0, REG_FUNC_CTRL1, CMD_SPDIF_LOOP, 0);
sdr_out8(base0, REG_EXT_INDEX, 0x03);
sdr_out8(base0, REG_MIX_INPUT, 0x0f);
}
/* Initialize the mixer (### INIT_MIXER ###) */
static void dev_init_mixer(u32_t *base) {
dev_mixer_write(base, 0, 0);
dev_mixer_write(base, MIXER_ADCL, 0x1f);
dev_mixer_write(base, MIXER_ADCR, 0x7f);
dev_mixer_write(base, MIXER_OUT_MUTE, 0x7f);
}
/* Set DAC and ADC sample rate (### SET_SAMPLE_RATE ###) */
static void dev_set_sample_rate(u32_t *base, u16_t sample_rate) {
int i;
u32_t data, rate = 0, base0 = base[0];
for (i = 0; i < 8; i++) {
if (sample_rate == g_sample_rate[i]) {
rate = i;
break;
}
}
data = sdr_in32(base0, REG_FUNC_CTRL1);
data &=~ (0xe000 | 0x1c00);
data |= (rate << 13) & 0xe000;
data |= (rate << 10) & 0x1c00;
sdr_out32(base0, REG_FUNC_CTRL1, data);
}
/* Set DAC and ADC format (### SET_FORMAT ###)*/
static void dev_set_format(u32_t *base, u32_t bits, u32_t sign,
u32_t stereo, u32_t sample_count) {
u32_t format = 0, data, base0 = base[0];
if (stereo == 1)
format |= FMT_STEREO;
if (bits == 16)
format |= FMT_BIT16;
data = sdr_in32(base0, REG_FORMAT);
data &= ~0x00000003;
data |= format << 0;
data &= ~0x0000000c;
data |= format << 2;
sdr_out32(base0, REG_FORMAT, data);
dev_io_set_clear(base0, REG_EXT_MISC, 0x10000000, 0);
sdr_out16(base0, REG_DAC_SAMPLE_COUNT, sample_count - 1);
sdr_out16(base0, REG_ADC_SAMPLE_COUNT, sample_count - 1);
}
/* Start the channel (### START_CHANNEL ###) */
static void dev_start_channel(u32_t *base, int sub_dev) {
u32_t data, base0 = base[0];
if (sub_dev == DAC) {
dev_io_set_clear(base0, REG_FUNC_CTRL, CMD_ENA_C0, 1);
}
else if (sub_dev == ADC) {
dev_io_set_clear(base0, REG_FUNC_CTRL, CMD_ENA_C1, 1);
}
}
/* Stop the channel (### STOP_CHANNEL ###) */
static void dev_stop_channel(u32_t *base, int sub_dev) {
u32_t data, base0 = base[0];
if (sub_dev == DAC) {
dev_io_set_clear(base0, REG_FUNC_CTRL, CMD_ENA_C0, 0);
dev_io_set_clear(base0, REG_FUNC_CTRL, CMD_RESET_C0, 1);
micro_delay(100);
dev_io_set_clear(base0, REG_FUNC_CTRL, CMD_RESET_C0, 0);
}
else if (sub_dev == ADC) {
dev_io_set_clear(base0, REG_FUNC_CTRL, CMD_ENA_C1, 0);
dev_io_set_clear(base0, REG_FUNC_CTRL, CMD_RESET_C1, 1);
micro_delay(100);
dev_io_set_clear(base0, REG_FUNC_CTRL, CMD_RESET_C1, 0);
}
}
/* Set DMA address and length (### SET_DMA ###) */
static void dev_set_dma(u32_t *base, u32_t dma, u32_t len, int sub_dev) {
u32_t base0 = base[0];
if (sub_dev == DAC) {
sdr_out32(base0, REG_DAC_DMA_ADDR, dma);
sdr_out16(base0, REG_DAC_DMA_LEN, len - 1);
}
else if (sub_dev == ADC) {
sdr_out32(base0, REG_ADC_DMA_ADDR, dma);
sdr_out16(base0, REG_ADC_DMA_LEN, len - 1);
}
}
/* Read current address (### READ_DMA_CURRENT_ADDR ###) */
static u32_t dev_read_dma_current(u32_t *base, int sub_dev) {
u32_t data, base0 = base[0];
if (sub_dev == DAC)
data = sdr_in16(base0, REG_DAC_CUR_ADDR);
else if (sub_dev == ADC)
data = sdr_in16(base0, REG_ADC_CUR_ADDR);
return data;
}
/* Pause the DMA (### PAUSE_DMA ###) */
static void dev_pause_dma(u32_t *base, int sub_dev) {
u32_t base0 = base[0];
if (sub_dev == DAC)
dev_io_set_clear(base0, REG_FUNC_CTRL, CMD_PAUSE_C0, 1);
else if (sub_dev == ADC)
dev_io_set_clear(base0, REG_FUNC_CTRL, CMD_PAUSE_C1, 1);
}
/* Resume the DMA (### RESUME_DMA ###) */
static void dev_resume_dma(u32_t *base, int sub_dev) {
u32_t base0 = base[0];
if (sub_dev == DAC)
dev_io_set_clear(base0, REG_FUNC_CTRL, CMD_PAUSE_C0, 0);
else if (sub_dev == ADC)
dev_io_set_clear(base0, REG_FUNC_CTRL, CMD_PAUSE_C1, 0);
}
/* Read and clear interrupt status (### READ_CLEAR_INTR_STS ###)
* -- Return interrupt status */
static u32_t dev_read_clear_intr_status(u32_t *base) {
u32_t data, base0 = base[0];
data = sdr_in32(base0, REG_INTR_STS);
dev_intr_enable(base, INTR_DISABLE);
dev_intr_enable(base, INTR_ENABLE);
return data;
}
/* Enable or disable interrupt (### INTR_ENBALE_DISABLE ###) */
static void dev_intr_enable(u32_t *base, int flag) {
u32_t data, base0 = base[0];
data = sdr_in32(base0, REG_INTR_STS);
if (flag == INTR_ENABLE)
sdr_out32(base0, REG_INTR_CTRL, data | CMD_INTR_ENABLE);
else if (flag == INTR_DISABLE)
sdr_out32(base0, REG_INTR_CTRL, data & ~CMD_INTR_ENABLE);
}
/* ======= Common driver function ======= */
/* Probe the device */
static int dev_probe(void) {
int devind, i, ioflag;
u32_t device, bar, size, base;
u16_t vid, did, temp;
u8_t *reg;
pci_init();
device = pci_first_dev(&devind, &vid, &did);
while (device > 0) {
if (vid == VENDOR_ID && did == DEVICE_ID)
break;
device = pci_next_dev(&devind, &vid, &did);
}
if (vid != VENDOR_ID || did != DEVICE_ID)
return EIO;
pci_reserve(devind);
for (i = 0; i < 6; i++)
dev.base[i] = 0;
#ifdef DMA_BASE_IOMAP
for (i = 0; i < 6; i++) {
if (pci_get_bar(devind, PCI_BAR + i * 4, &base, &size, &ioflag)) {
/* printf("SDR: Fail to get PCI BAR %d\n", i); */
continue;
}
if (ioflag) {
/* printf("SDR: PCI BAR %d is not for memory\n", i); */
continue;
}
if ((reg = vm_map_phys(SELF, (void *)base, size)) == MAP_FAILED) {
printf("SDR: Fail to map hardware registers from PCI\n");
return -EIO;
}
dev.base[i] = (u32_t)reg;
}
#else
/* Get PCI BAR0-5 */
for (i = 0; i < 6; i++)
dev.base[i] = pci_attr_r32(devind, PCI_BAR + i * 4) & 0xffffffe0;
#endif
dev.name = pci_dev_name(vid, did);
dev.irq = pci_attr_r8(devind, PCI_ILR);
dev.revision = pci_attr_r8(devind, PCI_REV);
dev.did = did;
dev.vid = vid;
dev.devind = devind;
temp = pci_attr_r16(devind, PCI_CR);
pci_attr_w16(devind, PCI_CR, temp | 0x105);
#ifdef MY_DEBUG
printf("SDR: Hardware name is %s\n", dev.name);
for (i = 0; i < 6; i++)
printf("SDR: PCI BAR%d is 0x%08x\n", i, dev.base[i]);
printf("SDR: IRQ number is 0x%02x\n", dev.irq);
#endif
return OK;
}
/* Set sample rate in configuration */
static int set_sample_rate(u32_t rate, int num) {
aud_conf[num].sample_rate = rate;
return OK;
}
/* Set stereo in configuration */
static int set_stereo(u32_t stereo, int num) {
aud_conf[num].stereo = stereo;
return OK;
}
/* Set sample bits in configuration */
static int set_bits(u32_t bits, int num) {
aud_conf[num].nr_of_bits = bits;
return OK;
}
/* Set fragment size in configuration */
static int set_frag_size(u32_t frag_size, int num) {
if (frag_size > (sub_dev[num].DmaSize / sub_dev[num].NrOfDmaFragments) ||
frag_size < sub_dev[num].MinFragmentSize) {
return EINVAL;
}
aud_conf[num].fragment_size = frag_size;
return OK;
}
/* Set frame sign in configuration */
static int set_sign(u32_t val, int num) {
aud_conf[num].sign = val;
return OK;
}
/* Get maximum fragment size */
static int get_max_frag_size(u32_t *val, int *len, int num) {
*len = sizeof(*val);
*val = (sub_dev[num].DmaSize / sub_dev[num].NrOfDmaFragments);
return OK;
}
/* Return 1 if there are free buffers */
static int free_buf(u32_t *val, int *len, int num) {
*len = sizeof(*val);
if (sub_dev[num].BufLength == sub_dev[num].NrOfExtraBuffers)
*val = 0;
else
*val = 1;
return OK;
}
/* Get the current sample counter */
static int get_samples_in_buf(u32_t *result, int *len, int chan) {
u32_t res;
/* READ_DMA_CURRENT_ADDR */
res = dev_read_dma_current(dev.base, chan);
*result = (u32_t)(sub_dev[chan].BufLength * 8192) + res;
return OK;
}
/* ======= [Audio interface] Initialize data structure ======= */
int drv_init(void) {
drv.DriverName = DRIVER_NAME;
drv.NrOfSubDevices = 3;
drv.NrOfSpecialFiles = 3;
sub_dev[DAC].readable = 0;
sub_dev[DAC].writable = 1;
sub_dev[DAC].DmaSize = 64 * 1024;
sub_dev[DAC].NrOfDmaFragments = 2;
sub_dev[DAC].MinFragmentSize = 1024;
sub_dev[DAC].NrOfExtraBuffers = 4;
sub_dev[ADC].readable = 1;
sub_dev[ADC].writable = 0;
sub_dev[ADC].DmaSize = 64 * 1024;
sub_dev[ADC].NrOfDmaFragments = 2;
sub_dev[ADC].MinFragmentSize = 1024;
sub_dev[ADC].NrOfExtraBuffers = 4;
sub_dev[MIX].writable = 0;
sub_dev[MIX].readable = 0;
special_file[0].minor_dev_nr = 0;
special_file[0].write_chan = DAC;
special_file[0].read_chan = NO_CHANNEL;
special_file[0].io_ctl = DAC;
special_file[1].minor_dev_nr = 1;
special_file[1].write_chan = NO_CHANNEL;
special_file[1].read_chan = ADC;
special_file[1].io_ctl = ADC;
special_file[2].minor_dev_nr = 2;
special_file[2].write_chan = NO_CHANNEL;
special_file[2].read_chan = NO_CHANNEL;
special_file[2].io_ctl = MIX;
return OK;
}
/* ======= [Audio interface] Initialize hardware ======= */
int drv_init_hw(void) {
int i;
/* Match the device */
if (dev_probe()) {
printf("SDR: No sound card found\n");
return EIO;
}
/* Reset the device */
/* ### RESET_HARDWARE_CAN_FAIL ### */
if (dev_reset(dev.base)) {
printf("SDR: Fail to reset the device\n");
return EIO;
}
/* Configure the hardware */
/* ### CONF_HARDWARE ### */
dev_configure(dev.base);
/* Initialize the mixer */
/* ### INIT_MIXER ### */
dev_init_mixer(dev.base);
/* Set default mixer volume */
dev_set_default_volume(dev.base);
/* Initialize subdevice data */
for (i = 0; i < drv.NrOfSubDevices; i++) {
if (i == MIX)
continue;
aud_conf[i].busy = 0;
aud_conf[i].stereo = 1;
aud_conf[i].sample_rate = 44100;
aud_conf[i].nr_of_bits = 16;
aud_conf[i].sign = 1;
aud_conf[i].fragment_size =
sub_dev[i].DmaSize / sub_dev[i].NrOfDmaFragments;
}
return OK;
}
/* ======= [Audio interface] Driver reset =======*/
int drv_reset(void) {
/* ### RESET_HARDWARE_CAN_FAIL ### */
return dev_reset(dev.base);
}
/* ======= [Audio interface] Driver start ======= */
int drv_start(int sub_dev, int DmaMode) {
int sample_count;
/* Set DAC and ADC sample rate */
/* ### SET_SAMPLE_RATE ### */
dev_set_sample_rate(dev.base, aud_conf[sub_dev].sample_rate);
sample_count = aud_conf[sub_dev].fragment_size;
#ifdef DMA_LENGTH_BY_FRAME
sample_count = sample_count / (aud_conf[sub_dev].nr_of_bits * (aud_conf[sub_dev].stereo + 1) / 8);
#endif
/* Set DAC and ADC format */
/* ### SET_FORMAT ### */
dev_set_format(dev.base, aud_conf[sub_dev].nr_of_bits,
aud_conf[sub_dev].sign, aud_conf[sub_dev].stereo, sample_count);
drv_reenable_int(sub_dev);
/* Start the channel */
/* ### START_CHANNEL ### */
dev_start_channel(dev.base, sub_dev);
aud_conf[sub_dev].busy = 1;
return OK;
}
/* ======= [Audio interface] Driver start ======= */
int drv_stop(int sub_dev) {
u32_t data;
/* INTR_ENABLE_DISABLE */
dev_intr_enable(dev.base, INTR_DISABLE);
/* ### STOP_CHANNEL ### */
dev_stop_channel(dev.base, sub_dev);
aud_conf[sub_dev].busy = 0;
return OK;
}
/* ======= [Audio interface] Enable interrupt ======= */
int drv_reenable_int(int chan) {
/* INTR_ENABLE_DISABLE */
dev_intr_enable(dev.base, INTR_ENABLE);
return OK;
}
/* ======= [Audio interface] I/O control ======= */
int drv_io_ctl(unsigned long request, void *val, int *len, int sub_dev) {
int status;
switch (request) {
case DSPIORATE:
status = set_sample_rate(*((u32_t *)val), sub_dev);
break;
case DSPIOSTEREO:
status = set_stereo(*((u32_t *)val), sub_dev);
break;
case DSPIOBITS:
status = set_bits(*((u32_t *)val), sub_dev);
break;
case DSPIOSIZE:
status = set_frag_size(*((u32_t *)val), sub_dev);
break;
case DSPIOSIGN:
status = set_sign(*((u32_t *)val), sub_dev);
break;
case DSPIOMAX:
status = get_max_frag_size(val, len, sub_dev);
break;
case DSPIORESET:
status = drv_reset();
break;
case DSPIOFREEBUF:
status = free_buf(val, len, sub_dev);
break;
case DSPIOSAMPLESINBUF:
status = get_samples_in_buf(val, len, sub_dev);
break;
case DSPIOPAUSE:
status = drv_pause(sub_dev);
break;
case DSPIORESUME:
status = drv_resume(sub_dev);
break;
case MIXIOGETVOLUME:
/* ### GET_SET_VOLUME ### */
status = get_set_volume(dev.base, val, GET_VOL);
break;
case MIXIOSETVOLUME:
/* ### GET_SET_VOLUME ### */
status = get_set_volume(dev.base, val, SET_VOL);
break;
default:
status = EINVAL;
break;
}
return status;
}
/* ======= [Audio interface] Get request number ======= */
int drv_get_irq(char *irq) {
*irq = dev.irq;
return OK;
}
/* ======= [Audio interface] Get fragment size ======= */
int drv_get_frag_size(u32_t *frag_size, int sub_dev) {
*frag_size = aud_conf[sub_dev].fragment_size;
return OK;
}
/* ======= [Audio interface] Set DMA channel ======= */
int drv_set_dma(u32_t dma, u32_t length, int chan) {
#ifdef DMA_LENGTH_BY_FRAME
length = length / (aud_conf[chan].nr_of_bits * (aud_conf[chan].stereo + 1) / 8);
#endif
/* ### SET_DMA ### */
dev_set_dma(dev.base, dma, length, chan);
return OK;
}
/* ======= [Audio interface] Get interrupt summary status ======= */
int drv_int_sum(void) {
u32_t status;
/* ### READ_CLEAR_INTR_STS ### */
status = dev_read_clear_intr_status(dev.base);
dev.intr_status = status;
#ifdef MY_DEBUG
printf("SDR: Interrupt status is 0x%08x\n", status);
#endif
return (status & (INTR_STS_DAC | INTR_STS_ADC));
}
/* ======= [Audio interface] Handle interrupt status ======= */
int drv_int(int sub_dev) {
u32_t mask;
/* ### CHECK_INTR_DAC ### */
if (sub_dev == DAC)
mask = INTR_STS_DAC;
/* ### CHECK_INTR_ADC ### */
else if (sub_dev == ADC)
mask = INTR_STS_ADC;
else
return 0;
return dev.intr_status & mask;
}
/* ======= [Audio interface] Pause DMA ======= */
int drv_pause(int sub_dev) {
/* ### PAUSE_DMA ### */
dev_pause_dma(dev.base, sub_dev);
return OK;
}
/* ======= [Audio interface] Resume DMA ======= */
int drv_resume(int sub_dev) {
/* ### RESUME_DMA ### */
dev_resume_dma(dev.base, sub_dev);
return OK;
}

View File

@ -0,0 +1,10 @@
service cmi8738
{
system
UMAP # 14
IRQCTL # 19
DEVIO # 21
;
pci device 13f6:0111;
};

View File

@ -0,0 +1,113 @@
#ifndef _SDR_H
#define _SDR_H
/* ======= General Parameter ======= */
/* Global configure */
#define DMA_LENGTH_BY_FRAME
#define MIXER_SB16
#include <minix/audio_fw.h>
#include <sys/types.h>
#include <sys/ioc_sound.h>
#include <minix/sound.h>
#include <machine/pci.h>
#include <sys/mman.h>
#include "io.h"
/* Subdevice type */
#define DAC 0
#define ADC 1
#define MIX 2
/* PCI number and driver name */
#define VENDOR_ID 0x13f6
#define DEVICE_ID 0x0111
#define DRIVER_NAME "CMI8738"
/* Volume option */
#define GET_VOL 0
#define SET_VOL 1
/* Interrupt control */
#define INTR_ENABLE 1
#define INTR_DISABLE 0
/* Interrupt status */
#define INTR_STS_DAC 0x00000001
#define INTR_STS_ADC 0x00000002
/* ======= Self-defined Parameter ======= */
#define REG_FUNC_CTRL 0x00
#define REG_FUNC_CTRL1 0x04
#define REG_FORMAT 0x08
#define REG_MISC_CTRL 0x18
#define REG_SB_DATA 0x22
#define REG_SB_ADDR 0x23
#define REG_MIX_INPUT 0x25
#define REG_EXT_MISC 0x90
#define REG_DAC_SAMPLE_COUNT 0x86
#define REG_ADC_SAMPLE_COUNT 0x8e
#define REG_DAC_DMA_ADDR 0x80
#define REG_DAC_DMA_LEN 0x84
#define REG_DAC_CUR_ADDR 0x80
#define REG_ADC_DMA_ADDR 0x88
#define REG_ADC_DMA_LEN 0x8c
#define REG_ADC_CUR_ADDR 0x88
#define REG_EXT_INDEX 0xf0
#define REG_INTR_CTRL 0x0c
#define REG_INTR_STS 0x10
#define FMT_BIT16 0x02
#define FMT_STEREO 0x01
#define MIXER_ADCL 0x3d
#define MIXER_ADCR 0x3e
#define MIXER_OUT_MUTE 0x3c
#define CMD_POWER_DOWN 0x80000000
#define CMD_RESET 0x40000000
#define CMD_ADC_C0 0x00000001
#define CMD_ADC_C1 0x00000002
#define CMD_N4SPK3D 0x04000000
#define CMD_SPDIF_ENA 0x00000200
#define CMD_SPDIF_LOOP 0x00000080
#define CMD_ENA_C0 0x00010000
#define CMD_ENA_C1 0x00020000
#define CMD_INTR_C0 0x00010000
#define CMD_INTR_C1 0x00020000
#define CMD_RESET_C0 0x00040000
#define CMD_RESET_C1 0x00080000
#define CMD_PAUSE_C0 0x00000004
#define CMD_PAUSE_C1 0x00000008
#define CMD_INTR_ENABLE 0x00030000
static u32_t g_sample_rate[] = {
5512, 11025, 22050, 44100, 8000, 16000, 32000, 48000
};
/* Driver Data Structure */
typedef struct aud_sub_dev_conf_t {
u32_t stereo;
u16_t sample_rate;
u32_t nr_of_bits;
u32_t sign;
u32_t busy;
u32_t fragment_size;
u8_t format;
} aud_sub_dev_conf_t;
typedef struct DEV_STRUCT {
char *name;
u16_t vid;
u16_t did;
u32_t devind;
u32_t base[6];
char irq;
char revision;
u32_t intr_status;
} DEV_STRUCT;
void dev_mixer_write(u32_t *base, u32_t reg, u32_t val);
u32_t dev_mixer_read(u32_t *base, u32_t reg);
#endif

View File

@ -0,0 +1,84 @@
#ifndef _IO_H
#define _IO_H
#include <sys/types.h>
#include <minix/syslib.h>
#include "cmi8738.h"
/* I/O function */
static u8_t my_inb(u32_t port) {
u32_t value;
int r;
#ifdef DMA_BASE_IOMAP
value = *(volatile u8_t *)(port);
#else
if ((r = sys_inb(port, &value)) != OK)
printf("SDR: sys_inb failed: %d\n", r);
#endif
return (u8_t)value;
}
#define sdr_in8(port, offset) (my_inb((port) + (offset)))
static u16_t my_inw(u32_t port) {
u32_t value;
int r;
#ifdef DMA_BASE_IOMAP
value = *(volatile u16_t *)(port);
#else
if ((r = sys_inw(port, &value)) != OK)
printf("SDR: sys_inw failed: %d\n", r);
#endif
return (u16_t)value;
}
#define sdr_in16(port, offset) (my_inw((port) + (offset)))
static u32_t my_inl(u32_t port) {
u32_t value;
int r;
#ifdef DMA_BASE_IOMAP
value = *(volatile u32_t *)(port);
#else
if ((r = sys_inl(port, &value)) != OK)
printf("SDR: sys_inl failed: %d\n", r);
#endif
return value;
}
#define sdr_in32(port, offset) (my_inl((port) + (offset)))
static void my_outb(u32_t port, u32_t value) {
int r;
#ifdef DMA_BASE_IOMAP
*(volatile u8_t *)(port) = value;
#else
if ((r = sys_outb(port, (u8_t)value)) != OK)
printf("SDR: sys_outb failed: %d\n", r);
#endif
}
#define sdr_out8(port, offset, value) \
(my_outb(((port) + (offset)), (value)))
static void my_outw(u32_t port, u32_t value) {
int r;
#ifdef DMA_BASE_IOMAP
*(volatile u16_t *)(port) = value;
#else
if ((r = sys_outw(port, (u16_t)value)) != OK)
printf("SDR: sys_outw failed: %d\n", r);
#endif
}
#define sdr_out16(port, offset, value) \
(my_outw(((port) + (offset)), (value)))
static void my_outl(u32_t port, u32_t value) {
int r;
#ifdef DMA_BASE_IOMAP
*(volatile u32_t *)(port) = value;
#else
if ((r = sys_outl(port, value)) != OK)
printf("SDR: sys_outl failed: %d\n", r);
#endif
}
#define sdr_out32(port, offset, value) \
(my_outl(((port) + (offset)), (value)))
#endif

View File

@ -0,0 +1,272 @@
#include "mixer.h"
#ifdef MIXER_AK4531
u8_t mixer_value[] = {
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x08,
0x7e, 0x3d, 0x01, 0x01, 0x00, 0x00, 0x03, 0x00,
0x00, 0x01
};
int get_set_volume(u32_t *base, struct volume_level *level, int flag) {
int max_level, cmd_left, cmd_right;
max_level = 0x1f;
/* Check device */
switch (level->device) {
case Master:
cmd_left = MASTER_VOLUME_LCH;
cmd_right = MASTER_VOLUME_RCH;
break;
case Dac:
return EINVAL;
case Fm:
cmd_left = FM_VOLUME_LCH;
cmd_right = FM_VOLUME_RCH;
break;
case Cd:
cmd_left = CD_AUDIO_VOLUME_LCH;
cmd_right = CD_AUDIO_VOLUME_RCH;
break;
case Line:
cmd_left = LINE_VOLUME_LCH;
cmd_right = LINE_VOLUME_RCH;
break;
case Mic:
cmd_left = cmd_right = MIC_VOLUME;
break;
case Speaker:
cmd_left = cmd_right = MONO_OUT_VOLUME;
max_level = 0x03;
break;
case Treble:
return EINVAL;
case Bass:
return EINVAL;
default:
return EINVAL;
}
/* Set volume */
if (flag) {
if (level->right < 0)
level->right = 0;
else if (level->right > max_level)
level->right = max_level;
if (level->left < 0)
level->left = 0;
else if (level->left > max_level)
level->left = max_level;
/* ### WRITE_MIXER_REG ### */
dev_mixer_write(base, cmd_left, 0x1f - level->left);
/* ### WRITE_MIXER_REG ### */
dev_mixer_write(base, cmd_right, 0x1f - level->right);
mixer_value[cmd_left] = 0x1f - level->left;
mixer_value[cmd_right] = 0x1f - level->right;
}
/* Get volume (mixer register can not be read in ak4531 codec) */
else {
/* ### READ_MIXER_REG ### */
dev_mixer_read(base, cmd_left);
/* ### READ_MIXER_REG ### */
dev_mixer_read(base, cmd_right);
level->left = 0x1f - mixer_value[cmd_left];
level->right = 0x1f - mixer_value[cmd_right];
}
return OK;
}
#endif
#ifdef MIXER_SB16
int get_set_volume(u32_t *base, struct volume_level *level, int flag) {
int max_level, shift, cmd_left, cmd_right;
max_level = 0x0f;
shift = 4;
/* Check device */
switch (level->device) {
case Master:
cmd_left = SB16_MASTER_LEFT;
cmd_right = SB16_MASTER_RIGHT;
break;
case Dac:
cmd_left = SB16_DAC_LEFT;
cmd_right = SB16_DAC_RIGHT;
break;
case Fm:
cmd_left = SB16_FM_LEFT;
cmd_right = SB16_FM_RIGHT;
break;
case Cd:
cmd_left = SB16_CD_LEFT;
cmd_right = SB16_CD_RIGHT;
break;
case Line:
cmd_left = SB16_LINE_LEFT;
cmd_left = SB16_LINE_RIGHT;
break;
case Mic:
cmd_left = cmd_right = SB16_MIC_LEVEL;
break;
case Speaker:
cmd_left = cmd_right = SB16_PC_LEVEL;
shift = 6;
max_level = 0x03;
break;
case Treble:
cmd_left = SB16_TREBLE_LEFT;
cmd_right = SB16_TREBLE_RIGHT;
shift = 4;
max_level = 0x0f;
break;
case Bass:
cmd_left = SB16_BASS_LEFT;
cmd_right = SB16_BASS_RIGHT;
shift = 4;
max_level = 0x0f;
break;
default:
return EINVAL;
}
/* Set volume */
if (flag) {
if (level->right < 0)
level->right = 0;
else if (level->right > max_level)
level->right = max_level;
if (level->left < 0)
level->left = 0;
else if (level->left > max_level)
level->left = max_level;
/* ### WRITE_MIXER_REG ### */
dev_mixer_write(base, cmd_left, level->left << shift);
/* ### WRITE_MIXER_REG ### */
dev_mixer_write(base, cmd_right, level->right << shift);
}
/* Get volume */
else {
/* ### READ_MIXER_REG ### */
level->left = dev_mixer_read(base, cmd_left);
/* ### READ_MIXER_REG ### */
level->right = dev_mixer_read(base, cmd_right);
level->left >>= shift;
level->right >>= shift;
}
return OK;
}
#endif
#ifdef MIXER_AC97
int get_set_volume(u32_t *base, struct volume_level *level, int flag) {
int max_level, cmd, data;
max_level = 0x1f;
/* Check device */
switch (level->device) {
case Master:
cmd = AC97_MASTER_VOLUME;
break;
case Dac:
return EINVAL;
case Fm:
cmd = AC97_PCM_OUT_VOLUME;
break;
case Cd:
cmd = AC97_CD_VOLUME;
break;
case Line:
cmd = AC97_LINE_IN_VOLUME;
break;
case Mic:
cmd = AC97_MIC_VOLUME;
break;
case Speaker:
return EINVAL;
case Treble:
return EINVAL;
case Bass:
return EINVAL;
default:
return EINVAL;
}
/* Set volume */
if (flag) {
if (level->right < 0)
level->right = 0;
else if (level->right > max_level)
level->right = max_level;
if (level->left < 0)
level->left = 0;
else if (level->left > max_level)
level->left = max_level;
data = (max_level - level->left) << 8 | (max_level - level->right);
/* ### WRITE_MIXER_REG ### */
dev_mixer_write(base, cmd, data);
}
/* Get volume */
else {
/* ### READ_MIXER_REG ### */
data = dev_mixer_read(base, cmd);
level->left = (u16_t)(data >> 8);
level->right = (u16_t)(data & 0xff);
if (level->right < 0)
level->right = 0;
else if (level->right > max_level)
level->right = max_level;
if (level->left < 0)
level->left = 0;
else if (level->left > max_level)
level->left = max_level;
level->left = max_level - level->left;
level->right = max_level - level->right;
}
return OK;
}
#endif
/* Set default mixer volume */
void dev_set_default_volume(u32_t *base) {
int i;
#ifdef MIXER_AK4531
for (i = 0; i <= 0x19; i++)
dev_mixer_write(base, i, mixer_value[i]);
#endif
#ifdef MIXER_SB16
dev_mixer_write(base, SB16_MASTER_LEFT, 0x18 << 3);
dev_mixer_write(base, SB16_MASTER_RIGHT, 0x18 << 3);
dev_mixer_write(base, SB16_DAC_LEFT, 0x0f << 4);
dev_mixer_write(base, SB16_DAC_RIGHT, 0x0f << 4);
dev_mixer_write(base, SB16_FM_LEFT, 0x08 << 4);
dev_mixer_write(base, SB16_FM_RIGHT, 0x08 << 4);
dev_mixer_write(base, SB16_CD_LEFT, 0x08 << 4);
dev_mixer_write(base, SB16_CD_RIGHT, 0x08 << 4);
dev_mixer_write(base, SB16_LINE_LEFT, 0x08 << 4);
dev_mixer_write(base, SB16_LINE_RIGHT, 0x08 << 4);
dev_mixer_write(base, SB16_MIC_LEVEL, 0x0f << 4);
dev_mixer_write(base, SB16_PC_LEVEL, 0x02 << 6);
dev_mixer_write(base, SB16_TREBLE_LEFT, 0x08 << 4);
dev_mixer_write(base, SB16_TREBLE_RIGHT, 0x08 << 4);
dev_mixer_write(base, SB16_BASS_LEFT, 0x08 << 4);
dev_mixer_write(base, SB16_BASS_RIGHT, 0x08 << 4);
#endif
#ifdef MIXER_AC97
dev_mixer_write(base, AC97_POWERDOWN, 0x0000);
for (i = 0; i < 50000; i++) {
if (dev_mixer_read(base, AC97_POWERDOWN) & 0x03)
break;
micro_delay(100);
}
if (i == 50000)
printf("SDR: AC97 is not ready\n");
dev_mixer_write(base, AC97_MASTER_VOLUME, 0x0000);
dev_mixer_write(base, AC97_MONO_VOLUME, 0x8000);
dev_mixer_write(base, AC97_PHONE_VOLUME, 0x8008);
dev_mixer_write(base, AC97_MIC_VOLUME, 0x0000);
dev_mixer_write(base, AC97_LINE_IN_VOLUME, 0x0303);
dev_mixer_write(base, AC97_CD_VOLUME, 0x0808);
dev_mixer_write(base, AC97_AUX_IN_VOLUME, 0x0808);
dev_mixer_write(base, AC97_PCM_OUT_VOLUME, 0x0808);
dev_mixer_write(base, AC97_RECORD_GAIN_VOLUME, 0x0000);
dev_mixer_write(base, AC97_RECORD_SELECT, 0x0000);
dev_mixer_write(base, AC97_GENERAL_PURPOSE, 0x0000);
#endif
}

View File

@ -0,0 +1,62 @@
#ifndef _MIXER_H
#define _MIXER_H
#include "cmi8738.h"
#ifdef MIXER_AK4531
#define MASTER_VOLUME_LCH 0x00
#define MASTER_VOLUME_RCH 0x01
#define FM_VOLUME_LCH 0x04
#define FM_VOLUME_RCH 0x05
#define CD_AUDIO_VOLUME_LCH 0x06
#define CD_AUDIO_VOLUME_RCH 0x07
#define LINE_VOLUME_LCH 0x08
#define LINE_VOLUME_RCH 0x09
#define MIC_VOLUME 0x0e
#define MONO_OUT_VOLUME 0x0f
#endif
#ifdef MIXER_SB16
#define SB16_MASTER_LEFT 0x30
#define SB16_MASTER_RIGHT 0x31
#define SB16_DAC_LEFT 0x32
#define SB16_DAC_RIGHT 0x33
#define SB16_FM_LEFT 0x34
#define SB16_FM_RIGHT 0x35
#define SB16_CD_LEFT 0x36
#define SB16_CD_RIGHT 0x37
#define SB16_LINE_LEFT 0x38
#define SB16_LINE_RIGHT 0x39
#define SB16_MIC_LEVEL 0x3a
#define SB16_PC_LEVEL 0x3b
#define SB16_TREBLE_LEFT 0x44
#define SB16_TREBLE_RIGHT 0x45
#define SB16_BASS_LEFT 0x46
#define SB16_BASS_RIGHT 0x47
#endif
#ifdef MIXER_AC97
#define AC97_MASTER_VOLUME 0x02
#define AC97_AUX_OUT_VOLUME 0x04
#define AC97_MONO_VOLUME 0x06
#define AC97_MASTER_TONE 0x08
#define AC97_PC_BEEP_VOLUME 0x0a
#define AC97_PHONE_VOLUME 0x0c
#define AC97_MIC_VOLUME 0x0e
#define AC97_LINE_IN_VOLUME 0x10
#define AC97_CD_VOLUME 0x12
#define AC97_VIDEO_VOLUME 0x14
#define AC97_AUX_IN_VOLUME 0x16
#define AC97_PCM_OUT_VOLUME 0x18
#define AC97_RECORD_GAIN_VOLUME 0x1c
#define AC97_RECORD_GAIN_MIC_VOL 0x1e
#define AC97_GENERAL_PURPOSE 0x20
#define AC97_POWERDOWN 0x26
#define AC97_RECORD_SELECT 0x1a
#define AC97_RESET 0x00
#endif
int get_set_volume(u32_t *pbase, struct volume_level *level, int flag);
void dev_set_default_volume(u32_t *pbase);
#endif

View File

@ -41,6 +41,7 @@ service_get_policies(struct policies * pol, index_t slot)
const char *policy_str;
} def_pol[] = {
/* audio */
{ .label = "cmi8738", .policy_str = "reset" },
{ .label = "es1370", .policy_str = "reset" },
{ .label = "es1371", .policy_str = "reset" },
{ .label = "sb16", .policy_str = "reset" },