From 333d49b95b34fdac9835248ba046b9a4f74e2f98 Mon Sep 17 00:00:00 2001 From: Sorgelig Date: Fri, 20 Aug 2021 01:37:53 +0800 Subject: [PATCH] Implement MiSTer audio driver. --- .../boot/dts/socfpga_cyclone5_de10_nano.dts | 13 + sound/drivers/Kconfig | 6 + sound/drivers/Makefile | 1 + sound/drivers/MiSTer-audio-spi.c | 274 ++++++++++++++++++ sound/drivers/dummy.c | 15 +- 5 files changed, 308 insertions(+), 1 deletion(-) create mode 100644 sound/drivers/MiSTer-audio-spi.c diff --git a/arch/arm/boot/dts/socfpga_cyclone5_de10_nano.dts b/arch/arm/boot/dts/socfpga_cyclone5_de10_nano.dts index ea279c7c6..017c345c3 100644 --- a/arch/arm/boot/dts/socfpga_cyclone5_de10_nano.dts +++ b/arch/arm/boot/dts/socfpga_cyclone5_de10_nano.dts @@ -126,6 +126,19 @@ }; }; +&spi0 { + status = "okay"; + timeouts = <3>; + spiusb { + compatible = "MiSTer,spi-audio"; + reg = <0>; + spi-max-frequency = <10000000>; + spi-cpha; + spi-cpol; + status = "okay"; + }; +}; + &spi1 { status = "okay"; timeouts = <3>; diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig index ca4cdf666..c3cb88b25 100644 --- a/sound/drivers/Kconfig +++ b/sound/drivers/Kconfig @@ -38,6 +38,12 @@ config SND_AC97_CODEC select AC97_BUS select SND_VMASTER +config SND_MISTER_AUDIO + tristate "MiSTer Audio (SPI)" + default n + help + Support for MiSTer audio through SPI. + menuconfig SND_DRIVERS bool "Generic sound devices" default y diff --git a/sound/drivers/Makefile b/sound/drivers/Makefile index c0fe4eccd..8459ed36a 100644 --- a/sound/drivers/Makefile +++ b/sound/drivers/Makefile @@ -20,5 +20,6 @@ obj-$(CONFIG_SND_SERIAL_U16550) += snd-serial-u16550.o obj-$(CONFIG_SND_MTPAV) += snd-mtpav.o obj-$(CONFIG_SND_MTS64) += snd-mts64.o obj-$(CONFIG_SND_PORTMAN2X4) += snd-portman2x4.o +obj-$(CONFIG_SND_MISTER_AUDIO) += MiSTer-audio-spi.o obj-$(CONFIG_SND) += opl3/ opl4/ mpu401/ vx/ pcsp/ diff --git a/sound/drivers/MiSTer-audio-spi.c b/sound/drivers/MiSTer-audio-spi.c new file mode 100644 index 000000000..ca2fab2a3 --- /dev/null +++ b/sound/drivers/MiSTer-audio-spi.c @@ -0,0 +1,274 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "MiSTer Audio (SPI)" +#define DRIVER_VERSION "1.0" +#define DRIVER_NAME "MrAudio" + +#define BUFFER_LEN 512*1024 // Holds about 2.6 seconds of audio. + +static int major = -1; +static struct cdev mycdev; +static struct class *myclass = NULL; + +static char msg[1024]; // The msg the device will give when asked */ +static char *msg_Ptr; +static int MrBufferWriteCount = 0; +static int MrBufferMaxWriteLen = 0; + +static char *MrBuffer = 0; + +typedef struct Info +{ + unsigned int addr; + unsigned int len; + unsigned int ptr; + unsigned int reserved; //IRQ rate may be. +} Info_t; + +static Info_t MrBufferInfo; + +static struct spi_device *g_spi; + +//////////////////////////////////////////////////////////////////////////////////////////////// + +static int show(struct seq_file *m, void *v) +{ + seq_printf(m, "TEST-->"); + return 0; +} + +static int device_open(struct inode *inode, struct file *file) +{ + int len; + int comp = 0; + int rptr = -1; + + if(spi_read(g_spi, &rptr, 4)) rptr = -1; + rptr >>= 8; + + if(rptr >= 0) + { + comp = rptr & 7; + rptr &= ~7; + } + + len = (MrBufferInfo.ptr < (unsigned int)rptr) ? (MrBufferInfo.ptr + MrBufferInfo.len - rptr) : (MrBufferInfo.ptr - rptr); + + sprintf(msg, "rptr: %6d, wptr: %6d, len: %6d, comp: %1d\n", rptr, MrBufferInfo.ptr, len, comp); + + msg_Ptr = msg; + return single_open(file, show, NULL); +} + +static ssize_t device_read(struct file *filp, + char *buffer, + size_t length, + loff_t * offset) +{ + int bytes_read = 0; // <-- Number of bytes actually written to the buffer + + if (*msg_Ptr == 0) // If we're at the end of the message, + return 0; // return 0 signifying end of file + + while (length && *msg_Ptr) // Actually put the data into the buffer + { + // The buffer is in the user data segment, not the kernel + // segment so "*" assignment won't work. We have to use + // put_user which copies data from the kernel data segment to + // the user data segment. + put_user(*(msg_Ptr++), buffer++); + length--; + bytes_read++; + } + + return bytes_read; // Most read functions return the number of bytes put into the buffer +} + +static ssize_t device_write(struct file *filp, + const char *userBuf, + size_t userBufLen, + loff_t * off) +{ + size_t count,i; + unsigned int buf[64]; + + userBufLen = userBufLen & ~3; // assume the length aligned to 32bits (2x16 samples) for faster access + + MrBufferWriteCount++; + if(userBufLen > MrBufferMaxWriteLen) MrBufferMaxWriteLen = userBufLen; + + if(!userBufLen || userBufLen > BUFFER_LEN) return -EFAULT; + + count = userBufLen; + while (count) + { + size_t c = count; + if (c > sizeof(buf)) c = sizeof(buf); + if (copy_from_user(buf, userBuf, c)) return -EFAULT; + userBuf += c; + count -= c; + c >>= 2; + for(i=0; i < c; i++) + { + // align to 64bit access + writel(buf[i], &MrBuffer[MrBufferInfo.ptr]); + MrBufferInfo.ptr +=4; + if(MrBufferInfo.ptr>=BUFFER_LEN) MrBufferInfo.ptr = 0; + } + } + + spi_write(g_spi, &MrBufferInfo, sizeof(MrBufferInfo)); + return userBufLen; +} + +static const struct file_operations fops = +{ + .open = device_open, + .owner = THIS_MODULE, + .read = device_read, + .write = device_write, + .release = single_release, +}; + + +//////////////////////////////////////////////////////////////////////////////////////////////// + +static void cleanup(int device_created) +{ + if (device_created) + { + device_destroy(myclass, major); + cdev_del(&mycdev); + } + + if (myclass) class_destroy(myclass); + if (major != -1) unregister_chrdev_region(major, 1); + + if(MrBuffer) dma_free_coherent(&g_spi->dev, BUFFER_LEN, MrBuffer, MrBufferInfo.addr); + MrBuffer = 0; +} + +static int device_init(void) +{ + int device_created = 0; + int i; + + if (dma_set_coherent_mask(&g_spi->dev, DMA_BIT_MASK(32))) + { + printk(KERN_INFO "Bad mask.\n"); + goto error; + } + + memset(&MrBufferInfo, 0, sizeof(MrBufferInfo)); + MrBufferInfo.len = BUFFER_LEN; + MrBuffer = dma_alloc_coherent(&g_spi->dev, BUFFER_LEN, &MrBufferInfo.addr, GFP_KERNEL); + if(!MrBuffer) + { + printk(KERN_INFO "MrAudio ERROR:--> dma_alloc_coherent(0x%X)\n", BUFFER_LEN); + goto error; + } + + printk(KERN_INFO "MrAudio: CMA addr = 0x%X\n", MrBufferInfo.addr); + + for(i=0; i alloc_chrdev_region(%d, '%s')\n", major, DRIVER_NAME "_proc"); + goto error; + } + // ls /sys/class + if ((myclass = class_create(THIS_MODULE, DRIVER_NAME "_sys")) == NULL) + { + printk(KERN_INFO "MrAudio ERROR:--> class_create('%s')\n", DRIVER_NAME "_SYS"); + goto error; + } + // ls /dev/ + if (device_create(myclass, NULL, major, NULL, DRIVER_NAME) == NULL) + { + printk(KERN_INFO "MrAudio ERROR:--> device_create('%s', %d, '%s')\n", + myclass->name, + major, + DRIVER_NAME); + goto error; + } + device_created = 1; + cdev_init(&mycdev, &fops); + if (cdev_add(&mycdev, major, 1) == -1) + { + printk(KERN_INFO "MrAudio ERROR:--> cdev_add(%d)\n", major); + goto error; + } + + printk(KERN_INFO "MrAudio Audio buffer '/dev/%s' created.\n" + "Class --> '%s'\n" + "Major --> %d\n", + DRIVER_NAME, + myclass->name, + major); + return 0; + +error: + cleanup(device_created); + return -1; +} + +//////////////////////////////////////////////////////////////////////////////////////////////// + +static int device_probe(struct spi_device *spi) +{ + g_spi = spi; + + if (spi_setup(spi) < 0) + { + dev_err(&spi->dev, "Unable to setup SPI bus"); + return -EFAULT; + } + + if (device_init()) + { + dev_err(&spi->dev, "device_init() failed\n"); + return -EFAULT; + } + return 0; +} + +static int device_remove(struct spi_device *spi) +{ + cleanup(1); + return 0; +} + +static const struct of_device_id device_of_match_table[] = { + { .compatible = "MiSTer,spi-audio", }, + {}, +}; +MODULE_DEVICE_TABLE(of, device_of_match_table); + +static struct spi_driver MiSTer_audio_spi_driver = { + .probe = device_probe, + .remove = device_remove, + .driver = + { + .name = "MiSTer-audio-spi", + .of_match_table = of_match_ptr(device_of_match_table), + }, +}; + +module_spi_driver(MiSTer_audio_spi_driver); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Sorgelig"); +MODULE_LICENSE("GPL"); + diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c index 2a7fc49c1..1e9ecdb5c 100644 --- a/sound/drivers/dummy.c +++ b/sound/drivers/dummy.c @@ -53,7 +53,7 @@ static int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8}; #ifdef CONFIG_HIGH_RES_TIMERS static bool hrtimer = 1; #endif -static bool fake_buffer = 1; +static bool fake_buffer = 0; module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for dummy soundcard."); @@ -203,6 +203,17 @@ static const struct dummy_model model_ca0106 = { .rate_max = 192000, }; +static struct dummy_model model_MiSTer = { + .name = "MiSTer", + .buffer_bytes_max = 32768, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, +}; + static const struct dummy_model *dummy_models[] = { &model_emu10k1, &model_rme9652, @@ -1031,6 +1042,8 @@ static int snd_dummy_probe(struct platform_device *devptr) return err; dummy = card->private_data; dummy->card = card; + dummy->model = m = &model_MiSTer; + for (mdl = dummy_models; *mdl && model[dev]; mdl++) { if (strcmp(model[dev], (*mdl)->name) == 0) { printk(KERN_INFO