[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[cdi-devel] [PATCH] ac97: CDI.storage driver



From: Max Reitz <max@xxxxxxxxxx>

+ ac97 driver appears as CDI.storage driver (path on týndur:
  ac97:/crd0)

Signed-off-by: Max Reitz <max@xxxxxxxxxx>
---
 ac97/device.h |   65 +++++++++++++
 ac97/main.c   |  281 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 346 insertions(+), 0 deletions(-)
 create mode 100644 ac97/device.h
 create mode 100644 ac97/main.c

diff --git a/ac97/device.h b/ac97/device.h
new file mode 100644
index 0000000..382a1b2
--- /dev/null
+++ b/ac97/device.h
@@ -0,0 +1,65 @@
+/******************************************************************************
+ * Copyright (c) 2009 Max Reitz                                               *
+ *                                                                            *
+ * Permission is hereby granted,  free of charge,  to any  person obtaining a *
+ * copy of this software and associated documentation files (the "Software"), *
+ * to deal in the Software without restriction,  including without limitation *
+ * the rights to use,  copy, modify, merge, publish,  distribute, sublicense, *
+ * and/or sell copies  of the  Software,  and to permit  persons to whom  the *
+ * Software is furnished to do so, subject to the following conditions:       *
+ *                                                                            *
+ * The above copyright notice and this permission notice shall be included in *
+ * all copies or substantial portions of the Software.                        *
+ *                                                                            *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
+ * IMPLIED, INCLUDING  BUT NOT  LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
+ * FITNESS FOR A PARTICULAR  PURPOSE AND  NONINFRINGEMENT.  IN NO EVENT SHALL *
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
+ * LIABILITY,  WHETHER IN AN ACTION OF CONTRACT,  TORT OR OTHERWISE,  ARISING *
+ * FROM,  OUT OF  OR IN CONNECTION  WITH THE  SOFTWARE  OR THE  USE OR  OTHER *
+ * DEALINGS IN THE SOFTWARE.                                                  *
+ ******************************************************************************/
+
+#ifndef _AC97_DEVICE_H_
+#define _AC97_DEVICE_H_
+
+#define USE_BUFS 16
+#define FRAMES_PER_BUF 0x4000
+
+#define NAM_RESET          0x0000
+#define NAM_MASTER_VOLUME  0x0002
+#define NAM_MONO_VOLUME    0x0006
+#define NAM_PC_BEEP        0x000A
+#define NAM_PCM_VOLUME     0x0018
+#define NAM_EXT_AUDIO_ID   0x0028
+#define NAM_EXT_AUDIO_STC  0x002A
+#define NAM_FRONT_SPLRATE  0x002C
+#define NAM_LR_SPLRATE     0x0032
+#define NABM_POBDBAR       0x0010
+#define NABM_POLVI         0x0015
+#define NABM_POSTATUS      0x0016
+#define NABM_POPICB        0x0018
+#define NABM_POCONTROL     0x001B
+#define NABM_GLB_CTRL_STAT 0x0060
+
+
+struct ac97_bufdesc {
+    uint32_t pbuf;
+    uint16_t samples;
+    uint16_t flags;
+} __attribute__ ((packed));
+
+struct ac97_card {
+    struct cdi_storage_device cdi_strg_dev;
+    struct cdi_pci_device *cdi_pci_dev;
+    int nambar;
+    int nabmbar;
+    int volume;
+    int sample_rate;
+    struct ac97_bufdesc *bufdesc;
+    struct ac97_bufdesc *pbufdesc;
+    void *buffer[USE_BUFS];
+    uintptr_t pbuffer[USE_BUFS];
+} __attribute__ ((packed));
+
+#endif
diff --git a/ac97/main.c b/ac97/main.c
new file mode 100644
index 0000000..18cf764
--- /dev/null
+++ b/ac97/main.c
@@ -0,0 +1,281 @@
+/******************************************************************************
+ * Copyright (c) 2009 Max Reitz                                               *
+ *                                                                            *
+ * Permission is hereby granted,  free of charge,  to any  person obtaining a *
+ * copy of this software and associated documentation files (the "Software"), *
+ * to deal in the Software without restriction,  including without limitation *
+ * the rights to use,  copy, modify, merge, publish,  distribute, sublicense, *
+ * and/or sell copies  of the  Software,  and to permit  persons to whom  the *
+ * Software is furnished to do so, subject to the following conditions:       *
+ *                                                                            *
+ * The above copyright notice and this permission notice shall be included in *
+ * all copies or substantial portions of the Software.                        *
+ *                                                                            *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
+ * IMPLIED, INCLUDING  BUT NOT  LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
+ * FITNESS FOR A PARTICULAR  PURPOSE AND  NONINFRINGEMENT.  IN NO EVENT SHALL *
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
+ * LIABILITY,  WHETHER IN AN ACTION OF CONTRACT,  TORT OR OTHERWISE,  ARISING *
+ * FROM,  OUT OF  OR IN CONNECTION  WITH THE  SOFTWARE  OR THE  USE OR  OTHER *
+ * DEALINGS IN THE SOFTWARE.                                                  *
+ ******************************************************************************/
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "cdi.h"
+#include "cdi/io.h"
+#include "cdi/lists.h"
+#include "cdi/misc.h"
+#include "cdi/pci.h"
+#include "cdi/storage.h"
+
+#include "device.h"
+
+
+static void driver_init(void);
+static void init_dev(struct cdi_device *device);
+static void deinit_dev(struct cdi_device *device);
+static void suicide(struct cdi_driver *drv);
+static int play(struct cdi_storage_device *dev, uint64_t start, uint64_t count,
+    void *buffer);
+static int read_dummy(struct cdi_storage_device *dev, uint64_t start,
+    uint64_t count, void *buffer);
+static void isr(struct cdi_device *device);
+
+static const char *driver_name = "ac97";
+static struct cdi_storage_driver driver_storage;
+
+
+static int card_counter = 0;
+static volatile int buffer_queue = 0, have_qc_waiting = 0, doing_buffer = 0;
+
+
+#ifdef CDI_STANDALONE
+int main(void)
+#else
+int init_ac97(void)
+#endif
+{
+    cdi_init();
+
+    driver_init();
+    cdi_storage_driver_register((struct cdi_storage_driver *)&driver_storage);
+
+    cdi_run_drivers();
+
+    return 0;
+}
+
+static void driver_init(void)
+{
+    struct cdi_pci_device *dev;
+    int i;
+    struct ac97_card *card;
+    cdi_list_t pci_devices;
+
+    cdi_storage_driver_init(&driver_storage);
+
+    driver_storage.drv.name = driver_name;
+    driver_storage.drv.type = CDI_STORAGE;
+
+    driver_storage.drv.init_device = &init_dev;
+    driver_storage.drv.remove_device = &deinit_dev;
+    driver_storage.drv.destroy = &suicide;
+
+    driver_storage.read_blocks = &read_dummy;
+    driver_storage.write_blocks = &play;
+
+    pci_devices = cdi_list_create();
+    cdi_pci_get_all_devices(pci_devices);
+
+    for (i = 0; (dev = cdi_list_get(pci_devices, i)); i++) {
+        if ((dev->vendor_id == 0x8086) && ((dev->device_id == 0x2415) ||
+            (dev->device_id == 0x2425) || (dev->device_id == 0x2445)))
+        {
+            card = malloc(sizeof(*card));
+            card->cdi_pci_dev = dev;
+            cdi_list_push(driver_storage.drv.devices, card);
+        } else {
+            cdi_pci_device_destroy(dev);
+        }
+    }
+
+    cdi_list_destroy(pci_devices);
+}
+
+static void init_dev(struct cdi_device *device)
+{
+    struct ac97_card *card = (struct ac97_card *)device;
+    cdi_list_t pci_res_list;
+    struct cdi_pci_resource *rsrc;
+    int i;
+
+    card->cdi_strg_dev.block_size = 4;
+    card->cdi_strg_dev.block_count = FRAMES_PER_BUF * USE_BUFS;
+    card->cdi_strg_dev.dev.name = malloc(10);
+    sprintf((char *)card->cdi_strg_dev.dev.name, "crd%i", card_counter++);
+
+    cdi_register_irq(card->cdi_pci_dev->irq, &isr, device);
+    cdi_pci_alloc_ioports(card->cdi_pci_dev);
+
+    card->nambar = 0;
+    card->nabmbar = 0;
+
+    pci_res_list = card->cdi_pci_dev->resources;
+    for (i = 0; (rsrc = cdi_list_get(pci_res_list, i)); i++) {
+        if (rsrc->type == CDI_PCI_IOPORTS) {
+            //Die Resourcen liegen rückwärts in der Liste!
+            //Wir nehmen nur die ersten zwei.
+            card->nabmbar = card->nambar;
+            card->nambar = rsrc->start;
+        }
+    }
+
+    cdi_alloc_phys_mem(sizeof(struct ac97_bufdesc) * 32,
+        (void **)&card->bufdesc, (void **)&card->pbufdesc);
+    for (i = 0; i < USE_BUFS; i++) {
+        cdi_alloc_phys_mem(FRAMES_PER_BUF * 4, &card->buffer[i],
+            (void **)&card->pbuffer[i]);
+    }
+    for (i = 0; i < 32; i++) {
+        card->bufdesc[i].pbuf = 0;
+        card->bufdesc[i].samples = 0;
+        card->bufdesc[i].flags = 0x4000;
+    }
+
+    cdi_outw(card->nambar + NAM_RESET, 42);
+    cdi_outb(card->nabmbar + NABM_GLB_CTRL_STAT, 0x02);
+    cdi_sleep_ms(100);
+    card->volume = 0; //Volle Dröhnung
+    cdi_outw(card->nambar + NAM_MASTER_VOLUME,
+        (card->volume << 8) | card->volume);
+    cdi_outw(card->nambar + NAM_MONO_VOLUME, card->volume);
+    cdi_outw(card->nambar + NAM_PC_BEEP, card->volume);
+    cdi_outw(card->nambar + NAM_PCM_VOLUME
+        (card->volume << 8) | card->volume);
+    cdi_sleep_ms(10);
+    if (!(cdi_inw(card->nambar + NAM_EXT_AUDIO_ID) & 1)) {
+        card->sample_rate = 48000;
+    } else {
+        cdi_outw(card->nambar + NAM_EXT_AUDIO_STC,
+            cdi_inw(card->nambar + NAM_EXT_AUDIO_STC) | 1);
+        cdi_sleep_ms(10);
+        cdi_outw(card->nambar + NAM_FRONT_SPLRATE, 44100);
+        cdi_outw(card->nambar + NAM_LR_SPLRATE, 44100);
+        cdi_sleep_ms(10);
+        card->sample_rate = cdi_inw(card->nambar + NAM_LR_SPLRATE);
+    }
+
+    cdi_storage_device_init((struct cdi_storage_device *)device);
+}
+
+static void deinit_dev(struct cdi_device *device)
+{
+}
+
+static void suicide(struct cdi_driver *drv)
+{
+}
+
+static int read_dummy(struct cdi_storage_device *dev, uint64_t start,
+    uint64_t count, void *buffer)
+{
+    return 0;
+}
+
+static int play(struct cdi_storage_device *dev, uint64_t start, uint64_t count,
+    void *buffer)
+{
+    struct ac97_card *card = (struct ac97_card *)dev;
+    int size = count * 4, i, final = 0;
+    ptrdiff_t off = 0;
+
+    if (!size) {
+        return 0;
+    }
+
+    if (have_qc_waiting < 0) { //Haha
+        have_qc_waiting = 0;
+    }
+    while (have_qc_waiting++ > 1) {
+        have_qc_waiting--;
+        cdi_wait_irq(card->cdi_pci_dev->irq, 100);
+    }
+
+    while (buffer_queue > 1) {
+        cdi_reset_wait_irq(card->cdi_pci_dev->irq);
+        cdi_wait_irq(card->cdi_pci_dev->irq, 100);
+    }
+
+    //Der letzte Buffer... Wir können den Rest also kopieren
+
+    for (i = 0; (i < USE_BUFS) && size; i++) {
+        card->bufdesc[i].pbuf = card->pbuffer[i];
+        if ((i == USE_BUFS - 1) || (size <= FRAMES_PER_BUF * 4)) {
+            while ((buffer_queue > 1) ||
+                (cdi_inw(card->nabmbar + NABM_POPICB) > 1764))
+            {
+                //Bis 20 ms vor Ablauf können wir noch CPU sparen
+                cdi_sleep_ms(1);
+            }
+            while (buffer_queue);
+            if (i) {
+                //Der erste Buffer ist gefüllt, wir können das Abspielen also
+                //beginnen, bevor wir den letzten füllen
+                cdi_outl(card->nabmbar + NABM_POBDBAR,
+                    (uint32_t)card->pbufdesc);
+                cdi_outb(card->nabmbar + NABM_POLVI, 15);
+                cdi_outb(card->nabmbar + NABM_POCONTROL, 0x15);
+                buffer_queue = i + 1;
+                doing_buffer = 0;
+            }
+        }
+        if (size >= FRAMES_PER_BUF * 4) {
+            card->bufdesc[i].samples = FRAMES_PER_BUF * 2;
+            size -= FRAMES_PER_BUF * 4;
+            memcpy(card->buffer[i], buffer + off, FRAMES_PER_BUF * 4);
+            off += FRAMES_PER_BUF * 4;
+        } else {
+            card->bufdesc[i].samples = size >> 1;
+            memcpy(card->buffer[i], buffer + off, size);
+            size = 0;
+        }
+        card->bufdesc[i].flags = 0x8000; //IOC
+        if (!size || (i == USE_BUFS - 1)) {
+            card->bufdesc[i].flags |= 0x4000; //BUP
+            final = i;
+        }
+    }
+
+    if (!final) {
+        cdi_outl(card->nabmbar + NABM_POBDBAR, (uint32_t)card->pbufdesc);
+        cdi_outb(card->nabmbar + NABM_POLVI, 15);
+        cdi_outb(card->nabmbar + NABM_POCONTROL, 0x15);
+        buffer_queue = 1;
+        doing_buffer = 0;
+    }
+
+    for (; i < 32; i++) {
+        card->bufdesc[i].samples = 0;
+        card->bufdesc[i].flags = 0xC000;
+    }
+
+    have_qc_waiting = 0;
+
+    return count;
+}
+
+static void isr(struct cdi_device *device)
+{
+    struct ac97_card *card = (struct ac97_card *)device;
+
+    cdi_outb(card->nabmbar + NABM_POSTATUS,
+        cdi_inb(card->nabmbar + NABM_POSTATUS));
+    if (buffer_queue > 0) {
+        card->bufdesc[doing_buffer++].samples = 0;
+        buffer_queue--;
+    }
+}
-- 
1.6.3.3