summaryrefslogtreecommitdiff
path: root/kernel/drv/oss_ymf7xx/oss_ymf7xx.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/drv/oss_ymf7xx/oss_ymf7xx.c')
-rw-r--r--kernel/drv/oss_ymf7xx/oss_ymf7xx.c1625
1 files changed, 1625 insertions, 0 deletions
diff --git a/kernel/drv/oss_ymf7xx/oss_ymf7xx.c b/kernel/drv/oss_ymf7xx/oss_ymf7xx.c
new file mode 100644
index 0000000..8369c2a
--- /dev/null
+++ b/kernel/drv/oss_ymf7xx/oss_ymf7xx.c
@@ -0,0 +1,1625 @@
+/*
+ * Purpose: Driver for Yamaha YMF7xx PCI audio controller.
+ */
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+
+#include "oss_ymf7xx_cfg.h"
+#include "ymf7xx.h"
+#include "oss_pci.h"
+#include "ac97.h"
+#include "uart401.h"
+
+#define YAMAHA_VENDOR_ID 0x1073
+#define YAMAHA_YMF724_ID 0x0004
+#define YAMAHA_YMF724F_ID 0x000d
+#define YAMAHA_YMF734_ID 0x0005
+#define YAMAHA_YMF740_ID 0x000a
+#define YAMAHA_YMF740C_ID 0x000c
+#define YAMAHA_YMF744_ID 0x0010
+#define YAMAHA_YMF754_ID 0x0012
+
+#define WRITEB(a,d) devc->bRegister[a] = d
+#define READB(a) devc->bRegister[a]
+#define WRITEW(a,d) devc->wRegister[a>>1] = d
+#define READW(a) devc->wRegister[a>>1]
+#define WRITEL(a,d) devc->dwRegister[a>>2] = d
+#define READL(a) (devc->dwRegister[a>>2])
+
+#ifdef OSS_BIG_ENDIAN
+static __inline__ unsigned int
+ymf_swap (unsigned int x)
+{
+ return ((x & 0x000000ff) << 24) |
+ ((x & 0x0000ff00) << 8) |
+ ((x & 0x00ff0000) >> 8) | ((x & 0xff000000) >> 24);
+}
+
+#define LSWAP(x) ymf_swap(x)
+#else
+#define LSWAP(x) x
+#endif
+
+#define MAX_PORTC 8
+
+extern int yamaha_mpu_ioaddr;
+extern int yamaha_mpu_irq;
+extern int yamaha_fm_ioaddr;
+
+
+typedef struct ymf7xx_portc
+{
+ int speed, bits, channels;
+ int open_mode;
+ int trigger_bits;
+ int audio_enabled;
+ int audiodev;
+ int devs_opened;
+ int devnum;
+ PLAY_BANK *bank1, *bank2, *bank3, *bank4;
+ EFFECT_CNTRL_SLOT effectslot;
+ REC_CNTRL_SLOT recslot;
+ int dacfmt;
+}
+ymf7xx_portc;
+
+typedef struct ymf7xx_devc
+{
+ oss_device_t *osdev;
+ char *chip_name;
+ int deviceid;
+ unsigned int base0addr;
+ unsigned int *base0virt;
+ volatile unsigned int *dwRegister;
+ volatile unsigned short *wRegister;
+ volatile unsigned char *bRegister;
+ int irq;
+ oss_mutex_t mutex;
+ oss_mutex_t low_mutex;
+
+ /* Legacy */
+ int mpu_base, mpu_irq;
+ int fm_base;
+ int fm_attached, mpu_attached;
+
+ /* Mixer parameters */
+ ac97_devc ac97devc, ac97devc2;
+ int mixer_dev;
+ int mixlevels[10];
+
+ /* Audio parameters */
+ int audio_initialized;
+ ymf7xx_portc portc[MAX_PORTC];
+
+ /* Play Table */
+ volatile oss_native_word slt;
+ oss_native_word slt_phys;
+ volatile unsigned int *tab;
+ oss_native_word play_table;
+ oss_native_word play_table_virt;
+ unsigned char *dmabuf1;
+ oss_dma_handle_t dmabuf1_dma_handle;
+
+ /* Effect Table */
+ volatile unsigned int *effecttab;
+ oss_native_word effect_table;
+ oss_native_word effect_table_virt, eff_buf_phys;
+ unsigned char *dmabuf2, *eff_buf;
+ oss_dma_handle_t dmabuf2_dma_handle;
+ oss_dma_handle_t eff_buf_dma_handle;
+
+ /* Recording Table */
+ volatile unsigned int *rectab;
+ oss_native_word rec_table;
+ oss_native_word rec_table_virt;
+ unsigned char *dmabuf3;
+ oss_dma_handle_t dmabuf3_dma_handle;
+ int spdif_in;
+}
+ymf7xx_devc;
+
+int SetupPlaySlot (int dev, int slot);
+int ymf7xx_spdif_control (int dev, int ctrl, unsigned int cmd, int value);
+
+static int
+ac97_read (void *devc_, int addr)
+{
+ ymf7xx_devc *devc = devc_;
+ int count;
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags);
+ WRITEW (AC97_CMD_ADDRESS, addr | 0x8000);
+
+ for (count = 0; count < 1000; count++)
+ if ((READW (AC97_STATUS_ADDRESS) >> 15) == 0)
+ {
+ MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
+ return READW (AC97_STATUS_DATA);
+ }
+ DDB (cmn_err (CE_WARN, "AC97 mixer read timed out\n"));
+ MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
+ return OSS_EIO;
+}
+
+static int
+ac97_write (void *devc_, int addr, int data)
+{
+ ymf7xx_devc *devc = devc_;
+ oss_native_word flags;
+ int count;
+
+ MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags);
+ WRITEW (AC97_CMD_ADDRESS, addr);
+ WRITEW (AC97_CMD_DATA, data);
+
+ for (count = 0; count < 1000; count++)
+ if ((READW (AC97_STATUS_ADDRESS) >> 15) == 0)
+ {
+ MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
+ return 1;
+ }
+ DDB (cmn_err (CE_WARN, "AC97 mixer write timed out\n"));
+ MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
+ return 0;
+}
+
+static void
+install_ucode (ymf7xx_devc * devc, int addr, unsigned int *src, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ {
+ WRITEL (addr, *src);
+ addr += 4;
+ src++;
+ }
+}
+
+static int
+ymf7xxintr (oss_device_t * osdev)
+{
+ ymf7xx_devc *devc = (ymf7xx_devc *) osdev->devc;
+ ymf7xx_portc *portc;
+ dmap_t *dmapin, *dmapout;
+ unsigned int status;
+ int i, n;
+ int currdac = 0;
+ int curradc = 0;
+ int serviced = 0;
+
+
+ status = READL (STATUS);
+
+ if ((status & 0x80000000))
+ {
+ serviced = 1;
+ for (i = 0; i < MAX_PORTC; i++)
+ {
+ portc = &devc->portc[i];
+
+ if (portc->trigger_bits & PCM_ENABLE_OUTPUT)
+ {
+ dmapout = audio_engines[portc->audiodev]->dmap_out;
+ currdac = LSWAP (portc->bank1->PgStart);
+ currdac /= dmapout->fragment_size / portc->dacfmt;
+
+ if (currdac < 0 || currdac >= dmapout->nfrags)
+ currdac = 0;
+ n = 0;
+ while (dmap_get_qhead (dmapout) != currdac
+ && n++ < dmapout->nfrags)
+ oss_audio_outputintr (portc->audiodev, 1);
+ }
+ if (portc->trigger_bits & PCM_ENABLE_INPUT)
+ {
+ dmapin = audio_engines[portc->audiodev]->dmap_in;
+
+ if (devc->spdif_in)
+ curradc = LSWAP (portc->recslot.bank1->PgStartAdr);
+ else
+ curradc = LSWAP (portc->recslot.bank3->PgStartAdr);
+
+ curradc /= dmapin->fragment_size;
+
+ if (curradc < 0 || curradc >= dmapin->nfrags)
+ curradc = 0;
+ n = 0;
+ while (dmap_get_qtail (dmapin) != curradc
+ && n++ < dmapin->nfrags)
+ oss_audio_inputintr (portc->audiodev, 0);
+ }
+ WRITEL (STATUS, 0x80000000);
+ WRITEL (MODE, READL (MODE) | 0x00000002);
+ }
+ }
+
+ return serviced;
+}
+
+/*
+ *****************************************************************************
+ */
+
+static int
+ymf7xx_audio_set_rate (int dev, int arg)
+{
+ ymf7xx_portc *portc = audio_engines[dev]->portc;
+
+ if (arg == 0)
+ return portc->speed;
+
+ if (arg > 48000)
+ arg = 48000;
+ if (arg < 5000)
+ arg = 5000;
+ portc->speed = arg;
+ return portc->speed;
+}
+
+static short
+ymf7xx_audio_set_channels (int dev, short arg)
+{
+ ymf7xx_portc *portc = audio_engines[dev]->portc;
+
+ if ((arg != 1) && (arg != 2))
+ return portc->channels;
+ portc->channels = arg;
+
+ return portc->channels;
+}
+
+static unsigned int
+ymf7xx_audio_set_format (int dev, unsigned int arg)
+{
+ ymf7xx_portc *portc = audio_engines[dev]->portc;
+
+ if (arg == 0)
+ return portc->bits;
+
+ if (!(arg & (AFMT_U8 | AFMT_S16_LE | AFMT_AC3)))
+ return portc->bits;
+ portc->bits = arg;
+
+ return portc->bits;
+}
+
+/*ARGSUSED*/
+static int
+ymf7xx_audio_ioctl (int dev, unsigned int cmd, ioctl_arg arg)
+{
+ return OSS_EINVAL;
+}
+
+static void ymf7xx_audio_trigger (int dev, int state);
+
+static void
+ymf7xx_audio_reset (int dev)
+{
+ ymf7xx_audio_trigger (dev, 0);
+}
+
+static void
+ymf7xx_audio_reset_input (int dev)
+{
+ ymf7xx_portc *portc = audio_engines[dev]->portc;
+ ymf7xx_audio_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_INPUT);
+}
+
+static void
+ymf7xx_audio_reset_output (int dev)
+{
+ ymf7xx_portc *portc = audio_engines[dev]->portc;
+ ymf7xx_audio_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_OUTPUT);
+}
+
+/*ARGSUSED*/
+static int
+ymf7xx_audio_open (int dev, int mode, int open_flags)
+{
+ ymf7xx_portc *portc = audio_engines[dev]->portc;
+ ymf7xx_devc *devc = audio_engines[dev]->devc;
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ if (portc->open_mode)
+ {
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ return OSS_EBUSY;
+ }
+
+ portc->open_mode = mode;
+ portc->audio_enabled = ~mode;
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+
+ return 0;
+}
+
+static void
+ymf7xx_audio_close (int dev, int mode)
+{
+ ymf7xx_portc *portc = audio_engines[dev]->portc;
+
+ ymf7xx_audio_reset (dev);
+ portc->open_mode = 0;
+ portc->audio_enabled &= ~mode;
+}
+
+/*ARGSUSED*/
+static void
+ymf7xx_audio_output_block (int dev, oss_native_word buf, int count,
+ int fragsize, int intrflag)
+{
+ ymf7xx_portc *portc = audio_engines[dev]->portc;
+
+ portc->audio_enabled |= PCM_ENABLE_OUTPUT;
+ portc->trigger_bits &= ~PCM_ENABLE_OUTPUT;
+}
+
+/*ARGSUSED*/
+static void
+ymf7xx_audio_start_input (int dev, oss_native_word buf, int count,
+ int fragsize, int intrflag)
+{
+ ymf7xx_portc *portc = audio_engines[dev]->portc;
+
+ portc->audio_enabled |= PCM_ENABLE_INPUT;
+ portc->trigger_bits &= ~PCM_ENABLE_INPUT;
+}
+
+static void
+ymf7xx_audio_trigger (int dev, int state)
+{
+ ymf7xx_devc *devc = audio_engines[dev]->devc;
+ ymf7xx_portc *portc = audio_engines[dev]->portc;
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+
+ if (portc->open_mode & OPEN_WRITE)
+ {
+ if (state & PCM_ENABLE_OUTPUT)
+ {
+ if ((portc->audio_enabled & PCM_ENABLE_OUTPUT) &&
+ !(portc->trigger_bits & PCM_ENABLE_OUTPUT))
+ {
+ portc->devs_opened = SetupPlaySlot (dev, portc->devnum);
+
+ WRITEL (CONTROL_SELECT, 1);
+ WRITEL (MODE, READL (MODE) | 0x00000003);
+ portc->trigger_bits |= PCM_ENABLE_OUTPUT;
+ }
+ }
+ else
+ {
+ if ((portc->audio_enabled & PCM_ENABLE_OUTPUT) &&
+ (portc->trigger_bits & PCM_ENABLE_OUTPUT))
+ {
+ portc->audio_enabled &= ~PCM_ENABLE_OUTPUT;
+ portc->trigger_bits &= ~PCM_ENABLE_OUTPUT;
+
+ devc->tab[portc->devs_opened] = 0;
+ if (portc->channels > 1)
+ devc->tab[portc->devs_opened - 1] = 0;
+ }
+ }
+ }
+
+ if ((portc->open_mode & OPEN_READ)
+ && !(audio_engines[dev]->flags & ADEV_NOINPUT))
+ {
+ if (state & PCM_ENABLE_INPUT)
+ {
+ if ((portc->audio_enabled & PCM_ENABLE_INPUT) &&
+ !(portc->trigger_bits & PCM_ENABLE_INPUT))
+ {
+ /* 0x01 = REC SLOT, 0x02 = ADC SLOT */
+ if (devc->spdif_in)
+ WRITEL (MAP_OF_REC, 0x1);
+ else
+ WRITEL (MAP_OF_REC, 0x2);
+ WRITEL (CONTROL_SELECT, 0);
+ WRITEL (MODE, READL (MODE) | 0x00000003);
+ portc->trigger_bits |= PCM_ENABLE_INPUT;
+ }
+ }
+ else
+ {
+ if ((portc->audio_enabled & PCM_ENABLE_INPUT) &&
+ (portc->trigger_bits & PCM_ENABLE_INPUT))
+ {
+ portc->audio_enabled &= ~PCM_ENABLE_INPUT;
+ portc->trigger_bits &= ~PCM_ENABLE_INPUT;
+
+ /* set the map of rec 0 */
+ WRITEL (MAP_OF_REC, 0x0);
+ }
+ }
+ }
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+}
+
+static unsigned int
+SetFormatField (ymf7xx_portc * portc)
+{
+ unsigned int val;
+
+ val = 0x00000000;
+ if (portc->bits == 8)
+ val |= 0x80000000;
+ if (portc->channels > 1)
+ val |= 0x00010000;
+ return val;
+}
+
+static unsigned int
+SetPgDeltaField (ymf7xx_portc * portc)
+{
+ unsigned int val;
+
+#if 0
+ double x;
+ x = (double) portc->speed / 48000.0;
+ val = (int) (x * (1 << 28)) & 0x7FFFFF00;
+#else
+ oss_native_word x;
+ x = (portc->speed * (1 << 15) + 187) / 375;
+ val = (x * (1 << 6)) & 0x7FFFFF00;
+#endif
+ return val;
+}
+
+static unsigned int
+SetLpfKField (ymf7xx_portc * portc)
+{
+ unsigned int i, val = 0, sr = portc->speed;
+ int freq[8] = { 100, 2000, 8000, 11025, 16000, 22050, 32000, 48000 };
+ int LpfK[8] =
+ { 0x0057, 0x06aa, 0x18B2, 0x2093, 0x2b9a, 0x35a1, 0x3eaa, 0x4000 };
+
+ if (sr == 44100)
+ {
+ val = 0x4646 << 16;
+ }
+ else
+ {
+ for (i = 0; i < 8; i++)
+ {
+ if (sr <= freq[i])
+ {
+ val = LpfK[i] << 16;
+ break;
+ }
+ }
+ }
+ return val;
+}
+
+static unsigned int
+SetLpfQField (ymf7xx_portc * portc)
+{
+ unsigned int i, val = 0, sr = portc->speed;
+ int freq[8] = { 100, 2000, 8000, 11025, 16000, 22050, 32000, 48000 };
+ int LpfQ[8] =
+ { 0x3528, 0x34a7, 0x3202, 0x3177, 0x3139, 0x31c9, 0x33d0, 0x4000 };
+
+ if (sr == 44100)
+ {
+ val = 0x370A << 16;
+ }
+ else
+ {
+ for (i = 0; i < 8; i++)
+ {
+ if (sr <= freq[i])
+ {
+ val = LpfQ[i] << 16;
+ break;
+ }
+ }
+ }
+ return val;
+}
+
+void
+SetupRecSlot (int dev)
+{
+ ymf7xx_devc *devc = audio_engines[dev]->devc;
+ ymf7xx_portc *portc = audio_engines[dev]->portc;
+ dmap_t *dmap = audio_engines[dev]->dmap_in;
+ int banksize;
+
+ banksize = READL (REC_CNTRL_SIZE);
+ portc->recslot.base = (devc->rec_table_virt);
+
+ /* banks 1 and 2 are for REC Slot and 3 and 4 are for ADC Slot */
+ if (devc->spdif_in)
+ {
+ portc->recslot.bank1 =
+ (REC_BANK *) (portc->recslot.base + (banksize * 0));
+ portc->recslot.bank2 =
+ (REC_BANK *) (portc->recslot.base + (banksize * 4));
+
+ WRITEB (REC_FORMAT,
+ (unsigned char) ((portc->channels & 0x02) +
+ ((portc->bits & 0x08) >> 3)));
+ WRITEW (REC_SAMPLING_RATE,
+ (unsigned short) (48000 * 4096 / portc->speed) - 1);
+
+ portc->recslot.bank1->PgBase = portc->recslot.bank2->PgBase = LSWAP (dmap->dmabuf_phys); /* use the REC slot */
+
+ portc->recslot.bank1->PgLoopEndAdr = portc->recslot.bank2->PgLoopEndAdr = LSWAP (dmap->bytes_in_use); /* use the REC slot */
+ }
+ else
+ {
+ portc->recslot.bank3 =
+ (REC_BANK *) (portc->recslot.base + (banksize * 8));
+ portc->recslot.bank4 =
+ (REC_BANK *) (portc->recslot.base + (banksize * 12));
+
+ WRITEB (ADC_FORMAT,
+ (unsigned char) ((portc->channels & 0x02) +
+ ((portc->bits & 0x08) >> 3)));
+ WRITEW (ADC_SAMPLING_RATE,
+ (unsigned short) (48000 * 4096 / portc->speed) - 1);
+
+ portc->recslot.bank3->PgBase = portc->recslot.bank4->PgBase = LSWAP (dmap->dmabuf_phys); /* use the ADC slot */
+
+ portc->recslot.bank3->PgLoopEndAdr = portc->recslot.bank4->PgLoopEndAdr = LSWAP (dmap->bytes_in_use); /* use the ADC slot */
+ }
+
+}
+
+/*ARGSUSED*/
+static int
+ymf7xx_audio_prepare_for_input (int dev, int bsize, int bcount)
+{
+ ymf7xx_devc *devc = audio_engines[dev]->devc;
+ ymf7xx_portc *portc = audio_engines[dev]->portc;
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ SetupRecSlot (dev);
+
+ /* set the input level to max and go! */
+ WRITEL (NATIVE_REC_INPUT, 0xffffffff);
+ WRITEL (NATIVE_ADC_INPUT, 0xffffffff);
+ portc->audio_enabled &= ~PCM_ENABLE_INPUT;
+ portc->trigger_bits &= ~PCM_ENABLE_INPUT;
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ return 0;
+}
+
+void
+SetupEffectSlot (int dev)
+{
+ ymf7xx_devc *devc = audio_engines[dev]->devc;
+ ymf7xx_portc *portc = audio_engines[dev]->portc;
+
+ int banksize;
+
+ banksize = READL (EFF_CNTRL_SIZE);
+ portc->effectslot.base = (devc->effect_table_virt);
+
+ /* slots 1-5 each having 2 banks */
+ portc->effectslot.bank1 =
+ (EFFECT_BANK *) (portc->effectslot.base + (banksize * 0));
+ portc->effectslot.bank2 =
+ (EFFECT_BANK *) (portc->effectslot.base + (banksize * 4));
+ portc->effectslot.bank3 =
+ (EFFECT_BANK *) (portc->effectslot.base + (banksize * 8));
+ portc->effectslot.bank4 =
+ (EFFECT_BANK *) (portc->effectslot.base + (banksize * 12));
+ portc->effectslot.bank5 =
+ (EFFECT_BANK *) (portc->effectslot.base + (banksize * 16));
+ portc->effectslot.bank6 =
+ (EFFECT_BANK *) (portc->effectslot.base + (banksize * 20));
+ portc->effectslot.bank7 =
+ (EFFECT_BANK *) (portc->effectslot.base + (banksize * 24));
+ portc->effectslot.bank8 =
+ (EFFECT_BANK *) (portc->effectslot.base + (banksize * 28));
+ portc->effectslot.bank9 =
+ (EFFECT_BANK *) (portc->effectslot.base + (banksize * 32));
+ portc->effectslot.bank10 =
+ (EFFECT_BANK *) (portc->effectslot.base + (banksize * 36));
+
+#if 0
+/* Dry Left Channel */
+ portc->effectslot.bank1->PgBase = portc->effectslot.bank2->PgBase =
+ LSWAP (devc->eff_buf_phys + 0 * 8192);
+ portc->effectslot.bank1->PgLoopEnd = portc->effectslot.bank2->PgLoopEnd =
+ LSWAP (4096);
+ portc->effectslot.bank1->PgStart = portc->effectslot.bank2->PgStart = 0;
+
+/* Dry Right Channel */
+ portc->effectslot.bank3->PgBase = portc->effectslot.bank4->PgBase =
+ LSWAP (devc->eff_buf_phys + 1 * 8192);
+ portc->effectslot.bank3->PgLoopEnd = portc->effectslot.bank4->PgLoopEnd =
+ LSWAP (4096);
+ portc->effectslot.bank3->PgStart = portc->effectslot.bank4->PgStart = 0;
+
+/* Effect 1 */
+ portc->effectslot.bank5->PgBase = portc->effectslot.bank6->PgBase =
+ LSWAP (devc->eff_buf_phys + 2 * 8192);
+ portc->effectslot.bank5->PgLoopEnd = portc->effectslot.bank6->PgLoopEnd =
+ LSWAP (4096);
+ portc->effectslot.bank5->PgStart = portc->effectslot.bank6->PgStart = 0;
+
+#endif
+
+/* Effect 2 */
+ portc->effectslot.bank7->PgBase = portc->effectslot.bank8->PgBase =
+ LSWAP (devc->eff_buf_phys + 0 * 8192);
+ portc->effectslot.bank7->PgLoopEnd = portc->effectslot.bank8->PgLoopEnd =
+ LSWAP (4096);
+ portc->effectslot.bank7->PgStart = portc->effectslot.bank8->PgStart = 0;
+
+/* Effect 3 */
+ portc->effectslot.bank9->PgBase = portc->effectslot.bank10->PgBase =
+ LSWAP (devc->eff_buf_phys + 1 * 8192);
+ portc->effectslot.bank9->PgLoopEnd = portc->effectslot.bank10->PgLoopEnd =
+ LSWAP (4096);
+ portc->effectslot.bank9->PgStart = portc->effectslot.bank10->PgStart = 0;
+
+ WRITEL (AC97_SEC_CONFIG, (READL (AC97_SEC_CONFIG) & ~0x0030) | 0x0010);
+ WRITEL (MAP_OF_EFFECTS, 0x18); /* effect 2,3 */
+ WRITEL (MODE, READL (MODE) | (1 << 30)); /* AC3 Setup */
+}
+
+
+void
+SetupPlayBank (int dev, int slot, oss_native_word sbase)
+{
+ ymf7xx_devc *devc = audio_engines[dev]->devc;
+ ymf7xx_portc *portc = audio_engines[dev]->portc;
+ dmap_t *dmap = audio_engines[dev]->dmap_out;
+
+ PLAY_BANK *bank1;
+ PLAY_BANK *bank2;
+ bank1 = (PLAY_BANK *) sbase;
+ bank2 =
+ (PLAY_BANK *) (sbase + READL (PLAY_CNTRL_SIZE) * sizeof (unsigned int));
+
+ memset ((void *) bank1, 0, sizeof (PLAY_BANK));
+ memset ((void *) bank2, 0, sizeof (PLAY_BANK));
+
+ if (slot == 2)
+ {
+ portc->bank3 = bank1;
+ portc->bank4 = bank2;
+ }
+ else
+ {
+ portc->bank1 = bank1;
+ portc->bank2 = bank2;
+ }
+
+ /* setup format field */
+ bank1->Format = LSWAP (SetFormatField (portc));
+ bank1->EgGain = bank1->EgGainEnd = LSWAP (0x40000000);
+
+ if (portc->channels == 1)
+ {
+ /* Gain */
+ bank1->LchGain = bank1->LchGainEnd = LSWAP (0x40000000);
+ bank1->RchGain = bank1->RchGainEnd = LSWAP (0x40000000);
+ bank1->Effect2Gain = bank1->Effect2GainEnd = LSWAP (0x40000000);
+ bank1->Effect3Gain = bank1->Effect3GainEnd = LSWAP (0x40000000);
+ }
+ else
+ {
+ if (slot == 2)
+ {
+ bank1->Format = LSWAP (LSWAP (bank1->Format) + 1);
+ bank1->RchGain = bank1->RchGainEnd = LSWAP (0x40000000);
+ bank1->Effect2Gain = bank1->Effect2GainEnd = LSWAP (0x40000000);
+ }
+ else
+ {
+ bank1->LchGain = bank1->LchGainEnd = LSWAP (0x40000000);
+ bank1->Effect3Gain = bank1->Effect3GainEnd = LSWAP (0x40000000);
+ }
+ }
+
+ bank1->LoopDefault = 0;
+ bank1->NumOfFrames = 0;
+ bank1->LoopCount = 0;
+ bank1->PgStart = 0;
+ bank1->PgLoop = 0;
+
+ /* PgBase */
+ bank1->PgBase = LSWAP (dmap->dmabuf_phys);
+
+ /* PgLoopEnd */
+ bank1->PgLoopEnd = LSWAP (dmap->bytes_in_use / portc->dacfmt);
+
+ /* PgDelta & PgDeltaEnd */
+ bank1->PgDelta = bank1->PgDeltaEnd = LSWAP (SetPgDeltaField (portc));
+
+ if (portc->channels == 4)
+ bank1->PgDelta = bank1->PgDeltaEnd *= 2;
+
+ /* LpfK & LpfKEnd */
+ bank1->LpfK = bank1->LpfKEnd = LSWAP (SetLpfKField (portc));
+
+ /* LpfQ */
+ bank1->LpfQ = LSWAP (SetLpfQField (portc));
+
+ memcpy (bank2, bank1, sizeof (PLAY_BANK));
+}
+
+int
+SetupPlaySlot (int dev, int slot)
+{
+ int tmp;
+ oss_native_word sltbase;
+ oss_native_word physbase;
+
+ ymf7xx_devc *devc = audio_engines[dev]->devc;
+ ymf7xx_portc *portc = audio_engines[dev]->portc;
+
+ tmp = slot * 2 + 1;
+ sltbase = (oss_native_word) devc->slt + (tmp * 0x120);
+ physbase = devc->slt_phys + (tmp * 0x120);
+ SetupPlayBank (dev, 1, sltbase);
+ devc->tab[tmp] = LSWAP (physbase);
+
+ if (portc->channels > 1)
+ {
+ sltbase += 0x120;
+ physbase += 0x120;
+ SetupPlayBank (dev, 2, sltbase);
+ tmp++;
+ devc->tab[tmp] = LSWAP (physbase);
+ }
+ return tmp;
+}
+
+/*ARGSUSED*/
+static int
+ymf7xx_audio_prepare_for_output (int dev, int bsize, int bcount)
+{
+ ymf7xx_devc *devc = audio_engines[dev]->devc;
+ ymf7xx_portc *portc = audio_engines[dev]->portc;
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+
+ if (portc->bits == AFMT_AC3)
+ {
+ portc->channels = 2;
+ portc->bits = 16;
+ ymf7xx_spdif_control (devc->mixer_dev, 1, SNDCTL_MIX_WRITE, 1); /* enable SPDIF */
+ ymf7xx_spdif_control (devc->mixer_dev, 4, SNDCTL_MIX_WRITE, 1); /* enable AC3 */
+ }
+ else
+ ymf7xx_spdif_control (devc->mixer_dev, 4, SNDCTL_MIX_WRITE, 0); /* enable AC3 */
+
+
+ portc->dacfmt = portc->channels * (portc->bits / 8);
+
+ if (portc->dacfmt > 4)
+ portc->dacfmt = 4;
+ /* portc->devs_opened = SetupPlaySlot (dev, portc->devnum); -> trigger */
+ /* set effects slot */
+ SetupEffectSlot (dev);
+
+ if (portc->bits != AFMT_AC3)
+ WRITEL (NATIVE_DAC, 0xFFFFFFFF);
+ else
+ WRITEL (NATIVE_DAC, 0x00000000);
+
+ portc->audio_enabled &= ~PCM_ENABLE_OUTPUT;
+ portc->trigger_bits &= ~PCM_ENABLE_OUTPUT;
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ return 0;
+}
+
+/*ARGSUSED*/
+static int
+ymf7xx_get_buffer_pointer (int dev, dmap_t * dmap, int direction)
+{
+ ymf7xx_portc *portc = audio_engines[dev]->portc;
+
+ unsigned int ptr = 0;
+
+ if (direction == PCM_ENABLE_OUTPUT)
+ {
+ ptr = LSWAP (portc->bank1->PgStart);
+ ptr *= portc->dacfmt;
+ }
+ if (direction == PCM_ENABLE_INPUT)
+ {
+ ptr = LSWAP (portc->recslot.bank3->PgStartAdr);
+ }
+ return ptr;
+}
+
+static audiodrv_t ymf7xx_audio_driver = {
+ ymf7xx_audio_open,
+ ymf7xx_audio_close,
+ ymf7xx_audio_output_block,
+ ymf7xx_audio_start_input,
+ ymf7xx_audio_ioctl,
+ ymf7xx_audio_prepare_for_input,
+ ymf7xx_audio_prepare_for_output,
+ ymf7xx_audio_reset,
+ NULL,
+ NULL,
+ ymf7xx_audio_reset_input,
+ ymf7xx_audio_reset_output,
+ ymf7xx_audio_trigger,
+ ymf7xx_audio_set_rate,
+ ymf7xx_audio_set_format,
+ ymf7xx_audio_set_channels,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL, /* ymf7xx_alloc_buffer */
+ NULL, /* ymf7xx_free_buffer */
+ NULL,
+ NULL,
+ ymf7xx_get_buffer_pointer
+};
+
+#if 0
+static void
+init_audio (ymf7xx_devc * devc)
+{
+ devc->audio_initialized = 1;
+}
+
+uninit_audio (ymf7xx_devc * devc)
+{
+ devc->audio_initialized = 0;
+ WRITEL (CONFIG, 0);
+ WRITEL (MODE, 0);
+}
+#endif
+
+#ifdef OBSOLETED_STUFF
+/*
+ * This device has "ISA style" MIDI and FM subsystems. Such devices don't
+ * use PCI config space for the I/O ports and interrupts. Instead the driver
+ * needs to allocate proper resources itself. This functionality is no longer
+ * possible. For this reason the MIDI and FM parts are not accessible.
+ */
+static void
+attach_fm (ymf7xx_devc * devc)
+{
+ devc->fm_attached = 0;
+ if (!opl3_detect (0x388, devc->osdev))
+ {
+ cmn_err (CE_WARN, "OPL3 not detected\n");
+ return;
+ }
+ opl3_init (0x388, devc->osdev);
+ devc->fm_attached = 1;
+}
+
+
+static void
+attach_mpu (ymf7xx_devc * devc)
+{
+ struct address_info hw_config;
+
+ hw_config.io_base = devc->mpu_base;
+ hw_config.irq = devc->mpu_irq;
+ hw_config.dma = -1;
+ hw_config.dma2 = -1;
+ hw_config.always_detect = 0;
+ hw_config.name = "Yamaha DS-XG MPU401";
+ hw_config.driver_use_1 = 0;
+ hw_config.driver_use_2 = 0;
+ hw_config.osdev = devc->osdev;
+#ifdef CREATE_OSP
+ CREATE_OSP (hw_config.osdev);
+#endif
+ hw_config.card_subtype = 0;
+
+ if (!probe_uart401 (&hw_config))
+ {
+ cmn_err (CE_WARN, "MPU-401 was not detected\n");
+ return;
+ }
+ DDB (cmn_err (CE_WARN, "MPU-401 detected - Good\n"));
+ devc->mpu_attached = 1;
+ attach_uart401 (&hw_config);
+}
+
+static void
+unload_mpu (ymf7xx_devc * devc)
+{
+ struct address_info hw_config;
+
+ hw_config.io_base = devc->mpu_base;
+ hw_config.irq = devc->mpu_irq;
+ hw_config.dma = -1;
+ hw_config.dma2 = -1;
+ hw_config.always_detect = 0;
+ hw_config.name = "Yahama DS-XG MPU401";
+ hw_config.driver_use_1 = 0;
+ hw_config.driver_use_2 = 0;
+ hw_config.osdev = devc->osdev;
+#ifdef CREATE_OSP
+ CREATE_OSP (hw_config.osdev);
+#endif
+ hw_config.card_subtype = 0;
+
+ devc->mpu_attached = 0;
+ unload_uart401 (&hw_config);
+}
+#endif
+
+int
+ymf7xx_spdif_control (int dev, int ctrl, unsigned int cmd, int value)
+{
+ ymf7xx_devc *devc = mixer_devs[dev]->hw_devc;
+/* int left, right; */
+
+ if (cmd == SNDCTL_MIX_READ)
+ {
+ value = 0;
+ switch (ctrl)
+ {
+ case 1: /* S/PDIF Enable/Disable */
+ value = READL (SPDIFOUT_CONTROL) & 0x1;
+ break;
+
+ case 2: /* S/PDIF Record Enable */
+ value = devc->spdif_in;
+ break;
+
+ case 3: /* S/PDIF Loopback */
+ value = READL (SPDIFIN_CONTROL) & (1 << 4);
+ break;
+
+ case 4: /* AC3 Output */
+ value = READL (SPDIFOUT_CONTROL) & (1 << 1);
+ break;
+#if 0
+ case 5: /* CopyProtection Bit */
+ value = READL (SPDIFOUT_STATUS) & (1 << 2);
+ break;
+ case 6: /* SPDIF OUTVOL */
+ value = devc->mixlevels[0];
+ break;
+ case 7: /* SPDIF LOOPVOL */
+ value = devc->mixlevels[1];
+ break;
+ case 8: /* SPDIF AC3VOL */
+ value = devc->mixlevels[2];
+ break;
+#endif
+ }
+ }
+ if (cmd == SNDCTL_MIX_WRITE)
+ {
+ switch (ctrl)
+ {
+ case 1: /* S/PDIF OUTPUT ENABLE/DISABLE */
+ if (value)
+ WRITEL (SPDIFOUT_CONTROL, READL (SPDIFOUT_CONTROL) | 0x1);
+ else
+ WRITEL (SPDIFOUT_CONTROL, READL (SPDIFOUT_CONTROL) & ~0x1);
+ break;
+
+ case 2: /* Record S/PDIF IN ENABLE DISABLE */
+ if (value)
+ {
+ WRITEL (SPDIFIN_CONTROL, READL (SPDIFIN_CONTROL) | 0x1);
+ devc->spdif_in = 1;
+ }
+ else
+ {
+ WRITEL (SPDIFIN_CONTROL, READL (SPDIFIN_CONTROL) & ~0x1);
+ devc->spdif_in = 0;
+ }
+ break;
+
+ case 3: /* S/PDIF Loopback Mode */
+ if (value)
+ WRITEL (SPDIFIN_CONTROL, READL (SPDIFIN_CONTROL) | (1 << 4));
+ else
+ WRITEL (SPDIFIN_CONTROL, READL (SPDIFIN_CONTROL) & ~(1 << 4));
+ break;
+
+ case 4: /* AC3 Output Mode */
+ if (value)
+ WRITEL (SPDIFOUT_CONTROL, READL (SPDIFOUT_CONTROL) | (1 << 1));
+ else
+ WRITEL (SPDIFOUT_CONTROL, READL (SPDIFOUT_CONTROL) & ~(1 << 1));
+ break;
+
+#if 0
+ case 5: /* Copy Protect Mode */
+ {
+ int ac3_mode;
+
+ ac3_mode = READL (SPDIFOUT_CONTROL) & (1 << 1);
+
+ if (value)
+ {
+ if (ac3_mode)
+ WRITEL (SPDIFOUT_STATUS,
+ READL (SPDIFOUT_STATUS) | (1 << 1));
+ else
+ WRITEL (SPDIFOUT_STATUS, READL (SPDIFOUT_STATUS) & ~0x3E);
+ }
+ else
+ {
+ if (ac3_mode)
+ WRITEL (SPDIFOUT_STATUS,
+ READL (SPDIFOUT_STATUS) | (3 << 1));
+ else
+
+ WRITEL (SPDIFOUT_STATUS,
+ READL (SPDIFOUT_STATUS) | (1 << 2));
+ }
+ }
+ break;
+
+ case 6:
+ left = value & 0xff;
+ right = (value >> 8) & 0xff;
+ if (left > 100)
+ left = 100;
+ if (right > 100)
+ right = 100;
+ value = left | (right << 8);
+ left = left * 0xFFFF / 100;
+ right = right * 0xFFFF / 100;
+ WRITEL (SPDIFOUTVOL, left << 16 | right);
+ devc->mixlevels[0] = value;
+ break;
+ case 7:
+ left = value & 0xff;
+ right = (value >> 8) & 0xff;
+ if (left > 100)
+ left = 100;
+ if (right > 100)
+ right = 100;
+ value = left | (right << 8);
+ left = left * 0xFFFF / 100;
+ right = right * 0xFFFF / 100;
+ WRITEL (SPDIFLOOPVOL, left << 16 | right);
+ devc->mixlevels[1] = value;
+ break;
+ case 8:
+ left = value & 0xff;
+ right = (value >> 8) & 0xff;
+ if (left > 100)
+ left = 100;
+ if (right > 100)
+ right = 100;
+ value = left | (right << 8);
+ left = left * 0xFFFF / 100;
+ right = right * 0xFFFF / 100;
+ WRITEL (AC3_OUTPUT, left << 16 | right);
+ devc->mixlevels[2] = value;
+ break;
+#endif
+ }
+ }
+ return value;
+}
+
+static int
+ymf7xx_mix_init (int dev)
+{
+ int group, err;
+
+ if ((group = mixer_ext_create_group (dev, 0, "SPDIF")) < 0)
+ return group;
+
+ if ((err = mixer_ext_create_control (dev, group, 1, ymf7xx_spdif_control,
+ MIXT_ONOFF, "ENABLE", 1,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+
+ if ((err = mixer_ext_create_control (dev, group, 2, ymf7xx_spdif_control,
+ MIXT_ONOFF, "RECORD", 1,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+
+ if ((err = mixer_ext_create_control (dev, group, 3, ymf7xx_spdif_control,
+ MIXT_ONOFF, "LOOP", 1,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+
+#if 0
+ if ((err = mixer_ext_create_control (dev, group, 4, ymf7xx_spdif_control,
+ MIXT_ONOFF, "AC3", 1,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+ if ((err = mixer_ext_create_control (dev, group, 5, ymf7xx_spdif_control,
+ MIXT_ONOFF, "COPYPROT", 1,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+
+ if ((err = mixer_ext_create_control (dev, group, 6, ymf7xx_spdif_control,
+ MIXT_STEREOSLIDER, "OUTVOL", 100,
+ MIXF_MAINVOL | MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+ if ((err = mixer_ext_create_control (dev, group, 7, ymf7xx_spdif_control,
+ MIXT_STEREOSLIDER, "LOOPVOL", 100,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+ if ((err = mixer_ext_create_control (dev, group, 8, ymf7xx_spdif_control,
+ MIXT_STEREOSLIDER, "AC3VOL", 100,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+#endif
+
+ return 0;
+}
+
+static int
+init_ymf7xx (ymf7xx_devc * devc)
+{
+
+ int my_mixer, i;
+ int first_dev = 0;
+
+ WRITEL (NATIVE_DAC, 0x00000000);
+ WRITEL (MODE, 0x00010000);
+ WRITEL (MODE, 0x00000000);
+ WRITEL (MAP_OF_REC, 0x00000000);
+ WRITEL (MAP_OF_EFFECTS, 0x00000000);
+ WRITEL (PLAY_CNTRL_BASE, 0x00000000);
+ WRITEL (REC_CNTRL_BASE, 0x00000000);
+ WRITEL (EFF_CNTRL_BASE, 0x00000000);
+ WRITEL (CONTROL_SELECT, 1);
+ WRITEL (GLOBAL_CONTROL, READL (GLOBAL_CONTROL) & ~0x0007);
+ WRITEL (ZVOUTVOL, 0xFFFFFFFF);
+ WRITEL (ZVLOOPVOL, 0xFFFFFFFF);
+ WRITEL (SPDIFOUTVOL, 0xFFFFFFFF);
+ WRITEL (SPDIFLOOPVOL, 0x3FFF3FFF);
+ devc->spdif_in = 0;
+
+ if (ac97_read (devc, 0x02) == 0xFFFF)
+ {
+ for (i = 0; i < 100; i++)
+ if (ac97_read (devc, 0x02) != 0xFFFF)
+ {
+ break;
+ }
+ }
+
+ install_ucode (devc, 0x1000, dsp, dsp_size);
+ switch (devc->deviceid)
+ {
+ case YAMAHA_YMF724F_ID:
+ case YAMAHA_YMF740C_ID:
+ case YAMAHA_YMF744_ID:
+ case YAMAHA_YMF754_ID:
+ install_ucode (devc, 0x4000, cntrl1E, cntrl1E_size);
+ break;
+ default:
+ install_ucode (devc, 0x4000, cntrl, cntrl_size);
+ }
+ WRITEL (CONFIG, 1);
+
+/* add an extra reset to init the mixers */
+ ac97_write (devc, 0x02, 0x0000);
+ ac97_write (devc, 0x18, 0x0808);
+
+/* Now check to see if the DSP is started or not */
+ {
+ int fEnd;
+ i = 10000;
+ do
+ {
+ if (i-- <= 0)
+ {
+ cmn_err (CE_WARN, "CTR/DSP Init/ TimeOut\n");
+ return 0;
+ }
+ fEnd = READL (CONTROL_SELECT) & 0x1;
+ }
+ while (fEnd == 0x1);
+ }
+
+ my_mixer =
+ ac97_install (&devc->ac97devc, "Yamaha DS-XG", ac97_read, ac97_write,
+ devc, devc->osdev);
+ if (my_mixer >= 0)
+ {
+ devc->mixer_dev = my_mixer;
+ mixer_ext_set_init_fn (my_mixer, ymf7xx_mix_init, 20);
+ }
+ else
+ return 0;
+
+#ifdef OBSOLETED_STUFF
+ if (devc->fm_base > 0)
+ attach_fm (devc);
+ if (devc->mpu_base > 0)
+ attach_mpu (devc);
+#endif
+
+ if (devc->play_table_virt == 0)
+ {
+ /* Allocate the Play Table */
+ oss_native_word phaddr;
+ devc->dmabuf1 =
+ CONTIG_MALLOC (devc->osdev, 0x1000, MEMLIMIT_32BITS, &phaddr, devc->dmabuf1_dma_handle);
+ devc->play_table_virt = (oss_native_word) devc->dmabuf1;
+ devc->play_table = phaddr;
+
+ /* Allocate Effect Table */
+ devc->dmabuf2 =
+ CONTIG_MALLOC (devc->osdev, 1024, MEMLIMIT_32BITS, &phaddr, devc->dmabuf2_dma_handle);
+ devc->effect_table_virt = (oss_native_word) devc->dmabuf2;
+ devc->effect_table = phaddr;
+
+ /* Allocate Effect Scratch Buffer */
+ devc->eff_buf =
+ CONTIG_MALLOC (devc->osdev, 2 * 8192, MEMLIMIT_32BITS, &phaddr, devc->eff_buf_dma_handle);
+ devc->eff_buf_phys = phaddr;
+
+ /* Allocate the Record Table */
+ devc->dmabuf3 =
+ CONTIG_MALLOC (devc->osdev, 1024, MEMLIMIT_32BITS, &phaddr, devc->dmabuf3_dma_handle);
+ devc->rec_table_virt = (oss_native_word) devc->dmabuf3;
+ devc->rec_table = phaddr;
+
+ /* Setup Play Table */
+ devc->tab = (unsigned int *) devc->play_table_virt;
+ devc->slt = (oss_native_word) (devc->play_table_virt + 0x100);
+ devc->slt_phys = (devc->play_table + 0x100);
+ memset ((void *) devc->tab, 0, 0x1000);
+ WRITEL (PLAY_CNTRL_BASE, (unsigned int) devc->play_table);
+ devc->tab[0] = LSWAP (20); /* setup 20 slots for 8 playback devices */
+ /* Setup Effect Table and init Effect Slots */
+ devc->effecttab = (unsigned int *) devc->effect_table_virt;
+ memset ((void *) devc->effecttab, 0, 1024);
+ WRITEL (EFF_CNTRL_BASE, (unsigned int) devc->effect_table);
+
+ //SetupEffectSlot (dev);
+
+ /* Setup Record Table */
+ devc->rectab = (unsigned int *) devc->rec_table_virt;
+ memset ((void *) devc->rectab, 0, 1024);
+ WRITEL (REC_CNTRL_BASE, (unsigned int) devc->rec_table);
+ }
+
+ for (i = 0; i < MAX_PORTC; i++)
+ {
+ int adev;
+ char tmp_name[100];
+ ymf7xx_portc *portc = &devc->portc[i];
+ int caps = ADEV_AUTOMODE | ADEV_NOVIRTUAL;
+
+ if (i == 0)
+ {
+ strcpy (tmp_name, devc->chip_name);
+ caps |= ADEV_DUPLEX;
+ }
+ else
+ {
+ strcpy (tmp_name, devc->chip_name);
+ caps |= ADEV_NOINPUT;
+ if (i > 1)
+ caps |= ADEV_SHADOW;
+ }
+
+
+ if ((adev = oss_install_audiodev (OSS_AUDIO_DRIVER_VERSION,
+ devc->osdev,
+ devc->osdev,
+ tmp_name,
+ &ymf7xx_audio_driver,
+ sizeof (audiodrv_t),
+ caps,
+ AFMT_U8 | AFMT_S16_LE | AFMT_AC3,
+ devc, -1)) < 0)
+ {
+ adev = -1;
+ return 0;
+ }
+ else
+ {
+ if (i == 0)
+ first_dev = adev;
+ audio_engines[adev]->mixer_dev = my_mixer;
+ audio_engines[adev]->portc = portc;
+ audio_engines[adev]->rate_source = first_dev;
+ audio_engines[adev]->min_block = 64;
+ audio_engines[adev]->min_rate = 5000;
+ audio_engines[adev]->max_rate = 48000;
+ audio_engines[adev]->vmix_flags |= VMIX_MULTIFRAG;
+ audio_engines[adev]->caps |= PCM_CAP_FREERATE;
+ audio_engines[adev]->min_channels = 2;
+ audio_engines[adev]->max_channels = 4;
+ portc->open_mode = 0;
+ portc->audiodev = adev;
+ portc->devnum = i;
+#ifdef CONFIG_OSS_VMIX
+ if (i == 0)
+ vmix_attach_audiodev(devc->osdev, adev, -1, 0);
+#endif
+ }
+ }
+ return 1;
+}
+
+int
+oss_ymf7xx_attach (oss_device_t * osdev)
+{
+
+
+ unsigned char pci_irq_line, pci_revision;
+ unsigned short pci_command, vendor, device;
+ unsigned char pci_reset;
+ unsigned short pci_legacy;
+ ymf7xx_devc *devc;
+
+ DDB (cmn_err (CE_WARN, "Entered DS-XG attach routine\n"));
+
+ pci_read_config_word (osdev, PCI_VENDOR_ID, &vendor);
+ pci_read_config_word (osdev, PCI_DEVICE_ID, &device);
+
+ if (((vendor != YAMAHA_VENDOR_ID)) ||
+ (device != YAMAHA_YMF740_ID &&
+ device != YAMAHA_YMF744_ID &&
+ device != YAMAHA_YMF754_ID &&
+ device != YAMAHA_YMF740C_ID &&
+ device != YAMAHA_YMF724_ID &&
+ device != YAMAHA_YMF734_ID && device != YAMAHA_YMF724F_ID))
+ return 0;
+
+ oss_pci_byteswap (osdev, 1);
+
+ if ((devc = PMALLOC (osdev, sizeof (*devc))) == NULL)
+ {
+ cmn_err (CE_WARN, "Out of memory\n");
+ return 0;
+ }
+
+ devc->osdev = osdev;
+ osdev->devc = devc;
+
+ pci_read_config_byte (osdev, PCI_REVISION_ID, &pci_revision);
+ pci_read_config_word (osdev, PCI_COMMAND, &pci_command);
+ pci_read_config_irq (osdev, PCI_INTERRUPT_LINE, &pci_irq_line);
+ pci_read_config_dword (osdev, PCI_MEM_BASE_ADDRESS_0, &devc->base0addr);
+
+ if (devc->base0addr == 0)
+ {
+ cmn_err (CE_WARN, "I/O address not assigned by BIOS.\n");
+ return 0;
+ }
+
+ if (pci_irq_line == 0)
+ {
+ cmn_err (CE_WARN, "IRQ not assigned by BIOS (%d).\n", pci_irq_line);
+ return 0;
+ }
+
+ DDB (cmn_err
+ (CE_WARN, "rev %x I/O base %04x\n", pci_revision, devc->base0addr));
+
+ devc->deviceid = device;
+ switch (device)
+ {
+ case YAMAHA_YMF724_ID:
+ devc->chip_name = "Yamaha YMF724";
+ break;
+
+ case YAMAHA_YMF724F_ID:
+ devc->chip_name = "Yamaha YMF724F";
+ break;
+
+ case YAMAHA_YMF734_ID:
+ devc->chip_name = "Yamaha YMF734";
+ break;
+
+ case YAMAHA_YMF740_ID:
+ devc->chip_name = "Yamaha YMF740";
+ break;
+
+ case YAMAHA_YMF740C_ID:
+ devc->chip_name = "Yamaha YMF740C";
+ break;
+
+ case YAMAHA_YMF744_ID:
+ devc->chip_name = "Yamaha YMF744";
+ break;
+
+ case YAMAHA_YMF754_ID:
+ devc->chip_name = "Yamaha YMF754";
+ break;
+
+ default:
+ devc->chip_name = "Yamaha DS-XG";
+ }
+
+ devc->fm_base = yamaha_fm_ioaddr;
+ devc->mpu_base = yamaha_mpu_ioaddr;
+ devc->mpu_irq = yamaha_mpu_irq;
+
+ /* reset the device */
+ pci_read_config_byte (osdev, 0x48, &pci_reset);
+
+ if (pci_reset & 0x03)
+ {
+ pci_write_config_byte (osdev, 0x48, pci_reset & 0xFC);
+ pci_write_config_byte (osdev, 0x48, pci_reset | 0x03);
+ pci_write_config_byte (osdev, 0x48, pci_reset & 0xFC);
+ }
+
+/* Legacy I/O setup - setup MPU and FM io/irq values */
+ devc->fm_attached = 0;
+ devc->mpu_attached = 0;
+ /*pcipci_legacy = 0x0020|0x00C0|0x0004; // 10 bit address decode and Joystick */
+ pci_legacy = 0x4;
+ if (devc->fm_base)
+ pci_legacy |= 0x0002; /* FM enable */
+
+ if (devc->mpu_base)
+ {
+ pci_legacy |= 0x0008; /* MPU I/O enable */
+
+ switch (devc->mpu_irq)
+ {
+ case 5:
+ pci_legacy |= 0x0010;
+ break;
+ case 7:
+ pci_legacy |= 0x0810;
+ break;
+ case 9:
+ pci_legacy |= 0x1010;
+ break;
+ case 10:
+ pci_legacy |= 0x1810;
+ break;
+ case 11:
+ pci_legacy |= 0x2010;
+ break;
+ default:
+ devc->mpu_irq = 0;
+ }
+ }
+
+ pci_write_config_word (osdev, 0x40, pci_legacy);
+
+ pci_legacy = 0x0000;
+
+ switch (devc->fm_base)
+ {
+ case 0x388:
+ pci_legacy |= 0x0000;
+ break;
+ case 0x398:
+ pci_legacy |= 0x0001;
+ break;
+ case 0x3a0:
+ pci_legacy |= 0x0002;
+ break;
+ case 0x3a8:
+ pci_legacy |= 0x0003;
+ break;
+ default:
+ devc->fm_base = 0;
+ }
+
+ switch (devc->mpu_base)
+ {
+ case 0x330:
+ pci_legacy |= 0x0000;
+ break;
+ case 0x300:
+ pci_legacy |= 0x0010;
+ break;
+ case 0x332:
+ pci_legacy |= 0x0020;
+ break;
+ case 0x334:
+ pci_legacy |= 0x0020;
+ break;
+ default:
+ devc->mpu_base = 0;
+ }
+ pci_write_config_word (osdev, 0x42, pci_legacy);
+
+ /* activate the device */
+ pci_command |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
+ pci_write_config_word (osdev, PCI_COMMAND, pci_command);
+
+ /* Map the shared memory area */
+ devc->base0virt =
+ (unsigned int *) MAP_PCI_MEM (devc->osdev, 0, devc->base0addr, 32 * 1024);
+ devc->dwRegister = (unsigned int *) devc->base0virt;
+ devc->wRegister = (unsigned short *) devc->base0virt;
+ devc->bRegister = (unsigned char *) devc->base0virt;
+
+
+ MUTEX_INIT (devc->osdev, devc->mutex, MH_DRV);
+ MUTEX_INIT (devc->osdev, devc->low_mutex, MH_DRV + 1);
+
+ oss_register_device (osdev, devc->chip_name);
+
+ if (oss_register_interrupts (devc->osdev, 0, ymf7xxintr, NULL) < 0)
+ {
+ cmn_err (CE_WARN, "Can't allocate IRQ%d\n", pci_irq_line);
+ return 0;
+ }
+
+ return init_ymf7xx (devc); /* Detected */
+}
+
+
+int
+oss_ymf7xx_detach (oss_device_t * osdev)
+{
+ ymf7xx_devc *devc = (ymf7xx_devc *) osdev->devc;
+
+ if (oss_disable_device (osdev) < 0)
+ return 0;
+
+ WRITEL (CONFIG, 0);
+ WRITEL (MODE, 0);
+
+ if (devc->dmabuf1)
+ {
+ CONTIG_FREE (devc->osdev, devc->dmabuf1, 0x1000, devc->dmabuf1_dma_handle);
+ devc->dmabuf1 = 0;
+ }
+
+ if (devc->dmabuf2)
+ {
+ CONTIG_FREE (devc->osdev, devc->dmabuf2, 1024, devc->dmabuf2_dma_handle);
+ devc->dmabuf2 = 0;
+ }
+
+ if (devc->eff_buf)
+ {
+ CONTIG_FREE (devc->osdev, devc->eff_buf, 16 * 1024, devc->eff_buf_dma_handle);
+ devc->eff_buf = 0;
+ }
+
+ if (devc->dmabuf3)
+ {
+ CONTIG_FREE (devc->osdev, devc->dmabuf3, 1024, devc->dmabuf3_dma_handle);
+ devc->dmabuf3 = 0;
+ }
+
+ devc->play_table_virt = 0;
+ devc->effect_table_virt = 0;
+ devc->rec_table_virt = 0;
+
+#ifdef OBSOLETED_STUFF
+ if (devc->mpu_attached)
+ {
+ unload_mpu (devc);
+ devc->mpu_attached = 0;
+ }
+#endif
+
+ oss_unregister_interrupts (devc->osdev);
+
+ MUTEX_CLEANUP (devc->mutex);
+ MUTEX_CLEANUP (devc->low_mutex);
+
+ if (devc->base0addr != 0)
+ {
+ UNMAP_PCI_MEM (devc->osdev, 0, devc->base0addr, devc->base0virt,
+ 32 * 1024);
+ devc->base0addr = 0;
+ }
+ oss_unregister_device (devc->osdev);
+ return 1;
+}