summaryrefslogtreecommitdiff
path: root/firmware/target/arm/as3525/as3525-codec.c
blob: 0bd49464fae8e4977bc60eeb23640974bda1847c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id$
 *
 * Copyright (C) 2008 by Bertrik Sikken
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/

/*
    Provides access to the codec/charger/rtc/adc part of the as3525.
    This part is on address 0x46 of the internal i2c bus in the as3525.
    Registers in the codec part seem to be nearly identical to the registers
    in the AS3514 (used in the "v1" versions of the sansa c200 and e200).
    
    I2C register description:
    * I2C2_CNTRL needs to be set to 0x51 for transfers to work at all.
      bit 1 indicates direction of transfer (0 = write, 1 = read)
    * I2C2_SR (status register) indicates in bit 0 if a transfer is busy.
    * I2C2_SLAD0 contains the i2c slave address to read from / write to.
    * I2C2_CPSR0/1 is the divider from the peripheral clock to the i2c clock.
    * I2C2_DACNT sets the number of bytes to transfer and actually starts it.
    
    When a transfer is attempted to a non-existing i2c slave address,
    interrupt bit 7 is raised and DACNT is not decremented after the transfer.
 */

#include "as3525-codec.h"
#include "as3525.h"

#define AUDIO_I2C_ADDR  0x46

#define I2C2_DATA       *((volatile unsigned int *)(I2C_AUDIO_BASE + 0x00))
#define I2C2_SLAD0      *((volatile unsigned int *)(I2C_AUDIO_BASE + 0x04))
#define I2C2_CNTRL      *((volatile unsigned int *)(I2C_AUDIO_BASE + 0x0C))
#define I2C2_DACNT      *((volatile unsigned int *)(I2C_AUDIO_BASE + 0x10))
#define I2C2_CPSR0      *((volatile unsigned int *)(I2C_AUDIO_BASE + 0x1C))
#define I2C2_CPSR1      *((volatile unsigned int *)(I2C_AUDIO_BASE + 0x20))
#define I2C2_IMR        *((volatile unsigned int *)(I2C_AUDIO_BASE + 0x24))
#define I2C2_RIS        *((volatile unsigned int *)(I2C_AUDIO_BASE + 0x28))
#define I2C2_MIS        *((volatile unsigned int *)(I2C_AUDIO_BASE + 0x2C))
#define I2C2_SR         *((volatile unsigned int *)(I2C_AUDIO_BASE + 0x30))
#define I2C2_INT_CLR    *((volatile unsigned int *)(I2C_AUDIO_BASE + 0x40))
#define I2C2_SADDR      *((volatile unsigned int *)(I2C_AUDIO_BASE + 0x44))


/* initialises the internal i2c bus and prepares for transfers to the codec */
void as3525_codec_init(void)
{
    /* reset device */
    CCU_SRC = CCU_SRC_I2C_AUDIO_EN;
    CCU_SRL = CCU_SRL_MAGIC_NUMBER;
    CCU_SRL = 0;
    
    /* enable clock */
    CGU_PERI |= CGU_I2C_AUDIO_MASTER_CLOCK_ENABLE;

    /* prescaler for i2c clock */
    I2C2_CPSR0 = 60;    /* 24 MHz / 400 kHz */
    I2C2_CPSR1 = 0;     /* MSB */    
    
    /* set i2c slave address of codec part */
    I2C2_SLAD0 = AUDIO_I2C_ADDR << 1;

    I2C2_CNTRL = 0x51;
}


/* returns != 0 when busy */
static int i2c_busy(void)
{
    return (I2C2_SR & 1);
}


/* returns 0 on success, <0 otherwise */
int as3525_codec_write(int index, int value)
{
    if (index == 0x21) {
        /* prevent setting of the LREG_CP_not bit */
        value &= ~(1 << 5);
    }
    
    /* check if still busy */
    if (i2c_busy()) {
        return -1;
    }
    
    /* start transfer */
    I2C2_SADDR = index;
    I2C2_CNTRL &= ~(1 << 1);
    I2C2_DATA = value;
    I2C2_DACNT = 1;
    
    /* wait for transfer*/
    while (i2c_busy());

    return 0;
}


/* returns value read on success, <0 otherwise */
int as3525_codec_read(int index)
{
    /* check if still busy */
    if (i2c_busy()) {
        return -1;
    }
    
    /* start transfer */
    I2C2_SADDR = index;
    I2C2_CNTRL |= (1 << 1);
    I2C2_DACNT = 1;
    
    /* wait for transfer*/
    while (i2c_busy());
    
    return I2C2_DATA;
}