Chinaunix首页 | 论坛 | 博客
  • 博客访问: 384324
  • 博文数量: 81
  • 博客积分: 1290
  • 博客等级: 中尉
  • 技术积分: 821
  • 用 户 组: 普通用户
  • 注册时间: 2011-07-17 07:48
个人简介

Just do IT.

文章分类

全部博文(81)

分类:

2012-05-09 22:47:46

原文地址:S3C2440 ISS 音频驱动 作者:iibull

当前很多音频系统以CD的形式,数字音频带,数字音频处理器和数字TV音响,在市场上吸
引消费者。S3C2440A的Inter-IC Sound (IIS)总线接口作为一个编解码接口连接外部 8/16
位立体声音频解码IC用于迷你碟机和可携式应用。IIS总线接口支持IIS总线数据格式和
MSB-justified数据格式。该接口对FIFO的访问采用了DMA模式取代了中断。它可以在同一
时间接收和发送数据。

总线接口,寄存器组和状态机(BRFC):总线接口逻辑和 FIFO 访问由状态机控制。
5 位双预定标器(IPSR):一个预定标器用于 IIS 总线接口的主时钟发生器,另外一个用作
外部编解码时钟发生器。
64 位 FIFO(TxFIFO 和 RxFIFO):在发送数据传输时,数据写到 TxFIFO;在接收数据
传输时,从 RxFIFO 读取数据。
主 IISCLK 发生器(SCLKG):在主设备模式,串行位时钟是从主时钟生成。
通道发生器和状态机(CHNC):IISCLK 和 iislrck 是由通道状态机生成并控制。
15 位移位寄存器( SFTR):在发送模式下并行数据移位成串行数据输出,在接收模式下
串行数据输入移位成并行数据。

通常传输
IIS 控制寄存器对于发送接收 FIFO 有一个 FIFO 准备标志位。当 FIFO 准备发送数据时,如
果 FIFO 非空,FIFO 准备标志位置 1。如果 FIFO 为空,FIFO 准备标志位置 0。当接收 FIFO
非满,对于接收 FIFO 的 FIFO 准备标志位置 1。其指出 FIFO 准备好接收数据。如果接收
FIFO 为满,FIFO 准备标志置 0。这些标志用于决定 CPU 读写 FIFO 的时间。用这种方法当
CUP 在访问发送接收 FIFO 时,串行数据能被发送和接收。
DMA 传输
在此模式下,发送或接收 FIFO 对 DMA 控制器是可访问的。在发送或接收模式下的 DMA
服务请求是由 FIFO 准备标志自动执行。
发送和接收模式
在此模式下 IIS 总线接口可以同时接收和发送数据。

IIS 总线格式
IIS 总线有四线包括串行数据输入( IISDI ),串行数据输出( IISDO ),左右通道选择
(IISLRCK)和串行位时钟(IISCLK)。生成 IISLRCK 和 IISCLK 的设备是主设备。
串行数据以 2 的补码发送,MSB(Most Significant Bit 最高位)先发。因为发送器和接收器可
能有不同的字长,MSB(最高位)先发。发送器不必知道接收器可以处理多少位,接收器
也不必知道会收到多少位。
当系统字长大于发生器的字长,字为了数据发送而被截断(最低位被置 0)。如果接收器
接收大于其字长的位,在 LSB(最低位)后的位被忽略。另外,如果接收器收到的位数小
于其字长,缺少的位被置 0。因此 MSB 有一个固定的位置,而 LSB 的位置取决于字长。只
要 IISLRCK 发送改变,发送器在一个时钟周期内发送下一个字的 MSB。
由发送器发送的串行数据可以和时钟信号的下降沿和上升沿同步。但是,串行数据必须在
串行时钟信号的上升沿锁存到接收器。因此当同步上升沿的数据发送时有一些限制。
左右通道选择线指出了正在发送的通道。IISLRCK 可以在串行时钟的下降沿或上升沿被改
变,当时其不需要对称。在从设备,信号在串行时钟的下降沿或上升沿被锁存。在 MSB 被
发送,IISLRCK 线改变一个时钟周期。此允许发送器导出用于建立发送的串行数据同步时
序。此外,其使能接收存储前一个字且为下一个字的接收清除输入。


主设备时钟频率(PCLK 或 MPLLin)可以在如表 21-1 所示的采样频率中选择。因为主设备
 时钟由 IIS 预分频器(预定标器)产生,预分频器(预定标器)的值和主设备时钟类型
(256 或 384fs)应该合适确定。
 串行位时钟频率类型可以由每个通道的串行位和如表 21-2 所示的主设备时钟中来选择。
 表 CODEC 时钟(CODECLK=256 或 384fs)
IISLRCK      8.000     11.025   16.000  22.050    32.000   44.100  48.000    64.000    88.200  96.000
(fs)         KHz       KHz      KHz     KHz       KHz      KHz     KHz       KHz       KHz     KHz
CODECLK      256fs
(MHz)        2.0480    2.8224   4.0960  5.6448    8.1920   11.2896 12.2880   16.3840   22.5792 24.5760
             384fs
             3.0720    4.2336   6.1440  8.4672    12.2880  16.9344 18.4320   24.5760   33.8688 36.8640
  表  可用串行位时钟频率(IISCLK=16 或 32 或 48fs)
      Serial bit per channel                         8-bit                      16-bit
      Serial clock frequency (IISCLK)
      @CODECLK = 256fs                 16fs, 32fs                   32fs
      @CODECLK = 384fs                 16fs, 32fs, 48fs             32fs, 48fs

IIS 总线接口特殊寄存器
(1)IIS 控制寄存器 (IISCON)
(2)IIS 模式寄存器 (IISMOD)
(3)IIS 预定标器寄存器 (IISPSR)
(4)IIS FIFO 控制寄存器 (IISFCON)
(5)IIS FIFO 寄存器 (IISFIFO)


IIS的驱动步骤
  iis->DMA->uda1341--->输出
  L3->DMA  

   还有申请DMA的中断号!


  由图可以看出要设置的各个引脚!
首先初始化DMA
#define DISRC2  (*(volatile unsigned long *)(dma_base + 0x80))
#define DISRCC2 (*(volatile unsigned long *)(dma_base + 0x84))
#define DIDST2  (*(volatile unsigned long *)(dma_base + 0x88))
#define DIDSTC2 (*(volatile unsigned long *)(dma_base + 0x8C))
#define DCON2   (*(volatile unsigned long *)(dma_base + 0x90))
#define DSTAT2  (*(volatile unsigned long *)(dma_base + 0x94))
#define DCSRC2  (*(volatile unsigned long *)(dma_base + 0x98))
#define DCDST2  (*(volatile unsigned long *)(dma_base + 0x9C))
#define DMASKTRIG2  (*(volatile unsigned long *)(dma_base + 0xA0))

static unsigned long dma_base;
void dma_2ch_init(unsigned long dma_virt)
{
    dma_base = dma_virt;

    DIDST2 =  (0x55000010);
    DIDSTC2 = 1 | (1 << 1) ;

    //DCON2 =  ( 1 << 20)  | (0 << 22) | (1 << 23) | (0 << 27) | (1 << 29) | (0 << 31);
    DCON2  = 0xa09000000;
}

void dma_2ch_set_size(int size)
{
    size = size / 2;
    DCON2  |= size;
}

void dma_2ch_set_src(unsigned char *dma_phys)
{
    DISRC2  = dma_phys;
    DISRCC2 = 0 | (0 << 1);
}

void  dma_2ch_enable(void)
{
    DMASKTRIG2 = (1 << 1);
}

void dma_2ch_disable(void)
{
    DMASKTRIG2 &= ~(1 << 1);
    DMASKTRIG2 |= (1 << 2);
}


再初始化IIS
#define GPECON  (*(volatile unsigned int *)(gpio_base + 0x40))
#define GPEDAT  (*(volatile unsigned int *)(gpio_base + 0x44))
#define GPEUP   (*(volatile unsigned int *)(gpio_base + 0x48))

#define IISCON  (*(volatile unsigned int *)(iis_base +0x00))
#define IISMOD  (*(volatile unsigned int *)(iis_base +0x04))
#define IISPSR  (*(volatile unsigned int *)(iis_base +0x08))
#define IISFCON (*(volatile unsigned int *)(iis_base +0x0C))
#define ISFIFO  (*(volatile unsigned int *)(iis_base +0x10))

static unsigned long gpio_base, iis_base;

void iis_init(unsigned long gpio_virt, unsigned long iis_virt)
{
    gpio_base = gpio_virt;
    iis_base = iis_virt;

    GPECON  &= ~0xfff;
    GPECON  |= 0xaaa;
    GPEUP   |= 0xfff;

    IISMOD  =  1 | (1 << 2) | (1 << 3) | (1 << 4) | (0 << 5)| (2 << 6)  ;  
    IISPSR  = 2 | (2 << 5);
    IISFCON = (1 << 13) | (1 << 15);
    IISCON  =  (1 << 1) | (1 << 5);
}

void iis_enable(void)
{
    IISCON  |= 1;
}

void iis_disable(void)
{
    IISCON  &= ~1;
}

初始化L3。其实就是对GPIO的GPB2,3,4的设置而已
#include
#include
#include

//--------- l3 low-level functions ---------
void l3_setmode(int mode);
void l3_setdir(int in);
void l3_setclk(int scl);
void l3_setdat(int sda);
int  l3_getdat(void);

void l3_write(unsigned char addr, unsigned char *buf, int len)
{
    int i, j;

    // ensure that we come the l3 in stop mode.
    l3_setclk(1);
    l3_setdat(1);
    l3_setmode(1);
    l3_setdir(0);
    udelay(1);

    // send address

    l3_setmode(0);
    udelay(1);

    for (i = 0; i < 8; i ++) {
        l3_setclk(0);
        udelay(1);
        l3_setdat(addr & 1);
        udelay(1);
        l3_setclk(1);
        udelay(1);

        addr >>= 1;
    }
    udelay(1);

    // send data
    l3_setdir(0);
    for (j = 0; j < len; j ++) {
        unsigned char data;
        data = buf[j];

        if (j) {

            udelay(1);
            l3_setmode(0);
            udelay(1);
        }

        l3_setmode(1);
        udelay(1);
        for (i = 0; i < 8; i ++) {
            l3_setclk(0);
            udelay(1);
            l3_setdat(data & 1);
            udelay(1);
            l3_setclk(1);
            udelay(1);

            data >>= 1;
        }
    }

    // ensure that we leave the l3 in stop mode.
    l3_setclk(1);
    l3_setdat(1);
    l3_setmode(1);
    l3_setdir(1);
}

void l3_read(unsigned char addr, unsigned char *buf, int len)
{
    int i, j;
    unsigned char data;

    // ensure that we come the l3 in stop mode.
    l3_setclk(1);
    l3_setdat(1);
    l3_setmode(1);
    l3_setdir(0);
    udelay(1);

    //send address
    l3_setmode(0);
    udelay(1);

    for (i = 0; i < 8; i ++) {
        l3_setclk(0);
        udelay(1);
        l3_setdat(addr & 1);
        udelay(1);
        l3_setclk(1);
        udelay(1);

        addr >>= 1;
    }
    udelay(1);


    // read data
    l3_setdir(1);

    for (j = 0; j < len; j ++) {
        unsigned char data;
        if (j) {
            udelay(1);
            l3_setmode(0);
            udelay(1);
        }

        l3_setmode(1);
        udelay(1);
        for (i = 0; i < 8; i ++) {
            l3_setclk(0);
            udelay(2);
            l3_setclk(1);
            if (l3_getdat()) {
                data |=  (1 << i);
            }
            udelay(1);
        }
        buf[j] = data;
    }

    // ensure that we leave the l3 in stop mode.
    l3_setclk(1);
    l3_setdat(1);
    l3_setmode(1);
    l3_setdir(1);

    return data;
}

//--------------------- low level ----------------
#define GPBCON (*(volatile unsigned long *)(gpio_base + 0x10))
#define GPBDAT (*(volatile unsigned long *)(gpio_base + 0x14))
#define GPBUP  (*(volatile unsigned long *)(gpio_base + 0x18))

#define L3_MODE     2  
#define L3_DATA     3
#define L3_CLOCK    4

static unsigned long gpio_base;
void l3_init(unsigned long gpio_virt)
{
    gpio_base = gpio_virt;

    // make sure the pull-up is disabled
    GPBUP   |= ~( 1 << L3_MODE);
    GPBUP   |= ~( 1 << L3_DATA);
    GPBUP   |= ~( 1 << L3_CLOCK);

    // configure the gpio functional       
    GPBCON  &= ~(0x3 << (2 * L3_MODE));
    GPBCON  |=  (0x1 << (2 * L3_MODE));

    GPBCON  &= ~(0x3 << (2 * L3_DATA));
    GPBCON  |=  (0x1 << (2 * L3_DATA));

    GPBCON  &= ~(0x3 << (2 * L3_CLOCK));
    GPBCON  |=  (0x1 << (2 * L3_CLOCK));
}

void l3_setmode(int mode)
{
    if (mode) {
        GPBDAT |= ( 1 << L3_MODE);
    } else {
        GPBDAT &= ~( 1 << L3_MODE);
    }
}

void l3_setdir(int in)
{
    if (in) {
        GPBCON  &= ~(0x3 << (2 * L3_DATA));
        GPBCON  |=  (0x0 << (2 * L3_DATA));
    } else {
        GPBCON  &= ~(0x3 << (2 * L3_DATA));
        GPBCON  |=  (0x1 << (2 * L3_DATA));
    }
}

void l3_setclk(int scl)
{
    if (scl) {
        GPBDAT  |= (1 << L3_CLOCK);
    } else {
        GPBDAT  &= ~( 1 << L3_CLOCK);
    }
}

void l3_setdat(int sda)
{
    if (sda) {
        GPBDAT  |= (1 << L3_DATA);
    } else {
        GPBDAT  &= ~( 1 << L3_DATA);
    }
}

int l3_getdat(void)
{
    int t;
    t = (GPBDAT & (1 << L3_DATA)) >> L3_DATA;
    return t;
}

最后对设置UDA了
// UDA1341 L3 address and command types
#define UDA1341_L3ADDR          5
#define UDA1341_DATA0           (UDA1341_L3ADDR << 2 | 0)
#define UDA1341_DATA1           (UDA1341_L3ADDR << 2 | 1)
#define UDA1341_STATUS          (UDA1341_L3ADDR << 2 | 2)


// registers
#define STATUS_REG0(value)  ((0 << 7) | value)
#   define RESET        (1 << 6)
#   define SC_384fs     (1 << 4)
#   define MSB_16       (4 << 1)       
#   define DC       (1)

#define STATUS_REG1(value)  ((1 <<7) | value)
#   define PWR_DAC      (1)
#   define PWR_ADC      (2)

#define DATA0_REG0(value)   ((0 << 6) | value)
#   define VOLUME(x)    (x)

#define DATA0_REG2(value)   ((2 <<6) | value)
#   define DE_44K       (2 << 3)


void uda1341_init(void)
{
    char value[10];

    value[0] = STATUS_REG0(RESET | SC_384fs | MSB_16);
    value[1] = STATUS_REG1(PWR_DAC | PWR_ADC);
    l3_write(UDA1341_STATUS, value, 2);

    value[0] = DATA0_REG0(VOLUME(1600));
    value[1] = DATA0_REG2(DE_44K);
    l3_write(UDA1341_DATA0, value, 2);
}

恩。基本上所有设置都完成了。到最后的驱动实现了!

下面只列出初始所以引脚的操作。

irqreturn_t dma_2ch_handle(int irq, struct s3c2440_audio *dev)

    //中断处理.....




int oss_open(struct inode *no, struct file *fp)


  //设备的打开..
DMA的使能!



int oss_write(struct file *fp, char *buf, size_t count, loff_t *off)
{
  //从用户空间读取数据


struct file_operations oss_ops = {
    .open = oss_open,
    .release = oss_release,
    .write = oss_write,
};

int iis_probe(struct device *dev)
{
    int ret = 0;
   
    init_waitqueue_head(&audio_dev.wq);

    audio_dev.iis_clk = clk_get(NULL, "iis");
    clk_use(audio_dev.iis_clk);
    clk_enable(audio_dev.iis_clk);

    audio_dev.gpio_virt = ioremap(0x56000000, SZ_1M);
    audio_dev.dma_virt = ioremap(0x4b000000, SZ_1M);
    audio_dev.iis_virt = ioremap(0x55000000, SZ_1M);

    l3_init(audio_dev.gpio_virt);


    iis_init(audio_dev.gpio_virt, audio_dev.iis_virt);
    dma_2ch_init(audio_dev.dma_virt);
    uda1341_init();

    audio_dev.buf_size = 44100 * 4 * 4;
    audio_dev.buf_virt = dma_alloc_coherent(dev, audio_dev.buf_size, &audio_dev.buf_phys, GFP_KERNEL);
    memset(audio_dev.buf_virt, 0x00, audio_dev.buf_size);

    audio_dev.data = NULL;
    audio_dev.data_state = 0;

    dma_2ch_set_size(audio_dev.buf_size);
    dma_2ch_set_src(audio_dev.buf_phys);

    ret = request_irq(IRQ_DMA2, dma_2ch_handle, 0, "iis dma 2 channel", &audio_dev);
    audio_dev.dsp = register_sound_dsp(&oss_ops, -1);
    return ret;
}
struct device_driver  iis_dri = {
    .name = "s3c2410-iis",
    .bus = &platform_bus_type,
    .probe = iis_probe,
    .remove = iis_remove,
};

int test_init(void)
{
    driver_register(&iis_dri);
    return 0;
}
测试的时候配置一下内核加载驱动
────────────────────────────────────────────────────────────────────┐ │ 
  │ │ <*> Open Sound System (DEPRECATED)                                  │ │ 
  │ │ < > S3C2440(bit2440) uda1341 driver                                 │ │ 
  │ │ <*> UDA1341 Stereo Codec                                            │ │ 
  │ │ < > Support for Turtle Beach MultiSound Classic, Tahiti, Monterey   │ │ 
  │ │ < > Support for Turtle Beach MultiSound Pinnacle, Fiji              │ │ 
  │ │ < > OSS sound modules                                               │ │ 
  │ │ < > TV card (bt848) mixer support                                   │ │ 
  │ │ < > AD1980 front/back switch plugin                                 │ │ 
阅读(2428) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~