[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