summaryrefslogtreecommitdiffstats
path: root/firmware/target/arm/s3c2440/gigabeat-fx/wmcodec-meg-fx.c
blob: eea4c58e4b8b4467995b76e43cbd8d03a0d0fc2f (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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id$
 *
 * Gigabeat specific code for the Wolfson codec
 *
 * Based on code from the ipodlinux project - http://ipodlinux.org/
 * Adapted for Rockbox in December 2005
 *
 * Original file: linux/arch/armnommu/mach-ipod/audio.c
 *
 * Copyright (c) 2003-2005 Bernard Leach (leachbj@bouncycastle.org)
 *
 * 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.
 *
 ****************************************************************************/
#include "cpu.h"
#include "kernel.h"
#include "sound.h"
#include "i2c-s3c2440.h"
#include "system-target.h"
#include "timer.h"
#include "wmcodec.h"

#ifdef HAVE_HARDWARE_BEEP
static void beep_stop(void);
#endif

void audiohw_init(void)
{
    /* GPC5 controls headphone output */
    GPCCON &= ~(0x3 << 10);
    GPCCON |= (1 << 10);
    GPCDAT |= (1 << 5);

    audiohw_preinit();

#ifdef HAVE_HARDWARE_BEEP
    /* pin pullup ON */
    GPBUP &= ~(1 << 3);
    beep_stop();
    /* set pin to TIMER3 output (functional TOUT3) */
    GPBCON = (GPBCON & ~(0x3 << 6)) | (2 << 6);
#endif
}

void wmcodec_write(int reg, int data)
{
    unsigned char d[2];
    d[0] = (reg << 1) | ((data & 0x100) >> 8);
    d[1] = data;
    i2c_write(0x34, d, 2);
}

#ifdef HAVE_HARDWARE_BEEP
/** Beeping via TIMER3 output to codec MONO input **/
static int beep_cycles = 0;
static int beep_cycle_count = 0;

static void beep_stop(void)
{
    int oldstatus = disable_interrupt_save(IRQ_FIQ_STATUS);

    /* stop interrupt */
    INTMSK |= TIMER3_MASK;
    /* stop timer */
    TCON &= ~(1 << 16);
    /* be sure timer PWM pin is LOW to avoid noise */
    TCON ^= (GPBDAT & (1 << 3)) << 15;
    /* clear pending */
    SRCPND = TIMER3_MASK;
    INTPND = TIMER3_MASK;

    restore_interrupt(oldstatus);
}

/* Timer interrupt called on every cycle */
void TIMER3(void)
{
    if (++beep_cycles >= beep_cycle_count)
    {
        /* beep is complete */
        beep_stop();
    }

    /* clear pending */
    SRCPND = TIMER3_MASK;
    INTPND = TIMER3_MASK;
}

void beep_play(unsigned int frequency, unsigned int duration,
               unsigned int amplitude)
{
    #define TIMER3_TICK_SEC (TIMER_FREQ / TIMER234_PRESCALE)

    unsigned long tcnt, tcmp;
    int oldstatus;

    if (frequency == 0 || duration == 0 || amplitude == 0)
    {
        beep_stop(); /* won't hear it anyway */
        return;
    }

    /* pretend this is pcm */
    if (amplitude > 32767)
        amplitude = 32767;

    /* limit frequency range to keep math in range */
    if (frequency > 19506)
        frequency = 19506;
    else if (frequency < 18)
        frequency = 18;

    /* set timer */
    tcnt = TIMER3_TICK_SEC / frequency;

    oldstatus = disable_interrupt_save(IRQ_FIQ_STATUS);

    beep_cycles = 0;
    beep_cycle_count = TIMER3_TICK_SEC * duration / (tcnt*1000);

    /* divider = 1/2 */
    TCFG1 = (TCFG1 & ~(0xf << 12)) | (0 << 12);
    /* stop TIMER3, inverter OFF */
    TCON &= ~((1 << 18) | (1 << 16));
    /* set countdown */
    TCNTB3 = tcnt;
    /* set PWM counter - control volume with duty cycle. */
    tcmp = tcnt*amplitude / (65536*2 - 2*amplitude);
    TCMPB3 = tcmp < 1 ? 1 : tcmp;
    /* manual update: on (to reset count), interval mode (auto reload) */
    TCON |= (1 << 19) | (1 << 17);
    /* clear manual bit */
    TCON &= ~(1 << 17);
    /* start timer */
    TCON |= (1 << 16);
    /* enable timer interrupt */
    INTMSK &= ~TIMER3_MASK;

    restore_interrupt(oldstatus);
}
#endif /* HAVE_HARDWARE_BEEP */