mirror of
git://projects.qi-hardware.com/openwrt-xburst.git
synced 2024-11-25 16:57:31 +02:00
dc3d3f1c49
it's basically also provided by ingenic and nativly based on 2.6.27, adjusted to fit into the OpenWrt-environment
2909 lines
76 KiB
C
Executable File
2909 lines
76 KiB
C
Executable File
/*
|
|
* linux/drivers/sound/Jz_i2s.c
|
|
*
|
|
* JzSOC On-Chip I2S audio driver.
|
|
*
|
|
* Copyright (C) 2005 by Junzheng Corp.
|
|
* Modified by cjfeng on Aug 9,2007,and not any bug on Jz4730 using
|
|
* dma channel 4&3,noah is tested.
|
|
*
|
|
* 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.
|
|
*
|
|
* Because the normal application of AUDIO devices are focused on Little_endian,
|
|
* then we only perform the little endian data format in driver.
|
|
*
|
|
*/
|
|
|
|
#include <linux/init.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/pm.h>
|
|
//#include <linux/pm_legacy.h>
|
|
#include <sound/driver.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/sound.h>
|
|
#include <linux/slab.h>
|
|
#include <sound/core.h>
|
|
#include <sound/initval.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/soundcard.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/mm.h>
|
|
#include <asm/hardirq.h>
|
|
#include <asm/jzsoc.h>
|
|
#include "sound_config.h"
|
|
|
|
#if defined(CONFIG_I2S_DLV)
|
|
#include "jzdlv.h"
|
|
#endif
|
|
|
|
#define DPRINTK(args...) printk(args)
|
|
#define DMA_ID_I2S_TX DMA_ID_AIC_TX
|
|
#define DMA_ID_I2S_RX DMA_ID_AIC_RX
|
|
#define NR_I2S 2
|
|
#define JZCODEC_RW_BUFFER_SIZE 5
|
|
#define JZCODEC_RW_BUFFER_TOTAL 4
|
|
|
|
#define USE_NONE 1
|
|
#define USE_MIC 2
|
|
#define USE_LINEIN 3
|
|
|
|
typedef struct hpvol_shift_s
|
|
{
|
|
int hpvol;
|
|
int shift;
|
|
} hpvol_shift_t;
|
|
|
|
mixer_info info;
|
|
_old_mixer_info old_info;
|
|
int codec_volue_shift;
|
|
hpvol_shift_t hpvol_shift_table[72];
|
|
int abnormal_data_count;
|
|
unsigned long i2s_clk;
|
|
|
|
void (*set_codec_mode)(void) = NULL;
|
|
void (*clear_codec_mode)(void) = NULL;
|
|
void (*set_codec_gpio_pin)(void) = NULL;
|
|
void (*each_time_init_codec)(void) = NULL;
|
|
int (*set_codec_startup_param)(void) = NULL;
|
|
void (*set_codec_volume_table)(void) = NULL;
|
|
void (*set_codec_record)(int mode) = NULL;
|
|
void (*set_codec_replay)(void) = NULL;
|
|
void (*set_codec_replay_record)(int mode) = NULL;
|
|
void (*turn_on_codec)(void) = NULL;
|
|
void (*turn_off_codec)(void) = NULL;
|
|
void (*set_codec_speed)(int rate) = NULL;
|
|
void (*reset_codec)(void) = NULL;
|
|
void (*codec_mixer_old_info_id_name)(void) = NULL;
|
|
void (*codec_mixer_info_id_name)(void) = NULL;
|
|
void (*set_codec_bass)(int val) = NULL;
|
|
void (*set_codec_volume)(int val) = NULL;
|
|
void (*set_codec_mic)(int val) = NULL;
|
|
void (*set_codec_line)(int val) = NULL;
|
|
void (*i2s_resume_codec)(void) = NULL;
|
|
void (*i2s_suspend_codec)(int wr,int rd) = NULL;
|
|
void (*init_codec_pin)(void) = NULL;
|
|
void (*set_codec_some_func)(void) = NULL;
|
|
void (*clear_codec_record)(void) = NULL;
|
|
void (*clear_codec_replay)(void) = NULL;
|
|
void (*set_replay_hp_or_speaker)(void) = NULL;
|
|
void (*set_codec_direct_mode)(void) = NULL;
|
|
void (*clear_codec_direct_mode)(void) = NULL;
|
|
void (*set_codec_linein2hp)(void) = NULL;
|
|
void (*clear_codec_linein2hp)(void) = NULL;
|
|
|
|
static int jz_audio_rate;
|
|
static int jz_audio_format;
|
|
static int jz_audio_volume;
|
|
static int jz_audio_channels;
|
|
static int jz_audio_b; /* bits expand multiple */
|
|
static int jz_audio_fragments; /* unused fragment amount */
|
|
static int jz_audio_fragstotal;
|
|
static int jz_audio_fragsize;
|
|
static int jz_audio_speed;
|
|
|
|
static int codec_bass_gain;
|
|
static int audio_mix_modcnt;
|
|
static int jz_audio_dma_tran_count; /* bytes count of one DMA transfer */
|
|
#if defined(CONFIG_I2S_DLV)
|
|
int jz_mic_only = 1;
|
|
static int jz_codec_config = 0;
|
|
static unsigned long ramp_up_start;
|
|
static unsigned long ramp_up_end;
|
|
static unsigned long gain_up_start;
|
|
static unsigned long gain_up_end;
|
|
static unsigned long ramp_down_start;
|
|
static unsigned long ramp_down_end;
|
|
static unsigned long gain_down_start;
|
|
static unsigned long gain_down_end;
|
|
#endif
|
|
|
|
static int codec_mic_gain;
|
|
static int pop_dma_flag;
|
|
static int last_dma_buffer_id;
|
|
static int drain_flag;
|
|
static int use_mic_line_flag;
|
|
|
|
static void (*old_mksound)(unsigned int hz, unsigned int ticks);
|
|
extern void (*kd_mksound)(unsigned int hz, unsigned int ticks);
|
|
static void jz_update_filler(int bits, int channels);
|
|
|
|
static int Init_In_Out_queue(int fragstotal,int fragsize);
|
|
static int Free_In_Out_queue(int fragstotal,int fragsize);
|
|
static irqreturn_t jz_i2s_replay_dma_irq(int irqnr, void *ref);
|
|
static irqreturn_t jz_i2s_record_dma_irq(int irqnr, void *ref);
|
|
static void (*replay_filler)(signed long src_start, int count, int id);
|
|
static int (*record_filler)(unsigned long dst_start, int count, int id);
|
|
#if defined(CONFIG_I2S_ICODEC)
|
|
static void write_mute_to_dma_buffer(signed long l_sample, signed long r_sample);
|
|
#endif
|
|
static void jz_audio_reset(void);
|
|
static struct file_operations jz_i2s_audio_fops;
|
|
|
|
static DECLARE_WAIT_QUEUE_HEAD (rx_wait_queue);
|
|
static DECLARE_WAIT_QUEUE_HEAD (tx_wait_queue);
|
|
static DECLARE_WAIT_QUEUE_HEAD (drain_wait_queue);
|
|
static DECLARE_WAIT_QUEUE_HEAD (pop_wait_queue);
|
|
|
|
struct jz_i2s_controller_info
|
|
{
|
|
int io_base;
|
|
int dma1; /* for play */
|
|
int dma2; /* for record */
|
|
char *name;
|
|
int dev_audio;
|
|
struct i2s_codec *i2s_codec[NR_I2S];
|
|
int opened1;
|
|
int opened2;
|
|
unsigned char *tmp1; /* tmp buffer for sample conversions */
|
|
unsigned char *tmp2;
|
|
spinlock_t lock;
|
|
spinlock_t ioctllock;
|
|
|
|
wait_queue_head_t dac_wait;
|
|
wait_queue_head_t adc_wait;
|
|
int nextIn; /* byte index to next-in to DMA buffer */
|
|
int nextOut; /* byte index to next-out from DMA buffer */
|
|
int count; /* current byte count in DMA buffer */
|
|
int finish; /* current transfered byte count in DMA buffer */
|
|
unsigned total_bytes; /* total bytes written or read */
|
|
unsigned blocks;
|
|
unsigned error; /* over/underrun */
|
|
#ifdef CONFIG_PM
|
|
struct pm_dev *pm;
|
|
#endif
|
|
};
|
|
|
|
|
|
static struct jz_i2s_controller_info *i2s_controller = NULL;
|
|
struct i2s_codec
|
|
{
|
|
/* I2S controller connected with */
|
|
void *private_data;
|
|
char *name;
|
|
int id;
|
|
int dev_mixer;
|
|
/* controller specific lower leverl i2s accessing routines */
|
|
u16 (*codec_read) (u8 reg); /* the function accessing Codec REGs */
|
|
void (*codec_write) (u8 reg, u16 val);
|
|
/* Wait for codec-ready */
|
|
void (*codec_wait) (struct i2s_codec *codec);
|
|
/* OSS mixer masks */
|
|
int modcnt;
|
|
int supported_mixers;
|
|
int stereo_mixers;
|
|
int record_sources;
|
|
int bit_resolution;
|
|
/* OSS mixer interface */
|
|
int (*read_mixer) (struct i2s_codec *codec, int oss_channel);
|
|
void (*write_mixer)(struct i2s_codec *codec, int oss_channel,
|
|
unsigned int left, unsigned int right);
|
|
int (*recmask_io) (struct i2s_codec *codec, int rw, int mask);
|
|
int (*mixer_ioctl)(struct i2s_codec *codec, unsigned int cmd, unsigned long arg);
|
|
/* saved OSS mixer states */
|
|
unsigned int mixer_state[SOUND_MIXER_NRDEVICES];
|
|
};
|
|
|
|
|
|
typedef struct buffer_queue_s
|
|
{
|
|
int count;
|
|
int *id;
|
|
int lock;
|
|
} buffer_queue_t;
|
|
|
|
typedef struct left_right_sample_s
|
|
{
|
|
signed long left;
|
|
signed long right;
|
|
} left_right_sample_t;
|
|
|
|
static unsigned long pop_turn_onoff_buf;
|
|
static unsigned long pop_turn_onoff_pbuf;
|
|
|
|
static unsigned long *out_dma_buf = NULL;
|
|
static unsigned long *out_dma_pbuf = NULL;
|
|
static unsigned long *out_dma_buf_data_count = NULL;
|
|
static unsigned long *in_dma_buf = NULL;
|
|
static unsigned long *in_dma_pbuf = NULL;
|
|
static unsigned long *in_dma_buf_data_count = NULL;
|
|
|
|
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 = 0;
|
|
|
|
static left_right_sample_t save_last_samples[64];
|
|
|
|
static inline int get_buffer_id(struct buffer_queue_s *q)
|
|
{
|
|
int r;
|
|
unsigned long flags;
|
|
int i;
|
|
|
|
spin_lock_irqsave(&q->lock, flags);
|
|
if (q->count == 0) {
|
|
spin_unlock_irqrestore(&q->lock, flags);
|
|
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(&q->lock, flags);
|
|
|
|
return r;
|
|
}
|
|
|
|
static inline void put_buffer_id(struct buffer_queue_s *q, int id)
|
|
{
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&q->lock, flags);
|
|
*(q->id + q->count) = id;
|
|
q->count ++;
|
|
spin_unlock_irqrestore(&q->lock, flags);
|
|
}
|
|
|
|
static inline int elements_in_queue(struct buffer_queue_s *q)
|
|
{
|
|
int r;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&q->lock, flags);
|
|
r = q->count;
|
|
spin_unlock_irqrestore(&q->lock, flags);
|
|
|
|
return r;
|
|
}
|
|
|
|
static inline void audio_start_dma(int chan, void *dev_id, unsigned long phyaddr,int count, int mode)
|
|
{
|
|
unsigned long flags;
|
|
struct jz_i2s_controller_info * controller = (struct jz_i2s_controller_info *) dev_id;
|
|
|
|
spin_lock_irqsave(&controller->ioctllock, flags);
|
|
jz_audio_dma_tran_count = count / jz_audio_b;
|
|
spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
flags = claim_dma_lock();
|
|
disable_dma(chan);
|
|
clear_dma_ff(chan);
|
|
#if 1
|
|
jz_set_oss_dma(chan, mode, jz_audio_format);
|
|
#else
|
|
set_dma_mode(chan, mode);
|
|
#endif
|
|
set_dma_addr(chan, phyaddr);
|
|
if (count == 0) {
|
|
count++;
|
|
printk("JzSOC DMA controller can't set dma 0 count!\n");
|
|
}
|
|
set_dma_count(chan, count);
|
|
enable_dma(chan);
|
|
release_dma_lock(flags);
|
|
}
|
|
|
|
static irqreturn_t jz_i2s_record_dma_irq (int irq, void *dev_id)
|
|
{
|
|
int id1, id2;
|
|
unsigned long flags;
|
|
struct jz_i2s_controller_info * controller = (struct jz_i2s_controller_info *) dev_id;
|
|
int dma = controller->dma2;
|
|
|
|
disable_dma(dma);
|
|
if (__dmac_channel_address_error_detected(dma)) {
|
|
printk(KERN_DEBUG "%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(drain_flag == 1)
|
|
wake_up(&drain_wait_queue);
|
|
/* for DSP_GETIPTR */
|
|
spin_lock_irqsave(&controller->ioctllock, flags);
|
|
controller->total_bytes += jz_audio_dma_tran_count;
|
|
controller->blocks ++;
|
|
spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
id1 = get_buffer_id(&in_busy_queue);
|
|
put_buffer_id(&in_full_queue, id1);
|
|
|
|
wake_up(&rx_wait_queue);
|
|
wake_up(&controller->adc_wait);
|
|
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_cache_wback_inv(*(in_dma_buf + id2), *(in_dma_buf_data_count + id2));
|
|
audio_start_dma(dma,dev_id,
|
|
*(in_dma_pbuf + id2),
|
|
*(in_dma_buf_data_count + id2),
|
|
DMA_MODE_READ);
|
|
} else
|
|
in_busy_queue.count = 0;
|
|
}
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static irqreturn_t jz_i2s_replay_dma_irq (int irq, void *dev_id)
|
|
{
|
|
int id;
|
|
unsigned long flags;
|
|
struct jz_i2s_controller_info * controller = (struct jz_i2s_controller_info *) dev_id;
|
|
int dma = controller->dma1;
|
|
|
|
disable_dma(dma);
|
|
if (__dmac_channel_address_error_detected(dma)) {
|
|
printk(KERN_DEBUG "%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(pop_dma_flag == 1) {
|
|
pop_dma_flag = 0;
|
|
wake_up(&pop_wait_queue);
|
|
} else {
|
|
if(drain_flag == 1) {
|
|
/* Is replay dma buffer over ? */
|
|
if(elements_in_queue(&out_full_queue) <= 0) {
|
|
drain_flag = 0;
|
|
wake_up(&drain_wait_queue);
|
|
}
|
|
}
|
|
|
|
/* for DSP_GETOPTR */
|
|
spin_lock_irqsave(&controller->ioctllock, flags);
|
|
controller->total_bytes += jz_audio_dma_tran_count;
|
|
controller->blocks ++;
|
|
spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
if ((id = get_buffer_id(&out_busy_queue)) < 0)
|
|
printk(KERN_DEBUG "Strange DMA finish interrupt for I2S 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);
|
|
if(*(out_dma_buf_data_count + id) > 0) {
|
|
audio_start_dma(dma, dev_id, *(out_dma_pbuf + id),
|
|
*(out_dma_buf_data_count + id),
|
|
DMA_MODE_WRITE);
|
|
last_dma_buffer_id = id;
|
|
}
|
|
} else
|
|
out_busy_queue.count = 0;
|
|
|
|
if (elements_in_queue(&out_empty_queue) > 0) {
|
|
wake_up(&tx_wait_queue);
|
|
wake_up(&controller->dac_wait);
|
|
}
|
|
}
|
|
}
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static void jz_i2s_initHw(int set)
|
|
{
|
|
#if defined(CONFIG_MIPS_JZ_URANUS)
|
|
i2s_clk = 48000000;
|
|
#else
|
|
i2s_clk = __cpm_get_i2sclk();
|
|
#endif
|
|
__i2s_disable();
|
|
if(set)
|
|
__i2s_reset();
|
|
schedule_timeout(5);
|
|
if(each_time_init_codec)
|
|
each_time_init_codec();
|
|
__i2s_disable_record();
|
|
__i2s_disable_replay();
|
|
__i2s_disable_loopback();
|
|
__i2s_set_transmit_trigger(4);
|
|
__i2s_set_receive_trigger(3);
|
|
}
|
|
|
|
static int Init_In_Out_queue(int fragstotal,int fragsize)
|
|
{
|
|
int i;
|
|
|
|
/* recording */
|
|
in_empty_queue.count = fragstotal;
|
|
in_dma_buf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL);
|
|
if (!in_dma_buf)
|
|
goto all_mem_err;
|
|
in_dma_pbuf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL);
|
|
if (!in_dma_pbuf)
|
|
goto all_mem_err;
|
|
in_dma_buf_data_count = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL);
|
|
if (!in_dma_buf_data_count)
|
|
goto all_mem_err;
|
|
in_empty_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL);
|
|
if (!in_empty_queue.id)
|
|
goto all_mem_err;
|
|
in_full_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL);
|
|
if (!in_full_queue.id)
|
|
goto all_mem_err;
|
|
in_busy_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL);
|
|
if (!in_busy_queue.id)
|
|
goto all_mem_err;
|
|
|
|
for (i=0;i < fragstotal;i++)
|
|
*(in_empty_queue.id + i) = i;
|
|
in_full_queue.count = 0;
|
|
in_busy_queue.count = 0;
|
|
|
|
for (i = 0; i < fragstotal; i++) {
|
|
*(in_dma_buf + i) = __get_free_pages(GFP_KERNEL | GFP_DMA, get_order(fragsize));
|
|
if (*(in_dma_buf + i) == 0)
|
|
goto mem_failed_in;
|
|
*(in_dma_pbuf + i) = virt_to_phys((void *)(*(in_dma_buf + i)));
|
|
dma_cache_wback_inv(*(in_dma_buf + i), fragsize);
|
|
}
|
|
|
|
/* playing */
|
|
out_empty_queue.count = fragstotal;
|
|
out_dma_buf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL);
|
|
if (!out_dma_buf)
|
|
goto all_mem_err;
|
|
out_dma_pbuf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL);
|
|
if (!out_dma_pbuf)
|
|
goto all_mem_err;
|
|
out_dma_buf_data_count = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL);
|
|
|
|
if (!out_dma_buf_data_count)
|
|
goto all_mem_err;
|
|
out_empty_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL);
|
|
if (!out_empty_queue.id)
|
|
goto all_mem_err;
|
|
out_full_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL);
|
|
if (!out_full_queue.id)
|
|
goto all_mem_err;
|
|
out_busy_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL);
|
|
if (!out_busy_queue.id)
|
|
goto all_mem_err;
|
|
for (i=0;i < fragstotal;i++)
|
|
*(out_empty_queue.id + i) = i;
|
|
|
|
out_busy_queue.count = 0;
|
|
out_full_queue.count = 0;
|
|
/* alloc DMA buffer */
|
|
for (i = 0; i < fragstotal; i++) {
|
|
*(out_dma_buf + i) = __get_free_pages(GFP_KERNEL | GFP_DMA, get_order(fragsize));
|
|
if (*(out_dma_buf + i) == 0) {
|
|
printk(" can't allocate required DMA(OUT) buffers.\n");
|
|
goto mem_failed_out;
|
|
}
|
|
*(out_dma_pbuf + i) = virt_to_phys((void *)(*(out_dma_buf + i)));
|
|
}
|
|
|
|
return 1;
|
|
all_mem_err:
|
|
printk("error:allocate memory occur error 1!\n");
|
|
return 0;
|
|
mem_failed_out:
|
|
printk("error:allocate memory occur error 2!\n");
|
|
for (i = 0; i < fragstotal; i++) {
|
|
if(*(out_dma_buf + i))
|
|
free_pages(*(out_dma_buf + i), get_order(fragsize));
|
|
}
|
|
|
|
return 0;
|
|
mem_failed_in:
|
|
printk("error:allocate memory occur error 3!\n");
|
|
for (i = 0; i < fragstotal; i++) {
|
|
if(*(in_dma_buf + i))
|
|
free_pages(*(in_dma_buf + i), get_order(fragsize));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int Free_In_Out_queue(int fragstotal,int fragsize)
|
|
{
|
|
int i;
|
|
/* playing */
|
|
if(out_dma_buf != NULL) {
|
|
for (i = 0; i < fragstotal; i++) {
|
|
if(*(out_dma_buf + i))
|
|
free_pages(*(out_dma_buf + i), get_order(fragsize));
|
|
*(out_dma_buf + i) = 0;
|
|
}
|
|
kfree(out_dma_buf);
|
|
out_dma_buf = NULL;
|
|
}
|
|
if(out_dma_pbuf) {
|
|
kfree(out_dma_pbuf);
|
|
out_dma_pbuf = NULL;
|
|
}
|
|
if(out_dma_buf_data_count) {
|
|
kfree(out_dma_buf_data_count);
|
|
out_dma_buf_data_count = NULL;
|
|
}
|
|
if(out_empty_queue.id) {
|
|
kfree(out_empty_queue.id);
|
|
out_empty_queue.id = NULL;
|
|
}
|
|
if(out_full_queue.id) {
|
|
kfree(out_full_queue.id);
|
|
out_full_queue.id = NULL;
|
|
}
|
|
if(out_busy_queue.id) {
|
|
kfree(out_busy_queue.id);
|
|
out_busy_queue.id = NULL;
|
|
}
|
|
out_empty_queue.count = fragstotal;
|
|
out_busy_queue.count = 0;
|
|
out_full_queue.count = 0;
|
|
|
|
/* recording */
|
|
if(in_dma_buf) {
|
|
for (i = 0; i < fragstotal; i++) {
|
|
if(*(in_dma_buf + i)) {
|
|
dma_cache_wback_inv(*(in_dma_buf + i), fragsize);
|
|
free_pages(*(in_dma_buf + i), get_order(fragsize));
|
|
}
|
|
*(in_dma_buf + i) = 0;
|
|
}
|
|
kfree(in_dma_buf);
|
|
in_dma_buf = NULL;
|
|
}
|
|
if(in_dma_pbuf) {
|
|
kfree(in_dma_pbuf);
|
|
in_dma_pbuf = NULL;
|
|
}
|
|
if(in_dma_buf_data_count) {
|
|
kfree(in_dma_buf_data_count);
|
|
in_dma_buf_data_count = NULL;
|
|
}
|
|
if(in_empty_queue.id) {
|
|
kfree(in_empty_queue.id);
|
|
in_empty_queue.id = NULL;
|
|
}
|
|
if(in_full_queue.id) {
|
|
kfree(in_full_queue.id);
|
|
in_full_queue.id = NULL;
|
|
}
|
|
if(in_busy_queue.id) {
|
|
kfree(in_busy_queue.id);
|
|
in_busy_queue.id = NULL;
|
|
}
|
|
|
|
in_empty_queue.count = fragstotal;
|
|
in_full_queue.count = 0;
|
|
in_busy_queue.count = 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void jz_i2s_full_reset(struct jz_i2s_controller_info *controller)
|
|
{
|
|
jz_i2s_initHw(0);
|
|
}
|
|
|
|
static int jz_audio_set_speed(int dev, int rate)
|
|
{
|
|
/* 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 99999999 ? */
|
|
jz_audio_speed = rate;
|
|
#if defined(CONFIG_I2S_DLV)
|
|
if (rate > 96000)
|
|
rate = 96000;
|
|
#else
|
|
if (rate > 48000)
|
|
rate = 48000;
|
|
#endif
|
|
if (rate < 8000)
|
|
rate = 8000;
|
|
jz_audio_rate = rate;
|
|
|
|
if(set_codec_speed)
|
|
set_codec_speed(rate);
|
|
|
|
return jz_audio_rate;
|
|
}
|
|
|
|
|
|
static int record_fill_1x8_u(unsigned long dst_start, int count, int id)
|
|
{
|
|
int cnt = 0;
|
|
unsigned long data;
|
|
volatile unsigned long *s = (unsigned long*)(*(in_dma_buf + id));
|
|
volatile unsigned char *dp = (unsigned char*)dst_start;
|
|
|
|
while (count > 0) {
|
|
count -= 2; /* count in dword */
|
|
cnt++;
|
|
data = *(s++);
|
|
*(dp ++) = ((data << 16) >> 24) + 0x80;
|
|
s++; /* skip the other channel */
|
|
}
|
|
|
|
return cnt;
|
|
}
|
|
|
|
|
|
static int record_fill_2x8_u(unsigned long dst_start, int count, int id)
|
|
{
|
|
int cnt = 0;
|
|
unsigned long d1, d2;
|
|
volatile unsigned long *s = (unsigned long*)(*(in_dma_buf + id));
|
|
volatile unsigned char *dp = (unsigned char*)dst_start;
|
|
|
|
while (count > 0) {
|
|
count -= 2;
|
|
cnt += 2;
|
|
d1 = *(s++);
|
|
*(dp ++) = ((d1 << 16) >> 24) + 0x80;
|
|
d2 = *(s++);
|
|
*(dp ++) = ((d2 << 16) >> 24) + 0x80;
|
|
}
|
|
|
|
return cnt;
|
|
}
|
|
|
|
|
|
static int record_fill_1x16_s(unsigned long dst_start, int count, int id)
|
|
{
|
|
int cnt = 0;
|
|
unsigned long d1;
|
|
unsigned long *s = (unsigned long*)(*(in_dma_buf + id));
|
|
unsigned short *dp = (unsigned short *)dst_start;
|
|
|
|
while (count > 0) {
|
|
count -= 2; /* count in dword */
|
|
cnt += 2; /* count in byte */
|
|
d1 = *(s++);
|
|
*(dp ++) = (d1 << 16) >> 16;
|
|
s++; /* skip the other channel */
|
|
}
|
|
|
|
return cnt;
|
|
}
|
|
|
|
|
|
static int record_fill_2x16_s(unsigned long dst_start, int count, int id)
|
|
{
|
|
int cnt = 0;
|
|
unsigned long d1, d2;
|
|
unsigned long *s = (unsigned long*)(*(in_dma_buf + id));
|
|
unsigned short *dp = (unsigned short *)dst_start;
|
|
while (count > 0) {
|
|
count -= 2; /* count in dword */
|
|
cnt += 4; /* count in byte */
|
|
d1 = *(s++);
|
|
d2 = *(s++);
|
|
if(abnormal_data_count > 0) {
|
|
d1 = d2 = 0;
|
|
abnormal_data_count --;
|
|
}
|
|
*(dp ++) = (d1 << 16) >> 16;
|
|
*(dp ++) = (d2 << 16) >> 16;
|
|
}
|
|
|
|
return cnt;
|
|
}
|
|
|
|
static void replay_fill_1x8_u(signed long src_start, int count, int id)
|
|
{
|
|
int cnt = 0;
|
|
unsigned char data;
|
|
unsigned long ddata;
|
|
volatile unsigned char *s = (unsigned char *)src_start;
|
|
volatile unsigned long *dp = (unsigned long*)(*(out_dma_buf + id));
|
|
|
|
while (count > 0) {
|
|
count--;
|
|
cnt += 1;
|
|
data = *(s++) - 0x80;
|
|
ddata = (unsigned long) data << 8;
|
|
*(dp ++) = ddata;
|
|
*(dp ++) = ddata;
|
|
|
|
/* save last left and right */
|
|
if(count == 1) {
|
|
save_last_samples[id].left = ddata;
|
|
save_last_samples[id].right = ddata;
|
|
}
|
|
}
|
|
cnt = cnt * 2 * jz_audio_b;
|
|
*(out_dma_buf_data_count + id) = cnt;
|
|
}
|
|
|
|
|
|
static void replay_fill_2x8_u(signed long src_start, int count, int id)
|
|
{
|
|
int cnt = 0;
|
|
unsigned char d1;
|
|
unsigned long dd1;
|
|
volatile unsigned char *s = (unsigned char *)src_start;
|
|
volatile unsigned long *dp = (unsigned long*)(*(out_dma_buf + id));
|
|
|
|
while (count > 0) {
|
|
count -= 1;
|
|
cnt += 1 ;
|
|
d1 = *(s++) - 0x80;
|
|
dd1 = (unsigned long) d1 << 8;
|
|
*(dp ++) = dd1;
|
|
/* save last left */
|
|
if(count == 2)
|
|
save_last_samples[id].left = dd1;
|
|
/* save last right */
|
|
if(count == 1)
|
|
save_last_samples[id].right = dd1;
|
|
}
|
|
cnt *= jz_audio_b;
|
|
*(out_dma_buf_data_count + id) = cnt;
|
|
}
|
|
|
|
|
|
static void replay_fill_1x16_s(signed long src_start, int count, int id)
|
|
{
|
|
int cnt = 0;
|
|
signed short d1;
|
|
signed long l1;
|
|
volatile signed short *s = (signed short *)src_start;
|
|
volatile signed long *dp = (signed long*)(*(out_dma_buf + id));
|
|
|
|
while (count > 0) {
|
|
count -= 2;
|
|
cnt += 2 ;
|
|
d1 = *(s++);
|
|
l1 = (signed long)d1;
|
|
#if defined(CONFIG_I2S_ICODEC)
|
|
l1 >>= codec_volue_shift;
|
|
#endif
|
|
*(dp ++) = l1;
|
|
*(dp ++) = l1;
|
|
|
|
/* save last left and right */
|
|
if(count == 1) {
|
|
save_last_samples[id].left = l1;
|
|
save_last_samples[id].right = l1;
|
|
}
|
|
}
|
|
cnt = cnt * 2 * jz_audio_b;
|
|
*(out_dma_buf_data_count + id) = cnt;
|
|
}
|
|
|
|
static void replay_fill_2x16_s(signed long src_start, int count, int id)
|
|
{
|
|
int cnt = 0;
|
|
signed short d1;
|
|
signed long l1;
|
|
volatile signed short *s = (signed short *)src_start;
|
|
volatile signed long *dp = (signed long*)(*(out_dma_buf + id));
|
|
#if defined(CONFIG_I2S_ICODEC)
|
|
int mute_cnt = 0;
|
|
signed long tmp1,tmp2;
|
|
volatile signed long *before_dp;
|
|
int sam_rate = jz_audio_rate / 20;
|
|
|
|
tmp1 = tmp2 = 0;
|
|
while (count > 0) {
|
|
count -= 2;
|
|
cnt += 2;
|
|
d1 = *(s++);
|
|
|
|
l1 = (signed long)d1;
|
|
l1 >>= codec_volue_shift;
|
|
|
|
if(l1 == 0) {
|
|
mute_cnt ++;
|
|
if(mute_cnt >= sam_rate) {
|
|
before_dp = dp - 10;
|
|
*(before_dp) = (signed long)1;
|
|
before_dp = dp - 11;
|
|
*(before_dp) = (signed long)1;
|
|
mute_cnt = 0;
|
|
}
|
|
} else
|
|
mute_cnt = 0;
|
|
|
|
*(dp ++) = l1;
|
|
|
|
tmp1 = tmp2;
|
|
tmp2 = l1;
|
|
}
|
|
|
|
/* save last left */
|
|
save_last_samples[id].left = tmp1;
|
|
/* save last right */
|
|
save_last_samples[id].right = tmp2;
|
|
#endif
|
|
#if defined(CONFIG_I2S_DLV)
|
|
while (count > 0) {
|
|
count -= 2;
|
|
cnt += 2;
|
|
d1 = *(s++);
|
|
|
|
l1 = (signed long)d1;
|
|
|
|
*(dp ++) = l1;
|
|
}
|
|
#endif
|
|
cnt *= jz_audio_b;
|
|
*(out_dma_buf_data_count + id) = cnt;
|
|
}
|
|
|
|
static void replay_fill_2x18_s(unsigned long src_start, int count, int id)
|
|
{
|
|
int cnt = 0;
|
|
signed long d1;
|
|
signed long l1;
|
|
volatile signed long *s = (signed long *)src_start;
|
|
volatile signed long *dp = (signed long*)(*(out_dma_buf + id));
|
|
while (count > 0) {
|
|
count -= 4;
|
|
cnt += 4;
|
|
d1 = *(s++);
|
|
l1 = (signed long)d1;
|
|
*(dp ++) = l1;
|
|
}
|
|
|
|
cnt *= jz_audio_b;
|
|
*(out_dma_buf_data_count + id) = cnt;
|
|
}
|
|
|
|
static unsigned int jz_audio_set_format(int dev, unsigned int fmt)
|
|
{
|
|
switch (fmt) {
|
|
case AFMT_U8:
|
|
__i2s_set_oss_sample_size(8);
|
|
__i2s_set_iss_sample_size(8);
|
|
jz_audio_format = fmt;
|
|
jz_update_filler(jz_audio_format,jz_audio_channels);
|
|
break;
|
|
case AFMT_S16_LE:
|
|
#if defined(CONFIG_I2S_DLV)
|
|
/* DAC path and ADC path */
|
|
write_codec_file(2, 0x00);
|
|
//write_codec_file(2, 0x60);
|
|
#endif
|
|
jz_audio_format = fmt;
|
|
jz_update_filler(jz_audio_format,jz_audio_channels);
|
|
__i2s_set_oss_sample_size(16);
|
|
__i2s_set_iss_sample_size(16);
|
|
break;
|
|
case 18:
|
|
__i2s_set_oss_sample_size(18);
|
|
jz_audio_format = fmt;
|
|
jz_update_filler(jz_audio_format,jz_audio_channels);
|
|
break;
|
|
case AFMT_QUERY:
|
|
break;
|
|
}
|
|
|
|
return jz_audio_format;
|
|
}
|
|
|
|
|
|
static short jz_audio_set_channels(int dev, short channels)
|
|
{
|
|
switch (channels) {
|
|
case 1:
|
|
if(set_codec_some_func)
|
|
set_codec_some_func();
|
|
jz_audio_channels = channels;
|
|
jz_update_filler(jz_audio_format, jz_audio_channels);
|
|
#if defined(CONFIG_I2S_DLV)
|
|
write_codec_file_bit(1, 1, 6);//CR1.MONO->1 for Mono
|
|
#endif
|
|
break;
|
|
case 2:
|
|
jz_audio_channels = channels;
|
|
jz_update_filler(jz_audio_format, jz_audio_channels);
|
|
#if defined(CONFIG_I2S_DLV)
|
|
write_codec_file_bit(1, 0, 6);//CR1.MONO->0 for Stereo
|
|
#endif
|
|
break;
|
|
case 0:
|
|
break;
|
|
}
|
|
|
|
return jz_audio_channels;
|
|
}
|
|
|
|
static void init_codec(void)
|
|
{
|
|
/* inititalize internal I2S codec */
|
|
if(init_codec_pin)
|
|
init_codec_pin();
|
|
|
|
#if defined(CONFIG_I2S_ICDC)
|
|
/* initialize AIC but not reset it */
|
|
jz_i2s_initHw(0);
|
|
#endif
|
|
if(reset_codec)
|
|
reset_codec();
|
|
}
|
|
|
|
static void jz_audio_reset(void)
|
|
{
|
|
__i2s_disable_replay();
|
|
__i2s_disable_receive_dma();
|
|
__i2s_disable_record();
|
|
__i2s_disable_transmit_dma();
|
|
#if defined(CONFIG_I2S_DLV)
|
|
REG_AIC_I2SCR = 0x10;
|
|
#endif
|
|
init_codec();
|
|
}
|
|
|
|
static int jz_audio_release(struct inode *inode, struct file *file);
|
|
static int jz_audio_open(struct inode *inode, struct file *file);
|
|
static int jz_audio_ioctl(struct inode *inode, struct file *file,unsigned int cmd, unsigned long arg);
|
|
static unsigned int jz_audio_poll(struct file *file,struct poll_table_struct *wait);
|
|
static ssize_t jz_audio_write(struct file *file, const char *buffer,size_t count, loff_t *ppos);
|
|
static ssize_t jz_audio_read(struct file *file, char *buffer,size_t count, loff_t *ppos);
|
|
|
|
/* static struct file_operations jz_i2s_audio_fops */
|
|
static struct file_operations jz_i2s_audio_fops =
|
|
{
|
|
owner: THIS_MODULE,
|
|
open: jz_audio_open,
|
|
release: jz_audio_release,
|
|
write: jz_audio_write,
|
|
read: jz_audio_read,
|
|
poll: jz_audio_poll,
|
|
ioctl: jz_audio_ioctl
|
|
};
|
|
|
|
static int jz_i2s_open_mixdev(struct inode *inode, struct file *file)
|
|
{
|
|
int i;
|
|
int minor = MINOR(inode->i_rdev);
|
|
struct jz_i2s_controller_info *controller = i2s_controller;
|
|
|
|
for (i = 0; i < NR_I2S; i++)
|
|
if (controller->i2s_codec[i] != NULL && controller->i2s_codec[i]->dev_mixer == minor)
|
|
goto match;
|
|
|
|
if (!controller)
|
|
return -ENODEV;
|
|
match:
|
|
file->private_data = controller->i2s_codec[i];
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int jz_i2s_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
|
|
{
|
|
struct i2s_codec *codec = (struct i2s_codec *)file->private_data;
|
|
return codec->mixer_ioctl(codec, cmd, arg);
|
|
}
|
|
|
|
static loff_t jz_i2s_llseek(struct file *file, loff_t offset, int origin)
|
|
{
|
|
return -ESPIPE;
|
|
}
|
|
|
|
static struct file_operations jz_i2s_mixer_fops =
|
|
{
|
|
owner: THIS_MODULE,
|
|
llseek: jz_i2s_llseek,
|
|
ioctl: jz_i2s_ioctl_mixdev,
|
|
open: jz_i2s_open_mixdev,
|
|
};
|
|
|
|
static int i2s_mixer_ioctl(struct i2s_codec *codec, unsigned int cmd, unsigned long arg)
|
|
{
|
|
int ret;
|
|
long val = 0;
|
|
switch (cmd) {
|
|
case SOUND_MIXER_INFO:
|
|
|
|
if(codec_mixer_info_id_name)
|
|
codec_mixer_info_id_name();
|
|
info.modify_counter = audio_mix_modcnt;
|
|
|
|
return copy_to_user((void *)arg, &info, sizeof(info));
|
|
case SOUND_OLD_MIXER_INFO:
|
|
|
|
if(codec_mixer_old_info_id_name)
|
|
codec_mixer_old_info_id_name();
|
|
|
|
return copy_to_user((void *)arg, &old_info, sizeof(info));
|
|
case SOUND_MIXER_READ_STEREODEVS:
|
|
|
|
return put_user(0, (long *) arg);
|
|
case SOUND_MIXER_READ_CAPS:
|
|
|
|
val = SOUND_CAP_EXCL_INPUT;
|
|
return put_user(val, (long *) arg);
|
|
case SOUND_MIXER_READ_DEVMASK:
|
|
break;
|
|
case SOUND_MIXER_READ_RECMASK:
|
|
break;
|
|
case SOUND_MIXER_READ_RECSRC:
|
|
break;
|
|
case SOUND_MIXER_WRITE_SPEAKER:
|
|
|
|
ret = get_user(val, (long *) arg);
|
|
if (ret)
|
|
return ret;
|
|
val = val & 0xff;
|
|
if(val < 0)
|
|
val = 0;
|
|
if(val > 100)
|
|
val = 100;
|
|
switch(val) {
|
|
case 100:
|
|
if(set_codec_direct_mode)
|
|
set_codec_direct_mode();
|
|
break;
|
|
case 0:
|
|
if(clear_codec_direct_mode)
|
|
clear_codec_direct_mode();
|
|
break;
|
|
}
|
|
break;
|
|
case SOUND_MIXER_WRITE_BASS:
|
|
|
|
ret = get_user(val, (long *) arg);
|
|
if (ret)
|
|
return ret;
|
|
|
|
val = val & 0xff;
|
|
if(val < 0)
|
|
val = 0;
|
|
if(val > 100)
|
|
val = 100;
|
|
codec_bass_gain = val;
|
|
if(set_codec_bass)
|
|
set_codec_bass(val);
|
|
|
|
return 0;
|
|
case SOUND_MIXER_READ_BASS:
|
|
|
|
val = codec_bass_gain;
|
|
ret = val << 8;
|
|
val = val | ret;
|
|
|
|
return put_user(val, (long *) arg);
|
|
case SOUND_MIXER_WRITE_VOLUME:
|
|
ret = get_user(val, (long *) arg);
|
|
if (ret)
|
|
return ret;
|
|
val = val & 0xff;
|
|
if(val < 0)
|
|
val = 0;
|
|
if(val > 100)
|
|
val = 100;
|
|
|
|
jz_audio_volume = val;
|
|
if(set_codec_volume)
|
|
set_codec_volume(val);
|
|
|
|
return 0;
|
|
case SOUND_MIXER_READ_VOLUME:
|
|
|
|
val = jz_audio_volume;
|
|
ret = val << 8;
|
|
val = val | ret;
|
|
|
|
return put_user(val, (long *) arg);
|
|
|
|
case SOUND_MIXER_WRITE_MIC:
|
|
|
|
ret = get_user(val, (long *) arg);
|
|
if (ret)
|
|
return ret;
|
|
|
|
val = val & 0xff;
|
|
if(val < 0)
|
|
val = 0;
|
|
if(val > 100)
|
|
val = 100;
|
|
codec_mic_gain = val;
|
|
use_mic_line_flag = USE_MIC;
|
|
if(set_codec_mic)
|
|
set_codec_mic(val);
|
|
|
|
return 0;
|
|
case SOUND_MIXER_READ_MIC:
|
|
|
|
val = codec_mic_gain;
|
|
ret = val << 8;
|
|
val = val | ret;
|
|
|
|
return put_user(val, (long *) arg);
|
|
|
|
case SOUND_MIXER_WRITE_LINE:
|
|
|
|
ret = get_user(val, (long *) arg);
|
|
if (ret)
|
|
return ret;
|
|
|
|
val = val & 0xff;
|
|
if(val < 0)
|
|
val = 0;
|
|
if(val > 100)
|
|
val = 100;
|
|
use_mic_line_flag = USE_LINEIN;
|
|
codec_mic_gain = val;
|
|
if(set_codec_line)
|
|
set_codec_line(val);
|
|
|
|
return 0;
|
|
case SOUND_MIXER_READ_LINE:
|
|
|
|
val = codec_mic_gain;
|
|
ret = val << 8;
|
|
val = val | ret;
|
|
|
|
return put_user(val, (long *) arg);
|
|
default:
|
|
return -ENOSYS;
|
|
}
|
|
audio_mix_modcnt ++;
|
|
return 0;
|
|
}
|
|
|
|
|
|
int i2s_probe_codec(struct i2s_codec *codec)
|
|
{
|
|
/* generic OSS to I2S wrapper */
|
|
codec->mixer_ioctl = i2s_mixer_ioctl;
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* I2S codec initialisation. */
|
|
static int __init jz_i2s_codec_init(struct jz_i2s_controller_info *controller)
|
|
{
|
|
int num_i2s = 0;
|
|
struct i2s_codec *codec;
|
|
|
|
for (num_i2s = 0; num_i2s < NR_I2S; num_i2s++) {
|
|
if ((codec = kmalloc(sizeof(struct i2s_codec),GFP_KERNEL)) == NULL)
|
|
return -ENOMEM;
|
|
memset(codec, 0, sizeof(struct i2s_codec));
|
|
codec->private_data = controller;
|
|
codec->id = num_i2s;
|
|
|
|
if (i2s_probe_codec(codec) == 0)
|
|
break;
|
|
if ((codec->dev_mixer = register_sound_mixer(&jz_i2s_mixer_fops, -1)) < 0) {
|
|
printk(KERN_ERR "Jz I2S: couldn't register mixer!\n");
|
|
kfree(codec);
|
|
break;
|
|
}
|
|
controller->i2s_codec[num_i2s] = codec;
|
|
}
|
|
return num_i2s;
|
|
}
|
|
|
|
|
|
static void jz_update_filler(int format, int channels)
|
|
{
|
|
#define TYPE(fmt,ch) (((fmt)<<2) | ((ch)&3))
|
|
|
|
switch (TYPE(format, channels))
|
|
{
|
|
|
|
case TYPE(AFMT_U8, 1):
|
|
jz_audio_b = 4; /* 4bytes * 8bits =32bits */
|
|
replay_filler = replay_fill_1x8_u;
|
|
record_filler = record_fill_1x8_u;
|
|
break;
|
|
case TYPE(AFMT_U8, 2):
|
|
jz_audio_b = 4;
|
|
replay_filler = replay_fill_2x8_u;
|
|
record_filler = record_fill_2x8_u;
|
|
break;
|
|
case TYPE(AFMT_S16_LE, 1):
|
|
jz_audio_b = 2; /* 2bytes * 16bits =32bits */
|
|
replay_filler = replay_fill_1x16_s;
|
|
record_filler = record_fill_1x16_s;
|
|
break;
|
|
case TYPE(AFMT_S16_LE, 2):
|
|
jz_audio_b = 2;
|
|
replay_filler = replay_fill_2x16_s;
|
|
record_filler = record_fill_2x16_s;
|
|
break;
|
|
case TYPE(18, 2):
|
|
jz_audio_b = 1;
|
|
replay_filler = replay_fill_2x18_s;
|
|
record_filler = record_fill_2x16_s;
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_PROC_FS
|
|
extern struct proc_dir_entry *proc_jz_root;
|
|
int i2s_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int jz_i2s_init_proc(struct jz_i2s_controller_info *controller)
|
|
{
|
|
if (!create_proc_read_entry ("i2s", 0, proc_jz_root, i2s_read_proc, controller->i2s_codec[0]))
|
|
return -EIO;
|
|
return 0;
|
|
}
|
|
|
|
static void jz_i2s_cleanup_proc(struct jz_i2s_controller_info *controller)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
static void __init attach_jz_i2s(struct jz_i2s_controller_info *controller)
|
|
{
|
|
char *name;
|
|
int adev; /* No of Audio device. */
|
|
|
|
name = controller->name;
|
|
/* initialize AIC controller and reset it */
|
|
jz_i2s_initHw(1);
|
|
adev = register_sound_dsp(&jz_i2s_audio_fops, -1);
|
|
if (adev < 0)
|
|
goto audio_failed;
|
|
/* initialize I2S codec and register /dev/mixer */
|
|
if (jz_i2s_codec_init(controller) <= 0)
|
|
goto mixer_failed;
|
|
|
|
#ifdef CONFIG_PROC_FS
|
|
if (jz_i2s_init_proc(controller) < 0) {
|
|
printk(KERN_ERR "%s: can't create I2S proc filesystem.\n", name);
|
|
goto proc_failed;
|
|
}
|
|
#endif
|
|
|
|
controller->tmp1 = (void *)__get_free_pages(GFP_KERNEL, 8);
|
|
if (!controller->tmp1) {
|
|
printk(KERN_ERR "%s: can't allocate tmp buffers.\n", controller->name);
|
|
goto tmp1_failed;
|
|
}
|
|
controller->tmp2 = (void *)__get_free_pages(GFP_KERNEL, 8);
|
|
if (!controller->tmp2) {
|
|
printk(KERN_ERR "%s: can't allocate tmp buffers.\n", controller->name);
|
|
goto tmp2_failed;
|
|
}
|
|
if ((controller->dma2 = jz_request_dma(DMA_ID_I2S_RX, "audio adc", jz_i2s_record_dma_irq, IRQF_DISABLED, controller)) < 0) {
|
|
printk(KERN_ERR "%s: can't reqeust DMA ADC channel.\n", name);
|
|
goto dma2_failed;
|
|
}
|
|
if ((controller->dma1 = jz_request_dma(DMA_ID_I2S_TX, "audio dac", jz_i2s_replay_dma_irq, IRQF_DISABLED, controller)) < 0) {
|
|
printk(KERN_ERR "%s: can't reqeust DMA DAC channel.\n", name);
|
|
goto dma1_failed;
|
|
}
|
|
printk("JzSOC On-Chip I2S controller registered (DAC: DMA(play):%d/IRQ%d,\n ADC: DMA(record):%d/IRQ%d)\n", controller->dma1, get_dma_done_irq(controller->dma1), controller->dma2, get_dma_done_irq(controller->dma2));
|
|
|
|
controller->dev_audio = adev;
|
|
pop_turn_onoff_buf = __get_free_pages(GFP_KERNEL | GFP_DMA, 8);
|
|
if(!pop_turn_onoff_buf)
|
|
printk("pop_turn_onoff_buf alloc is wrong!\n");
|
|
pop_turn_onoff_pbuf = virt_to_phys((void *)pop_turn_onoff_buf);
|
|
|
|
return;
|
|
dma2_failed:
|
|
jz_free_dma(controller->dma1);
|
|
dma1_failed:
|
|
free_pages((unsigned long)controller->tmp2, 8);
|
|
tmp2_failed:
|
|
free_pages((unsigned long)controller->tmp1, 8);
|
|
tmp1_failed:
|
|
|
|
#ifdef CONFIG_PROC_FS
|
|
jz_i2s_cleanup_proc(controller);
|
|
#endif
|
|
proc_failed:
|
|
/* unregister mixer dev */
|
|
mixer_failed:
|
|
unregister_sound_dsp(adev);
|
|
audio_failed:
|
|
return;
|
|
}
|
|
|
|
static int __init probe_jz_i2s(struct jz_i2s_controller_info **controller)
|
|
{
|
|
if ((*controller = kmalloc(sizeof(struct jz_i2s_controller_info),
|
|
GFP_KERNEL)) == NULL) {
|
|
printk(KERN_ERR "Jz I2S Controller: out of memory.\n");
|
|
return -ENOMEM;
|
|
}
|
|
(*controller)->name = "Jz I2S controller";
|
|
(*controller)->opened1 = 0;
|
|
(*controller)->opened2 = 0;
|
|
init_waitqueue_head(&(*controller)->adc_wait);
|
|
init_waitqueue_head(&(*controller)->dac_wait);
|
|
spin_lock_init(&(*controller)->lock);
|
|
init_waitqueue_head(&rx_wait_queue);
|
|
init_waitqueue_head(&tx_wait_queue);
|
|
init_waitqueue_head(&pop_wait_queue);
|
|
init_waitqueue_head(&drain_wait_queue);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void __exit unload_jz_i2s(struct jz_i2s_controller_info *controller)
|
|
{
|
|
int adev = controller->dev_audio;
|
|
|
|
jz_i2s_full_reset(controller);
|
|
controller->dev_audio = -1;
|
|
if (old_mksound)
|
|
kd_mksound = old_mksound;/* Our driver support bell for kb, see vt.c */
|
|
|
|
#ifdef CONFIG_PROC_FS
|
|
jz_i2s_cleanup_proc(controller);
|
|
#endif
|
|
|
|
jz_free_dma(controller->dma1);
|
|
jz_free_dma(controller->dma2);
|
|
free_pages((unsigned long)controller->tmp1, 8);
|
|
free_pages((unsigned long)controller->tmp2, 8);
|
|
free_pages((unsigned long)pop_turn_onoff_buf, 8);
|
|
|
|
if (adev >= 0) {
|
|
/* unregister_sound_mixer(audio_devs[adev]->mixer_dev); */
|
|
unregister_sound_dsp(controller->dev_audio);
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_PM
|
|
static int jz_i2s_suspend(struct jz_i2s_controller_info *controller, int state)
|
|
{
|
|
if(i2s_suspend_codec)
|
|
i2s_suspend_codec(controller->opened1,controller->opened2);
|
|
printk("Aic and codec are suspended!\n");
|
|
return 0;
|
|
}
|
|
|
|
static int jz_i2s_resume(struct jz_i2s_controller_info *controller)
|
|
{
|
|
if(i2s_resume_codec)
|
|
i2s_resume_codec();
|
|
|
|
#if defined(CONFIG_I2S_AK4642EN)
|
|
jz_i2s_initHw(0);
|
|
jz_audio_reset();
|
|
__i2s_enable();
|
|
jz_audio_set_speed(controller->dev_audio,jz_audio_speed);
|
|
/* playing */
|
|
if(controller->opened1) {
|
|
if(set_codec_replay)
|
|
set_codec_replay();
|
|
int dma = controller->dma1;
|
|
int id;
|
|
unsigned long flags;
|
|
disable_dma(dma);
|
|
if(__dmac_channel_address_error_detected(dma)) {
|
|
printk(KERN_DEBUG "%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);
|
|
|
|
/* for DSP_GETOPTR */
|
|
spin_lock_irqsave(&controller->ioctllock, flags);
|
|
controller->total_bytes += jz_audio_dma_tran_count;
|
|
controller->blocks ++;
|
|
spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
while((id = get_buffer_id(&out_busy_queue)) >= 0)
|
|
put_buffer_id(&out_empty_queue, id);
|
|
|
|
out_busy_queue.count=0;
|
|
if((id = get_buffer_id(&out_full_queue)) >= 0) {
|
|
put_buffer_id(&out_empty_queue, id);
|
|
}
|
|
if (elements_in_queue(&out_empty_queue) > 0) {
|
|
wake_up(&tx_wait_queue);
|
|
wake_up(&controller->dac_wait);
|
|
} else
|
|
printk("pm out_empty_queue empty");
|
|
}
|
|
|
|
/* recording */
|
|
if(controller->opened2) {
|
|
if(set_codec_record)
|
|
set_codec_record(use_mic_line_flag);
|
|
int dma = controller->dma2;
|
|
int id1, id2;
|
|
unsigned long flags;
|
|
disable_dma(dma);
|
|
if (__dmac_channel_address_error_detected(dma)) {
|
|
printk(KERN_DEBUG "%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);
|
|
}
|
|
/* for DSP_GETIPTR */
|
|
spin_lock_irqsave(&controller->ioctllock, flags);
|
|
controller->total_bytes += jz_audio_dma_tran_count;
|
|
controller->blocks ++;
|
|
spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
id1 = get_buffer_id(&in_busy_queue);
|
|
put_buffer_id(&in_full_queue, id1);
|
|
wake_up(&rx_wait_queue);
|
|
wake_up(&controller->adc_wait);
|
|
if ((id2 = get_buffer_id(&in_empty_queue)) >= 0) {
|
|
put_buffer_id(&in_full_queue, id2);
|
|
}
|
|
in_busy_queue.count = 0;
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int jz_i2s_pm_callback(struct pm_dev *pm_dev, pm_request_t req, void *data)
|
|
{
|
|
int ret;
|
|
struct jz_i2s_controller_info *controller = pm_dev->data;
|
|
|
|
if (!controller) return -EINVAL;
|
|
|
|
switch (req) {
|
|
case PM_SUSPEND:
|
|
ret = jz_i2s_suspend(controller, (int)data);
|
|
break;
|
|
case PM_RESUME:
|
|
ret = jz_i2s_resume(controller);
|
|
break;
|
|
default:
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
#endif /* CONFIG_PM */
|
|
|
|
#if defined(CONFIG_I2S_DLV)
|
|
static irqreturn_t aic_codec_irq(int irq, void *dev_id)
|
|
{
|
|
u8 file_9 = read_codec_file(9);
|
|
u8 file_8 = read_codec_file(8);
|
|
|
|
//printk("--- 8:0x%x 9:0x%x ---\n",file_8,file_9);
|
|
if ((file_9 & 0x1f) == 0x10) {
|
|
|
|
write_codec_file(8, 0x3f);
|
|
write_codec_file_bit(5, 1, 6);//SB_OUT->1
|
|
mdelay(300);
|
|
while ((read_codec_file(9) & 0x4) != 0x4);
|
|
while ((read_codec_file(9) & 0x10) == 0x10) {
|
|
write_codec_file(9, 0x10);
|
|
}
|
|
write_codec_file_bit(5, 0, 6);//SB_OUT->0
|
|
mdelay(300);
|
|
while ((read_codec_file(9) & 0x8) != 0x8);
|
|
write_codec_file(9, file_9);
|
|
write_codec_file(8, file_8);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
if (file_9 & 0x8)
|
|
ramp_up_end = jiffies;
|
|
else if (file_9 & 0x4)
|
|
ramp_down_end = jiffies;
|
|
else if (file_9 & 0x2)
|
|
gain_up_end = jiffies;
|
|
else if (file_9 & 0x1)
|
|
gain_down_end = jiffies;
|
|
|
|
write_codec_file(9, file_9);
|
|
if (file_9 & 0xf)
|
|
wake_up(&pop_wait_queue);
|
|
while (REG_ICDC_RGDATA & 0x100);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
#endif
|
|
|
|
static int __init init_jz_i2s(void)
|
|
{
|
|
int errno;
|
|
#if defined(CONFIG_I2S_DLV)
|
|
int retval;
|
|
ramp_up_start = 0;
|
|
ramp_up_end = 0;
|
|
gain_up_start = 0;
|
|
gain_up_end = 0;
|
|
ramp_down_start = 0;
|
|
ramp_down_end = 0;
|
|
gain_down_start = 0;
|
|
gain_down_end = 0;
|
|
#endif
|
|
use_mic_line_flag = USE_NONE;
|
|
abnormal_data_count = 0;
|
|
if(set_codec_mode)
|
|
set_codec_mode();
|
|
|
|
drain_flag = 0;
|
|
if ((errno = probe_jz_i2s(&i2s_controller)) < 0)
|
|
return errno;
|
|
if(set_codec_gpio_pin)
|
|
set_codec_gpio_pin();
|
|
|
|
attach_jz_i2s(i2s_controller);
|
|
if(set_codec_startup_param)
|
|
set_codec_startup_param();
|
|
if(set_codec_volume_table)
|
|
set_codec_volume_table();
|
|
|
|
#if defined(CONFIG_I2S_DLV)
|
|
jz_codec_config = 0;
|
|
retval = request_irq(IRQ_AIC, aic_codec_irq, IRQF_DISABLED, "aic_codec_irq", NULL);
|
|
if (retval) {
|
|
printk("Could not get aic codec irq %d\n", IRQ_AIC);
|
|
return retval;
|
|
}
|
|
#endif
|
|
|
|
out_empty_queue.id = NULL;
|
|
out_full_queue.id = NULL;
|
|
out_busy_queue.id = NULL;
|
|
in_empty_queue.id = NULL;
|
|
in_full_queue.id = NULL;
|
|
in_busy_queue.id = NULL;
|
|
|
|
jz_audio_fragsize = JZCODEC_RW_BUFFER_SIZE * PAGE_SIZE;
|
|
jz_audio_fragstotal = JZCODEC_RW_BUFFER_TOTAL ;
|
|
Init_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize);
|
|
|
|
#ifdef CONFIG_PM
|
|
i2s_controller->pm = pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN,
|
|
jz_i2s_pm_callback);
|
|
if (i2s_controller->pm)
|
|
i2s_controller->pm->data = i2s_controller;
|
|
#endif
|
|
#if defined(CONFIG_I2S_DLV)
|
|
__cpm_start_idct();
|
|
__cpm_start_db();
|
|
__cpm_start_me();
|
|
__cpm_start_mc();
|
|
__cpm_start_ipu();
|
|
#endif
|
|
printk("JZ I2S OSS audio driver initialized\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void __exit cleanup_jz_i2s(void)
|
|
{
|
|
#ifdef CONFIG_PM
|
|
/* pm_unregister(i2s_controller->pm); */
|
|
#endif
|
|
#if defined(CONFIG_I2S_DLV)
|
|
free_irq(IRQ_AIC, NULL);
|
|
#endif
|
|
unload_jz_i2s(i2s_controller);
|
|
Free_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize);
|
|
if(clear_codec_mode)
|
|
clear_codec_mode();
|
|
}
|
|
|
|
module_init(init_jz_i2s);
|
|
module_exit(cleanup_jz_i2s);
|
|
|
|
#if defined(CONFIG_SOC_JZ4730)
|
|
static int drain_adc(struct jz_i2s_controller_info *ctrl, int nonblock)
|
|
{
|
|
DECLARE_WAITQUEUE(wait, current);
|
|
unsigned long flags;
|
|
int count,con;
|
|
|
|
if(elements_in_queue(&in_busy_queue) > 0) {
|
|
if (nonblock)
|
|
return -EBUSY;
|
|
drain_flag = 1;
|
|
sleep_on(&drain_wait_queue);
|
|
drain_flag = 0;
|
|
} else {
|
|
add_wait_queue(&ctrl->adc_wait, &wait);
|
|
for (con = 0; con < 1000; con ++) {
|
|
udelay(1);
|
|
set_current_state(TASK_INTERRUPTIBLE);
|
|
spin_lock_irqsave(&ctrl->lock, flags);
|
|
count = get_dma_residue(ctrl->dma2);
|
|
spin_unlock_irqrestore(&ctrl->lock, flags);
|
|
if (count <= 0)
|
|
break;
|
|
if (nonblock) {
|
|
remove_wait_queue(&ctrl->adc_wait, &wait);
|
|
current->state = TASK_RUNNING;
|
|
return -EBUSY;
|
|
}
|
|
}
|
|
remove_wait_queue(&ctrl->adc_wait, &wait);
|
|
current->state = TASK_RUNNING;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int drain_dac(struct jz_i2s_controller_info *ctrl, int nonblock)
|
|
{
|
|
DECLARE_WAITQUEUE(wait, current);
|
|
unsigned long flags;
|
|
int count;
|
|
|
|
if(elements_in_queue(&out_full_queue) > 0) {
|
|
if (nonblock)
|
|
return -EBUSY;
|
|
|
|
drain_flag = 1;
|
|
sleep_on(&drain_wait_queue);
|
|
drain_flag = 0;
|
|
} else {
|
|
add_wait_queue(&(ctrl->dac_wait), &wait);
|
|
for (;;) {
|
|
set_current_state(TASK_INTERRUPTIBLE);
|
|
if(elements_in_queue(&out_full_queue) <= 0) {
|
|
spin_lock_irqsave(&ctrl->lock, flags);
|
|
count = get_dma_residue(ctrl->dma1);
|
|
spin_unlock_irqrestore(&ctrl->lock, flags);
|
|
if(count <= 0)
|
|
break;
|
|
}
|
|
if (nonblock) {
|
|
remove_wait_queue(&ctrl->dac_wait, &wait);
|
|
current->state = TASK_RUNNING;
|
|
return -EBUSY;
|
|
}
|
|
}
|
|
remove_wait_queue(&ctrl->dac_wait, &wait);
|
|
current->state = TASK_RUNNING;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#if defined(CONFIG_SOC_JZ4740)
|
|
#define MAXDELAY 50000
|
|
static int drain_dac(struct jz_i2s_controller_info *ctrl, int nonblock)
|
|
{
|
|
int count,ele,i=0;
|
|
int tfl;
|
|
|
|
for (;;) {
|
|
if(!nonblock) {//blocked
|
|
if ( i < MAXDELAY ) {
|
|
udelay(10);
|
|
i++;
|
|
} else
|
|
break;
|
|
|
|
ele = elements_in_queue(&out_full_queue);
|
|
if(ele <= 0) {
|
|
udelay(10);
|
|
spin_lock(&ctrl->lock);
|
|
count = get_dma_residue(ctrl->dma1);
|
|
spin_unlock(&ctrl->lock);
|
|
if (count <= 0)
|
|
break;
|
|
}
|
|
} else {//non-blocked
|
|
mdelay(100);
|
|
ele = elements_in_queue(&out_full_queue);
|
|
|
|
if(ele <= 0) {
|
|
mdelay(100);
|
|
|
|
spin_lock(&ctrl->lock);
|
|
count = get_dma_residue(ctrl->dma1);
|
|
spin_unlock(&ctrl->lock);
|
|
if (count <= 0)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* wait for TX fifo */
|
|
while (1) {
|
|
tfl = __aic_get_transmit_resident();
|
|
if (tfl == 0)
|
|
break;
|
|
udelay(2);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int drain_adc(struct jz_i2s_controller_info *ctrl, int nonblock)
|
|
{
|
|
int count,i=0;
|
|
|
|
for (;;) {
|
|
if ( i < MAXDELAY )
|
|
{
|
|
udelay(10);
|
|
i++;
|
|
}
|
|
else
|
|
break;
|
|
spin_lock(&ctrl->lock);
|
|
count = get_dma_residue(ctrl->dma2);
|
|
spin_unlock(&ctrl->lock);
|
|
if (count <= 0)
|
|
break;
|
|
|
|
if (nonblock) {
|
|
return -EBUSY;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#if defined(CONFIG_SOC_JZ4750)
|
|
#define MAXDELAY 50000
|
|
static int drain_adc(struct jz_i2s_controller_info *ctrl, int nonblock)
|
|
{
|
|
//DECLARE_WAITQUEUE(wait, current);
|
|
unsigned long flags;
|
|
int count,i=0;
|
|
|
|
//add_wait_queue(&ctrl->adc_wait, &wait);
|
|
for (;;) {
|
|
if (i < MAXDELAY) {
|
|
udelay(10);
|
|
i++;
|
|
} else
|
|
break;
|
|
//set_current_state(TASK_INTERRUPTIBLE);
|
|
spin_lock_irqsave(&ctrl->lock, flags);
|
|
//spin_lock(&ctrl->lock);
|
|
count = get_dma_residue(ctrl->dma2);
|
|
spin_unlock_irqrestore(&ctrl->lock, flags);
|
|
//spin_unlock(&ctrl->lock);
|
|
if (count <= 0)
|
|
break;
|
|
|
|
/*if (signal_pending(current))
|
|
break;*/
|
|
if (nonblock) {
|
|
//remove_wait_queue(&ctrl->adc_wait, &wait);
|
|
//current->state = TASK_RUNNING;
|
|
return -EBUSY;
|
|
}
|
|
}
|
|
//remove_wait_queue(&ctrl->adc_wait, &wait);
|
|
//current->state = TASK_RUNNING;
|
|
/*if (signal_pending(current))
|
|
return -ERESTARTSYS;*/
|
|
return 0;
|
|
}
|
|
static int drain_dac(struct jz_i2s_controller_info *ctrl, int nonblock)
|
|
{
|
|
unsigned long flags;
|
|
int count,ele,busyele,emptyele,i=0;
|
|
|
|
for (;;) {
|
|
if(!nonblock) {//blocked
|
|
if (i < MAXDELAY) {
|
|
udelay(10);
|
|
i++;
|
|
} else
|
|
break;
|
|
|
|
ele = elements_in_queue(&out_full_queue);
|
|
if(ele <= 0) {
|
|
udelay(200);
|
|
|
|
busyele = elements_in_queue(&out_busy_queue);
|
|
emptyele = elements_in_queue(&out_empty_queue);
|
|
if (busyele <= 0 && emptyele >= jz_audio_fragstotal) {
|
|
spin_lock_irqsave(&ctrl->lock, flags);
|
|
count = get_dma_residue(ctrl->dma1);
|
|
spin_unlock_irqrestore(&ctrl->lock, flags);
|
|
if (count <= 0)
|
|
break;
|
|
}
|
|
}
|
|
} else {//non-blocked
|
|
//mdelay(100);
|
|
ele = elements_in_queue(&out_full_queue);
|
|
|
|
if(ele <= 0) {
|
|
//mdelay(100);
|
|
busyele = elements_in_queue(&out_busy_queue);
|
|
emptyele = elements_in_queue(&out_empty_queue);
|
|
|
|
if (busyele <= 0 && emptyele >= jz_audio_fragstotal) {
|
|
spin_lock_irqsave(&ctrl->lock, flags);
|
|
count = get_dma_residue(ctrl->dma1);
|
|
spin_unlock_irqrestore(&ctrl->lock, flags);
|
|
if (count <= 0)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static int jz_audio_release(struct inode *inode, struct file *file)
|
|
{
|
|
unsigned long flags;
|
|
#if defined(CONFIG_I2S_DLV)
|
|
unsigned long tfl;
|
|
#endif
|
|
struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data;
|
|
#if defined(CONFIG_I2S_DLV)
|
|
jz_codec_config = 0;
|
|
#endif
|
|
if (controller == NULL)
|
|
return -ENODEV;
|
|
|
|
pop_dma_flag = 0;
|
|
|
|
if (controller->opened1 == 1 && controller->opened2 == 1) {
|
|
controller->opened1 = 0;
|
|
__i2s_enable_transmit_dma();
|
|
__i2s_enable_replay();
|
|
drain_dac(controller, file->f_flags & O_NONBLOCK);
|
|
#if defined(CONFIG_I2S_DLV)
|
|
/* wait for fifo empty */
|
|
write_codec_file_bit(1, 1, 5);//DAC_MUTE->1
|
|
gain_down_start = jiffies;
|
|
sleep_on(&pop_wait_queue);
|
|
//gain_down_end = jiffies;
|
|
/*while (1) {
|
|
tfl = REG_AIC_SR & 0x00003f00;
|
|
if (tfl == 0) {
|
|
udelay(500);
|
|
break;
|
|
}
|
|
mdelay(2);
|
|
}*/
|
|
mdelay(100);
|
|
#endif
|
|
disable_dma(controller->dma1);
|
|
set_dma_count(controller->dma1, 0);
|
|
__i2s_disable_transmit_dma();
|
|
__i2s_disable_replay();
|
|
|
|
#if defined(CONFIG_I2S_ICODEC)
|
|
if(clear_codec_replay)
|
|
clear_codec_replay();
|
|
#endif
|
|
spin_lock_irqsave(&controller->ioctllock, flags);
|
|
controller->total_bytes = 0;
|
|
controller->count = 0;
|
|
controller->finish = 0;
|
|
jz_audio_dma_tran_count = 0;
|
|
controller->blocks = 0;
|
|
controller->nextOut = 0;
|
|
spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
|
|
controller->opened2 = 0;
|
|
first_record_call = 1;
|
|
__i2s_enable_receive_dma();
|
|
__i2s_enable_record();
|
|
drain_adc(controller, file->f_flags & O_NONBLOCK);
|
|
disable_dma(controller->dma2);
|
|
set_dma_count(controller->dma2, 0);
|
|
__i2s_disable_receive_dma();
|
|
__i2s_disable_record();
|
|
#if defined(CONFIG_I2S_ICODEC)
|
|
if(clear_codec_record)
|
|
clear_codec_record();
|
|
#endif
|
|
spin_lock_irqsave(&controller->ioctllock, flags);
|
|
controller->total_bytes = 0;
|
|
jz_audio_dma_tran_count = 0;
|
|
controller->count = 0;
|
|
controller->finish = 0;
|
|
controller->blocks = 0;
|
|
controller->nextIn = 0;
|
|
spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
#if defined(CONFIG_I2S_DLV)
|
|
write_codec_file_bit(5, 1, 6);//SB_OUT->1
|
|
ramp_down_start = jiffies;
|
|
sleep_on(&pop_wait_queue);
|
|
//ramp_down_end = jiffies;
|
|
if (use_mic_line_flag == USE_LINEIN) {
|
|
unset_record_line_input_audio_with_audio_data_replay();
|
|
//printk("3 use_mic_line_flag=%d\n",use_mic_line_flag);
|
|
}
|
|
if (use_mic_line_flag == USE_MIC) {
|
|
unset_record_mic_input_audio_with_audio_data_replay();
|
|
//printk("4 use_mic_line_flag=%d\n",use_mic_line_flag);
|
|
}
|
|
|
|
#if 0
|
|
unset_record_playing_audio_mixed_with_mic_input_audio();
|
|
#endif
|
|
#endif
|
|
__i2s_disable();
|
|
if(turn_off_codec)
|
|
turn_off_codec();
|
|
abnormal_data_count = 0;
|
|
} else if (controller->opened1 == 1) {
|
|
//controller->opened1 = 0;
|
|
__i2s_enable_transmit_dma();
|
|
__i2s_enable_replay();
|
|
drain_dac(controller, file->f_flags & O_NONBLOCK);
|
|
/* add some mute to anti-pop */
|
|
#if defined(CONFIG_I2S_ICODEC)
|
|
//write_mute_to_dma_buffer(save_last_samples[last_dma_buffer_id].left,save_last_samples[last_dma_buffer_id].right);
|
|
#endif
|
|
#if defined(CONFIG_I2S_DLV)
|
|
write_codec_file_bit(1, 1, 5);//DAC_MUTE->1
|
|
gain_down_start = jiffies;
|
|
sleep_on(&pop_wait_queue);
|
|
//gain_down_end = jiffies;
|
|
while (1) {
|
|
tfl = REG_AIC_SR & 0x00003f00;
|
|
if (tfl == 0) {
|
|
udelay(500);
|
|
break;
|
|
}
|
|
mdelay(2);
|
|
}
|
|
#endif
|
|
disable_dma(controller->dma1);
|
|
set_dma_count(controller->dma1, 0);
|
|
__i2s_disable_transmit_dma();
|
|
__i2s_disable_replay();
|
|
#if defined(CONFIG_I2S_ICODEC)
|
|
if(clear_codec_replay)
|
|
clear_codec_replay();
|
|
#endif
|
|
__aic_flush_fifo();
|
|
|
|
spin_lock_irqsave(&controller->ioctllock, flags);
|
|
controller->total_bytes = 0;
|
|
controller->count = 0;
|
|
controller->finish = 0;
|
|
jz_audio_dma_tran_count = 0;
|
|
controller->blocks = 0;
|
|
controller->nextOut = 0;
|
|
spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
#if defined(CONFIG_I2S_DLV)
|
|
write_codec_file_bit(5, 1, 6);//SB_OUT->1
|
|
ramp_down_start = jiffies;
|
|
sleep_on(&pop_wait_queue);
|
|
//ramp_down_end = jiffies;
|
|
unset_audio_data_replay();
|
|
#endif
|
|
__i2s_disable();
|
|
#if defined(CONFIG_I2S_ICODEC)
|
|
if(turn_off_codec)
|
|
turn_off_codec();
|
|
#endif
|
|
} else if (controller->opened2 == 1) {
|
|
controller->opened2 = 0;
|
|
first_record_call = 1;
|
|
__i2s_enable_receive_dma();
|
|
__i2s_enable_record();
|
|
drain_adc(controller, file->f_flags & O_NONBLOCK);
|
|
disable_dma(controller->dma2);
|
|
set_dma_count(controller->dma2, 0);
|
|
__i2s_disable_receive_dma();
|
|
__i2s_disable_record();
|
|
#if defined(CONFIG_I2S_ICODEC)
|
|
if(clear_codec_record)
|
|
clear_codec_record();
|
|
#endif
|
|
spin_lock_irqsave(&controller->ioctllock, flags);
|
|
controller->total_bytes = 0;
|
|
jz_audio_dma_tran_count = 0;
|
|
controller->count = 0;
|
|
controller->finish = 0;
|
|
controller->blocks = 0;
|
|
controller->nextIn = 0;
|
|
spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
#if defined(CONFIG_I2S_DLV)
|
|
#if 0
|
|
/* unset Record MIC input audio with direct playback */
|
|
unset_record_mic_input_audio_with_direct_playback();
|
|
#endif
|
|
#if 1
|
|
/* unset Record MIC input audio without playback */
|
|
unset_record_mic_input_audio_without_playback();
|
|
#endif
|
|
#if 0
|
|
/* tested */
|
|
/* unset Record LINE input audio without playback */
|
|
unset_record_line_input_audio_without_playback();
|
|
#endif
|
|
#endif
|
|
__i2s_disable();
|
|
#if defined(CONFIG_I2S_ICODEC)
|
|
if(turn_off_codec)
|
|
turn_off_codec();
|
|
#endif
|
|
abnormal_data_count = 0;
|
|
}
|
|
|
|
#if defined(CONFIG_I2S_DLV)
|
|
write_codec_file(9, 0xff);
|
|
write_codec_file(8, 0x3f);
|
|
/* __cpm_stop_idct();
|
|
__cpm_stop_db();
|
|
__cpm_stop_me();
|
|
__cpm_stop_mc();
|
|
__cpm_stop_ipu();*/
|
|
#endif
|
|
|
|
if (controller->opened1 == 1 && controller->opened2 == 1) {
|
|
controller->opened1 = 0;
|
|
controller->opened2 = 0;
|
|
//print_pop_duration();
|
|
//__dmac_disable_module(0);
|
|
} else if ( controller->opened1 == 1 ) {
|
|
controller->opened1 = 0;
|
|
//print_pop_duration();
|
|
} else if ( controller->opened2 == 1 ) {
|
|
controller->opened2 = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int jz_audio_open(struct inode *inode, struct file *file)
|
|
{
|
|
int i;
|
|
struct jz_i2s_controller_info *controller = i2s_controller;
|
|
#if defined(CONFIG_I2S_DLV)
|
|
jz_codec_config = 0;
|
|
#endif
|
|
if (controller == NULL)
|
|
return -ENODEV;
|
|
|
|
mdelay(2);
|
|
#if defined(CONFIG_I2S_DLV)
|
|
REG_DMAC_DMACKE(0) = 0x3f;
|
|
#endif
|
|
pop_dma_flag = 0;
|
|
if (controller->opened1 == 1 || controller->opened2 == 1 ) {
|
|
printk("\naudio is busy!\n");
|
|
return -EBUSY;
|
|
}
|
|
|
|
if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
|
|
if (controller->opened1 == 1)
|
|
return -EBUSY;
|
|
controller->opened1 = 1;
|
|
/* for ioctl */
|
|
controller->total_bytes = 0;
|
|
jz_audio_dma_tran_count = 0;
|
|
controller->count = 0;
|
|
controller->finish = 0;
|
|
controller->blocks = 0;
|
|
controller->nextOut = 0;
|
|
|
|
for(i=0;i < 64;i++) {
|
|
save_last_samples[i].left = 0;
|
|
save_last_samples[i].right = 0;
|
|
}
|
|
|
|
out_empty_queue.count = jz_audio_fragstotal;
|
|
for (i=0;i < jz_audio_fragstotal;i++)
|
|
*(out_empty_queue.id + i) = i;
|
|
out_busy_queue.count = 0;
|
|
out_full_queue.count = 0;
|
|
last_dma_buffer_id = 0;
|
|
|
|
if (controller->opened2 == 1)
|
|
return -EBUSY;
|
|
|
|
controller->opened2 = 1;
|
|
first_record_call = 1;
|
|
/* for ioctl */
|
|
controller->total_bytes = 0;
|
|
jz_audio_dma_tran_count = 0;
|
|
controller->count = 0;
|
|
controller->finish = 0;
|
|
controller->blocks = 0;
|
|
controller->nextIn = 0;
|
|
|
|
in_empty_queue.count = jz_audio_fragstotal;
|
|
for (i=0;i < jz_audio_fragstotal;i++)
|
|
*(in_empty_queue.id + i) = i;
|
|
|
|
in_full_queue.count = 0;
|
|
in_busy_queue.count = 0;
|
|
} else if (file->f_mode & FMODE_WRITE) {
|
|
if (controller->opened1 == 1)
|
|
return -EBUSY;
|
|
|
|
controller->opened1 = 1;
|
|
/* for ioctl */
|
|
controller->total_bytes = 0;
|
|
jz_audio_dma_tran_count = 0;
|
|
controller->count = 0;
|
|
controller->finish = 0;
|
|
controller->blocks = 0;
|
|
controller->nextOut = 0;
|
|
|
|
for(i=0;i < 64;i++) {
|
|
save_last_samples[i].left = 0;
|
|
save_last_samples[i].right = 0;
|
|
}
|
|
|
|
out_empty_queue.count = jz_audio_fragstotal;
|
|
for (i=0;i < jz_audio_fragstotal;i++)
|
|
*(out_empty_queue.id + i) = i;
|
|
out_busy_queue.count = 0;
|
|
out_full_queue.count = 0;
|
|
last_dma_buffer_id = 0;
|
|
} else if (file->f_mode & FMODE_READ) {
|
|
if (controller->opened2 == 1)
|
|
return -EBUSY;
|
|
|
|
controller->opened2 = 1;
|
|
first_record_call = 1;
|
|
/* for ioctl */
|
|
controller->total_bytes = 0;
|
|
jz_audio_dma_tran_count = 0;
|
|
controller->count = 0;
|
|
controller->finish = 0;
|
|
controller->blocks = 0;
|
|
controller->nextIn = 0;
|
|
|
|
in_empty_queue.count = jz_audio_fragstotal;
|
|
for (i=0;i < jz_audio_fragstotal;i++)
|
|
*(in_empty_queue.id + i) = i;
|
|
|
|
in_full_queue.count = 0;
|
|
in_busy_queue.count = 0;
|
|
}
|
|
|
|
file->private_data = controller;
|
|
jz_audio_reset();
|
|
REG_AIC_FR |= (1 << 6);
|
|
if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
|
|
#if defined(CONFIG_I2S_ICODEC)
|
|
if (set_codec_replay_record)
|
|
set_codec_replay_record(use_mic_line_flag);
|
|
#endif
|
|
#if defined(CONFIG_I2S_DLV)
|
|
if (use_mic_line_flag == USE_NONE) {
|
|
printk("you select mic or line recording please.or use mic recording!\n");
|
|
use_mic_line_flag = USE_MIC;
|
|
}
|
|
if (use_mic_line_flag == USE_LINEIN) {
|
|
/* Record LINE input audio with Audio data replay (full duplex for linein) */
|
|
/* codec_test_line */
|
|
set_record_line_input_audio_with_audio_data_replay();
|
|
|
|
}
|
|
if (use_mic_line_flag == USE_MIC) {
|
|
/* Record MIC input audio with Audio data replay (full duplex) */
|
|
/* codec_test_mic */
|
|
set_record_mic_input_audio_with_audio_data_replay();
|
|
}
|
|
#if 0
|
|
/* Record playing audio mixed with MIC input audio */
|
|
set_record_playing_audio_mixed_with_mic_input_audio();
|
|
#endif
|
|
|
|
#endif
|
|
} else if (file->f_mode & FMODE_WRITE) {
|
|
#if defined(CONFIG_I2S_ICODEC)
|
|
if(set_codec_replay)
|
|
set_codec_replay();
|
|
#endif
|
|
#if defined(CONFIG_I2S_DLV)
|
|
//mdelay(10);
|
|
/* Audio data replay */
|
|
set_audio_data_replay();
|
|
#endif
|
|
} else if (file->f_mode & FMODE_READ) {
|
|
#if defined(CONFIG_I2S_ICODEC)
|
|
abnormal_data_count = 0;
|
|
if(set_codec_record)
|
|
set_codec_record(use_mic_line_flag);
|
|
#endif
|
|
#if defined(CONFIG_I2S_DLV)
|
|
#if 0
|
|
/* Record MIC input audio with direct playback */
|
|
set_record_mic_input_audio_with_direct_playback();
|
|
#endif
|
|
|
|
#if 1
|
|
/* set Record MIC input audio without playback */
|
|
set_record_mic_input_audio_without_playback();
|
|
#endif
|
|
#if 0
|
|
/* tested */
|
|
/* set Record LINE input audio without playback */
|
|
set_record_line_input_audio_without_playback();
|
|
#endif
|
|
mdelay(1);
|
|
#endif
|
|
}
|
|
|
|
#if defined(CONFIG_I2S_DLV)
|
|
__aic_reset();
|
|
|
|
mdelay(10);
|
|
REG_AIC_I2SCR = 0x10;
|
|
mdelay(20);
|
|
__aic_flush_fifo();
|
|
#endif
|
|
|
|
__i2s_enable();
|
|
|
|
#if defined(CONFIG_I2S_DLV)
|
|
ndelay(100);
|
|
|
|
if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
|
|
#if defined(CONFIG_I2S_DLV)
|
|
//set SB_ADC or SB_DAC
|
|
__dmac_enable_module(0);
|
|
write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0
|
|
ramp_up_start = jiffies;
|
|
sleep_on(&pop_wait_queue);
|
|
//ramp_up_end = jiffies;
|
|
#endif
|
|
} else if (file->f_mode & FMODE_WRITE) {
|
|
#if defined(CONFIG_I2S_DLV)
|
|
write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0
|
|
ramp_up_start = jiffies;
|
|
/*while (!(REG_RTC_RCR & RTC_RCR_WRDY));
|
|
REG_RTC_RCR = 0x1;
|
|
while (!(REG_RTC_RCR & RTC_RCR_WRDY));
|
|
REG_RTC_RGR = 1;*/
|
|
sleep_on(&pop_wait_queue);
|
|
//ramp_up_end = jiffies;
|
|
write_codec_file_bit(5, 1, 4);//SB_ADC->1
|
|
#endif
|
|
} else if (file->f_mode & FMODE_READ) {
|
|
#if defined(CONFIG_I2S_DLV)
|
|
if (jz_mic_only)
|
|
write_codec_file_bit(5, 1, 7);//SB_DAC->1
|
|
else
|
|
write_codec_file_bit(5, 0, 7);//SB_DAC->0
|
|
mdelay(500);
|
|
#endif
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int jz_audio_ioctl(struct inode *inode, struct file *file,
|
|
unsigned int cmd, unsigned long arg)
|
|
{
|
|
int val,fullc,busyc,unfinish,newfragstotal,newfragsize;
|
|
struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data;
|
|
count_info cinfo;
|
|
audio_buf_info abinfo;
|
|
int id, i;
|
|
|
|
val = 0;
|
|
switch (cmd) {
|
|
case OSS_GETVERSION:
|
|
return put_user(SOUND_VERSION, (int *)arg);
|
|
case SNDCTL_DSP_RESET:
|
|
#if 0
|
|
jz_audio_reset();
|
|
__i2s_disable_replay();
|
|
__i2s_disable_receive_dma();
|
|
__i2s_disable_record();
|
|
__i2s_disable_transmit_dma();
|
|
#endif
|
|
return 0;
|
|
case SNDCTL_DSP_SYNC:
|
|
if (file->f_mode & FMODE_WRITE)
|
|
return drain_dac(controller, file->f_flags & O_NONBLOCK);
|
|
return 0;
|
|
case SNDCTL_DSP_SPEED:
|
|
/* set smaple rate */
|
|
if (get_user(val, (int *)arg))
|
|
return -EFAULT;
|
|
if (val >= 0)
|
|
jz_audio_set_speed(controller->dev_audio, val);
|
|
|
|
return put_user(val, (int *)arg);
|
|
case SNDCTL_DSP_STEREO:
|
|
/* set stereo or mono channel */
|
|
if (get_user(val, (int *)arg))
|
|
return -EFAULT;
|
|
jz_audio_set_channels(controller->dev_audio, val ? 2 : 1);
|
|
|
|
return 0;
|
|
case SNDCTL_DSP_GETBLKSIZE:
|
|
//return put_user(jz_audio_fragsize / jz_audio_b, (int *)arg);
|
|
return put_user(jz_audio_fragsize, (int *)arg);
|
|
case SNDCTL_DSP_GETFMTS:
|
|
/* Returns a mask of supported sample format*/
|
|
return put_user(AFMT_U8 | AFMT_S16_LE, (int *)arg);
|
|
case SNDCTL_DSP_SETFMT:
|
|
/* Select sample format */
|
|
if (get_user(val, (int *)arg))
|
|
return -EFAULT;
|
|
if (val != AFMT_QUERY)
|
|
jz_audio_set_format(controller->dev_audio,val);
|
|
else {
|
|
if (file->f_mode & FMODE_READ)
|
|
val = (jz_audio_format == 16) ? AFMT_S16_LE : AFMT_U8;
|
|
else
|
|
val = (jz_audio_format == 16) ? AFMT_S16_LE : AFMT_U8;
|
|
}
|
|
|
|
return put_user(val, (int *)arg);
|
|
case SNDCTL_DSP_CHANNELS:
|
|
if (get_user(val, (int *)arg))
|
|
return -EFAULT;
|
|
jz_audio_set_channels(controller->dev_audio, val);
|
|
|
|
return put_user(val, (int *)arg);
|
|
case SNDCTL_DSP_POST:
|
|
/* FIXME: the same as RESET ?? */
|
|
return 0;
|
|
case SNDCTL_DSP_SUBDIVIDE:
|
|
return 0;
|
|
case SNDCTL_DSP_SETFRAGMENT:
|
|
get_user(val, (long *) arg);
|
|
newfragsize = 1 << (val & 0xFFFF);
|
|
if (newfragsize < 4 * PAGE_SIZE)
|
|
newfragsize = 4 * PAGE_SIZE;
|
|
if (newfragsize > (16 * PAGE_SIZE))
|
|
newfragsize = 16 * PAGE_SIZE;
|
|
|
|
newfragstotal = (val >> 16) & 0x7FFF;
|
|
if (newfragstotal < 2)
|
|
newfragstotal = 2;
|
|
if (newfragstotal > 32)
|
|
newfragstotal = 32;
|
|
if((jz_audio_fragstotal == newfragstotal) && (jz_audio_fragsize == newfragsize))
|
|
return 0;
|
|
Free_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize);
|
|
mdelay(500);
|
|
jz_audio_fragstotal = newfragstotal;
|
|
jz_audio_fragsize = newfragsize;
|
|
|
|
Init_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize);
|
|
mdelay(10);
|
|
|
|
return 0;
|
|
case SNDCTL_DSP_GETCAPS:
|
|
return put_user(DSP_CAP_REALTIME|DSP_CAP_BATCH, (int *)arg);
|
|
case SNDCTL_DSP_NONBLOCK:
|
|
file->f_flags |= O_NONBLOCK;
|
|
return 0;
|
|
case SNDCTL_DSP_SETDUPLEX:
|
|
return -EINVAL;
|
|
case SNDCTL_DSP_GETOSPACE:
|
|
{
|
|
int i;
|
|
unsigned long bytes = 0;
|
|
if (!(file->f_mode & FMODE_WRITE))
|
|
return -EINVAL;
|
|
|
|
//spin_lock_irqsave(&controller->ioctllock, flags);
|
|
spin_lock(&controller->ioctllock);
|
|
jz_audio_fragments = elements_in_queue(&out_empty_queue);
|
|
for (i = 0; i < jz_audio_fragments; i++)
|
|
bytes += jz_audio_fragsize;
|
|
|
|
if (jz_audio_channels == 2)
|
|
bytes /= jz_audio_b;
|
|
else if (jz_audio_channels == 1)
|
|
bytes /= 4;
|
|
else
|
|
printk("SNDCTL_DSP_GETOSPACE : channels is wrong 1!\n");
|
|
|
|
//spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
spin_unlock(&controller->ioctllock);
|
|
/* unused fragment amount */
|
|
abinfo.fragments = jz_audio_fragments;
|
|
/* amount of fragments */
|
|
abinfo.fragstotal = jz_audio_fragstotal;
|
|
/* fragment size in bytes */
|
|
if (jz_audio_channels == 2)
|
|
abinfo.fragsize = jz_audio_fragsize / jz_audio_b;
|
|
else if (jz_audio_channels == 1)
|
|
abinfo.fragsize = jz_audio_fragsize / 4;
|
|
else
|
|
printk("SNDCTL_DSP_GETOSPACE : channels is wrong 2!\n");
|
|
|
|
/* write size count without blocking in bytes */
|
|
abinfo.bytes = (int)bytes;
|
|
|
|
return copy_to_user((void *)arg, &abinfo,
|
|
sizeof(abinfo)) ? -EFAULT : 0;
|
|
}
|
|
case SNDCTL_DSP_GETISPACE:
|
|
{
|
|
int i;
|
|
unsigned long bytes = 0;
|
|
if (!(file->f_mode & FMODE_READ))
|
|
return -EINVAL;
|
|
jz_audio_fragments = elements_in_queue(&in_empty_queue);
|
|
for (i = 0; i < jz_audio_fragments; i++)
|
|
bytes += jz_audio_fragsize;
|
|
|
|
if (jz_audio_channels == 2)
|
|
bytes /= jz_audio_b;
|
|
else if (jz_audio_channels == 1)
|
|
bytes /= 4;
|
|
else
|
|
printk("SNDCTL_DSP_GETISPACE : channels is wrong 1!\n");
|
|
|
|
abinfo.fragments = jz_audio_fragments;
|
|
abinfo.fragstotal = jz_audio_fragstotal;
|
|
|
|
if (jz_audio_channels == 2)
|
|
abinfo.fragsize = jz_audio_fragsize / jz_audio_b;
|
|
else if (jz_audio_channels == 1)
|
|
abinfo.fragsize = jz_audio_fragsize / 4;
|
|
else
|
|
printk("SNDCTL_DSP_GETISPACE : channels is wrong 2!\n");
|
|
|
|
abinfo.bytes = (int)bytes;
|
|
|
|
return copy_to_user((void *)arg, &abinfo,
|
|
sizeof(abinfo)) ? -EFAULT : 0;
|
|
}
|
|
case SNDCTL_DSP_GETTRIGGER:
|
|
val = 0;
|
|
if (file->f_mode & FMODE_READ && in_dma_buf)
|
|
val |= PCM_ENABLE_INPUT;
|
|
if (file->f_mode & FMODE_WRITE && out_dma_buf)
|
|
val |= PCM_ENABLE_OUTPUT;
|
|
|
|
return put_user(val, (int *)arg);
|
|
case SNDCTL_DSP_SETTRIGGER:
|
|
if (get_user(val, (int *)arg))
|
|
return -EFAULT;
|
|
return 0;
|
|
case SNDCTL_DSP_GETIPTR:
|
|
if (!(file->f_mode & FMODE_READ))
|
|
return -EINVAL;
|
|
|
|
//spin_lock_irqsave(&controller->ioctllock, flags);
|
|
spin_lock(&controller->ioctllock);
|
|
cinfo.bytes = controller->total_bytes;
|
|
cinfo.blocks = controller->blocks;
|
|
cinfo.ptr = controller->nextIn;
|
|
controller->blocks = 0;
|
|
//spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
spin_unlock(&controller->ioctllock);
|
|
|
|
return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
|
|
case SNDCTL_DSP_GETOPTR:
|
|
if (!(file->f_mode & FMODE_WRITE))
|
|
return -EINVAL;
|
|
|
|
//spin_lock_irqsave(&controller->ioctllock, flags);
|
|
spin_lock(&controller->ioctllock);
|
|
cinfo.bytes = controller->total_bytes;
|
|
cinfo.blocks = controller->blocks;
|
|
cinfo.ptr = controller->nextOut;
|
|
controller->blocks = 0;
|
|
//spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
spin_unlock(&controller->ioctllock);
|
|
|
|
return copy_to_user((void *) arg, &cinfo, sizeof(cinfo));
|
|
case SNDCTL_DSP_GETODELAY:
|
|
if (!(file->f_mode & FMODE_WRITE))
|
|
return -EINVAL;
|
|
|
|
//spin_lock_irqsave(&controller->ioctllock, flags);
|
|
spin_lock(&controller->ioctllock);
|
|
unfinish = 0;
|
|
fullc = elements_in_queue(&out_full_queue);
|
|
busyc = elements_in_queue(&out_busy_queue);
|
|
for(i = 0;i < fullc ;i ++) {
|
|
id = *(out_full_queue.id + i);
|
|
unfinish += *(out_dma_buf_data_count + id);
|
|
}
|
|
for(i = 0;i < busyc ;i ++) {
|
|
id = *(out_busy_queue.id + i);
|
|
unfinish += get_dma_residue(controller->dma1);
|
|
}
|
|
//spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
spin_unlock(&controller->ioctllock);
|
|
|
|
if (jz_audio_channels == 2)
|
|
unfinish /= jz_audio_b;
|
|
else if (jz_audio_channels == 1)
|
|
unfinish /= 4;
|
|
else
|
|
printk("SNDCTL_DSP_GETODELAY : channels is wrong !\n");
|
|
|
|
return put_user(unfinish, (int *) arg);
|
|
case SOUND_PCM_READ_RATE:
|
|
return put_user(jz_audio_rate, (int *)arg);
|
|
case SOUND_PCM_READ_CHANNELS:
|
|
return put_user(jz_audio_channels, (int *)arg);
|
|
case SOUND_PCM_READ_BITS:
|
|
return put_user((jz_audio_format & (AFMT_S8 | AFMT_U8)) ? 8 : 16, (int *)arg);
|
|
case SNDCTL_DSP_MAPINBUF:
|
|
case SNDCTL_DSP_MAPOUTBUF:
|
|
case SNDCTL_DSP_SETSYNCRO:
|
|
case SOUND_PCM_WRITE_FILTER:
|
|
case SOUND_PCM_READ_FILTER:
|
|
return -EINVAL;
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
|
|
|
|
static unsigned int jz_audio_poll(struct file *file,struct poll_table_struct *wait)
|
|
{
|
|
struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data;
|
|
unsigned int mask = 0;
|
|
|
|
if (file->f_mode & FMODE_WRITE) {
|
|
if (elements_in_queue(&out_empty_queue) > 0)
|
|
return POLLOUT | POLLWRNORM;
|
|
|
|
poll_wait(file, &controller->dac_wait, wait);
|
|
}
|
|
|
|
if (file->f_mode & FMODE_READ) {
|
|
if (elements_in_queue(&in_full_queue) > 0)
|
|
return POLLIN | POLLRDNORM;
|
|
|
|
poll_wait(file, &controller->adc_wait, wait);
|
|
}
|
|
|
|
//spin_lock_irqsave(&controller->lock, flags);
|
|
spin_lock(&controller->lock);
|
|
if (file->f_mode & FMODE_WRITE) {
|
|
if (elements_in_queue(&out_empty_queue) > 0)
|
|
mask |= POLLOUT | POLLWRNORM;
|
|
} else if (file->f_mode & FMODE_READ) {
|
|
if (elements_in_queue(&in_full_queue) > 0)
|
|
mask |= POLLIN | POLLRDNORM;
|
|
}
|
|
//spin_unlock_irqrestore(&controller->lock, flags);
|
|
spin_unlock(&controller->lock);
|
|
|
|
return mask;
|
|
}
|
|
|
|
static ssize_t jz_audio_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
|
|
{
|
|
struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data;
|
|
int id, ret = 0, left_count, copy_count, cnt = 0;
|
|
|
|
if (count < 0)
|
|
return -EINVAL;
|
|
|
|
__i2s_enable_receive_dma();
|
|
__i2s_enable_record();
|
|
|
|
//spin_lock_irqsave(&controller->ioctllock, flags);
|
|
spin_lock(&controller->ioctllock);
|
|
controller->nextIn = 0;
|
|
//spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
spin_unlock(&controller->ioctllock);
|
|
|
|
copy_count = jz_audio_fragsize / 4;
|
|
|
|
left_count = count;
|
|
if (first_record_call) {
|
|
first_record_call = 0;
|
|
audio_read_back_first:
|
|
if ((id = get_buffer_id(&in_empty_queue)) >= 0) {
|
|
put_buffer_id(&in_busy_queue, id);
|
|
spin_lock(&controller->lock);
|
|
*(in_dma_buf_data_count + id) = copy_count * 4;
|
|
|
|
spin_unlock(&controller->lock);
|
|
__i2s_enable_receive_dma();
|
|
__i2s_enable_record();
|
|
dma_cache_wback_inv(*(in_dma_buf + id), *(in_dma_buf_data_count + id));
|
|
audio_start_dma(controller->dma2,file->private_data,
|
|
*(in_dma_pbuf + id),
|
|
*(in_dma_buf_data_count + id),
|
|
DMA_MODE_READ);
|
|
sleep_on(&rx_wait_queue);
|
|
} else
|
|
goto audio_read_back_first;
|
|
}
|
|
|
|
while (left_count > 0) {
|
|
audio_read_back_second:
|
|
if (elements_in_queue(&in_full_queue) <= 0) {
|
|
if (file->f_flags & O_NONBLOCK)
|
|
return ret ? ret : -EAGAIN;
|
|
else
|
|
sleep_on(&rx_wait_queue);
|
|
}
|
|
|
|
if ((id = get_buffer_id(&in_full_queue)) >= 0) {
|
|
spin_lock(&controller->lock);
|
|
cnt = record_filler((unsigned long)controller->tmp2+ret, copy_count, id);
|
|
spin_unlock(&controller->lock);
|
|
put_buffer_id(&in_empty_queue, id);
|
|
} else
|
|
goto audio_read_back_second;
|
|
|
|
if (elements_in_queue(&in_busy_queue) == 0) {
|
|
if ((id=get_buffer_id(&in_empty_queue)) >= 0) {
|
|
put_buffer_id(&in_busy_queue, id);
|
|
spin_lock(&controller->lock);
|
|
*(in_dma_buf_data_count + id) = copy_count * 4;
|
|
spin_unlock(&controller->lock);
|
|
|
|
dma_cache_wback_inv(*(in_dma_buf + id), *(in_dma_buf_data_count + id));
|
|
audio_start_dma(controller->dma2,file->private_data,
|
|
*(in_dma_pbuf + id),
|
|
*(in_dma_buf_data_count + id),
|
|
DMA_MODE_READ);
|
|
}
|
|
}
|
|
if (ret + cnt > count) {
|
|
spin_lock(&controller->lock);
|
|
cnt = count - ret;
|
|
spin_unlock(&controller->lock);
|
|
}
|
|
if (copy_to_user(buffer+ret, controller->tmp2+ret, cnt))
|
|
return ret ? ret : -EFAULT;
|
|
|
|
spin_lock(&controller->lock);
|
|
ret += cnt;
|
|
spin_unlock(&controller->lock);
|
|
|
|
//spin_lock_irqsave(&controller->ioctllock, flags);
|
|
spin_lock(&controller->ioctllock);
|
|
controller->nextIn += ret;
|
|
//spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
spin_unlock(&controller->ioctllock);
|
|
|
|
spin_lock(&controller->lock);
|
|
left_count -= cnt;
|
|
spin_unlock(&controller->lock);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static ssize_t jz_audio_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
|
|
{
|
|
int id, ret = 0, left_count, copy_count = 0;
|
|
struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data;
|
|
|
|
if (count <= 0)
|
|
return -EINVAL;
|
|
|
|
if(set_replay_hp_or_speaker)
|
|
set_replay_hp_or_speaker();
|
|
|
|
__i2s_enable_transmit_dma();
|
|
__i2s_enable_replay();
|
|
|
|
//spin_lock_irqsave(&controller->ioctllock, flags);
|
|
spin_lock(&controller->ioctllock);
|
|
controller->nextOut = 0;
|
|
//spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
spin_unlock(&controller->ioctllock);
|
|
if (jz_audio_channels == 2)
|
|
copy_count = jz_audio_fragsize / jz_audio_b;
|
|
else if(jz_audio_channels == 1)
|
|
copy_count = jz_audio_fragsize / 4;
|
|
left_count = count;
|
|
if (copy_from_user(controller->tmp1, buffer, count)) {
|
|
printk("copy_from_user failed:%d",ret);
|
|
return ret ? ret : -EFAULT;
|
|
}
|
|
|
|
while (left_count > 0) {
|
|
audio_write_back:
|
|
if (file->f_flags & O_NONBLOCK)
|
|
udelay(2);
|
|
if (elements_in_queue(&out_empty_queue) == 0) {
|
|
if (file->f_flags & O_NONBLOCK)
|
|
return ret;
|
|
else
|
|
sleep_on(&tx_wait_queue);
|
|
}
|
|
/* 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) {
|
|
replay_filler((signed long)controller->tmp1 + ret, copy_count, id);
|
|
if(*(out_dma_buf_data_count + id) > 0) {
|
|
put_buffer_id(&out_full_queue, id);
|
|
dma_cache_wback_inv(*(out_dma_buf + id), *(out_dma_buf_data_count + id));
|
|
} else
|
|
put_buffer_id(&out_empty_queue, id);
|
|
} else
|
|
goto audio_write_back;
|
|
|
|
left_count = left_count - copy_count;
|
|
ret += copy_count;
|
|
|
|
//spin_lock_irqsave(&controller->ioctllock, flags);
|
|
spin_lock(&controller->ioctllock);
|
|
controller->nextOut += ret;
|
|
//spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
spin_unlock(&controller->ioctllock);
|
|
|
|
if (elements_in_queue(&out_busy_queue) == 0) {
|
|
if ((id=get_buffer_id(&out_full_queue)) >= 0) {
|
|
put_buffer_id(&out_busy_queue, id);
|
|
if(*(out_dma_buf_data_count + id) > 0) {
|
|
audio_start_dma(controller->dma1,
|
|
file->private_data,
|
|
*(out_dma_pbuf + id),
|
|
*(out_dma_buf_data_count + id),
|
|
DMA_MODE_WRITE);
|
|
last_dma_buffer_id = id;
|
|
#if defined(CONFIG_I2S_DLV)
|
|
if (jz_codec_config == 0) {
|
|
write_codec_file_bit(1, 0, 5);
|
|
gain_up_start = jiffies;
|
|
sleep_on(&pop_wait_queue);
|
|
//gain_up_end = jiffies;
|
|
jz_codec_config = 1;
|
|
//SB_ADC->1
|
|
//write_codec_file_bit(5, 1, 4);
|
|
//while(1);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
#if defined(CONFIG_I2S_ICODEC)
|
|
static void write_mute_to_dma_buffer(signed long l_sample, signed long r_sample)
|
|
{
|
|
int i,step_len;
|
|
unsigned long *pop_buf = (unsigned long*)pop_turn_onoff_buf;
|
|
unsigned int sample_oss = (REG_AIC_CR & 0x00380000) >> 19;
|
|
unsigned long l_sample_count,r_sample_count,sample_count;
|
|
struct jz_i2s_controller_info *controller = i2s_controller;
|
|
signed int left_sam=0,right_sam=0,l_val,r_val;
|
|
|
|
switch (sample_oss) {
|
|
case 0x0:
|
|
break;
|
|
case 0x1:
|
|
left_sam = (signed int)l_sample;
|
|
right_sam = (signed int)r_sample;
|
|
break;
|
|
case 0x2:
|
|
break;
|
|
case 0x3:
|
|
break;
|
|
case 0x4:
|
|
break;
|
|
}
|
|
|
|
if(left_sam == 0 && right_sam == 0)
|
|
return;
|
|
|
|
switch (sample_oss) {
|
|
case 0x0:
|
|
break;
|
|
case 0x1:
|
|
step_len = jz_audio_speed / 10 * 3;
|
|
step_len = step_len / 2;
|
|
step_len = 0x7fff / step_len + 1;
|
|
|
|
l_sample_count = 0;
|
|
l_val = left_sam;
|
|
|
|
while(1) {
|
|
if(l_val > 0) {
|
|
if(l_val >= step_len) {
|
|
l_val -= step_len;
|
|
l_sample_count ++;
|
|
} else
|
|
break;
|
|
}
|
|
|
|
if(l_val < 0) {
|
|
if(l_val <= -step_len) {
|
|
l_val += step_len;
|
|
l_sample_count ++;
|
|
} else
|
|
break;
|
|
}
|
|
|
|
if(l_val == 0)
|
|
break;
|
|
}
|
|
|
|
r_sample_count = 0;
|
|
r_val = right_sam;
|
|
while(1) {
|
|
if(r_val > 0) {
|
|
if(r_val >= step_len) {
|
|
r_val -= step_len;
|
|
r_sample_count ++;
|
|
} else
|
|
break;
|
|
}
|
|
|
|
if(r_val < 0) {
|
|
if(r_val <= -step_len) {
|
|
r_val += step_len;
|
|
r_sample_count ++;
|
|
} else
|
|
break;
|
|
}
|
|
|
|
if(r_val == 0)
|
|
break;
|
|
}
|
|
/* fill up */
|
|
if(l_sample_count > r_sample_count)
|
|
sample_count = l_sample_count;
|
|
else
|
|
sample_count = r_sample_count;
|
|
|
|
l_val = left_sam;
|
|
r_val = right_sam;
|
|
for(i=0;i <= sample_count;i++) {
|
|
|
|
*pop_buf = (unsigned long)l_val;
|
|
pop_buf ++;
|
|
|
|
if(l_val > step_len)
|
|
l_val -= step_len;
|
|
else if(l_val < -step_len)
|
|
l_val += step_len;
|
|
else if(l_val >= -step_len && l_val <= step_len)
|
|
l_val = 0;
|
|
|
|
*pop_buf = (unsigned long)r_val;
|
|
pop_buf ++;
|
|
if(r_val > step_len)
|
|
r_val -= step_len;
|
|
else if(r_val < -step_len)
|
|
r_val += step_len;
|
|
else if(r_val >= -step_len && r_val <= step_len)
|
|
r_val = 0;
|
|
}
|
|
|
|
*pop_buf = 0;
|
|
pop_buf ++;
|
|
*pop_buf = 0;
|
|
|
|
pop_buf ++;
|
|
sample_count += 2;
|
|
dma_cache_wback_inv(pop_turn_onoff_buf, sample_count*8);
|
|
|
|
pop_dma_flag = 1;
|
|
audio_start_dma(controller->dma1,controller,pop_turn_onoff_pbuf,sample_count*8,DMA_MODE_WRITE);
|
|
sleep_on(&pop_wait_queue);
|
|
pop_dma_flag = 0;
|
|
break;
|
|
case 0x2:
|
|
break;
|
|
case 0x3:
|
|
break;
|
|
case 0x4:
|
|
break;
|
|
}
|
|
}
|
|
#endif
|