www.pudn.com > ucosii_demo.rar > ac97.c, change:2007-08-16,size:37789b
/*
* ac97.c
*
* JzSOC On-Chip AC97 audio driver.
*
* Author: Seeger Chin
* e-mail: seeger.chin@gmail.com
*
* Copyright (C) 2006 Ingenic Semiconductor Corp.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <includes.h>
#include <regs.h>
#include <ops.h>
#include "pcm.h"
static unsigned int k_8000[] = {
0, 42, 85, 128, 170, 213,
};
static unsigned int reload_8000[] = {
1, 0, 0, 0, 0, 0,
};
static unsigned int k_11025[] = {
0, 58, 117, 176, 234, 37, 96, 154,
213, 16, 74, 133, 192, 250, 53, 112,
170, 229, 32, 90, 149, 208, 10, 69,
128, 186, 245, 48, 106, 165, 224, 26,
85, 144, 202, 5, 64, 122, 181, 240,
42, 101, 160, 218, 21, 80, 138, 197,
};
static unsigned int reload_11025[] = {
1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0,
0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1,
0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
};
static unsigned int k_16000[] = {
0, 85, 170,
};
static unsigned int reload_16000[] = {
1, 0, 0,
};
static unsigned int k_22050[] = {
0, 117, 234, 96, 213, 74, 192, 53,
170, 32, 149, 10, 128, 245, 106, 224,
85, 202, 64, 181, 42, 160, 21, 138,
};
static unsigned int reload_22050[] = {
1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0,
1, 0, 1, 0, 1, 0, 1, 0,
};
static unsigned int k_24000[] = {
0, 128,
};
static unsigned int reload_24000[] = {
1, 0,
};
static unsigned int k_32000[] = {
0, 170, 85,
};
static unsigned int reload_32000[] = {
1, 0, 1,
};
static unsigned int k_44100[] = {
0, 234, 213, 192, 170, 149, 128, 106,
85, 64, 42, 21,
};
static unsigned int reload_44100[] = {
1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
};
static unsigned int k_48000[] = {
0,
};
static unsigned int reload_48000[] = {
1,
};
static unsigned int f_scale_counts[8] = {
6, 48, 3, 24, 2, 3, 12, 1,
};
/* AC97 1.0 */
#define AC97_RESET 0x0000 //
#define AC97_MASTER_VOL_STEREO 0x0002 // Line Out
#define AC97_HEADPHONE_VOL 0x0004 //
#define AC97_MASTER_VOL_MONO 0x0006 // TAD Output
#define AC97_MASTER_TONE 0x0008 //
#define AC97_PCBEEP_VOL 0x000a // none
#define AC97_PHONE_VOL 0x000c // TAD Input (mono)
#define AC97_MIC_VOL 0x000e // MIC Input (mono)
#define AC97_LINEIN_VOL 0x0010 // Line Input (stereo)
#define AC97_CD_VOL 0x0012 // CD Input (stereo)
#define AC97_VIDEO_VOL 0x0014 // none
#define AC97_AUX_VOL 0x0016 // Aux Input (stereo)
#define AC97_PCMOUT_VOL 0x0018 // Wave Output (stereo)
#define AC97_RECORD_SELECT 0x001a //
#define AC97_RECORD_GAIN 0x001c
#define AC97_RECORD_GAIN_MIC 0x001e
#define AC97_GENERAL_PURPOSE 0x0020
#define AC97_3D_CONTROL 0x0022
#define AC97_MODEM_RATE 0x0024
#define AC97_POWER_CONTROL 0x0026
/* AC'97 2.0 */
#define AC97_EXTENDED_ID 0x0028 /* Extended Audio ID */
#define AC97_EXTENDED_STATUS 0x002A /* Extended Audio Status */
#define AC97_PCM_FRONT_DAC_RATE 0x002C /* PCM Front DAC Rate */
#define AC97_PCM_SURR_DAC_RATE 0x002E /* PCM Surround DAC Rate */
#define AC97_PCM_LFE_DAC_RATE 0x0030 /* PCM LFE DAC Rate */
#define AC97_PCM_LR_ADC_RATE 0x0032 /* PCM LR ADC Rate */
#define AC97_PCM_MIC_ADC_RATE 0x0034 /* PCM MIC ADC Rate */
#define AC97_CENTER_LFE_MASTER 0x0036 /* Center + LFE Master Volume */
#define AC97_SURROUND_MASTER 0x0038 /* Surround (Rear) Master Volume */
#define AC97_RESERVED_3A 0x003A /* Reserved in AC '97 2.2 */
/* AC'97 2.2 */
#define AC97_SPDIF_CONTROL 0x003A /* S/PDIF Control */
/* range 0x3c-0x58 - MODEM */
#define AC97_EXTENDED_MODEM_ID 0x003C
#define AC97_EXTEND_MODEM_STAT 0x003E
#define AC97_LINE1_RATE 0x0040
#define AC97_LINE2_RATE 0x0042
#define AC97_HANDSET_RATE 0x0044
#define AC97_LINE1_LEVEL 0x0046
#define AC97_LINE2_LEVEL 0x0048
#define AC97_HANDSET_LEVEL 0x004A
#define AC97_GPIO_CONFIG 0x004C
#define AC97_GPIO_POLARITY 0x004E
#define AC97_GPIO_STICKY 0x0050
#define AC97_GPIO_WAKE_UP 0x0052
#define AC97_GPIO_STATUS 0x0054
#define AC97_MISC_MODEM_STAT 0x0056
#define AC97_RESERVED_58 0x0058
/* registers 0x005a - 0x007a are vendor reserved */
#define AC97_VENDOR_ID1 0x007c
#define AC97_VENDOR_ID2 0x007e
/* volume control bit defines */
#define AC97_MUTE 0x8000
#define AC97_MICBOOST 0x0040
#define AC97_LEFTVOL 0x3f00
#define AC97_RIGHTVOL 0x003f
/* record mux defines */
#define AC97_RECMUX_MIC 0x0000
#define AC97_RECMUX_CD 0x0101
#define AC97_RECMUX_VIDEO 0x0202
#define AC97_RECMUX_AUX 0x0303
#define AC97_RECMUX_LINE 0x0404
#define AC97_RECMUX_STEREO_MIX 0x0505
#define AC97_RECMUX_MONO_MIX 0x0606
#define AC97_RECMUX_PHONE 0x0707
/* general purpose register bit defines */
#define AC97_GP_LPBK 0x0080 /* Loopback mode */
#define AC97_GP_MS 0x0100 /* Mic Select 0=Mic1, 1=Mic2 */
#define AC97_GP_MIX 0x0200 /* Mono output select 0=Mix, 1=Mic */
#define AC97_GP_RLBK 0x0400 /* Remote Loopback - Modem line codec */
#define AC97_GP_LLBK 0x0800 /* Local Loopback - Modem Line codec */
#define AC97_GP_LD 0x1000 /* Loudness 1=on */
#define AC97_GP_3D 0x2000 /* 3D Enhancement 1=on */
#define AC97_GP_ST 0x4000 /* Stereo Enhancement 1=on */
#define AC97_GP_POP 0x8000 /* Pcm Out Path, 0=pre 3D, 1=post 3D */
/* extended audio status and control bit defines */
#define AC97_EA_VRA 0x0001 /* Variable bit rate enable bit */
#define AC97_EA_DRA 0x0002 /* Double-rate audio enable bit */
#define AC97_EA_SPDIF 0x0004 /* S/PDIF Enable bit */
#define AC97_EA_VRM 0x0008 /* Variable bit rate for MIC enable bit */
#define AC97_EA_CDAC 0x0040 /* PCM Center DAC is ready (Read only) */
#define AC97_EA_SDAC 0x0040 /* PCM Surround DACs are ready (Read only) */
#define AC97_EA_LDAC 0x0080 /* PCM LFE DAC is ready (Read only) */
#define AC97_EA_MDAC 0x0100 /* MIC ADC is ready (Read only) */
#define AC97_EA_SPCV 0x0400 /* S/PDIF configuration valid (Read only) */
#define AC97_EA_PRI 0x0800 /* Turns the PCM Center DAC off */
#define AC97_EA_PRJ 0x1000 /* Turns the PCM Surround DACs off */
#define AC97_EA_PRK 0x2000 /* Turns the PCM LFE DAC off */
#define AC97_EA_PRL 0x4000 /* Turns the MIC ADC off */
#define AC97_EA_SLOT_MASK 0xffcf /* Mask for slot assignment bits */
#define AC97_EA_SPSA_3_4 0x0000 /* Slot assigned to 3 & 4 */
#define AC97_EA_SPSA_7_8 0x0010 /* Slot assigned to 7 & 8 */
#define AC97_EA_SPSA_6_9 0x0020 /* Slot assigned to 6 & 9 */
#define AC97_EA_SPSA_10_11 0x0030 /* Slot assigned to 10 & 11 */
/* S/PDIF control bit defines */
#define AC97_SC_PRO 0x0001 /* Professional status */
#define AC97_SC_NAUDIO 0x0002 /* Non audio stream */
#define AC97_SC_COPY 0x0004 /* Copyright status */
#define AC97_SC_PRE 0x0008 /* Preemphasis status */
#define AC97_SC_CC_MASK 0x07f0 /* Category Code mask */
#define AC97_SC_L 0x0800 /* Generation Level status */
#define AC97_SC_SPSR_MASK 0xcfff /* S/PDIF Sample Rate bits */
#define AC97_SC_SPSR_44K 0x0000 /* Use 44.1kHz Sample rate */
#define AC97_SC_SPSR_48K 0x2000 /* Use 48kHz Sample rate */
#define AC97_SC_SPSR_32K 0x3000 /* Use 32kHz Sample rate */
#define AC97_SC_DRS 0x4000 /* Double Rate S/PDIF */
#define AC97_SC_V 0x8000 /* Validity status */
/* powerdown control and status bit defines */
/* status */
#define AC97_PWR_MDM 0x0010 /* Modem section ready */
#define AC97_PWR_REF 0x0008 /* Vref nominal */
#define AC97_PWR_ANL 0x0004 /* Analog section ready */
#define AC97_PWR_DAC 0x0002 /* DAC section ready */
#define AC97_PWR_ADC 0x0001 /* ADC section ready */
/* control */
#define AC97_PWR_PR0 0x0100 /* ADC and Mux powerdown */
#define AC97_PWR_PR1 0x0200 /* DAC powerdown */
#define AC97_PWR_PR2 0x0400 /* Output mixer powerdown (Vref on) */
#define AC97_PWR_PR3 0x0800 /* Output mixer powerdown (Vref off) */
#define AC97_PWR_PR4 0x1000 /* AC-link powerdown */
#define AC97_PWR_PR5 0x2000 /* Internal Clk disable */
#define AC97_PWR_PR6 0x4000 /* HP amp powerdown */
#define AC97_PWR_PR7 0x8000 /* Modem off - if supported */
/* extended audio ID register bit defines */
#define AC97_EXTID_VRA 0x0001
#define AC97_EXTID_DRA 0x0002
#define AC97_EXTID_SPDIF 0x0004
#define AC97_EXTID_VRM 0x0008
#define AC97_EXTID_DSA0 0x0010
#define AC97_EXTID_DSA1 0x0020
#define AC97_EXTID_CDAC 0x0040
#define AC97_EXTID_SDAC 0x0080
#define AC97_EXTID_LDAC 0x0100
#define AC97_EXTID_AMAP 0x0200
#define AC97_EXTID_REV0 0x0400
#define AC97_EXTID_REV1 0x0800
#define AC97_EXTID_ID0 0x4000
#define AC97_EXTID_ID1 0x8000
/* extended status register bit defines */
#define AC97_EXTSTAT_VRA 0x0001
#define AC97_EXTSTAT_DRA 0x0002
#define AC97_EXTSTAT_SPDIF 0x0004
#define AC97_EXTSTAT_VRM 0x0008
#define AC97_EXTSTAT_SPSA0 0x0010
#define AC97_EXTSTAT_SPSA1 0x0020
#define AC97_EXTSTAT_CDAC 0x0040
#define AC97_EXTSTAT_SDAC 0x0080
#define AC97_EXTSTAT_LDAC 0x0100
#define AC97_EXTSTAT_MADC 0x0200
#define AC97_EXTSTAT_SPCV 0x0400
#define AC97_EXTSTAT_PRI 0x0800
#define AC97_EXTSTAT_PRJ 0x1000
#define AC97_EXTSTAT_PRK 0x2000
#define AC97_EXTSTAT_PRL 0x4000
/* useful power states */
#define AC97_PWR_D0 0x0000 /* everything on */
#define AC97_PWR_D1 AC97_PWR_PR0|AC97_PWR_PR1|AC97_PWR_PR4
#define AC97_PWR_D2 AC97_PWR_PR0|AC97_PWR_PR1|AC97_PWR_PR2|AC97_PWR_PR3|AC97_PWR_PR4
#define AC97_PWR_D3 AC97_PWR_PR0|AC97_PWR_PR1|AC97_PWR_PR2|AC97_PWR_PR3|AC97_PWR_PR4
#define AC97_PWR_ANLOFF AC97_PWR_PR2|AC97_PWR_PR3 /* analog section off */
/* Total number of defined registers. */
#define AC97_REG_CNT 64
/* OSS interface to the ac97s.. */
#define AC97_STEREO_MASK (SOUND_MASK_VOLUME|SOUND_MASK_PCM|\
SOUND_MASK_LINE|SOUND_MASK_CD|\
SOUND_MASK_ALTPCM|SOUND_MASK_IGAIN|\
SOUND_MASK_LINE1|SOUND_MASK_VIDEO)
#define AC97_SUPPORTED_MASK (AC97_STEREO_MASK | \
SOUND_MASK_BASS|SOUND_MASK_TREBLE|\
SOUND_MASK_SPEAKER|SOUND_MASK_MIC|\
SOUND_MASK_PHONEIN|SOUND_MASK_PHONEOUT)
#define AC97_RECORD_MASK (SOUND_MASK_MIC|\
SOUND_MASK_CD|SOUND_MASK_IGAIN|SOUND_MASK_VIDEO|\
SOUND_MASK_LINE1| SOUND_MASK_LINE|\
SOUND_MASK_PHONEIN)
#ifndef u8
#define u8 unsigned char
#endif
#ifndef u16
#define u16 unsigned short
#endif
#ifndef u32
#define u32 unsigned int
#endif
#define AUDIO_READ_DMA 0
#define AUDIO_WRITE_DMA 1
#ifdef PHYADDR
#undef PHYADDR
#endif
#define PHYADDR(n) ((n) & 0x1fffffff)
#ifdef KSEG1ADDR
#undef KSEG1ADDR
#endif
#define KSEG1ADDR(n) (PHYADDR(n) | 0xa0000000)
#ifdef PAGE_SIZE
#undef PAGE_SIZE
#endif
#define PAGE_SIZE 0x1000
extern void printf(const char *fmt, ...);
static const struct {
unsigned int id;
char *name;
} ac97_codec_ids[] = {
{0x41445303, "Analog Devices AD1819"},
{0x41445340, "Analog Devices AD1881"},
{0x41445348, "Analog Devices AD1881A"},
{0x41445360, "Analog Devices AD1885"},
{0x41445361, "Analog Devices AD1886"},
{0x41445460, "Analog Devices AD1885"},
{0x41445461, "Analog Devices AD1886"},
{0x414B4D00, "Asahi Kasei AK4540"},
{0x414B4D01, "Asahi Kasei AK4542"},
{0x414B4D02, "Asahi Kasei AK4543"},
{0x414C4710, "ALC200/200P"},
{0x414C4720, "ALC650"},
{0x43525900, "Cirrus Logic CS4297"},
{0x43525903, "Cirrus Logic CS4297"},
{0x43525913, "Cirrus Logic CS4297A rev A"},
{0x43525914, "Cirrus Logic CS4297A rev B"},
{0x43525923, "Cirrus Logic CS4298"},
{0x4352592B, "Cirrus Logic CS4294"},
{0x4352592D, "Cirrus Logic CS4294"},
{0x43525931, "Cirrus Logic CS4299 rev A"},
{0x43525933, "Cirrus Logic CS4299 rev C"},
{0x43525934, "Cirrus Logic CS4299 rev D"},
{0x4352594d, "Cirrus Logic CS4201"},
{0x45838308, "ESS Allegro ES1988"},
{0x49434511, "ICE1232"},
{0x4e534331, "National Semiconductor LM4549"},
{0x50534304, "Philips UCB1400"},
{0x53494c22, "Silicon Laboratory Si3036"},
{0x53494c23, "Silicon Laboratory Si3038"},
{0x545200FF, "TriTech TR?????"},
{0x54524102, "TriTech TR28022"},
{0x54524103, "TriTech TR28023"},
{0x54524106, "TriTech TR28026"},
{0x54524108, "TriTech TR28028"},
{0x54524123, "TriTech TR A5"},
{0x574D4C00, "Wolfson WM9700A"},
{0x574D4C03, "Wolfson WM9703/WM9707"},
{0x574D4C04, "Wolfson WM9704M/WM9704Q"},
{0x83847600, "SigmaTel STAC????"},
{0x83847604, "SigmaTel STAC9701/3/4/5"},
{0x83847605, "SigmaTel STAC9704"},
{0x83847608, "SigmaTel STAC9708"},
{0x83847609, "SigmaTel STAC9721/23"},
{0x83847644, "SigmaTel STAC9744/45"},
{0x83847656, "SigmaTel STAC9756/57"},
{0x83847666, "SigmaTel STAC9750T"},
{0x83847684, "SigmaTel STAC9783/84?"},
{0x57454301, "Winbond 83971D"},
{0xffffffff, "End flag"},
};
void ac97codec_info(unsigned short id1, unsigned short id2)
{
int i;
unsigned int id = (id1 < 16) | id2;
for (i=0;;i++) {
if (ac97_codec_ids[i].id == 0xffffffff) {
printf("AC97 codec: Unknown codec (%04x %04x)\n",
id1, id2);
break;
}
if (ac97_codec_ids[i].id == id) {
printf("AC97 codec: %s\n", ac97_codec_ids[i].name);
break;
}
}
}
/* maxinum number of AC97 codecs connected, AC97 2.0 defined 4 */
#define NR_AC97 2
#define STANDARD_SPEED 48000
#define MAX_RETRY 100
static int jz_audio_rate;
static char jz_audio_format;
static char jz_audio_channels;
static int jz_audio_k; /* rate expand multiple, for record */
static int jz_audio_q; /* rate expand compensate, for record */
static int jz_audio_count; /* total count of voice data */
static int last_jz_audio_count;
static int bitscale;
static unsigned int f_scale_count;
static unsigned int *f_scale_array;
static unsigned int *f_scale_reload;
static unsigned int f_scale_idx;
static void jz_update_filler(int bits, int channels);
static void jz_ac97_replay_dma_irq(unsigned int);
static void jz_ac97_record_dma_irq(unsigned int);
static void (*replay_filler)(unsigned long src_start, int count, int id);
static int (*record_filler)(unsigned long dst_start, int count, int id);
extern u16 ac97_codec_read(u8 reg);
extern void ac97_codec_write(u8 reg, u16 data);
#define QUEUE_MAX 2
typedef struct buffer_queue_s {
int count;
int id[QUEUE_MAX];
int lock;
} buffer_queue_t;
static unsigned long out_dma_buf[QUEUE_MAX+1];
static unsigned long out_dma_pbuf[QUEUE_MAX+1];
static unsigned long out_dma_buf_data_count[QUEUE_MAX+1];
static unsigned long in_dma_buf[QUEUE_MAX+1];
static unsigned long in_dma_pbuf[QUEUE_MAX+1];
static unsigned long in_dma_buf_data_count[QUEUE_MAX+1];
static buffer_queue_t out_empty_queue;
static buffer_queue_t out_full_queue;
static buffer_queue_t out_busy_queue;
static buffer_queue_t in_empty_queue;
static buffer_queue_t in_full_queue;
static buffer_queue_t in_busy_queue;
static int first_record_call = 1;
static OS_EVENT *tx_sem;
static OS_EVENT *rx_sem;
static inline int get_buffer_id(struct buffer_queue_s *q)
{
int r;
unsigned long flags;
int i;
flags = spin_lock_irqsave();
if (q->count == 0) return -1;
r = q->id[0];
for (i=0;i q->count-1;i++)
q->id[i] = q->id[i+1];
q->count --;
spin_unlock_irqrestore(flags);
return r;
}
static inline void put_buffer_id(struct buffer_queue_s *q, int id)
{
unsigned long flags;
flags = spin_lock_irqsave();
q->id[q->count] = id;
q->count ++;
spin_unlock_irqrestore(flags);
}
static inline int elements_in_queue(struct buffer_queue_s *q)
{
int r;
unsigned long flags;
flags=spin_lock_irqsave();
r = q->count;
spin_unlock_irqrestore(flags);
return r;
}
/****************************************************************************
* Architecture related routines
****************************************************************************/
static void jz_ac97_record_dma_irq (unsigned int arg)
{
int dma = AUDIO_READ_DMA;
int id1, id2;
dma_stop(dma);
if (__dmac_channel_address_error_detected(dma)) {
printf("%s: DMAC address error.\n", __FUNCTION__);
__dmac_channel_clear_address_error(dma);
}
if (__dmac_channel_transmit_end_detected(dma)) {
__dmac_channel_clear_transmit_end(dma);
id1 = get_buffer_id(&in_busy_queue);
put_buffer_id(&in_full_queue, id1);
// __dcache_invalidate_all();
OSSemPost(rx_sem);
if ((id2 = get_buffer_id(&in_empty_queue)) >= 0) {
put_buffer_id(&in_busy_queue, id2);
in_dma_buf_data_count[id2] = in_dma_buf_data_count[id1];
dma_start(dma, PHYADDR(AIC_DR), in_dma_pbuf[id2],
in_dma_buf_data_count[id2]);
} else
in_busy_queue.count = 0;
}
}
static void jz_ac97_replay_dma_irq (unsigned int arg)
{
int dma = AUDIO_WRITE_DMA;
int id;
dma_stop(dma);
if (__dmac_channel_address_error_detected(dma)) {
printf("%s: DMAC address error.\n", __FUNCTION__);
__dmac_channel_clear_address_error(dma);
}
if (__dmac_channel_transmit_end_detected(dma)) {
__dmac_channel_clear_transmit_end(dma);
if ((id = get_buffer_id(&out_busy_queue)) 0)
printf("Strange DMA finish interrupt for AC97 module\n");
put_buffer_id(&out_empty_queue, id);
if ((id = get_buffer_id(&out_full_queue)) >= 0) {
put_buffer_id(&out_busy_queue, id);
dma_start(dma, out_dma_pbuf[id], PHYADDR(AIC_DR),
out_dma_buf_data_count[id]);
} else
out_busy_queue.count = 0;
if (elements_in_queue(&out_empty_queue) > 0)
OSSemPost(tx_sem);
}
}
/*
* Initialize the onchip AC97 controller
*/
static void jz_ac97_initHw(void)
{
__ac97_disable();
__ac97_reset();
__ac97_enable();
__ac97_cold_reset_codec();
/* wait for a long time to let ac97 controller reset completely,
* otherwise, registers except ACFR will be clear by reset, can't be
* set correctly.
*/
udelay(160);
__ac97_disable_record();
__ac97_disable_replay();
__ac97_disable_loopback();
/* Set FIFO data size. Which shows valid data bits.
*/
__ac97_set_oass(8);
__ac97_set_iass(8);
__ac97_set_xs_stereo();
__ac97_set_rs_stereo();
}
static int jz_audio_set_speed(int rate)
{
/* 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 99999999 ? */
u32 dacp;
if (rate > 48000)
rate = 48000;
if (rate 8000)
rate = 8000;
/* Power down the DAC */
dacp=ac97_codec_read(AC97_POWER_CONTROL);
ac97_codec_write(AC97_POWER_CONTROL, dacp|0x0200);
ac97_codec_write(AC97_PCM_FRONT_DAC_RATE, 48000);
/* Power it back up */
ac97_codec_write(AC97_POWER_CONTROL, dacp);
jz_audio_rate = rate;
jz_audio_k = STANDARD_SPEED / rate;
if (rate * jz_audio_k != STANDARD_SPEED)
jz_audio_q = rate / ((STANDARD_SPEED / jz_audio_k) - rate );
else
jz_audio_q = 0x1fffffff; /* a very big value, don't compensate */
switch (rate) {
case 8000:
f_scale_count = f_scale_counts[0];
f_scale_array = k_8000;
f_scale_reload = reload_8000;
break;
case 11025:
f_scale_count = f_scale_counts[1];
f_scale_array = k_11025;
f_scale_reload = reload_11025;
break;
case 16000:
f_scale_count = f_scale_counts[2];
f_scale_array = k_16000;
f_scale_reload = reload_16000;
break;
case 22050:
f_scale_count = f_scale_counts[3];
f_scale_array = k_22050;
f_scale_reload = reload_22050;
break;
case 24000:
f_scale_count = f_scale_counts[4];
f_scale_array = k_24000;
f_scale_reload = reload_24000;
break;
case 32000:
f_scale_count = f_scale_counts[5];
f_scale_array = k_32000;
f_scale_reload = reload_32000;
break;
case 44100:
f_scale_count = f_scale_counts[6];
f_scale_array = k_44100;
f_scale_reload = reload_44100;
break;
case 48000:
f_scale_count = f_scale_counts[7];
f_scale_array = k_48000;
f_scale_reload = reload_48000;
break;
}
f_scale_idx = 0;
return jz_audio_rate;
}
static int record_fill_1x8_u(unsigned long dst_start, int count, int id)
{
int cnt = 0;
unsigned char data;
volatile unsigned char *s = (unsigned char *)(in_dma_buf[id]);
volatile unsigned char *dp = (unsigned char *)dst_start;
while (count > 0) {
count -= 2; /* count in dword */
if ((jz_audio_count++ % jz_audio_k) == 0) {
cnt++;
data = *s++;
*dp++ = data + 0x80;
s++; /* skip the other channel */
} else
s += 2; /* skip the redundancy */
if (jz_audio_count - last_jz_audio_count >= jz_audio_q) {
jz_audio_count++;
last_jz_audio_count = jz_audio_count;
count -= 2;
s += 2;
}
}
return cnt;
}
static int record_fill_2x8_u(unsigned long dst_start, int count, int id)
{
int cnt = 0;
unsigned char d1, d2;
volatile unsigned char *s = (unsigned char *)(in_dma_buf[id]);
volatile unsigned char *dp = (unsigned char *)dst_start;
while (count > 0) {
count -= 2;
if ((jz_audio_count++ % jz_audio_k) == 0) {
cnt += 2;
d1 = *s++;
*dp++ = d1 + 0x80;
d2 = *s++;
*dp++ = d2 + 0x80;
} else
s += 2; /* skip the redundancy */
if (jz_audio_count - last_jz_audio_count >= jz_audio_q * 2) {
jz_audio_count += 2;
last_jz_audio_count = jz_audio_count;
count -= 2;
s += 2;
}
}
return cnt;
}
static int record_fill_1x16_s(unsigned long dst_start, int count, int id)
{
int cnt = 0;
unsigned short d1;
unsigned short *s = (unsigned short *)(in_dma_buf[id]);
unsigned short *dp = (unsigned short *)dst_start;
while (count > 0) {
count -= 2; /* count in dword */
if ((jz_audio_count++ % jz_audio_k) == 0) {
cnt += 2; /* count in byte */
d1 = *s++;
*dp++ = d1;
s++; /* skip the other channel */
} else
s += 2; /* skip the redundancy */
if (jz_audio_count - last_jz_audio_count >= jz_audio_q * 2) {
jz_audio_count += 2;
last_jz_audio_count = jz_audio_count;
count -= 2;
s += 2;
}
}
return cnt;
}
static int record_fill_2x16_s(unsigned long dst_start, int count, int id)
{
int cnt = 0;
unsigned short d1, d2;
unsigned short *s = (unsigned short *)(in_dma_buf[id]);
unsigned short *dp = (unsigned short *)dst_start;
while (count > 0) {
count -= 2; /* count in dword */
if ((jz_audio_count++ % jz_audio_k) == 0) {
cnt += 4; /* count in byte */
d1 = *s++;
*dp++ = d1;
d2 = *s++;
*dp++ = d2;
} else
s += 2; /* skip the redundancy */
if (jz_audio_count - last_jz_audio_count >= jz_audio_q * 4) {
jz_audio_count += 4;
last_jz_audio_count = jz_audio_count;
count -= 2;
s += 2;
}
}
return cnt;
}
static void replay_fill_1x8_u(unsigned long src_start, int count, int id)
{
int i, cnt = 0;
unsigned char data;
unsigned char *s = (unsigned char *)src_start;
unsigned char *dp = (unsigned char *)(out_dma_buf[id]);
while (count > 0) {
count--;
jz_audio_count++;
cnt += jz_audio_k;
data = *s++ - 0x80;
for (i=0;i<jz_audio_k;i++) {
*dp++ = data;
*dp++ = data;
}
}
cnt = cnt * 2;
out_dma_buf_data_count[id] = cnt;
}
static void replay_fill_2x8_u(unsigned long src_start, int count, int id)
{
int i, cnt = 0;
unsigned char d1, d2;
unsigned char *s = (unsigned char *)src_start;
unsigned char *dp = (unsigned char*)(out_dma_buf[id]);
while (count > 0) {
count -= 2;
jz_audio_count += 2;
cnt += 2 * jz_audio_k;
d1 = *s++ - 0x80;
d2 = *s++ - 0x80;
for (i=0;i<jz_audio_k;i++) {
*dp++ = d1;
*dp++ = d2;
}
if (jz_audio_count - last_jz_audio_count >= jz_audio_q * 2) {
cnt += 2 * jz_audio_k;
last_jz_audio_count = jz_audio_count;
for (i=0;i<jz_audio_k;i++) {
*dp++ = d1;
*dp++ = d2;
}
}
}
out_dma_buf_data_count[id] = cnt;
}
static void replay_fill_1x16_s(unsigned long src_start, int count, int id)
{
int i, cnt = 0;
static short d1, d2, d;
short *s = (short *)src_start;
short *dp = (short *)(out_dma_buf[id]);
d2 = *s++;
count -= 2;
while (count >= 0) {
if (f_scale_reload[f_scale_idx]) {
d1 = d2;
d2 = *s++;
if (!count)
break;
count -= 2;
}
d = d1 + (((d2 - d1) * f_scale_array[f_scale_idx]) >> 8);
*dp++ = d;
*dp++ = d;
cnt += 4;
f_scale_idx ++;
if (f_scale_idx >= f_scale_count)
f_scale_idx = 0;
}
out_dma_buf_data_count[id] = cnt;
}
static void replay_fill_2x16_s(unsigned long src_start, int count, int id)
{
int i, cnt = 0;
static short d11, d12, d21, d22, d1, d2;
short *s = (short *)src_start;
short *dp = (short *)(out_dma_buf[id]);
d12 = *s++;
d22 = *s++;
count -= 4;
while (count >= 0) {
register unsigned int kvalue;
kvalue = f_scale_array[f_scale_idx];
if (f_scale_reload[f_scale_idx]) {
d11 = d12;
d12 = *s++;
d21 = d22;
d22 = *s++;
if (!count)
break;
count -= 4;
}
d1 = d11 + (((d12 - d11)*kvalue) >> 8);
d2 = d21 + (((d22 - d21)*kvalue) >> 8);
*dp++ = d1;
*dp++ = d2;
cnt += 4;
f_scale_idx ++;
if (f_scale_idx >= f_scale_count)
f_scale_idx = 0;
}
out_dma_buf_data_count[id] = cnt;
}
static unsigned int jz_audio_set_format(unsigned int fmt)
{
switch (fmt) {
case AFMT_U8:
case AFMT_S16_LE:
jz_audio_format = fmt;
jz_update_filler(fmt, jz_audio_channels);
break;
}
return jz_audio_format;
}
static short jz_audio_set_channels(short channels)
{
switch (channels) {
case 1:
__ac97_set_xs_stereo(); // always stereo when playing
__ac97_set_rs_stereo(); // always stereo when recording
jz_audio_channels = channels;
jz_update_filler(jz_audio_format, jz_audio_channels);
break;
case 2:
__ac97_set_xs_stereo();
__ac97_set_rs_stereo();
jz_audio_channels = channels;
jz_update_filler(jz_audio_format, jz_audio_channels);
break;
case 0:
break;
}
return jz_audio_channels;
}
static void jz_audio_reset(void)
{
int i;
__ac97_disable_replay();
__ac97_disable_receive_dma();
__ac97_disable_record();
__ac97_disable_transmit_dma();
in_empty_queue.count = QUEUE_MAX;
out_empty_queue.count = QUEUE_MAX;
for (i=0;i<QUEUE_MAX;i++) {
in_empty_queue.id[i] = i;
out_empty_queue.id[i] = i;
}
in_busy_queue.count = 0;
in_full_queue.count = 0;
out_busy_queue.count = 0;
out_full_queue.count = 0;
}
/* Read / Write AC97 codec registers */
static inline int jz_out_command_ready(void)
{
int t2 = 1000;
int done = 0;
while (! done && t2-- > 0) {
if (REG32(AC97_ACSR) & AIC_ACSR_CADT) {
REG32(AC97_ACSR) &= ~AIC_ACSR_CADT;
done = 1;
} else
udelay (1);
}
return done;
}
static inline int jz_in_status_ready(void)
{
int t2 = 1000;
int done = 0;
while (! done && t2-- > 0)
if (REG32(AC97_ACSR) & AIC_ACSR_SADR) {
REG32(AC97_ACSR) &= ~AIC_ACSR_SADR;
done = 1;
} else {
if (REG32(AC97_ACSR) & AIC_ACSR_RSTO) {
REG32(AC97_ACSR) &= ~AIC_ACSR_RSTO;
done = 0;
break;
}
udelay (1);
}
return done;
}
static int jz_readAC97Reg (u8 reg)
{
u16 value;
if (reg 128) {
u8 ret_reg;
__ac97_out_rcmd_addr(reg);
if (jz_out_command_ready())
while (jz_in_status_ready()) {
ret_reg = __ac97_in_status_addr();
value = __ac97_in_data();
if (ret_reg == reg)
return value;
else
return -1;
}
}
value = __ac97_in_data();
return -1;
}
static int jz_writeAC97Reg (u8 reg, u16 value)
{
unsigned long flags;
int done = 0;
flags = spin_lock_irqsave();
__ac97_out_wcmd_addr(reg);
__ac97_out_data(value);
if (jz_out_command_ready())
done = 1;
spin_unlock_irqrestore(flags);
return done;
}
u16 ac97_codec_read(u8 reg)
{
int res = jz_readAC97Reg(reg);
int count = 0;
while (res == -1) {
udelay(1000);
__ac97_warm_reset_codec();
udelay(1000);
res = jz_readAC97Reg(reg);
count ++;
if (count > MAX_RETRY)
break;
}
return (u16)res;
}
void ac97_codec_write(u8 reg, u16 data)
{
int done = jz_writeAC97Reg(reg, data);
int count = 0;
while (done == 0) {
count ++;
udelay (2000);
__ac97_warm_reset_codec();
udelay(2000);
done = jz_writeAC97Reg(reg, data);
if ( count > MAX_RETRY )
break;
}
}
/* AC97 codec initialisation. */
static int jz_ac97_codec_init(void)
{
int num_ac97 = 0;
int ready_2nd = 0;
unsigned short eid, t;
int i = 0;
if (__ac97_codec_is_low_power_mode()) {
printf("AC97 codec is low power mode, warm reset ...\n");
__ac97_warm_reset_codec();
udelay(10);
}
i = 0;
while (!__ac97_codec_is_ready()) {
i++;
if ( i > 100) {
printf("AC97 codec not ready, failed init ..\n");
return -1;
}
udelay(10);
}
i = 0;
/* AC97 probe codec */
ac97_codec_write(AC97_RESET, 0L);
udelay(10);
if (ac97_codec_read(AC97_RESET) & 0x8000) {
printf("AC97 codec not present...\n");
return -1;
}
ac97_codec_write(AC97_EXTENDED_MODEM_ID, 0L);
/* Probe for Modem Codec */
ac97_codec_read(AC97_EXTENDED_MODEM_ID);
ac97codec_info(ac97_codec_read(AC97_VENDOR_ID1), ac97_codec_read(AC97_VENDOR_ID2));
/* Codec capacity */
t = ac97_codec_read(AC97_RESET);
printf("Support:");
if (t & 0x04)
printf(" BASS TREBLE");
if (t & 0x10)
printf(" ALTPCM");
printf("\n");
ac97_codec_write(AC97_MASTER_VOL_STEREO, 0x2020);
if (ac97_codec_read(AC97_MASTER_VOL_STEREO) == 0x2020)
bitscale = 6;
else
bitscale = 5;
printf("AC97 Master Volume bit resolution: %dbit\n", bitscale);
eid = ac97_codec_read(AC97_EXTENDED_ID);
if (eid == 0xFFFF) {
printf("AC97: no codec attached?\n");
return -1;
}
if (!(eid & 0x0001))
printf("AC97 codec: only supports fixed 48KHz sample rate\n");
else {
printf("AC97 codec: supports variable sample rate\n");
/* enable head phone */
ac97_codec_write(0x6a, ac97_codec_read(0x6a)|0x40);
#if 0
ac97_codec_write(AC97_EXTENDED_STATUS, 9);
ac97_codec_write(AC97_EXTENDED_STATUS,
ac97_codec_read(AC97_EXTENDED_STATUS)|0xe800);
#endif
ac97_codec_write(AC97_POWER_CONTROL,
ac97_codec_read(AC97_POWER_CONTROL)&~0x7f00);
for (i=10;i;i--)
if ((ac97_codec_read(AC97_POWER_CONTROL) & 0xf) == 0xf)
break;
#if 0
if (!ac97_codec_read(AC97_EXTENDED_STATUS) & 1)
printf("Codec refuse to support VRA, using 48Khz\n");
#endif
}
return 0;
}
static void jz_update_filler(int format, int channels)
{
#define TYPE(fmt,ch) (((fmt)<<3) | ((ch)&7)) /* up to 8 chans supported. */
switch (TYPE(format, channels)) {
default:
case TYPE(AFMT_U8, 1):
__aic_disable_mono2stereo();
__aic_disable_unsignadj();
dma_block_size(AUDIO_READ_DMA, 4);
dma_block_size(AUDIO_WRITE_DMA, 4);
replay_filler = replay_fill_1x8_u;
record_filler = record_fill_1x8_u;
__ac97_set_oass(8);
__ac97_set_iass(8);
dma_src_size(AUDIO_READ_DMA, 8);
dma_dest_size(AUDIO_WRITE_DMA, 8);
break;
case TYPE(AFMT_U8, 2):
__aic_disable_mono2stereo();
__aic_disable_unsignadj();
dma_block_size(AUDIO_READ_DMA, 4);
dma_block_size(AUDIO_WRITE_DMA, 4);
replay_filler = replay_fill_2x8_u;
record_filler = record_fill_2x8_u;
__ac97_set_oass(8);
__ac97_set_iass(8);
dma_src_size(AUDIO_READ_DMA, 8);
dma_dest_size(AUDIO_WRITE_DMA, 8);
break;
case TYPE(AFMT_S16_LE, 1):
__aic_disable_mono2stereo();
dma_block_size(AUDIO_READ_DMA, 4);
dma_block_size(AUDIO_WRITE_DMA, 4);
__aic_disable_unsignadj();
replay_filler = replay_fill_1x16_s;
record_filler = record_fill_1x16_s;
__ac97_set_oass(16);
__ac97_set_iass(16);
dma_src_size(AUDIO_READ_DMA, 16);
dma_dest_size(AUDIO_WRITE_DMA, 16);
break;
case TYPE(AFMT_S16_LE, 2):
dma_block_size(AUDIO_READ_DMA, 4);
dma_block_size(AUDIO_WRITE_DMA, 4);
__aic_disable_mono2stereo();
__aic_disable_unsignadj();
replay_filler = replay_fill_2x16_s;
record_filler = record_fill_2x16_s;
__ac97_set_oass(16);
__ac97_set_iass(16);
dma_src_size(AUDIO_READ_DMA, 16);
dma_dest_size(AUDIO_WRITE_DMA, 16);
break;
}
}
int pcm_ioctl(unsigned int cmd, unsigned long arg)
{
switch (cmd) {
case PCM_SET_SAMPLE_RATE:
jz_audio_set_speed(arg);
break;
case PCM_SET_CHANNEL:
jz_audio_set_channels(arg);
break;
case PCM_SET_FORMAT:
jz_audio_set_format(arg);
break;
case PCM_SET_VOL:
if (arg > 100)
arg = 100;
arg = 100 - arg;
arg = arg * ((1 < bitscale) - 1) / 100;
arg = (arg < 8) | arg;
ac97_codec_write(AC97_MASTER_VOL_STEREO, arg);
ac97_codec_write(AC97_PCMOUT_VOL, arg);
break;
case PCM_GET_VOL:
ac97_codec_read(AC97_MASTER_VOL_STEREO);
break;
default:
printf("pcm_ioctl:Unsupported I/O command: %08x\n", cmd);
return -1;
}
return 0;
}
int pcm_can_write(void)
{
if (elements_in_queue(&out_empty_queue) > 0)
return 1;
return 0;
}
int pcm_can_read(void)
{
if (elements_in_queue(&in_full_queue) > 0)
return 1;
return 0;
}
int pcm_read(char *buffer, int count)
{
int id, ret = 0, left_count, copy_count, cnt = 0;
U8 err;
if (count 0)
return -1;
if (count 2*PAGE_SIZE / jz_audio_k) {
copy_count = count * 16 / (jz_audio_channels * jz_audio_format);
} else
copy_count = ((2*PAGE_SIZE / jz_audio_k + 3) / 4) * 4;
left_count = count;
if (first_record_call) {
first_record_call = 0;
if ((id = get_buffer_id(&in_empty_queue)) >= 0) {
put_buffer_id(&in_busy_queue, id);
in_dma_buf_data_count[id] = copy_count * (jz_audio_format/8);
__ac97_enable_receive_dma();
__ac97_enable_record();
dma_start(AUDIO_READ_DMA,
PHYADDR(AIC_DR), in_dma_pbuf[id],
in_dma_buf_data_count[id]);
OSSemPend(rx_sem, 0, &err);
}
}
while (left_count > 0) {
while (elements_in_queue(&in_full_queue) = 0)
OSSemPend(rx_sem, 0, &err);
if ((id = get_buffer_id(&in_full_queue)) >= 0) {
/*
* FIXME: maybe the buffer is too small.
*/
cnt = record_filler((unsigned long)buffer+ret, copy_count, id);
put_buffer_id(&in_empty_queue, id);
}
if (elements_in_queue(&in_busy_queue) == 0) {
if ((id=get_buffer_id(&in_empty_queue)) >= 0) {
put_buffer_id(&in_busy_queue, id);
in_dma_buf_data_count[id] = copy_count * (jz_audio_format/8);
__ac97_enable_receive_dma();
__ac97_enable_record();
dma_start(AUDIO_READ_DMA,
PHYADDR(AIC_DR), in_dma_pbuf[id],
in_dma_buf_data_count[id]);
}
}
if (ret + cnt > count)
cnt = count - ret;
ret += cnt;
left_count -= cnt;
}
return ret;
}
int pcm_write(char *buffer, int count)
{
int id, ret = 0, left_count, copy_count;
U8 err;
if (count = 0)
return -1;
/* The data buffer size of the user space is always a PAGE_SIZE
* scale, so the process can be simplified.
*/
if (count 2*PAGE_SIZE / jz_audio_k)
copy_count = count;
else
copy_count = ((2*PAGE_SIZE / jz_audio_k + 3) / 4) * 4;
left_count = count;
while (left_count > 0) {
while (elements_in_queue(&out_empty_queue) == 0)
OSSemPend(tx_sem, 0, &err);
/* the end fragment size in this write */
if (ret + copy_count > count)
copy_count = count - ret;
if ((id = get_buffer_id(&out_empty_queue)) >= 0) {
/*
* FIXME: maybe the buffer is too small.
*/
replay_filler( (unsigned long)buffer + ret, copy_count, id);
put_buffer_id(&out_full_queue, id);
__dcache_writeback_all();
}
left_count = left_count - copy_count;
ret += copy_count;
if (elements_in_queue(&out_busy_queue) == 0) {
if ((id=get_buffer_id(&out_full_queue)) >= 0) {
put_buffer_id(&out_busy_queue, id);
__ac97_enable_transmit_dma();
__ac97_enable_replay();
dma_start(AUDIO_WRITE_DMA,
out_dma_pbuf[id], PHYADDR(AIC_DR),
out_dma_buf_data_count[id]);
}
}
}
return ret;
}
static unsigned char dma_pool[PAGE_SIZE * (16 *( QUEUE_MAX + 1)*2)];
int pcm_init(void)
{
int i;
unsigned char *p = dma_pool;
jz_ac97_initHw();
if (jz_ac97_codec_init() 0)
return -1;
tx_sem = OSSemCreate(0);
rx_sem = OSSemCreate(0);
dma_request(AUDIO_READ_DMA, jz_ac97_record_dma_irq, 0,
DMAC_DCCSR_DAM|DMAC_DCCSR_RDIL_IGN,
DMAC_DRSR_RS_AICIN);
dma_request(AUDIO_WRITE_DMA, jz_ac97_replay_dma_irq, 0,
DMAC_DCCSR_SAM|DMAC_DCCSR_RDIL_IGN,
DMAC_DRSR_RS_AICOUT);
jz_audio_reset();
jz_audio_set_format(AFMT_U8);
jz_audio_set_speed(8000);
jz_audio_set_channels(1);
last_jz_audio_count = jz_audio_count = 0;
in_empty_queue.count = QUEUE_MAX;
out_empty_queue.count = QUEUE_MAX;
for (i=0;i<QUEUE_MAX;i++) {
in_empty_queue.id[i] = i;
out_empty_queue.id[i] = i;
}
in_full_queue.count = 0;
in_busy_queue.count = 0;
out_busy_queue.count = 0;
out_full_queue.count = 0;
/* Aligned in PAGE_SIZE */
p = (unsigned char *)(((unsigned int)p + PAGE_SIZE) & ~(PAGE_SIZE-1));
for (i = 0; i QUEUE_MAX; i++) {
out_dma_buf[i] = (unsigned long)p;
p += PAGE_SIZE*16;
out_dma_pbuf[i] = PHYADDR((unsigned int)out_dma_buf[i]);
in_dma_buf[i] = (unsigned long)p;
p += PAGE_SIZE*16;
in_dma_pbuf[i] = PHYADDR((unsigned int)in_dma_buf[i]);
}
pcm_ioctl(PCM_SET_SAMPLE_RATE, 16000);
pcm_ioctl(PCM_SET_FORMAT, AFMT_S16_LE);
pcm_ioctl(PCM_SET_CHANNEL, 2);
pcm_ioctl(PCM_SET_VOL, 100); /* 100% */
return 0;
}