/*************************************************************************** * __________ __ ___. * 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 #include "misc.h" 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 */