]> Zhao Yanbai Git Server - minix.git/commitdiff
Add the driver for CS4281 sound card 87/3387/1
authorJia-Ju Bai <baijiaju1990@163.com>
Wed, 4 Jan 2017 13:55:10 +0000 (13:55 +0000)
committerDavid van Moolenbroek <david@minix3.org>
Sun, 8 Jan 2017 18:01:20 +0000 (19:01 +0100)
Change-Id: Ifc1b7c129578c5efa5e328664d10d07ee5df786b

12 files changed:
distrib/sets/lists/minix-base/md.i386
distrib/sets/lists/minix-debug/md.i386
minix/drivers/audio/Makefile
minix/drivers/audio/cs4281/Makefile [new file with mode: 0644]
minix/drivers/audio/cs4281/README [new file with mode: 0644]
minix/drivers/audio/cs4281/cs4281.c [new file with mode: 0644]
minix/drivers/audio/cs4281/cs4281.conf [new file with mode: 0644]
minix/drivers/audio/cs4281/cs4281.h [new file with mode: 0644]
minix/drivers/audio/cs4281/io.h [new file with mode: 0644]
minix/drivers/audio/cs4281/mixer.c [new file with mode: 0644]
minix/drivers/audio/cs4281/mixer.h [new file with mode: 0644]
minix/fs/procfs/service.c

index ad60ac20c726b072d12a66a23677caa2a426757a..2a948084cf9acf6811addb6e43244a3bcae218d0 100644 (file)
@@ -10,6 +10,7 @@
 ./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/cs4281                              minix-base
 ./etc/system.conf.d/dec21140A                           minix-base
 ./etc/system.conf.d/dp8390                              minix-base
 ./etc/system.conf.d/dpeth                               minix-base
@@ -35,6 +36,7 @@
 ./service/at_wini                                       minix-base
 ./service/atl2                                          minix-base
 ./service/cmi8738                                       minix-base
+./service/cs4281                                        minix-base
 ./service/dec21140A                                     minix-base
 ./service/dp8390                                        minix-base
 ./service/dpeth                                         minix-base
index 109e8fcb0c13f5f4f16cfa836ea6c00c70c50a42..d8ad97c0c8f07c863ab93456f96bfcee9bdbf7cd 100644 (file)
@@ -18,6 +18,7 @@
 ./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/cs4281.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
index dbcdb9a916e7d50885446eeb0ce1ce621719ff2d..67ba29e69047c4291af401c4987ed7f233c6bfe0 100644 (file)
@@ -3,6 +3,7 @@
 .if ${MACHINE_ARCH} == "i386"
 SUBDIR+=       als4000
 SUBDIR+=       cmi8738
+SUBDIR+=       cs4281
 SUBDIR+=       es1370
 SUBDIR+=       es1371
 SUBDIR+=       sb16
diff --git a/minix/drivers/audio/cs4281/Makefile b/minix/drivers/audio/cs4281/Makefile
new file mode 100644 (file)
index 0000000..a6c1136
--- /dev/null
@@ -0,0 +1,12 @@
+# Makefile for the CS4281 driver
+PROG=  cs4281
+SRCS=  cs4281.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>
diff --git a/minix/drivers/audio/cs4281/README b/minix/drivers/audio/cs4281/README
new file mode 100644 (file)
index 0000000..8326fd9
--- /dev/null
@@ -0,0 +1,7 @@
+The cs4281 driver is for Cirrus Logic 4281 (CrystalClear PCI) sound card.
+
+This driver is referred to Minix(3.4.0) es1371 driver,
+Linux snd_cs4281 driver and Cirrus Logic 4281 Datasheet (Rev 1.1).
+
+Revision 1.0 2016/12/28
+Authored by Jia-Ju Bai <baijiaju1990@163.com>
diff --git a/minix/drivers/audio/cs4281/cs4281.c b/minix/drivers/audio/cs4281/cs4281.c
new file mode 100644 (file)
index 0000000..5ffceaf
--- /dev/null
@@ -0,0 +1,670 @@
+#include "cs4281.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 ====== */
+
+/* ====== 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 i, data, base0 = base[0];
+       sdr_out32(base0, REG_CODEC_ADDR, reg);
+       sdr_out32(base0, REG_CODEC_DATA, val);
+       sdr_out32(base0, REG_CODEC_CTRL, 0x0e);
+
+       for (i = 0; i < 50000; i++) {
+               micro_delay(10);
+               data = sdr_in32(base0, REG_CODEC_CTRL);
+               if (!(data & STS_CODEC_DONE))
+                       break;
+       }
+       if (i == 50000)
+               printf("SDR: Codec is not ready in write\n");
+}
+
+/* Read the data from mixer register (### READ_MIXER_REG ###) */
+u32_t dev_mixer_read(u32_t *base, u32_t reg) {
+       u32_t i, data, base0 = base[0];
+       sdr_in32(base0, REG_CODEC_SDA);
+       sdr_out32(base0, REG_CODEC_ADDR, reg & 0xff);
+       sdr_out32(base0, REG_CODEC_DATA, 0);
+       sdr_out32(base0, REG_CODEC_CTRL, 0x1e);
+       for (i = 0; i < 50000; i++) {
+               micro_delay(10);
+               data = sdr_in32(base0, REG_CODEC_CTRL);
+               if (!(data & STS_CODEC_DONE))
+                       break;
+       }
+       if (i == 50000)
+               printf("SDR: Codec is not ready in read1\n");
+       for (i = 0; i < 50000; i++) {
+               micro_delay(10);
+               data = sdr_in32(base0, REG_CODEC_STATUS);
+               if (data & STS_CODEC_VALID)
+                       break;
+       }
+       if (i == 50000)
+               printf("SDR: Codec is not ready in read2\n");
+       return sdr_in32(base0, REG_CODEC_SDA);
+}
+
+/* ====== 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];
+       data = sdr_in32(base0, REG_POWER_EXT);
+       if (data & CMD_POWER_DOWN)
+               sdr_out32(base0, REG_POWER_EXT, data & ~CMD_POWER_DOWN);
+       data = sdr_in32(base0, REG_CONF_LOAD);
+       if (data != 0x01)
+               sdr_out32(base0, REG_CONF_LOAD, 0x01);
+       sdr_out32(base0, REG_CONF_WRITE, 0x4281);
+       sdr_out32(base0, REG_SOUND_POWER, 0x3f);
+       sdr_out32(base0, REG_CLK_CTRL, 0);
+       sdr_out32(base0, REG_MASTER_CTRL, 0);
+       sdr_out32(base0, REG_CODEC_CTRL, 0);
+       micro_delay(100);
+       sdr_out32(base0, REG_SPOWER_CTRL, 0);
+       micro_delay(100);
+       sdr_out32(base0, REG_SPOWER_CTRL, 0x01);
+       micro_delay(100);
+       sdr_out32(base0, REG_MASTER_CTRL, 0x00010003);
+       return OK;
+}
+
+/* Configure hardware registers (### CONF_HARDWARE ###) */
+static void dev_configure(u32_t *base) {
+       u32_t i, data, base0 = base[0];
+       sdr_out32(base0, REG_MASTER_CTRL, CMD_PORT_TIMING | CMD_AC97_MODE |
+                                                                               CMD_MASTER_SERIAL);
+       sdr_out32(base0, REG_CLK_CTRL, 0x10);
+       micro_delay(50);
+       sdr_out32(base0, REG_CLK_CTRL, 0x30);
+       micro_delay(500);
+       sdr_out32(base0, REG_CODEC_CTRL, 0x02);
+       micro_delay(500);
+       sdr_out32(base0, REG_CODEC_CTRL, 0x06);
+       micro_delay(500);
+       sdr_out32(base0, REG_CODEC_OSV, 0x03);
+       sdr_out32(base0, REG_PCM_LVOL, 0x07);
+       sdr_out32(base0, REG_PCM_RVOL, 0x07);
+}
+
+/* Initialize the mixer (### INIT_MIXER ###) */
+static void dev_init_mixer(u32_t *base) {
+       dev_mixer_write(base, 0, 0);
+}
+
+/* Set DAC and ADC sample rate (### SET_SAMPLE_RATE ###) */
+static void dev_set_sample_rate(u32_t *base, u16_t sample_rate) {
+       u32_t i, data = 0, base0 = base[0];
+       for (i = 0; i < 6; i++) {
+               if (g_sample_rate[i] == sample_rate) {
+                       data = i;
+                       break;
+               }
+       }
+       sdr_out32(base0, REG_DAC_SAMPLE_RATE, data);
+       sdr_out32(base0, REG_ADC_SAMPLE_RATE, 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 base0 = base[0];
+       dmr_data = CMD_DMR_INIT;
+       if (stereo == 0)
+               dmr_data |= CMD_DMR_MONO;
+       if (sign == 0)
+               dmr_data |= CMD_DMR_UNSIGN;
+       if (bits == 8) {
+               dmr_data |= CMD_DMR_BIT8;
+               if (stereo == 0)
+                       dmr_data |= CMD_DMR_SWAP;
+       }
+       else if (bits == 32)
+               dmr_data |= CMD_DMR_BIT32;
+}
+
+/* Start the channel (### START_CHANNEL ###) */
+static void dev_start_channel(u32_t *base, int sub_dev) {
+       u32_t temp, base0 = base[0];
+
+       dcr_data = 0x30001;
+       if (sub_dev == DAC)
+               fcr_data = CMD_DAC_FCR_INIT;
+       if (sub_dev == ADC)
+               fcr_data = CMD_ADC_FCR_INIT;
+       dmr_data |= CMD_DMR_DMA;
+       dcr_data &= ~CMD_DCR_MASK;
+       fcr_data |= CMD_FCR_FEN;
+       if (sub_dev == DAC) {
+               dmr_data |= CMD_DMR_WRITE;
+               sdr_out32(base0, REG_DAC_FSIC, 0);
+               sdr_out32(base0, REG_DAC_DMR, dmr_data & ~CMD_DMR_DMA);
+               sdr_out32(base0, REG_DAC_DMR, dmr_data);
+               sdr_out32(base0, REG_DAC_FCR, fcr_data);
+               sdr_out32(base0, REG_DAC_DCR, dcr_data);
+       }
+       else if (sub_dev == ADC) {
+               dmr_data |= CMD_DMR_READ;
+               sdr_out32(base0, REG_ADC_FSIC, 0);
+               sdr_out32(base0, REG_ADC_DMR, dmr_data & ~CMD_DMR_DMA);
+               sdr_out32(base0, REG_ADC_DMR, dmr_data);
+               sdr_out32(base0, REG_ADC_FCR, fcr_data);
+               sdr_out32(base0, REG_ADC_DCR, dcr_data);
+       }
+
+}
+
+/* Stop the channel (### STOP_CHANNEL ###) */
+static void dev_stop_channel(u32_t *base, int sub_dev) {
+       u32_t base0 = base[0];
+       dmr_data &= ~(CMD_DMR_DMA | CMD_DMR_POLL);
+       dcr_data |= ~CMD_DCR_MASK;
+       fcr_data &= ~CMD_FCR_FEN;
+       if (sub_dev == DAC) {
+               sdr_out32(base0, REG_DAC_DMR, dmr_data);
+               sdr_out32(base0, REG_DAC_FCR, fcr_data);
+               sdr_out32(base0, REG_DAC_DCR, dcr_data);
+       }
+       else if (sub_dev == ADC) {
+               sdr_out32(base0, REG_ADC_DMR, dmr_data);
+               sdr_out32(base0, REG_ADC_FCR, fcr_data);
+               sdr_out32(base0, REG_ADC_DCR, dcr_data);
+       }
+}
+
+/* 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_out32(base0, REG_DAC_DMA_LEN, len - 1);
+       }
+       else if (sub_dev == ADC) {
+               sdr_out32(base0, REG_ADC_DMA_ADDR, dma);
+               sdr_out32(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_in32(base0, REG_DAC_DCC);
+       else if (sub_dev == ADC)
+               data = sdr_in16(base0, REG_ADC_DCC);
+       data &= 0xffff;
+       return (u16_t)data;
+}
+
+/* Pause the DMA (### PAUSE_DMA ###) */
+static void dev_pause_dma(u32_t *base, int sub_dev) {
+       u32_t base0 = base[0];
+       dcr_data |= CMD_DCR_MASK;
+       fcr_data |= CMD_FCR_FEN;
+       if (sub_dev == DAC) {
+               sdr_out32(base0, REG_DAC_DMR, dmr_data);
+               sdr_out32(base0, REG_DAC_FCR, fcr_data);
+               sdr_out32(base0, REG_DAC_DCR, dcr_data);
+       }
+       if (sub_dev == ADC) {
+               sdr_out32(base0, REG_ADC_DMR, dmr_data);
+               sdr_out32(base0, REG_ADC_FCR, fcr_data);
+               sdr_out32(base0, REG_ADC_DCR, dcr_data);
+       }
+}
+
+/* Resume the DMA (### RESUME_DMA ###) */
+static void dev_resume_dma(u32_t *base, int sub_dev) {
+       u32_t base0 = base[0];
+       dcr_data &= ~CMD_DCR_MASK;
+       fcr_data &= ~CMD_FCR_FEN;
+       if (sub_dev == DAC) {
+               sdr_out32(base0, REG_DAC_DMR, dmr_data);
+               sdr_out32(base0, REG_DAC_FCR, fcr_data);
+               sdr_out32(base0, REG_DAC_DCR, dcr_data);
+       }
+       if (sub_dev == ADC) {
+               sdr_out32(base0, REG_ADC_DMR, dmr_data);
+               sdr_out32(base0, REG_ADC_FCR, fcr_data);
+               sdr_out32(base0, REG_ADC_DCR, dcr_data);
+       }
+}
+
+/* Read and clear interrupt stats (### READ_CLEAR_INTR_STS ###)
+ * -- Return interrupt status */
+static u32_t dev_read_clear_intr_status(u32_t *base) {
+       u32_t status, base0 = base[0];
+       status = sdr_in32(base0, REG_INTR_STS);
+       sdr_in32(base0, REG_DAC_HDSR);
+       sdr_in32(base0, REG_ADC_HDSR);
+       sdr_out32(base0, REG_INTR_CTRL, CMD_INTR_ENABLE);
+       return status;
+}
+
+/* Enable or disable interrupt (### INTR_ENABLE_DISABLE ###) */
+static void dev_intr_enable(u32_t *base, int flag) {
+       u32_t data, base0 = base[0];
+       if (flag == INTR_ENABLE) {
+               sdr_out32(base0, REG_INTR_CTRL, CMD_INTR_ENABLE);
+               sdr_out32(base0, REG_INTR_MASK, ~(CMD_INTR_DMA | CMD_INTR_DMA0 |
+                                                                               CMD_INTR_DMA1));
+       }
+       else if (flag == INTR_DISABLE) {
+               sdr_out32(base0, REG_INTR_CTRL, ~CMD_INTR_ENABLE);
+               sdr_out32(base0, REG_INTR_MASK, 0x7fffffff);
+       }
+}
+
+/* ======= 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;
+}
diff --git a/minix/drivers/audio/cs4281/cs4281.conf b/minix/drivers/audio/cs4281/cs4281.conf
new file mode 100644 (file)
index 0000000..4c3fd48
--- /dev/null
@@ -0,0 +1,10 @@
+service cs4281
+{
+       system
+               UMAP            # 14
+               IRQCTL          # 19
+               DEVIO           # 21
+       ;
+       pci device 1013:6005;
+};
+
diff --git a/minix/drivers/audio/cs4281/cs4281.h b/minix/drivers/audio/cs4281/cs4281.h
new file mode 100644 (file)
index 0000000..00dc7f2
--- /dev/null
@@ -0,0 +1,139 @@
+#ifndef _SDR_H
+#define _SDR_H
+/* ======= General Parameter ======= */
+/* Global configure */
+#define DMA_LENGTH_BY_FRAME
+#define DMA_BASE_IOMAP
+#define MIXER_AC97
+
+#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              0x1013
+#define DEVICE_ID              0x6005
+#define DRIVER_NAME            "CS4281"
+
+/* 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           0x0100
+#define INTR_STS_ADC           0x0200
+
+/* ======= Self-defined Parameter ======= */
+#define REG_INTR_STS           0x0000
+#define REG_INTR_CTRL          0x0008
+#define REG_INTR_MASK          0x000c
+
+#define REG_CONF_WRITE         0x03e0
+#define REG_POWER_EXT          0x03e4
+#define REG_SPOWER_CTRL                0x03ec
+#define REG_CONF_LOAD          0x03f0
+#define REG_CLK_CTRL           0x0400
+#define REG_MASTER_CTRL                0x0420
+#define REG_CODEC_CTRL         0x0460
+#define REG_CODEC_STATUS       0x0464
+#define REG_CODEC_OSV          0x0468
+#define REG_CODEC_ADDR         0x046c
+#define REG_CODEC_DATA         0x0470
+#define REG_CODEC_SDA          0x047c
+#define REG_SOUND_POWER                0x0740
+#define REG_DAC_SAMPLE_RATE    0x0744
+#define REG_ADC_SAMPLE_RATE    0x0748
+#define REG_SRC_SLOT           0x075c
+#define REG_PCM_LVOL           0x0760
+#define REG_PCM_RVOL           0x0764
+
+#define REG_DAC_HDSR           0x00f0
+#define REG_DAC_DCC                    0x0114
+#define REG_DAC_DMR                    0x0150
+#define REG_DAC_DCR                    0x0154
+#define REG_DAC_FCR                    0x0180
+#define REG_DAC_FSIC           0x0214
+#define REG_ADC_HDSR           0x00f4
+#define REG_ADC_DCC                    0x0124
+#define REG_ADC_DMR                    0x0158
+#define REG_ADC_DCR                    0x015c
+#define REG_ADC_FCR                    0x0184
+#define REG_ADC_FSIC           0x0214
+
+#define REG_DAC_DMA_ADDR       0x0118
+#define REG_DAC_DMA_LEN                0x011c
+#define REG_ADC_DMA_ADDR       0x0128
+#define REG_ADC_DMA_LEN                0x012c
+
+#define CODEC_REG_POWER                0x26
+
+#define STS_CODEC_DONE         0x0008
+#define STS_CODEC_VALID                0x0002
+
+#define CMD_POWER_DOWN         (1 << 14)
+#define CMD_PORT_TIMING                (1 << 16)
+#define CMD_AC97_MODE          (1 << 1)
+#define CMD_MASTER_SERIAL      (1 << 0)
+#define CMD_INTR_ENABLE                0x03
+#define CMD_INTR_DMA           0x00040000
+#define CMD_INTR_DMA0          0x0100
+#define CMD_INTR_DMA1          0x0200
+#define CMD_DMR_INIT           0x50
+#define CMD_DMR_WRITE          0x08
+#define CMD_DMR_READ           0x04
+#define CMD_DMR_BIT8           (1 << 16)
+#define CMD_DMR_MONO           (1 << 17)
+#define CMD_DMR_UNSIGN         (1 << 19)
+#define CMD_DMR_BIT32          (1 << 20)
+#define CMD_DMR_SWAP           (1 << 22)
+#define CMD_DMR_POLL           (1 << 28)
+#define CMD_DMR_DMA                    (1 << 29)
+#define CMD_DCR_MASK           (1 << 0)
+#define CMD_FCR_FEN                    (1 << 31)
+#define CMD_DAC_FCR_INIT       0x01002000
+#define CMD_ADC_FCR_INIT       0x0b0a2020
+
+static u32_t dcr_data, dmr_data, fcr_data;
+static u32_t g_sample_rate[] = {
+       48000, 44100, 22050, 16000, 11025, 8000
+};
+
+/* 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
diff --git a/minix/drivers/audio/cs4281/io.h b/minix/drivers/audio/cs4281/io.h
new file mode 100644 (file)
index 0000000..9c7caf4
--- /dev/null
@@ -0,0 +1,84 @@
+#ifndef _IO_H
+#define _IO_H
+
+#include <sys/types.h>
+#include <minix/syslib.h>
+#include "cs4281.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
diff --git a/minix/drivers/audio/cs4281/mixer.c b/minix/drivers/audio/cs4281/mixer.c
new file mode 100644 (file)
index 0000000..c07d1f2
--- /dev/null
@@ -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
+}
diff --git a/minix/drivers/audio/cs4281/mixer.h b/minix/drivers/audio/cs4281/mixer.h
new file mode 100644 (file)
index 0000000..8d04242
--- /dev/null
@@ -0,0 +1,62 @@
+#ifndef _MIXER_H
+#define _MIXER_H
+
+#include "cs4281.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
index 004fb3fd4b226a9e6cf9e0f3b450696041b25095..493f74eee769a921a773c4faae5f4906a0cfc0bf 100644 (file)
@@ -43,6 +43,7 @@ service_get_policies(struct policies * pol, index_t slot)
                /* audio */
                { .label = "als4000", .policy_str = "reset" },
                { .label = "cmi8738", .policy_str = "reset" },
+               { .label = "cs4281", .policy_str = "reset" },
                { .label = "es1370", .policy_str = "reset" },
                { .label = "es1371", .policy_str = "reset" },
                { .label = "sb16", .policy_str = "reset" },