RT5350 I2S audio问题

前面有弄RT5350的wifi开发板做ipcam,本来规划增加audio功能上去。
当时找了官方SDK user manual 看下,RT5350的I2S有预留,但原厂并未有驱动和支持的解码芯片出来。

而后,最近看到openwrt中,有支持WM8960,ES9023芯片。
也参考PI中,有搞定I2S audio功能。
所以我也想试试,淘宝了ES9023。坐等收货试验,openwrt已经build好。

参考链接:
ES9023
http://transing.xyz/2015/07/27/wifi-audio-on-rt5350-with-openwrt/
https://github.com/qdk0901/openwrt-rt5350

WM8960
http://vonger.cn/?p=1970
附上 8960 audio driver code

#include <linux/init.h>
#include <linux/module.h>

#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/dma-mapping.h>
#include <linux/sysfs.h>

#include <linux/vmalloc.h>

#define MAX_BUFF    0x200000

struct vocore_data {
    void __iomem *gdma;
    void __iomem *i2s;

    dma_addr_t addr;
    void *vaddr;

    u8 *buf;
    u32 size;       // current data size in buffer.
    u32 curr;       // current data pointer.
};

irqreturn_t vocore_gdma_irq_handler(int irq, void *dev_id)
{
    struct vocore_data *p = (struct vocore_data *)dev_get_drvdata(dev_id);
    u32 mask, done;

    mask = readl(p->gdma + 0x0200);
    done = readl(p->gdma + 0x0204);

    // MASK, DONE their register type is W1C(write one to clean)
    writel(mask, p->gdma + 0x0200);
    writel(done, p->gdma + 0x0204);

    memcpy(p->vaddr, p->buf + p->curr, PAGE_SIZE);
    p->curr += PAGE_SIZE;
    if(p->curr >= p->size)
        p->curr = 0;

    // must fill the address again, or it will send some "random" data.
    writel(p->addr, p->gdma + 0x0020);
    writel(0x10000A10, p->gdma + 0x0024);
    writel(0x10000046, p->gdma + 0x0028);
    return IRQ_HANDLED;
}

// input format must be 44.1KHz, 16bits, stereo pcm data, max data size is 2MB
static ssize_t vocore_gdma_data_store(struct device *dev,
    struct device_attribute *attr, const char *buf, size_t count)
{
    struct vocore_data *p = (struct vocore_data *)dev_get_drvdata(dev);
    if(p->buf == NULL)
        p->buf = (u8 *)vmalloc(MAX_BUFF);   // 2MB buffer

    if(p->size + count > MAX_BUFF) {
        printk("buffer has full = %d.\n", p->size);
        return count;
    }

    memcpy(p->buf + p->size, buf, count);
    p->size += count;

    return count;
}

static ssize_t vocore_gdma_play_store(struct device *dev,
    struct device_attribute *attr, const char *buf, size_t count)
{
    struct vocore_data *p = (struct vocore_data *)dev_get_drvdata(dev);

    switch(buf[0]) {
    case '0': {     // stop
        writel(0x00000044, p->gdma + 0x0028);
        writel(0x00004040, p->i2s + 0x0000);

        p->size = 0; // clean buffer.

        printk("vocore_gdma_play_store: stop!\n");
        break; }

    case '1': {     // start
        if(p->size < PAGE_SIZE) {
            printk("not enough data, can not start.\n");
            break;
        }

        memcpy(p->vaddr, p->buf, PAGE_SIZE);
        p->curr = PAGE_SIZE;

        // setup i2s
        writel(0x80000058, p->i2s + 0x0020);
        writel(0x0000000E, p->i2s + 0x0024);
        writel(0xC1004040, p->i2s + 0x0000);

        // start dma transfer
        writel(p->addr, p->gdma + 0x0020);
        writel(0x10000A10, p->gdma + 0x0024);
        writel(0x00200210, p->gdma + 0x002c);
        writel(0x10000046, p->gdma + 0x0028);

        printk("vocore_gdma_play_store: start!\n");
        break; }
    }

    return count;
}

static DEVICE_ATTR(data, S_IWUSR, NULL, vocore_gdma_data_store);
static DEVICE_ATTR(play, S_IWUSR, NULL, vocore_gdma_play_store);

static int vocore_gdma_probe(struct platform_device* dev)
{
    int irq, r;
    struct resource *res;
    struct vocore_data *p;

    p = (struct vocore_data *)kmalloc(sizeof(struct vocore_data), GFP_KERNEL);
    memset(p, 0, sizeof(struct vocore_data));
    platform_set_drvdata(dev, p);

    irq = platform_get_irq(dev, 0);
    printk("platform_get_irq %d\n", irq);

    r = request_irq(irq, vocore_gdma_irq_handler, IRQF_SHARED, "vocore-gdma", &dev->dev);
    if(r < 0) {
        printk("request_irq failed %d.\n", r);
        return 0;
    }
    printk("request_irq %d success.\n", irq);


    res = platform_get_resource(dev, IORESOURCE_MEM, 0);
    p->gdma = devm_ioremap_resource(&dev->dev, res);
    p->i2s = p->gdma - 0x00002800 + 0x00000a00;

    p->vaddr = dma_alloc_coherent(&dev->dev, PAGE_SIZE, &p->addr, GFP_DMA);
    printk("virtual address: %p, physics address: %p\n", p->vaddr, (void *)p->addr);

    device_create_file(&dev->dev, &dev_attr_data);
    device_create_file(&dev->dev, &dev_attr_play);
    return 0;
}

static int vocore_gdma_remove(struct platform_device* dev)
{
    struct vocore_data *p = (struct vocore_data *)platform_get_drvdata(dev);
    int irq = platform_get_irq(dev, 0);

    free_irq(irq, &dev->dev);
    printk("free_irq %d done.\n", irq);

    dma_free_coherent(&dev->dev, PAGE_SIZE, p->vaddr, p->addr);
    printk("free virtual address: %p, physics address: %p\n", p->vaddr, (void *)p->addr);

    if(p->buf)
        vfree(p->buf);
    kfree(p);

    device_remove_file(&dev->dev, &dev_attr_data);
    device_remove_file(&dev->dev, &dev_attr_play);
    return 0;
}


static const struct of_device_id gdma_rt_dt_ids[] = {
    { .compatible = "ralink,rt2880-gdma", },
    { }
};

static struct platform_driver rt_gdma_driver = {
    .probe = vocore_gdma_probe,
    .remove = vocore_gdma_remove,
    .driver = {
        .owner = THIS_MODULE,
        .name = "ralink-gdma",
        .of_match_table = gdma_rt_dt_ids,
    },
};

static int vocore_gdma_init(void)
{
    printk("vocore_gdma_init\n");
    return platform_driver_register(&rt_gdma_driver);
}

static void vocore_gdma_exit(void)
{
    platform_driver_unregister(&rt_gdma_driver);
    printk("vocore_gdma_exit\n");
}

module_init(vocore_gdma_init);
module_exit(vocore_gdma_exit);


MODULE_AUTHOR("Qin Wei <me@vonger.cn>");
MODULE_DESCRIPTION("VoCore Test GDMA&I2S driver");
MODULE_LICENSE("GPL");
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值