mirror of
git://projects.qi-hardware.com/openwrt-xburst.git
synced 2024-12-28 20:52:59 +02:00
6576d07d92
Signed-off-by: Florian Fainelli <florian@openwrt.org>
20428 lines
537 KiB
Diff
20428 lines
537 KiB
Diff
--- a/sound/core/pcm_native.c
|
|
+++ b/sound/core/pcm_native.c
|
|
@@ -1742,12 +1742,13 @@ static int snd_pcm_hw_rule_sample_bits(s
|
|
return snd_interval_refine(hw_param_interval(params, rule->var), &t);
|
|
}
|
|
|
|
-#if SNDRV_PCM_RATE_5512 != 1 << 0 || SNDRV_PCM_RATE_192000 != 1 << 12
|
|
+#if SNDRV_PCM_RATE_5512 != 1 << 0 || SNDRV_PCM_RATE_192000 != 1 << 14
|
|
#error "Change this table"
|
|
#endif
|
|
|
|
-static unsigned int rates[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100,
|
|
- 48000, 64000, 88200, 96000, 176400, 192000 };
|
|
+static unsigned int rates[] = { 5512, 8000, 11025, 12000, 16000, 22050, 24000,
|
|
+ 32000, 44100, 48000, 64000, 88200, 96000,
|
|
+ 176400, 192000 };
|
|
|
|
const struct snd_pcm_hw_constraint_list snd_pcm_known_rates = {
|
|
.count = ARRAY_SIZE(rates),
|
|
@@ -1758,6 +1759,7 @@ static int snd_pcm_hw_rule_rate(struct s
|
|
struct snd_pcm_hw_rule *rule)
|
|
{
|
|
struct snd_pcm_hardware *hw = rule->private;
|
|
+ hw->rates = 0x3fe;//12KHz and 24KHz bits are all zero,you need set 1
|
|
return snd_interval_list(hw_param_interval(params, rule->var),
|
|
snd_pcm_known_rates.count,
|
|
snd_pcm_known_rates.list, hw->rates);
|
|
--- a/sound/oss/Kconfig
|
|
+++ b/sound/oss/Kconfig
|
|
@@ -5,6 +5,91 @@
|
|
#
|
|
# Prompt user for primary drivers.
|
|
|
|
+config OSS_OBSOLETE
|
|
+ bool "Obsolete OSS drivers"
|
|
+ depends on SOUND_PRIME
|
|
+ help
|
|
+ This option enables support for obsolete OSS drivers that
|
|
+ are scheduled for removal in the near future.
|
|
+
|
|
+ Please contact Adrian Bunk <bunk@stusta.de> if you had to
|
|
+ say Y here because your hardware is not properly supported
|
|
+ by ALSA.
|
|
+
|
|
+ If unsure, say N.
|
|
+
|
|
+config SOUND_JZ_AC97
|
|
+ bool "Jz On-Chip AC97 driver"
|
|
+ depends on OSS_OBSOLETE
|
|
+ help
|
|
+ Say Y here if you have want to select the on-chip AC97 driver
|
|
+ on Jz4730/Jz4740/Jz5730.
|
|
+
|
|
+config SOUND_JZ_I2S
|
|
+ bool "Jz On-Chip I2S driver"
|
|
+ depends on OSS_OBSOLETE
|
|
+ help
|
|
+ Say Y here if you have want to select the on-chip I2S driver
|
|
+ on Jz4730/Jz4740/Jz5730.
|
|
+
|
|
+config SOUND_JZ_PCM
|
|
+ bool "Jz On-Chip PCM driver"
|
|
+ depends on SOC_JZ4750
|
|
+ help
|
|
+ Say Y here if you have want to select the on-chip PCM driver
|
|
+ on Jz4750.
|
|
+
|
|
+choice
|
|
+ prompt "I2S codec type"
|
|
+ depends on SOUND_JZ_I2S
|
|
+
|
|
+config I2S_AK4642EN
|
|
+ bool "AK4642EN"
|
|
+ depends on SOC_JZ4730
|
|
+ help
|
|
+ Answer Y if you have an external AK4642EN codec.
|
|
+
|
|
+config I2S_ICODEC
|
|
+ bool "Internal On-Chip codec"
|
|
+ depends on SOC_JZ4740
|
|
+ help
|
|
+ Answer Y if you have an internal I2S codec.
|
|
+
|
|
+config I2S_DLV
|
|
+ bool "Internal On-Chip codec on Jz4750 or Jz4750d"
|
|
+ depends on SOC_JZ4750 || SOC_JZ4750D
|
|
+ help
|
|
+ Answer Y if you have an internal I2S codec on Jz4750 or Jz4750d.
|
|
+
|
|
+endchoice
|
|
+
|
|
+choice
|
|
+ prompt "PCM codec type"
|
|
+ depends on SOUND_JZ_PCM
|
|
+
|
|
+config PCM_TLV320AIC1106
|
|
+ bool "TLV320AIC1106"
|
|
+ help
|
|
+ Answer Y if you have an TI tlv320aic 1106 codec.
|
|
+
|
|
+endchoice
|
|
+
|
|
+config SOUND_BT878
|
|
+ tristate "BT878 audio dma"
|
|
+ depends on SOUND_PRIME && PCI && OSS_OBSOLETE
|
|
+ ---help---
|
|
+ Audio DMA support for bt878 based grabber boards. As you might have
|
|
+ already noticed, bt878 is listed with two functions in /proc/pci.
|
|
+ Function 0 does the video stuff (bt848 compatible), function 1 does
|
|
+ the same for audio data. This is a driver for the audio part of
|
|
+ the chip. If you say 'Y' here you get a oss-compatible dsp device
|
|
+ where you can record from. If you want just watch TV you probably
|
|
+ don't need this driver as most TV cards handle sound with a short
|
|
+ cable from the TV card to your sound card's line-in.
|
|
+
|
|
+ To compile this driver as a module, choose M here: the module will
|
|
+ be called btaudio.
|
|
+
|
|
config SOUND_BCM_CS4297A
|
|
tristate "Crystal Sound CS4297a (for Swarm)"
|
|
depends on SIBYTE_SWARM
|
|
@@ -15,6 +100,13 @@ config SOUND_BCM_CS4297A
|
|
note that CONFIG_KGDB should not be enabled at the same
|
|
time, since it also attempts to use this UART port.
|
|
|
|
+config SOUND_ICH
|
|
+ tristate "Intel ICH (i8xx) audio support"
|
|
+ depends on SOUND_PRIME && PCI && OSS_OBSOLETE
|
|
+ help
|
|
+ Support for integral audio in Intel's I/O Controller Hub (ICH)
|
|
+ chipset, as used on the 810/820/840 motherboards.
|
|
+
|
|
config SOUND_VWSND
|
|
tristate "SGI Visual Workstation Sound"
|
|
depends on X86_VISWS
|
|
@@ -24,6 +116,14 @@ config SOUND_VWSND
|
|
<file:Documentation/sound/oss/vwsnd> for more info on this driver's
|
|
capabilities.
|
|
|
|
+config SOUND_VRC5477
|
|
+ tristate "NEC Vrc5477 AC97 sound"
|
|
+ depends on SOUND_PRIME && DDB5477
|
|
+ help
|
|
+ Say Y here to enable sound support for the NEC Vrc5477 chip, an
|
|
+ integrated, multi-function controller chip for MIPS CPUs. Works
|
|
+ with the AC97 codec.
|
|
+
|
|
config SOUND_AU1550_AC97
|
|
tristate "Au1550/Au1200 AC97 Sound"
|
|
depends on SOC_AU1550 || SOC_AU1200
|
|
@@ -253,9 +353,29 @@ config MSND_FIFOSIZE
|
|
and Pinnacle). Larger values reduce the chance of data overruns at
|
|
the expense of overall latency. If unsure, use the default.
|
|
|
|
+config SOUND_VIA82CXXX
|
|
+ tristate "VIA 82C686 Audio Codec"
|
|
+ depends on SOUND_PRIME && PCI && OSS_OBSOLETE
|
|
+ help
|
|
+ Say Y here to include support for the audio codec found on VIA
|
|
+ 82Cxxx-based chips. Typically these are built into a motherboard.
|
|
+
|
|
+ DO NOT select Sound Blaster or Adlib with this driver, unless
|
|
+ you have a Sound Blaster or Adlib card in addition to your VIA
|
|
+ audio chip.
|
|
+
|
|
+config MIDI_VIA82CXXX
|
|
+ bool "VIA 82C686 MIDI"
|
|
+ depends on SOUND_VIA82CXXX && ISA_DMA_API
|
|
+ help
|
|
+ Answer Y to use the MIDI interface of the Via686. You may need to
|
|
+ enable this in the BIOS before it will work. This is for connection
|
|
+ to external MIDI hardware, and is not required for software playback
|
|
+ of MIDI files.
|
|
+
|
|
menuconfig SOUND_OSS
|
|
tristate "OSS sound modules"
|
|
- depends on ISA_DMA_API && VIRT_TO_BUS
|
|
+ depends on SOUND_PRIME && ISA_DMA_API
|
|
help
|
|
OSS is the Open Sound System suite of sound card drivers. They make
|
|
sound programming easier since they provide a common API. Say Y or
|
|
@@ -287,6 +407,20 @@ config SOUND_DMAP
|
|
|
|
Say Y unless you have 16MB or more RAM or a PCI sound card.
|
|
|
|
+config SOUND_CS4232
|
|
+ tristate "Crystal CS4232 based (PnP) cards"
|
|
+ depends on SOUND_OSS && OSS_OBSOLETE
|
|
+ help
|
|
+ Say Y here if you have a card based on the Crystal CS4232 chip set,
|
|
+ which uses its own Plug and Play protocol.
|
|
+
|
|
+ If you compile the driver into the kernel, you have to add
|
|
+ "cs4232=<io>,<irq>,<dma>,<dma2>,<mpuio>,<mpuirq>" to the kernel
|
|
+ command line.
|
|
+
|
|
+ See <file:Documentation/sound/oss/CS4232> for more information on
|
|
+ configuring this card.
|
|
+
|
|
config SOUND_SSCAPE
|
|
tristate "Ensoniq SoundScape support"
|
|
help
|
|
@@ -553,6 +687,13 @@ config SOUND_WAVEARTIST
|
|
Say Y here to include support for the Rockwell WaveArtist sound
|
|
system. This driver is mainly for the NetWinder.
|
|
|
|
+config SOUND_TVMIXER
|
|
+ tristate "TV card (bt848) mixer support"
|
|
+ depends on SOUND_PRIME && I2C && VIDEO_V4L1 && OSS_OBSOLETE
|
|
+ help
|
|
+ Support for audio mixer facilities on the BT848 TV frame-grabber
|
|
+ card.
|
|
+
|
|
config SOUND_KAHLUA
|
|
tristate "XpressAudio Sound Blaster emulation"
|
|
depends on SOUND_SB
|
|
--- a/sound/oss/Makefile
|
|
+++ b/sound/oss/Makefile
|
|
@@ -9,6 +9,12 @@ obj-$(CONFIG_SOUND_OSS) += sound.o
|
|
|
|
# Please leave it as is, cause the link order is significant !
|
|
|
|
+obj-$(CONFIG_SOUND_JZ_AC97) += jz_ac97.o ac97_codec.o
|
|
+obj-$(CONFIG_I2S_AK4642EN) += ak4642en.o
|
|
+obj-$(CONFIG_I2S_ICODEC) += jzcodec.o jz_i2s.o
|
|
+obj-$(CONFIG_I2S_DLV) += jzdlv.o jz_i2s.o
|
|
+obj-$(CONFIG_SOUND_JZ_PCM) += jz_pcm_tlv320aic1106_dma.o
|
|
+
|
|
obj-$(CONFIG_SOUND_SH_DAC_AUDIO) += sh_dac_audio.o
|
|
obj-$(CONFIG_SOUND_AEDSP16) += aedsp16.o
|
|
obj-$(CONFIG_SOUND_PSS) += pss.o ad1848.o mpu401.o
|
|
--- /dev/null
|
|
+++ b/sound/oss/ak4642en.c
|
|
@@ -0,0 +1,712 @@
|
|
+/*
|
|
+ * linux/sound/oss/ak4642en.c
|
|
+ *
|
|
+ * AKM ak4642en codec chip driver to I2S interface
|
|
+ *
|
|
+ * Copyright (c) 2005-2007 Ingenic Semiconductor Inc.
|
|
+ * Author: <cjfeng@ingenic.cn>
|
|
+ *
|
|
+ * 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 <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"
|
|
+
|
|
+extern mixer_info info;
|
|
+extern _old_mixer_info old_info;
|
|
+extern int abnormal_data_count;
|
|
+
|
|
+extern void (*clear_codec_mode)(void);
|
|
+extern void (*set_codec_gpio_pin)(void);
|
|
+extern void (*each_time_init_codec)(void);
|
|
+extern void (*set_codec_record)(void);
|
|
+extern void (*set_codec_replay)(void);
|
|
+extern void (*clear_codec_record)(void);
|
|
+extern void (*clear_codec_replay)(void);
|
|
+extern void (*set_codec_speed)(int range);
|
|
+extern void (*codec_mixer_old_info_id_name)(void);
|
|
+extern void (*codec_mixer_info_id_name)(void);
|
|
+extern void (*set_codec_volume)(int val);
|
|
+extern void (*set_codec_mic)(int val);
|
|
+extern void (*i2s_resume_codec)(void);
|
|
+extern void (*i2s_suspend_codec)(int wr,int rd);
|
|
+extern void (*set_replay_hp_or_speaker)(void);
|
|
+
|
|
+#define I2S_PDN 68
|
|
+#define JACK_PLUG_PIN 83
|
|
+#define JACK_PLUG_IRQ (IRQ_GPIO_0 + JACK_PLUG_PIN)
|
|
+
|
|
+static int jack_plug_level, old_level;
|
|
+static unsigned int i2c_addr = 0x26; //AK4642EN device address at I2C bus
|
|
+static unsigned int i2c_clk = 100000;//AK4642EN 400kHz max,but 100kHz here
|
|
+static unsigned int spk_hp = 0;
|
|
+static int codec_volume;
|
|
+
|
|
+void set_ak4642en_gpio_pin(void);
|
|
+void each_time_init_ak4642en(void);
|
|
+void set_ak4642en_replay(void);
|
|
+void set_ak4642en_record(void);
|
|
+void turn_on_ak4642en(void);
|
|
+void turn_off_ak4642en(void);
|
|
+void set_ak4642en_speed(int rate);
|
|
+void reset_ak4642en(void);
|
|
+void ak4642en_mixer_old_info_id_name(void);
|
|
+void ak4642en_mixer_info_id_name(void);
|
|
+void set_ak4642en_bass(int val);
|
|
+void set_ak4642en_volume(int val);
|
|
+void set_ak4642en_mic(int val);
|
|
+void resume_ak4642en(void);
|
|
+void suspend_ak4642en(int wr,int rd);
|
|
+
|
|
+static void write_reg(u8 reg, u8 val)
|
|
+{
|
|
+ i2c_open();
|
|
+ i2c_setclk(i2c_clk);
|
|
+ i2c_write((i2c_addr >> 1), &val, reg, 1);
|
|
+ i2c_close();
|
|
+}
|
|
+
|
|
+#if 0
|
|
+static u8 read_reg(u8 reg)
|
|
+{
|
|
+ u8 val;
|
|
+ i2c_open();
|
|
+ i2c_setclk(i2c_clk);
|
|
+ i2c_read((i2c_addr >> 1), &val, reg, 1);
|
|
+ i2c_close();
|
|
+ return val;
|
|
+}
|
|
+
|
|
+static u16 i2s_codec_read(u8 reg)
|
|
+{
|
|
+ u16 value;
|
|
+ value = read_reg(reg);
|
|
+ return value;
|
|
+}
|
|
+#endif
|
|
+
|
|
+static void i2s_codec_write(u8 reg, u16 data)
|
|
+{
|
|
+ u8 val = data & 0xff;
|
|
+ write_reg(reg, val);
|
|
+}
|
|
+
|
|
+void set_ak4642en_gpio_pin(void)
|
|
+{
|
|
+ //set AIC pin to I2S slave mode,only GPIO70,71,77,78
|
|
+ __gpio_as_output(68);
|
|
+ __gpio_clear_pin(68);
|
|
+ __gpio_as_output(69);
|
|
+ __gpio_clear_pin(69);
|
|
+ __gpio_as_output(70);
|
|
+ __gpio_clear_pin(70);
|
|
+ __gpio_as_input(71);
|
|
+ __gpio_clear_pin(71);
|
|
+ __gpio_as_input(77);
|
|
+ __gpio_clear_pin(77);
|
|
+ __gpio_as_input(78);
|
|
+ __gpio_clear_pin(78);
|
|
+ REG_GPIO_GPALR(2) &= 0xC3FF0CFF;
|
|
+ REG_GPIO_GPALR(2) |= 0x14005000;
|
|
+ //set SCC clock initialization
|
|
+ REG_SCC1_CR(SCC1_BASE) = 0x00000000;
|
|
+ udelay(2);
|
|
+ REG_SCC1_CR(SCC1_BASE) |= 1 << 31;
|
|
+ udelay(2);
|
|
+
|
|
+ __gpio_as_output(I2S_PDN);
|
|
+ __gpio_set_pin(I2S_PDN);
|
|
+ udelay(5);
|
|
+ __gpio_clear_pin(I2S_PDN);
|
|
+ ndelay(300);//>150ns
|
|
+ __gpio_set_pin(I2S_PDN);
|
|
+ mdelay(1);
|
|
+ //set PLL Master mode
|
|
+ i2s_codec_write(0x01, 0x0008);//master
|
|
+ i2s_codec_write(0x04, 0x006b);//ref:12MHz;BITCLK:64fs;I2S compli
|
|
+ i2s_codec_write(0x05, 0x000b);//sync:48KHz;
|
|
+ i2s_codec_write(0x00, 0x0040);//PMVCM
|
|
+ i2s_codec_write(0x01, 0x0009);//master,PLL enable
|
|
+ mdelay(40);
|
|
+ jack_plug_level = 10;
|
|
+ old_level = 100;
|
|
+ spk_hp = 0;
|
|
+ __gpio_disable_pull(JACK_PLUG_PIN);
|
|
+ udelay(10);
|
|
+ __gpio_as_input(JACK_PLUG_PIN);
|
|
+ jack_plug_level = __gpio_get_pin(JACK_PLUG_PIN);
|
|
+ //i suppose jack_plug_lvel is 1 indicate with HPO
|
|
+ if (jack_plug_level > 1 || jack_plug_level <0)
|
|
+ printk("Audio ak4642en codec Jack plug level is wrong!\n");
|
|
+ if (jack_plug_level)
|
|
+ __gpio_as_irq_fall_edge(JACK_PLUG_PIN);
|
|
+ else
|
|
+ __gpio_as_irq_rise_edge(JACK_PLUG_PIN);
|
|
+}
|
|
+
|
|
+void clear_ak4642en_mode(void)
|
|
+{
|
|
+ spk_hp = 0;
|
|
+ i2s_codec_write(0x01, 0x0008);//master,PLL disable
|
|
+ //free_irq(JACK_PLUG_IRQ, i2s_controller);
|
|
+ __gpio_clear_pin(I2S_PDN);
|
|
+ udelay(2);
|
|
+ REG_SCC1_CR(SCC1_BASE) &= 0 << 31;
|
|
+ udelay(2);
|
|
+}
|
|
+
|
|
+void set_ak4642en_replay(void)
|
|
+{
|
|
+ //for poll
|
|
+ /*jack_plug_level is H for SPK,is L for HP*/
|
|
+ jack_plug_level = __gpio_get_pin(JACK_PLUG_PIN);
|
|
+ if(old_level == jack_plug_level)
|
|
+ return;
|
|
+ old_level = jack_plug_level;
|
|
+ if(spk_hp == 1)
|
|
+ {
|
|
+ if(jack_plug_level == 1)
|
|
+ {
|
|
+ //now HeadPhone output,so clear SPK
|
|
+ i2s_codec_write(0x02, 0x0020);
|
|
+ i2s_codec_write(0x02, 0x0000);
|
|
+ i2s_codec_write(0x00, 0x0040);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ //now Speaker output,so clear HP
|
|
+ i2s_codec_write(0x01, 0x0039);
|
|
+ i2s_codec_write(0x01, 0x0009);
|
|
+ i2s_codec_write(0x00, 0x0040);
|
|
+ i2s_codec_write(0x0e, 0x0000);
|
|
+ i2s_codec_write(0x0f, 0x0008);
|
|
+ }
|
|
+ }
|
|
+ spk_hp = 1;
|
|
+ if(jack_plug_level == 1)
|
|
+ {
|
|
+ //for HeadPhone output
|
|
+ i2s_codec_write(0x00, 0x0060); //
|
|
+ i2s_codec_write(0x0f, 0x0009); //5-10
|
|
+
|
|
+ i2s_codec_write(0x00, 0x0064); //
|
|
+ i2s_codec_write(0x09, 0x0091);// volume control 0dB
|
|
+ i2s_codec_write(0x0c, 0x0091);// 0dB(right)
|
|
+ //eq off
|
|
+ i2s_codec_write(0x11, 0x0000);//5-10
|
|
+ i2s_codec_write(0x01, 0x0039); //
|
|
+
|
|
+ i2s_codec_write(0x01, 0x0079); //
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ //for Speaker output
|
|
+ i2s_codec_write(0x00, 0x0040);
|
|
+ i2s_codec_write(0x02, 0x0020);
|
|
+
|
|
+ i2s_codec_write(0x03, 0x0018);//5-10
|
|
+ i2s_codec_write(0x06, 0x003c);
|
|
+
|
|
+ i2s_codec_write(0x08, 0x00A1);//5-10
|
|
+
|
|
+ i2s_codec_write(0x0b, 0x0040); //5-10
|
|
+
|
|
+ i2s_codec_write(0x07, 0x002d); //5-10
|
|
+ i2s_codec_write(0x09, 0x0091);
|
|
+ i2s_codec_write(0x0c, 0x0091);
|
|
+ //HP volume output value
|
|
+
|
|
+ i2s_codec_write(0x0a, codec_volume);//5-10
|
|
+ i2s_codec_write(0x0d, codec_volume);//5-10
|
|
+
|
|
+ i2s_codec_write(0x00, 0x0074);
|
|
+ i2s_codec_write(0x02, 0x00a0);
|
|
+ }
|
|
+}
|
|
+
|
|
+void set_ak4642en_record(void)
|
|
+{
|
|
+ abnormal_data_count = 0;
|
|
+ i2s_codec_write(0x02, 0x0004);
|
|
+ i2s_codec_write(0x03, 0x0038);// recording volume add
|
|
+ i2s_codec_write(0x06, 0x0000);//for ALC short waiting time
|
|
+ i2s_codec_write(0x08, 0x00e1);
|
|
+ i2s_codec_write(0x0b, 0x0000);
|
|
+ i2s_codec_write(0x07, 0x0021); // ALC on
|
|
+
|
|
+ i2s_codec_write(0x10, 0x0000);//0x0001
|
|
+ //i2s_codec_write(0x10, 0x0001);//0x0001
|
|
+ i2s_codec_write(0x01, 0x0039); //for open pop noise
|
|
+ i2s_codec_write(0x01, 0x0079);
|
|
+ i2s_codec_write(0x00, 0x0065);
|
|
+ mdelay(300);
|
|
+}
|
|
+
|
|
+void clear_ak4642en_replay(void)
|
|
+{
|
|
+ //for poll
|
|
+ old_level = 100;
|
|
+ spk_hp = 0;
|
|
+ if(jack_plug_level == 1)
|
|
+ {
|
|
+ //for HeadPhone output
|
|
+ i2s_codec_write(0x01, 0x0039); // for close pop noise
|
|
+ mdelay(300);
|
|
+ i2s_codec_write(0x01, 0x0009); //PLL on I2S
|
|
+ i2s_codec_write(0x07, 0x0001);
|
|
+ i2s_codec_write(0x11, 0x0000);
|
|
+ i2s_codec_write(0x00, 0x0040);
|
|
+ i2s_codec_write(0x0f, 0x0008); // for open pop noise
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ //for Speaker output
|
|
+ i2s_codec_write(0x02, 0x0020);
|
|
+ i2s_codec_write(0x07, 0x0001);
|
|
+ i2s_codec_write(0x11, 0x0000);
|
|
+ i2s_codec_write(0x02, 0x0000);
|
|
+ i2s_codec_write(0x00, 0x0040); // for close pop noise
|
|
+ }
|
|
+}
|
|
+
|
|
+void clear_ak4642en_record(void)
|
|
+{
|
|
+ //for Mic input(Stereo)
|
|
+ i2s_codec_write(0x02, 0x0001);
|
|
+ i2s_codec_write(0x07, 0x0001);
|
|
+ i2s_codec_write(0x11, 0x0000);
|
|
+}
|
|
+
|
|
+void each_time_init_ak4642en(void)
|
|
+{
|
|
+ __i2s_disable();
|
|
+ __i2s_as_slave();
|
|
+ __i2s_set_sample_size(16);
|
|
+}
|
|
+
|
|
+void set_ak4642en_speed(int rate)
|
|
+{
|
|
+ //codec work at frequency
|
|
+ unsigned short speed = 0;
|
|
+ unsigned short val = 0;
|
|
+ switch (rate)
|
|
+ {
|
|
+ case 8000:
|
|
+ speed = 0x00;
|
|
+ if(jack_plug_level == 1) //speaker
|
|
+ {
|
|
+ i2s_codec_write(0x16, 0x0000);
|
|
+ i2s_codec_write(0x17, 0x0000);
|
|
+ i2s_codec_write(0x18, 0x0000);
|
|
+ i2s_codec_write(0x19, 0x0000);
|
|
+ i2s_codec_write(0x1A, 0x0000);
|
|
+ i2s_codec_write(0x1B, 0x0000);
|
|
+ i2s_codec_write(0x1C, 0x0027);//800hz
|
|
+ i2s_codec_write(0x1D, 0x0018);
|
|
+ i2s_codec_write(0x1E, 0x00b2);
|
|
+ i2s_codec_write(0x1F, 0x002f);
|
|
+ i2s_codec_write(0x11, 0x0010); //eq on
|
|
+ }
|
|
+ break;
|
|
+ case 12000:
|
|
+ speed = 0x01;
|
|
+ if(jack_plug_level == 1)
|
|
+ {
|
|
+ i2s_codec_write(0x16, 0x0000);
|
|
+ i2s_codec_write(0x17, 0x0000);
|
|
+ i2s_codec_write(0x18, 0x0000);
|
|
+ i2s_codec_write(0x19, 0x0000);
|
|
+ i2s_codec_write(0x1A, 0x0000);
|
|
+ i2s_codec_write(0x1B, 0x0000);
|
|
+ i2s_codec_write(0x1C, 0x0064);
|
|
+ i2s_codec_write(0x1D, 0x001a);
|
|
+ i2s_codec_write(0x1E, 0x0038);
|
|
+ i2s_codec_write(0x1F, 0x002b);
|
|
+ i2s_codec_write(0x11, 0x0010); //eq on
|
|
+ }
|
|
+ break;
|
|
+ case 16000:
|
|
+ speed = 0x02;
|
|
+ if(jack_plug_level == 1)
|
|
+ {
|
|
+ i2s_codec_write(0x16, 0x00af);
|
|
+ i2s_codec_write(0x17, 0x0020);
|
|
+ i2s_codec_write(0x18, 0x0043);
|
|
+ i2s_codec_write(0x19, 0x001a);
|
|
+ i2s_codec_write(0x1A, 0x00af);
|
|
+ i2s_codec_write(0x1B, 0x0020);
|
|
+ i2s_codec_write(0x1C, 0x00a0);
|
|
+ i2s_codec_write(0x1D, 0x001b);
|
|
+ i2s_codec_write(0x1E, 0x00c0);
|
|
+ i2s_codec_write(0x1F, 0x0028);
|
|
+ i2s_codec_write(0x11, 0x0018); //eq on
|
|
+ }
|
|
+ break;
|
|
+ case 24000:
|
|
+ speed = 0x03;
|
|
+ if(jack_plug_level == 1)
|
|
+ {
|
|
+ i2s_codec_write(0x16, 0x0086);
|
|
+ i2s_codec_write(0x17, 0x0015);
|
|
+ i2s_codec_write(0x18, 0x005d);
|
|
+ i2s_codec_write(0x19, 0x0006);
|
|
+ i2s_codec_write(0x1A, 0x0086);
|
|
+ i2s_codec_write(0x1B, 0x0015);
|
|
+ i2s_codec_write(0x1C, 0x00f5);
|
|
+ i2s_codec_write(0x1D, 0x001c);
|
|
+ i2s_codec_write(0x1E, 0x0016);
|
|
+ i2s_codec_write(0x1F, 0x0026);
|
|
+ i2s_codec_write(0x11, 0x0018); //eq on
|
|
+ }
|
|
+ break;
|
|
+ case 7350:
|
|
+ speed = 0x04;
|
|
+ if(jack_plug_level == 1)
|
|
+ {
|
|
+ i2s_codec_write(0x16, 0x0000);
|
|
+ i2s_codec_write(0x17, 0x0000);
|
|
+ i2s_codec_write(0x18, 0x0000);
|
|
+ i2s_codec_write(0x19, 0x0000);
|
|
+ i2s_codec_write(0x1A, 0x0000);
|
|
+ i2s_codec_write(0x1B, 0x0000);
|
|
+ i2s_codec_write(0x1C, 0x0027);
|
|
+ i2s_codec_write(0x1D, 0x0018);
|
|
+ i2s_codec_write(0x1E, 0x00b2);
|
|
+ i2s_codec_write(0x1F, 0x002f);
|
|
+ i2s_codec_write(0x11, 0x0010); //eq on
|
|
+ }
|
|
+ break;
|
|
+ case 11025:
|
|
+ speed = 0x05;
|
|
+ if(jack_plug_level == 1)
|
|
+ {
|
|
+ i2s_codec_write(0x16, 0x0059);
|
|
+ i2s_codec_write(0x17, 0x000d);
|
|
+ i2s_codec_write(0x18, 0x00cb);
|
|
+ i2s_codec_write(0x19, 0x0037);
|
|
+ i2s_codec_write(0x1A, 0x0059);
|
|
+ i2s_codec_write(0x1B, 0x000d);
|
|
+ i2s_codec_write(0x1C, 0x0046);
|
|
+ i2s_codec_write(0x1D, 0x001e);
|
|
+ i2s_codec_write(0x1E, 0x0074);
|
|
+ i2s_codec_write(0x1F, 0x0023);
|
|
+ i2s_codec_write(0x11, 0x0018); //eq on
|
|
+ }
|
|
+ break;
|
|
+ case 14700:
|
|
+ speed = 0x06;
|
|
+ if(jack_plug_level == 1)
|
|
+ {
|
|
+ i2s_codec_write(0x16, 0x0000);
|
|
+ i2s_codec_write(0x17, 0x0000);
|
|
+ i2s_codec_write(0x18, 0x0000);
|
|
+ i2s_codec_write(0x19, 0x0000);
|
|
+ i2s_codec_write(0x1A, 0x0000);
|
|
+ i2s_codec_write(0x1B, 0x0000);
|
|
+ i2s_codec_write(0x1C, 0x004a);
|
|
+ i2s_codec_write(0x1D, 0x001b);
|
|
+ i2s_codec_write(0x1E, 0x006c);
|
|
+ i2s_codec_write(0x1F, 0x0029);
|
|
+ i2s_codec_write(0x11, 0x0010); //eq on
|
|
+ }
|
|
+ break;
|
|
+ case 22050:
|
|
+ speed = 0x07;
|
|
+ if(jack_plug_level == 1)
|
|
+ {
|
|
+ i2s_codec_write(0x16, 0x002d);
|
|
+ i2s_codec_write(0x17, 0x0017);
|
|
+ i2s_codec_write(0x18, 0x0050);
|
|
+ i2s_codec_write(0x19, 0x0009);
|
|
+ i2s_codec_write(0x1A, 0x002d);
|
|
+ i2s_codec_write(0x1B, 0x0017);
|
|
+ i2s_codec_write(0x1C, 0x00d7);
|
|
+ i2s_codec_write(0x1D, 0x001c);
|
|
+ i2s_codec_write(0x1E, 0x0093);
|
|
+ i2s_codec_write(0x1F, 0x0026);
|
|
+ i2s_codec_write(0x11, 0x0018); //eq on
|
|
+ }
|
|
+ break;
|
|
+ case 32000:
|
|
+ speed = 0x0a;
|
|
+ if(jack_plug_level == 1)
|
|
+ {
|
|
+ i2s_codec_write(0x16, 0x0012);
|
|
+ i2s_codec_write(0x17, 0x0011);
|
|
+ i2s_codec_write(0x18, 0x006e);
|
|
+ i2s_codec_write(0x19, 0x003e);
|
|
+ i2s_codec_write(0x1A, 0x0012);
|
|
+ i2s_codec_write(0x1B, 0x0011);
|
|
+ i2s_codec_write(0x1C, 0x00aa);
|
|
+ i2s_codec_write(0x1D, 0x001d);
|
|
+ i2s_codec_write(0x1E, 0x00ab);
|
|
+ i2s_codec_write(0x1F, 0x0024);
|
|
+ i2s_codec_write(0x11, 0x0018); //eq on
|
|
+ }
|
|
+ break;
|
|
+ case 48000:
|
|
+ speed = 0x0b;
|
|
+ if(jack_plug_level == 1)
|
|
+ {
|
|
+ i2s_codec_write(0x16, 0x0082);
|
|
+ i2s_codec_write(0x17, 0x000c);
|
|
+ i2s_codec_write(0x18, 0x004b);
|
|
+ i2s_codec_write(0x19, 0x0036);
|
|
+ i2s_codec_write(0x1A, 0x0082);
|
|
+ i2s_codec_write(0x1B, 0x000c);
|
|
+ i2s_codec_write(0x1C, 0x0068);
|
|
+ i2s_codec_write(0x1D, 0x001e);
|
|
+ i2s_codec_write(0x1E, 0x0030);
|
|
+ i2s_codec_write(0x1F, 0x0023);
|
|
+ i2s_codec_write(0x11, 0x0018); //eq on
|
|
+ }
|
|
+ break;
|
|
+ case 29400:
|
|
+ speed = 0x0e;
|
|
+ if(jack_plug_level == 1)
|
|
+ {
|
|
+ i2s_codec_write(0x16, 0x003d);
|
|
+ i2s_codec_write(0x17, 0x0012);
|
|
+ i2s_codec_write(0x18, 0x0083);
|
|
+ i2s_codec_write(0x19, 0x0000);
|
|
+ i2s_codec_write(0x1A, 0x003d);
|
|
+ i2s_codec_write(0x1B, 0x0012);
|
|
+ i2s_codec_write(0x1C, 0x0079);
|
|
+ i2s_codec_write(0x1D, 0x001d);
|
|
+ i2s_codec_write(0x1E, 0x000d);
|
|
+ i2s_codec_write(0x1F, 0x0025);
|
|
+ i2s_codec_write(0x11, 0x0018); //eq on
|
|
+ }
|
|
+ break;
|
|
+ case 44100:
|
|
+ speed = 0x0f;
|
|
+ if(jack_plug_level == 1)
|
|
+ {
|
|
+ i2s_codec_write(0x16, 0x0059);
|
|
+ i2s_codec_write(0x17, 0x000d);
|
|
+ i2s_codec_write(0x18, 0x00cb);
|
|
+ i2s_codec_write(0x19, 0x0037);
|
|
+ i2s_codec_write(0x1A, 0x0059);
|
|
+ i2s_codec_write(0x1B, 0x000d);
|
|
+ i2s_codec_write(0x1C, 0x0046);
|
|
+ i2s_codec_write(0x1D, 0x001e);
|
|
+ i2s_codec_write(0x1E, 0x0074);
|
|
+ i2s_codec_write(0x1F, 0x0023);
|
|
+ i2s_codec_write(0x11, 0x0018); //eq on
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ val = speed & 0x08;
|
|
+ val = val << 2;
|
|
+ speed = speed & 0x07;
|
|
+ val = val | speed;
|
|
+ i2s_codec_write(0x05, val);
|
|
+}
|
|
+
|
|
+void ak4642en_mixer_old_info_id_name(void)
|
|
+{
|
|
+ strncpy(info.id, "AK4642EN", sizeof(info.id));
|
|
+ strncpy(info.name,"AKM AK4642en codec", sizeof(info.name));
|
|
+}
|
|
+
|
|
+void ak4642en_mixer_info_id_name(void)
|
|
+{
|
|
+ strncpy(old_info.id, "AK4642EN", sizeof(old_info.id));
|
|
+ strncpy(old_info.name,"AKM AK4642en codec", sizeof(old_info.name));
|
|
+}
|
|
+
|
|
+void set_ak4642en_volume(int val)
|
|
+{
|
|
+ if ( val == 0 )
|
|
+ codec_volume = 255;
|
|
+ else if ( val > 1 && val <= 10)
|
|
+ codec_volume = 92;
|
|
+ else if ( val > 10 && val <= 20 )
|
|
+ codec_volume = 67;
|
|
+ else if ( val > 20 && val <= 30 )
|
|
+ codec_volume = 50;
|
|
+ else if ( val > 30 && val <= 40 )
|
|
+ codec_volume = 40;
|
|
+ else if ( val > 40 && val <= 50 )
|
|
+ codec_volume = 30;
|
|
+ else if ( val > 50 && val <= 60 )
|
|
+ codec_volume = 22;
|
|
+ else if ( val > 60&& val <= 70 )
|
|
+ codec_volume = 15;
|
|
+ else if ( val > 70 && val <= 80 )
|
|
+ codec_volume = 8;
|
|
+ else if ( val > 80 && val <= 90 )
|
|
+ codec_volume = 4;
|
|
+ else if ( val > 90 && val <= 100 )
|
|
+ codec_volume = 2;
|
|
+
|
|
+ i2s_codec_write(0x0a, codec_volume);
|
|
+ i2s_codec_write(0x0d, codec_volume);
|
|
+}
|
|
+
|
|
+void set_ak4642en_mic(int val)
|
|
+{
|
|
+ int mic_gain;
|
|
+ mic_gain = 241 * val /100;
|
|
+ i2s_codec_write(0x09, mic_gain);
|
|
+ i2s_codec_write(0x0c, mic_gain);
|
|
+}
|
|
+
|
|
+void resume_ak4642en(void)
|
|
+{
|
|
+ __gpio_as_output(17);
|
|
+ __gpio_set_pin(17); //enable ak4642
|
|
+ __gpio_as_output(68);
|
|
+ __gpio_clear_pin(68);
|
|
+ __gpio_as_output(69);
|
|
+ __gpio_clear_pin(69);
|
|
+ __gpio_as_output(70);
|
|
+ __gpio_clear_pin(70);
|
|
+ __gpio_as_input(71);
|
|
+ __gpio_clear_pin(71);
|
|
+ __gpio_as_input(77);
|
|
+ __gpio_clear_pin(77);
|
|
+ __gpio_as_input(78);
|
|
+ __gpio_clear_pin(78);
|
|
+ REG_GPIO_GPALR(2) &= 0xC3FF0CFF;
|
|
+ REG_GPIO_GPALR(2) |= 0x14005000;
|
|
+ //set SCC clock initialization
|
|
+ REG_SCC1_CR(SCC1_BASE) = 0x00000000;
|
|
+ udelay(2);
|
|
+ REG_SCC1_CR(SCC1_BASE) |= 1 << 31;
|
|
+ udelay(2);
|
|
+ __gpio_as_output(I2S_PDN);
|
|
+ __gpio_set_pin(I2S_PDN);
|
|
+ udelay(5);
|
|
+ __gpio_clear_pin(I2S_PDN);
|
|
+ ndelay(300);//>150ns
|
|
+ __gpio_set_pin(I2S_PDN);
|
|
+ mdelay(1);
|
|
+ //set PLL Master mode
|
|
+ i2s_codec_write(0x01, 0x0008);//master
|
|
+ i2s_codec_write(0x04, 0x006b);//ref:12MHz;BITCLK:64fs;I2S compli
|
|
+ i2s_codec_write(0x05, 0x000b);//sync:48KHz;
|
|
+ i2s_codec_write(0x00, 0x0040);//PMVCM
|
|
+ i2s_codec_write(0x01, 0x0009);//master,PLL enable
|
|
+ jack_plug_level = 10;
|
|
+ old_level = 100;
|
|
+ spk_hp = 0;
|
|
+ __gpio_as_input(JACK_PLUG_PIN);
|
|
+ jack_plug_level = __gpio_get_pin(JACK_PLUG_PIN);
|
|
+ //i suppose jack_plug_lvel is 1 indicate with HPO
|
|
+ if(jack_plug_level > 1 || jack_plug_level <0)
|
|
+ printk("Audio ak4642en codec Jack plug level is wrong!\n");
|
|
+ if(jack_plug_level)
|
|
+ __gpio_as_irq_fall_edge(JACK_PLUG_PIN);
|
|
+ else
|
|
+ __gpio_as_irq_rise_edge(JACK_PLUG_PIN);
|
|
+
|
|
+ i2s_codec_write(0x00, 0x0065); //for resume power
|
|
+ i2s_codec_write(0x01, 0x0039); //for open pop noise
|
|
+ i2s_codec_write(0x01, 0x0079);
|
|
+ i2s_codec_write(0x0a, codec_volume);
|
|
+ i2s_codec_write(0x0d, codec_volume);
|
|
+}
|
|
+
|
|
+void suspend_ak4642en(int wr,int rd)
|
|
+{
|
|
+ if(wr) //playing
|
|
+ {
|
|
+ if(jack_plug_level == 0)
|
|
+ {
|
|
+ i2s_codec_write(0x01, 0x0039); // for close pop noise
|
|
+ mdelay(500);
|
|
+ i2s_codec_write(0x01, 0x0009); //PLL on I2S
|
|
+ i2s_codec_write(0x07, 0x0001);
|
|
+ i2s_codec_write(0x11, 0x0000);
|
|
+ i2s_codec_write(0x00, 0x0040);
|
|
+ i2s_codec_write(0x0f, 0x0008); // for open pop noise
|
|
+
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ //for Speaker output
|
|
+ i2s_codec_write(0x02, 0x0020);
|
|
+ i2s_codec_write(0x07, 0x0001);
|
|
+ i2s_codec_write(0x11, 0x0000);
|
|
+ i2s_codec_write(0x02, 0x0000);
|
|
+ i2s_codec_write(0x00, 0x0040); // for close pop noise
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if(rd) // recording
|
|
+ {
|
|
+ i2s_codec_write(0x02, 0x0001); // 5-11 a1
|
|
+ i2s_codec_write(0x07, 0x0001);
|
|
+ i2s_codec_write(0x11, 0x0000);
|
|
+ mdelay(300);
|
|
+ }
|
|
+ __gpio_as_output(17);
|
|
+ __gpio_clear_pin(17);//disable ak4642
|
|
+ __i2s_disable();
|
|
+}
|
|
+
|
|
+static int __init init_ak4642en(void)
|
|
+{
|
|
+ set_codec_gpio_pin = set_ak4642en_gpio_pin;
|
|
+ each_time_init_codec = each_time_init_ak4642en;
|
|
+ clear_codec_mode = clear_ak4642en_mode;
|
|
+
|
|
+ set_codec_record = set_ak4642en_record;
|
|
+ set_codec_replay = set_ak4642en_replay;
|
|
+ set_replay_hp_or_speaker = set_ak4642en_replay;
|
|
+
|
|
+ set_codec_speed = set_ak4642en_speed;
|
|
+ clear_codec_record = clear_ak4642en_record;
|
|
+ clear_codec_replay = clear_ak4642en_replay;
|
|
+
|
|
+ codec_mixer_old_info_id_name = ak4642en_mixer_old_info_id_name;
|
|
+ codec_mixer_info_id_name = ak4642en_mixer_info_id_name;
|
|
+
|
|
+ set_codec_volume = set_ak4642en_volume;
|
|
+
|
|
+ set_codec_mic = set_ak4642en_mic;
|
|
+
|
|
+ i2s_resume_codec = resume_ak4642en;
|
|
+ i2s_suspend_codec = suspend_ak4642en;
|
|
+ printk("---> ak4642en initialization!\n");
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void __exit cleanup_ak4642en(void)
|
|
+{
|
|
+ spk_hp = 0;
|
|
+ i2s_codec_write(0x01, 0x0008);//master,PLL disable
|
|
+ //free_irq(JACK_PLUG_IRQ, i2s_controller);
|
|
+ __gpio_clear_pin(I2S_PDN);
|
|
+ udelay(2);
|
|
+ REG_SCC1_CR(SCC1_BASE) &= 0 << 31;
|
|
+ udelay(2);
|
|
+}
|
|
+
|
|
+module_init(init_ak4642en);
|
|
+module_exit(cleanup_ak4642en);
|
|
--- /dev/null
|
|
+++ b/sound/oss/jz_ac97.c
|
|
@@ -0,0 +1,2252 @@
|
|
+/*
|
|
+ * linux/drivers/sound/jz_ac97.c
|
|
+ *
|
|
+ * Jz On-Chip AC97 audio driver.
|
|
+ *
|
|
+ * Copyright (C) 2005 - 2007, Ingenic Semiconductor Inc.
|
|
+ *
|
|
+ * 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.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#define __NO_VERSION__
|
|
+
|
|
+#include <linux/init.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/pm.h>
|
|
+#include <linux/sched.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/interrupt.h>
|
|
+#include <linux/sound.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/proc_fs.h>
|
|
+#include <linux/soundcard.h>
|
|
+#include <linux/ac97_codec.h>
|
|
+#include <asm/hardirq.h>
|
|
+#include <asm/jzsoc.h>
|
|
+//#include <asm/mach-jz4730/dma.h>
|
|
+#include "sound_config.h"
|
|
+
|
|
+#define DMA_ID_AC97_TX DMA_ID_AIC_TX
|
|
+#define DMA_ID_AC97_RX DMA_ID_AIC_RX
|
|
+
|
|
+/* maxinum number of AC97 codecs connected, AC97 2.0 defined 4 */
|
|
+#define NR_AC97 2
|
|
+
|
|
+#define STANDARD_SPEED 48000
|
|
+#define MAX_RETRY 100
|
|
+
|
|
+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,
|
|
+};
|
|
+
|
|
+static int jz_audio_rate;
|
|
+static char jz_audio_format;
|
|
+static char jz_audio_channels;
|
|
+static int jz_audio_k; /* rate expand multiple */
|
|
+static int jz_audio_q; /* rate expand compensate */
|
|
+static int jz_audio_count; /* total count of voice data */
|
|
+static int last_jz_audio_count;
|
|
+
|
|
+static int jz_audio_fragments;//unused fragment amount
|
|
+static int jz_audio_fragstotal;
|
|
+static int jz_audio_fragsize;
|
|
+static int jz_audio_dma_tran_count;//bytes count of one DMA transfer
|
|
+
|
|
+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 (*old_mksound)(unsigned int hz, unsigned int ticks);
|
|
+extern void (*kd_mksound)(unsigned int hz, unsigned int ticks);
|
|
+extern void jz_set_dma_block_size(int dmanr, int nbyte);
|
|
+extern void jz_set_dma_dest_width(int dmanr, int nbit);
|
|
+extern void jz_set_dma_src_width(int dmanr, int nbit);
|
|
+
|
|
+static void jz_update_filler(int bits, int channels);
|
|
+
|
|
+static void Init_In_Out_queue(int fragstotal,int fragsize);
|
|
+static void Free_In_Out_queue(int fragstotal,int fragsize);
|
|
+
|
|
+static irqreturn_t
|
|
+jz_ac97_replay_dma_irq(int irqnr, void *dev_id);
|
|
+static irqreturn_t
|
|
+jz_ac97_record_dma_irq(int irqnr, void *dev_id);
|
|
+
|
|
+static void
|
|
+(*replay_filler)(unsigned long src_start, int count, int id);
|
|
+static int
|
|
+(*record_filler)(unsigned long dst_start, int count, int id);
|
|
+
|
|
+static struct file_operations jz_ac97_audio_fops;
|
|
+
|
|
+DECLARE_WAIT_QUEUE_HEAD (rx_wait_queue);
|
|
+DECLARE_WAIT_QUEUE_HEAD (tx_wait_queue);
|
|
+
|
|
+struct jz_ac97_controller_info
|
|
+{
|
|
+ int io_base;
|
|
+ int dma1; /* play */
|
|
+ int dma2; /* record */
|
|
+
|
|
+ char *name;
|
|
+
|
|
+ int dev_audio;
|
|
+ struct ac97_codec *ac97_codec[NR_AC97];
|
|
+
|
|
+ unsigned short ac97_features;
|
|
+
|
|
+ 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
|
|
+
|
|
+ /* We use two devices, because we can do simultaneous play and record.
|
|
+ This keeps track of which device is being used for what purpose;
|
|
+ these are the actual device numbers. */
|
|
+ int dev_for_play;
|
|
+ int dev_for_record;
|
|
+
|
|
+ int playing;
|
|
+ int recording;
|
|
+ int patched;
|
|
+ unsigned long rec_buf_size;
|
|
+ unsigned long playback_buf_size;
|
|
+
|
|
+#ifdef CONFIG_PM
|
|
+ struct pm_dev *pm;
|
|
+#endif
|
|
+};
|
|
+
|
|
+static struct jz_ac97_controller_info *ac97_controller = NULL;
|
|
+
|
|
+static int jz_readAC97Reg(struct ac97_codec *dev, u8 reg);
|
|
+static int jz_writeAC97Reg(struct ac97_codec *dev, u8 reg, u16 data);
|
|
+static u16 ac97_codec_read(struct ac97_codec *codec, u8 reg);
|
|
+static void ac97_codec_write(struct ac97_codec *codec, u8 reg, u16 data);
|
|
+
|
|
+#define QUEUE_MAX 2
|
|
+
|
|
+typedef struct buffer_queue_s {
|
|
+ int count;
|
|
+ int *id;
|
|
+ spinlock_t lock;
|
|
+} buffer_queue_t;
|
|
+
|
|
+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 inline int get_buffer_id(struct buffer_queue_s *q)
|
|
+{
|
|
+ int r, i;
|
|
+ unsigned long flags;
|
|
+ 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;
|
|
+}
|
|
+
|
|
+/****************************************************************************
|
|
+ * Architecture related routines
|
|
+ ****************************************************************************/
|
|
+static inline
|
|
+void audio_start_dma(int chan, void *dev_id, unsigned long phyaddr,int count, int mode)
|
|
+{
|
|
+ unsigned long flags;
|
|
+ struct jz_ac97_controller_info * controller =
|
|
+ (struct jz_ac97_controller_info *) dev_id;
|
|
+ //for DSP_GETOPTR
|
|
+ spin_lock_irqsave(&controller->ioctllock, flags);
|
|
+ jz_audio_dma_tran_count = count;
|
|
+ spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
+
|
|
+ flags = claim_dma_lock();
|
|
+ disable_dma(chan);
|
|
+ clear_dma_ff(chan);
|
|
+ set_dma_mode(chan, mode);
|
|
+ set_dma_addr(chan, phyaddr);
|
|
+ if (count == 0) {
|
|
+ count++;
|
|
+ printk(KERN_DEBUG "%s: JzSOC DMA controller can't set dma count zero!\n",
|
|
+ __FUNCTION__);
|
|
+ }
|
|
+ set_dma_count(chan, count);
|
|
+ enable_dma(chan);
|
|
+ release_dma_lock(flags);
|
|
+}
|
|
+
|
|
+static irqreturn_t
|
|
+jz_ac97_record_dma_irq (int irq, void *dev_id)
|
|
+{
|
|
+ struct jz_ac97_controller_info * controller =
|
|
+ (struct jz_ac97_controller_info *) dev_id;
|
|
+ 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_busy_queue, id2);
|
|
+
|
|
+ *(in_dma_buf_data_count + id2) = *(in_dma_buf_data_count + id1);
|
|
+ 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_ac97_replay_dma_irq (int irq, void *dev_id)
|
|
+{
|
|
+ struct jz_ac97_controller_info * controller =
|
|
+ (struct jz_ac97_controller_info *) dev_id;
|
|
+ int dma = controller->dma1, 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);
|
|
+ if ((id = get_buffer_id(&out_busy_queue)) < 0) {
|
|
+ printk(KERN_DEBUG "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);
|
|
+ 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);
|
|
+ }
|
|
+ } 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;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initialize the onchip AC97 controller
|
|
+ */
|
|
+static void jz_ac97_initHw(struct jz_ac97_controller_info *controller)
|
|
+{
|
|
+ __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();
|
|
+
|
|
+ /* Check the trigger threshold reset value to detect version */
|
|
+ if (((REG_AIC_FR & AIC_FR_TFTH_MASK) >> AIC_FR_TFTH_BIT) == 8) {
|
|
+ printk("JzAC97: patched controller detected.\n");
|
|
+ controller->patched = 1;
|
|
+ } else {
|
|
+ printk("JzAC97: standard controller detected.\n");
|
|
+ controller->patched = 0;
|
|
+ }
|
|
+
|
|
+ /* 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();
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initialize all of in(out)_empty_queue value
|
|
+ */
|
|
+static void Init_In_Out_queue(int fragstotal,int fragsize)
|
|
+{
|
|
+ int i;
|
|
+ if(out_dma_buf || in_dma_buf)
|
|
+ return;
|
|
+ in_empty_queue.count = fragstotal;
|
|
+ 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;
|
|
+ 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;
|
|
+ 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++) {
|
|
+ *(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;
|
|
+ /*alloc DMA buffer*/
|
|
+ for (i = 0; i < jz_audio_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)));
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < jz_audio_fragstotal; i++) {
|
|
+ *(in_dma_buf + i) = __get_free_pages(GFP_KERNEL | GFP_DMA, get_order(fragsize));
|
|
+ if (*(in_dma_buf + i) == 0) {
|
|
+ printk(" can't allocate required DMA(IN) buffers.\n");
|
|
+ goto mem_failed_in;
|
|
+ }
|
|
+ *(in_dma_pbuf + i) = virt_to_phys((void *)(*(in_dma_buf + i)));
|
|
+ dma_cache_wback_inv(*(in_dma_buf + i), 4096*8);//fragsize
|
|
+ *(in_dma_buf + i) = KSEG1ADDR(*(in_dma_buf + i));
|
|
+ }
|
|
+ return ;
|
|
+
|
|
+ all_mem_err:
|
|
+ printk("error:allocate memory occur error!\n");
|
|
+ return ;
|
|
+
|
|
+mem_failed_out:
|
|
+
|
|
+ for (i = 0; i < jz_audio_fragstotal; i++) {
|
|
+ if(*(out_dma_buf + i))
|
|
+ free_pages(*(out_dma_buf + i), get_order(fragsize));
|
|
+ }
|
|
+ return ;
|
|
+
|
|
+mem_failed_in:
|
|
+
|
|
+ for (i = 0; i < jz_audio_fragstotal; i++) {
|
|
+ if(*(in_dma_buf + i))
|
|
+ free_pages(*(in_dma_buf + i), get_order(fragsize));
|
|
+ }
|
|
+ return ;
|
|
+
|
|
+}
|
|
+static void Free_In_Out_queue(int fragstotal,int fragsize)
|
|
+{
|
|
+ int i;
|
|
+ if(out_dma_buf != NULL)
|
|
+ {
|
|
+ for (i = 0; i < jz_audio_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(in_dma_buf)
|
|
+ {
|
|
+ for (i = 0; i < jz_audio_fragstotal; i++)
|
|
+ {
|
|
+ if(*(in_dma_buf + i))
|
|
+ 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;
|
|
+ }
|
|
+ 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;
|
|
+ }
|
|
+
|
|
+ in_empty_queue.count = fragstotal;
|
|
+ out_empty_queue.count = fragstotal;
|
|
+ in_full_queue.count = 0;
|
|
+ in_busy_queue.count = 0;
|
|
+ out_busy_queue.count = 0;
|
|
+ out_full_queue.count = 0;
|
|
+ return ;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Reset everything
|
|
+ */
|
|
+static void
|
|
+jz_ac97_full_reset(struct jz_ac97_controller_info *controller)
|
|
+{
|
|
+ jz_ac97_initHw(controller);
|
|
+}
|
|
+
|
|
+
|
|
+static void
|
|
+jz_ac97_mksound(unsigned int hz, unsigned int ticks)
|
|
+{
|
|
+// printk("BEEP - %d %d!\n", hz, ticks);
|
|
+}
|
|
+
|
|
+static int jz_audio_set_speed(int dev, int rate)
|
|
+{
|
|
+ /* 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 99999999 ? */
|
|
+ u32 dacp;
|
|
+ struct ac97_codec *codec=ac97_controller->ac97_codec[0];
|
|
+
|
|
+ if (rate > 48000)
|
|
+ rate = 48000;
|
|
+ if (rate < 8000)
|
|
+ rate = 8000;
|
|
+
|
|
+
|
|
+ /* Power down the DAC */
|
|
+ dacp=ac97_codec_read(codec, AC97_POWER_CONTROL);
|
|
+ ac97_codec_write(codec, AC97_POWER_CONTROL, dacp|0x0200);
|
|
+ /* Load the rate; only 48Khz playback available, read always zero */
|
|
+ if ((ac97_controller->patched) && (ac97_controller->ac97_features & 1))
|
|
+ ac97_codec_write(codec, AC97_PCM_FRONT_DAC_RATE, rate);
|
|
+ else
|
|
+ ac97_codec_write(codec, AC97_PCM_FRONT_DAC_RATE, 48000);
|
|
+
|
|
+ /* Power it back up */
|
|
+ ac97_codec_write(codec, 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 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 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(int dev, unsigned int fmt)
|
|
+{
|
|
+ switch (fmt) {
|
|
+ case AFMT_U8:
|
|
+ case AFMT_S16_LE:
|
|
+ jz_audio_format = fmt;
|
|
+ jz_update_filler(fmt, jz_audio_channels);
|
|
+ case AFMT_QUERY:
|
|
+ break;
|
|
+ }
|
|
+ return jz_audio_format;
|
|
+}
|
|
+
|
|
+static short jz_audio_set_channels(int dev, short channels)
|
|
+{
|
|
+ switch (channels) {
|
|
+ case 1:
|
|
+ __ac97_set_xs_stereo(); // always stereo when recording
|
|
+ __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)
|
|
+{
|
|
+ __ac97_disable_replay();
|
|
+ __ac97_disable_receive_dma();
|
|
+ __ac97_disable_record();
|
|
+ __ac97_disable_transmit_dma();
|
|
+}
|
|
+
|
|
+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_ac97_audio_fops */
|
|
+static struct file_operations jz_ac97_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
|
|
+};
|
|
+
|
|
+/* 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;
|
|
+ printk(KERN_DEBUG "%s: RSTO receive status timeout.\n",
|
|
+ __FUNCTION__);
|
|
+ done = 0;
|
|
+ break;
|
|
+ }
|
|
+ udelay (1);
|
|
+ }
|
|
+ }
|
|
+ return done;
|
|
+}
|
|
+
|
|
+static int jz_readAC97Reg (struct ac97_codec *dev, u8 reg)
|
|
+{
|
|
+ u16 value;
|
|
+
|
|
+ if (reg < 128) {
|
|
+ u8 ret_reg;
|
|
+ __ac97_out_rcmd_addr(reg);//output read addr
|
|
+ if (jz_out_command_ready())//judge if send completely?
|
|
+ while (jz_in_status_ready()) {//judge if receive completely?
|
|
+ ret_reg = __ac97_in_status_addr();//slot1:send addr
|
|
+ value = __ac97_in_data();
|
|
+ if (ret_reg == reg)
|
|
+ return value;
|
|
+ else {
|
|
+// printk(KERN_DEBUG "%s: index (0x%02x)->(0x%02x) 0x%x\n", __FUNCTION__, reg, ret_reg, value);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ value = __ac97_in_data();
|
|
+ printk (KERN_DEBUG "timeout while reading AC97 codec (0x%x)\n", reg);
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+static u16 ac97_codec_read(struct ac97_codec *codec, u8 reg)
|
|
+{
|
|
+ int res = jz_readAC97Reg(codec, reg);
|
|
+ int count = 0;
|
|
+ while (res == -EINVAL) {
|
|
+ udelay(1000);
|
|
+ __ac97_warm_reset_codec();
|
|
+ udelay(1000);
|
|
+ res = jz_readAC97Reg(codec, reg);
|
|
+ count ++;
|
|
+ if (count > MAX_RETRY){
|
|
+ printk(KERN_WARNING"After try %d when read AC97 codec 0x%x, can't success, give up operate!!\n",
|
|
+ MAX_RETRY, reg);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ return (u16)res;
|
|
+}
|
|
+
|
|
+static int jz_writeAC97Reg (struct ac97_codec *dev, u8 reg, u16 value)
|
|
+{
|
|
+ //unsigned long flags;
|
|
+ int done = 0;
|
|
+
|
|
+ //save_and_cli(flags);
|
|
+
|
|
+ __ac97_out_wcmd_addr(reg);
|
|
+ __ac97_out_data(value);
|
|
+ if (jz_out_command_ready())
|
|
+ done = 1;
|
|
+ else
|
|
+ printk (KERN_DEBUG "Tiemout AC97 codec write (0x%X<==0x%X)\n", reg, value);
|
|
+ //restore_flags(flags);
|
|
+ return done;
|
|
+}
|
|
+static void ac97_codec_write(struct ac97_codec *codec, u8 reg, u16 data)
|
|
+{
|
|
+ int done = jz_writeAC97Reg(codec, reg, data);
|
|
+ int count = 0;
|
|
+ while (done == 0) {
|
|
+ count ++;
|
|
+ udelay (2000);
|
|
+ __ac97_warm_reset_codec();
|
|
+ udelay(2000);
|
|
+ done = jz_writeAC97Reg(codec, reg, data);
|
|
+ if ( count > MAX_RETRY ){
|
|
+ printk (KERN_DEBUG " After try %d when write AC97 codec (0x%x), can't sucess, give up!! \n",
|
|
+ MAX_RETRY, reg);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/* OSS /dev/mixer file operation methods */
|
|
+
|
|
+static int jz_ac97_open_mixdev(struct inode *inode, struct file *file)
|
|
+{
|
|
+ int i;
|
|
+ int minor = MINOR(inode->i_rdev);
|
|
+ struct jz_ac97_controller_info *controller = ac97_controller;
|
|
+
|
|
+ for (i = 0; i < NR_AC97; i++)
|
|
+ if (controller->ac97_codec[i] != NULL &&
|
|
+ controller->ac97_codec[i]->dev_mixer == minor)
|
|
+ goto match;
|
|
+
|
|
+ if (!controller)
|
|
+ return -ENODEV;
|
|
+
|
|
+ match:
|
|
+ file->private_data = controller->ac97_codec[i];
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int jz_ac97_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd,
|
|
+ unsigned long arg)
|
|
+{
|
|
+ struct ac97_codec *codec = (struct ac97_codec *)file->private_data;
|
|
+
|
|
+ return codec->mixer_ioctl(codec, cmd, arg);
|
|
+}
|
|
+
|
|
+static loff_t jz_ac97_llseek(struct file *file, loff_t offset, int origin)
|
|
+{
|
|
+ return -ESPIPE;
|
|
+}
|
|
+
|
|
+static /*const*/ struct file_operations jz_ac97_mixer_fops = {
|
|
+ owner: THIS_MODULE,
|
|
+ llseek: jz_ac97_llseek,
|
|
+ ioctl: jz_ac97_ioctl_mixdev,
|
|
+ open: jz_ac97_open_mixdev,
|
|
+};
|
|
+
|
|
+/* AC97 codec initialisation. */
|
|
+static int __init jz_ac97_codec_init(struct jz_ac97_controller_info *controller)
|
|
+{
|
|
+ int num_ac97 = 0;
|
|
+ int ready_2nd = 0;
|
|
+ struct ac97_codec *codec;
|
|
+ unsigned short eid;
|
|
+ int i = 0;
|
|
+
|
|
+ if (__ac97_codec_is_low_power_mode()) {
|
|
+ printk(KERN_DEBUG "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 ) {
|
|
+ printk(KERN_WARNING "AC97 codec not ready, failed init ..\n");
|
|
+ return -ENODEV;
|
|
+ }
|
|
+ udelay(10);
|
|
+ }
|
|
+ i = 0;
|
|
+
|
|
+ /* Reset the mixer. */
|
|
+ for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) {
|
|
+ if ((codec = kmalloc(sizeof(struct ac97_codec),
|
|
+ GFP_KERNEL)) == NULL)
|
|
+ return -ENOMEM;
|
|
+ memset(codec, 0, sizeof(struct ac97_codec));
|
|
+
|
|
+ /* initialize some basic codec information,
|
|
+ other fields will be filled
|
|
+ in ac97_probe_codec */
|
|
+ codec->private_data = controller;
|
|
+ codec->id = num_ac97;
|
|
+
|
|
+ codec->codec_read = ac97_codec_read;
|
|
+ codec->codec_write = ac97_codec_write;
|
|
+
|
|
+ if (ac97_probe_codec(codec) == 0)
|
|
+ break;
|
|
+
|
|
+ eid = ac97_codec_read(codec, AC97_EXTENDED_ID);
|
|
+ if (eid == 0xFFFF) {
|
|
+ printk(KERN_WARNING "Jz AC97: no codec attached?\n");
|
|
+ kfree(codec);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ controller->ac97_features = eid;
|
|
+
|
|
+ if (!(eid & 0x0001))
|
|
+ printk(KERN_WARNING "AC97 codec: only 48Khz playback available.\n");
|
|
+ else {
|
|
+ printk(KERN_WARNING "AC97 codec: supports variable sample rate.\n");
|
|
+ /* Enable HPEN: UCB1400 only */
|
|
+ ac97_codec_write(codec, 0x6a,
|
|
+ ac97_codec_read(codec, 0x6a) | 0x40);
|
|
+
|
|
+ /* Enable variable rate mode */
|
|
+ ac97_codec_write(codec, AC97_EXTENDED_STATUS, 9);
|
|
+ ac97_codec_write(codec,
|
|
+ AC97_EXTENDED_STATUS,
|
|
+ ac97_codec_read(codec, AC97_EXTENDED_STATUS) | 0xE800);
|
|
+ /* power up everything, modify this
|
|
+ when implementing power saving */
|
|
+ ac97_codec_write(codec,
|
|
+ AC97_POWER_CONTROL,
|
|
+ ac97_codec_read(codec, AC97_POWER_CONTROL) & ~0x7f00);
|
|
+ /* wait for analog ready */
|
|
+ for (i=10; i && ((ac97_codec_read(codec, AC97_POWER_CONTROL) & 0xf) != 0xf); i--) {
|
|
+// current->state = TASK_UNINTERRUPTIBLE;
|
|
+// schedule_timeout(HZ/20);
|
|
+ }
|
|
+
|
|
+ if (!(ac97_codec_read(codec, AC97_EXTENDED_STATUS) & 1)) {
|
|
+ printk(KERN_WARNING "Jz AC97: Codec refused to allow VRA, using 48Khz only.\n");
|
|
+ controller->ac97_features &= ~1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if ((codec->dev_mixer =
|
|
+ register_sound_mixer(&jz_ac97_mixer_fops, -1)) < 0) {
|
|
+ printk(KERN_ERR "Jz AC97: couldn't register mixer!\n");
|
|
+ kfree(codec);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ controller->ac97_codec[num_ac97] = codec;
|
|
+
|
|
+ /* if there is no secondary codec at all, don't probe any more */
|
|
+ if (!ready_2nd)
|
|
+ return num_ac97+1;
|
|
+ }
|
|
+ return num_ac97;
|
|
+}
|
|
+
|
|
+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):
|
|
+ if ((ac97_controller->patched) &&
|
|
+ (ac97_controller->ac97_features & 1)) {
|
|
+ __aic_enable_mono2stereo();
|
|
+ __aic_enable_unsignadj();
|
|
+ jz_set_dma_block_size(ac97_controller->dma1, 16);
|
|
+ jz_set_dma_block_size(ac97_controller->dma2, 16);
|
|
+ } else {
|
|
+ __aic_disable_mono2stereo();
|
|
+ __aic_disable_unsignadj();
|
|
+ jz_set_dma_block_size(ac97_controller->dma1, 4);
|
|
+ jz_set_dma_block_size(ac97_controller->dma2, 4);
|
|
+ }
|
|
+ replay_filler = replay_fill_1x8_u;
|
|
+ record_filler = record_fill_1x8_u;
|
|
+ __ac97_set_oass(8);
|
|
+ __ac97_set_iass(8);
|
|
+ jz_set_dma_dest_width(ac97_controller->dma1, 8);
|
|
+ jz_set_dma_src_width(ac97_controller->dma2, 8);
|
|
+ break;
|
|
+ case TYPE(AFMT_U8, 2):
|
|
+ if ((ac97_controller->patched) &&
|
|
+ (ac97_controller->ac97_features & 1)) {
|
|
+ __aic_enable_mono2stereo();
|
|
+ __aic_enable_unsignadj();
|
|
+ jz_set_dma_block_size(ac97_controller->dma1, 16);
|
|
+ jz_set_dma_block_size(ac97_controller->dma2, 16);
|
|
+ } else {
|
|
+ __aic_disable_mono2stereo();
|
|
+ __aic_disable_unsignadj();
|
|
+ jz_set_dma_block_size(ac97_controller->dma1, 4);
|
|
+ jz_set_dma_block_size(ac97_controller->dma2, 4);
|
|
+ }
|
|
+ replay_filler = replay_fill_2x8_u;
|
|
+ record_filler = record_fill_2x8_u;
|
|
+ __ac97_set_oass(8);
|
|
+ __ac97_set_iass(8);
|
|
+ jz_set_dma_dest_width(ac97_controller->dma1, 8);
|
|
+ jz_set_dma_src_width(ac97_controller->dma2, 8);
|
|
+ break;
|
|
+ case TYPE(AFMT_S16_LE, 1):
|
|
+ if ((ac97_controller->patched) &&
|
|
+ (ac97_controller->ac97_features & 1)) {
|
|
+ __aic_enable_mono2stereo();
|
|
+ jz_set_dma_block_size(ac97_controller->dma1, 16);
|
|
+ jz_set_dma_block_size(ac97_controller->dma2, 16);
|
|
+ } else {
|
|
+ __aic_disable_mono2stereo();
|
|
+ jz_set_dma_block_size(ac97_controller->dma1, 4);
|
|
+ jz_set_dma_block_size(ac97_controller->dma2, 4);
|
|
+ }
|
|
+ __aic_disable_unsignadj();
|
|
+ replay_filler = replay_fill_1x16_s;
|
|
+ record_filler = record_fill_1x16_s;
|
|
+ __ac97_set_oass(16);
|
|
+ __ac97_set_iass(16);
|
|
+
|
|
+ jz_set_dma_dest_width(ac97_controller->dma1, 16);
|
|
+ jz_set_dma_src_width(ac97_controller->dma2, 16);
|
|
+
|
|
+ break;
|
|
+
|
|
+ case TYPE(AFMT_S16_LE, 2):
|
|
+ if ((ac97_controller->patched) &&
|
|
+ (ac97_controller->ac97_features & 1)) {
|
|
+ jz_set_dma_block_size(ac97_controller->dma1, 16);
|
|
+ jz_set_dma_block_size(ac97_controller->dma2, 16);
|
|
+ } else {
|
|
+ jz_set_dma_block_size(ac97_controller->dma1, 4);
|
|
+ jz_set_dma_block_size(ac97_controller->dma2, 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);
|
|
+ jz_set_dma_dest_width(ac97_controller->dma1, 16);
|
|
+ jz_set_dma_src_width(ac97_controller->dma2, 16);
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_PROC_FS
|
|
+
|
|
+extern struct proc_dir_entry *proc_jz_root;
|
|
+
|
|
+static int jz_ac97_init_proc(struct jz_ac97_controller_info *controller)
|
|
+{
|
|
+ if (!create_proc_read_entry ("ac97", 0, proc_jz_root,
|
|
+ ac97_read_proc, controller->ac97_codec[0]))
|
|
+ return -EIO;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void jz_ac97_cleanup_proc(struct jz_ac97_controller_info *controller)
|
|
+{
|
|
+}
|
|
+
|
|
+#endif
|
|
+
|
|
+static void __init attach_jz_ac97(struct jz_ac97_controller_info *controller)
|
|
+{
|
|
+ char *name;
|
|
+ int adev;
|
|
+
|
|
+ name = controller->name;
|
|
+ jz_ac97_initHw(controller);
|
|
+
|
|
+ /* register /dev/audio ? */
|
|
+ adev = register_sound_dsp(&jz_ac97_audio_fops, -1);
|
|
+ if (adev < 0)
|
|
+ goto audio_failed;
|
|
+
|
|
+ /* initialize AC97 codec and register /dev/mixer */
|
|
+ if (jz_ac97_codec_init(controller) <= 0)
|
|
+ goto mixer_failed;
|
|
+
|
|
+#ifdef CONFIG_PROC_FS
|
|
+ if (jz_ac97_init_proc(controller) < 0) {
|
|
+ printk(KERN_ERR "%s: can't create AC97 proc filesystem.\n", name);
|
|
+ goto proc_failed;
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ controller->tmp1 = (void *)__get_free_pages(GFP_KERNEL, 8);//4
|
|
+ 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);//4
|
|
+ if (!controller->tmp2) {
|
|
+ printk(KERN_ERR "%s: can't allocate tmp buffers.\n", controller->name);
|
|
+ goto tmp2_failed;
|
|
+ }
|
|
+
|
|
+ if ((controller->dma1 = jz_request_dma(DMA_ID_AC97_TX, "audio dac",
|
|
+ jz_ac97_replay_dma_irq,
|
|
+ IRQF_DISABLED, controller)) < 0) {
|
|
+ printk(KERN_ERR "%s: can't reqeust DMA DAC channel.\n", name);
|
|
+ goto dma1_failed;
|
|
+ }
|
|
+ if ((controller->dma2 = jz_request_dma(DMA_ID_AC97_RX, "audio adc",
|
|
+ jz_ac97_record_dma_irq,
|
|
+ IRQF_DISABLED, controller)) < 0) {
|
|
+ printk(KERN_ERR "%s: can't reqeust DMA ADC channel.\n", name);
|
|
+ goto dma2_failed;
|
|
+ }
|
|
+
|
|
+ printk("Jz On-Chip AC97 controller registered (DAC: DMA%d/IRQ%d, ADC: DMA%d/IRQ%d)\n",
|
|
+ controller->dma1, get_dma_done_irq(controller->dma1),
|
|
+ controller->dma2, get_dma_done_irq(controller->dma2));
|
|
+
|
|
+ old_mksound = kd_mksound; /* see vt.c */
|
|
+ kd_mksound = jz_ac97_mksound;
|
|
+ controller->dev_audio = adev;
|
|
+ return;
|
|
+
|
|
+ dma2_failed:
|
|
+ jz_free_dma(controller->dma1);
|
|
+ dma1_failed:
|
|
+ free_pages((unsigned long)controller->tmp2, 8);//4
|
|
+ tmp2_failed:
|
|
+ free_pages((unsigned long)controller->tmp1, 8);//4
|
|
+ tmp1_failed:
|
|
+#ifdef CONFIG_PROC_FS
|
|
+ jz_ac97_cleanup_proc(controller);
|
|
+#endif
|
|
+ proc_failed:
|
|
+ /* unregister mixer dev */
|
|
+ mixer_failed:
|
|
+ unregister_sound_dsp(adev);
|
|
+ audio_failed:
|
|
+ return;
|
|
+}
|
|
+
|
|
+static int __init probe_jz_ac97(struct jz_ac97_controller_info **controller)
|
|
+{
|
|
+ if ((*controller = kmalloc(sizeof(struct jz_ac97_controller_info),
|
|
+ GFP_KERNEL)) == NULL) {
|
|
+ printk(KERN_ERR "Jz AC97 Controller: out of memory.\n");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ memset( *controller, 0, sizeof(struct jz_ac97_controller_info) );
|
|
+ (*controller)->name = "Jz AC97 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);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void __exit unload_jz_ac97(struct jz_ac97_controller_info *controller)
|
|
+{
|
|
+ int adev = controller->dev_audio;
|
|
+
|
|
+ jz_ac97_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_ac97_cleanup_proc(controller);
|
|
+#endif
|
|
+
|
|
+ jz_free_dma(controller->dma1);
|
|
+ jz_free_dma(controller->dma2);
|
|
+
|
|
+ free_pages((unsigned long)controller->tmp1, 8);//4
|
|
+ free_pages((unsigned long)controller->tmp2, 8);//4
|
|
+
|
|
+ if (adev >= 0) {
|
|
+ //unregister_sound_mixer(audio_devs[adev]->mixer_dev);
|
|
+ unregister_sound_dsp(controller->dev_audio);
|
|
+ }
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_PM
|
|
+
|
|
+static int reserve_mastervol, reserve_micvol;
|
|
+static int reserve_power1, reserve_power2;
|
|
+static int reserve_power3, reserve_power4;
|
|
+static int reserve_power5, reserve_power6;
|
|
+
|
|
+static int jz_ac97_suspend(struct jz_ac97_controller_info *controller, int state)
|
|
+{
|
|
+ struct ac97_codec *codec = controller->ac97_codec[0];
|
|
+
|
|
+ /* save codec states */
|
|
+ reserve_mastervol = ac97_codec_read(codec, 0x0002);
|
|
+ reserve_micvol = ac97_codec_read(codec, 0x000e);
|
|
+
|
|
+ reserve_power1 = ac97_codec_read(codec, 0x0026);
|
|
+ reserve_power2 = ac97_codec_read(codec, 0x006c);
|
|
+ reserve_power3 = ac97_codec_read(codec, 0x005c);
|
|
+ reserve_power4 = ac97_codec_read(codec, 0x0064);
|
|
+ reserve_power5 = ac97_codec_read(codec, 0x0066);
|
|
+ reserve_power6 = ac97_codec_read(codec, 0x006a);
|
|
+
|
|
+ /* put codec into power-saving mode */
|
|
+ ac97_codec_write(codec, 0x5c, 0xffff);
|
|
+ ac97_codec_write(codec, 0x64, 0x0000);
|
|
+ ac97_codec_write(codec, 0x66, 0x0000);
|
|
+ ac97_codec_write(codec, 0x6a, 0x0000);
|
|
+
|
|
+ ac97_codec_write(codec, 0x6c, 0x0030);
|
|
+ ac97_codec_write(codec, 0x26, 0x3b00);
|
|
+
|
|
+ ac97_save_state(codec);
|
|
+ __ac97_disable();
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int jz_ac97_resume(struct jz_ac97_controller_info *controller)
|
|
+{
|
|
+ struct ac97_codec *codec = controller->ac97_codec[0];
|
|
+
|
|
+ jz_ac97_full_reset(controller);
|
|
+ ac97_probe_codec(codec);
|
|
+ ac97_restore_state(codec);
|
|
+
|
|
+ ac97_codec_write(codec, 0x0026, reserve_power1);
|
|
+ ac97_codec_write(codec, 0x006c, reserve_power2);
|
|
+ ac97_codec_write(codec, 0x005c, reserve_power3);
|
|
+ ac97_codec_write(codec, 0x0064, reserve_power4);
|
|
+ ac97_codec_write(codec, 0x0066, reserve_power5);
|
|
+ ac97_codec_write(codec, 0x006a, reserve_power6);
|
|
+
|
|
+ ac97_codec_write(codec, 0x0002, reserve_mastervol);
|
|
+ ac97_codec_write(codec, 0x000e, reserve_micvol);
|
|
+
|
|
+ /* Enable variable rate mode */
|
|
+ ac97_codec_write(codec, AC97_EXTENDED_STATUS, 9);
|
|
+ ac97_codec_write(codec,
|
|
+ AC97_EXTENDED_STATUS,
|
|
+ ac97_codec_read(codec, AC97_EXTENDED_STATUS) | 0xE800);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int jz_ac97_pm_callback(struct pm_dev *pm_dev, pm_request_t req, void *data)
|
|
+{
|
|
+ int ret;
|
|
+ struct jz_ac97_controller_info *controller = pm_dev->data;
|
|
+
|
|
+ if (!controller) return -EINVAL;
|
|
+
|
|
+ switch (req) {
|
|
+ case PM_SUSPEND:
|
|
+ ret = jz_ac97_suspend(controller, (int)data);
|
|
+ break;
|
|
+
|
|
+ case PM_RESUME:
|
|
+ ret = jz_ac97_resume(controller);
|
|
+ break;
|
|
+ default:
|
|
+ ret = -EINVAL;
|
|
+ break;
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+#endif /* CONFIG_PM */
|
|
+
|
|
+static int __init init_jz_ac97(void)
|
|
+{
|
|
+ int errno;
|
|
+
|
|
+ if ((errno = probe_jz_ac97(&ac97_controller)) < 0)
|
|
+ return errno;
|
|
+ attach_jz_ac97(ac97_controller);
|
|
+
|
|
+ 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;
|
|
+
|
|
+#ifdef CONFIG_PM
|
|
+ ac97_controller->pm = pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN,
|
|
+ jz_ac97_pm_callback);
|
|
+ if (ac97_controller->pm)
|
|
+ ac97_controller->pm->data = ac97_controller;
|
|
+#endif
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void __exit cleanup_jz_ac97(void)
|
|
+{
|
|
+ unload_jz_ac97(ac97_controller);
|
|
+}
|
|
+
|
|
+module_init(init_jz_ac97);
|
|
+module_exit(cleanup_jz_ac97);
|
|
+
|
|
+
|
|
+static int drain_adc(struct jz_ac97_controller_info *ctrl, int nonblock)
|
|
+{
|
|
+ DECLARE_WAITQUEUE(wait, current);
|
|
+ unsigned long flags;
|
|
+ int count;
|
|
+ unsigned tmo;
|
|
+
|
|
+ add_wait_queue(&ctrl->adc_wait, &wait);
|
|
+ for (;;) {
|
|
+ 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 (signal_pending(current))
|
|
+ break;
|
|
+ if (nonblock) {
|
|
+ remove_wait_queue(&ctrl->adc_wait, &wait);
|
|
+ current->state = TASK_RUNNING;
|
|
+ return -EBUSY;
|
|
+ }
|
|
+ tmo = (PAGE_SIZE * HZ) / STANDARD_SPEED;
|
|
+ tmo *= jz_audio_k * (jz_audio_format == AFMT_S16_LE) ? 2 : 4;
|
|
+ tmo /= jz_audio_channels;
|
|
+ }
|
|
+ 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_ac97_controller_info *ctrl, int nonblock)
|
|
+{
|
|
+ DECLARE_WAITQUEUE(wait, current);
|
|
+ unsigned long flags;
|
|
+ int count;
|
|
+ unsigned tmo;
|
|
+
|
|
+ add_wait_queue(&(ctrl->dac_wait), &wait);
|
|
+ for (;;) {
|
|
+ set_current_state(TASK_INTERRUPTIBLE);
|
|
+ spin_lock_irqsave(&ctrl->lock, flags);
|
|
+ count = get_dma_residue(ctrl->dma1);
|
|
+ spin_unlock_irqrestore(&ctrl->lock, flags);
|
|
+ if (count <= 0 && elements_in_queue(&out_full_queue) <= 0)
|
|
+ break;
|
|
+ if (signal_pending(current))
|
|
+ break;
|
|
+ if (nonblock) {
|
|
+ remove_wait_queue(&ctrl->dac_wait, &wait);
|
|
+ current->state = TASK_RUNNING;
|
|
+ return -EBUSY;
|
|
+ }
|
|
+ tmo = (PAGE_SIZE * HZ) / STANDARD_SPEED;
|
|
+ tmo *= jz_audio_k * (jz_audio_format == AFMT_S16_LE) ? 2 : 4;
|
|
+ tmo /= jz_audio_channels;
|
|
+ }
|
|
+ remove_wait_queue(&ctrl->dac_wait, &wait);
|
|
+ current->state = TASK_RUNNING;
|
|
+ if (signal_pending(current))
|
|
+ return -ERESTARTSYS;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Audio operation routines implementation
|
|
+ */
|
|
+static int jz_audio_release(struct inode *inode, struct file *file)
|
|
+{
|
|
+ struct jz_ac97_controller_info *controller =
|
|
+ (struct jz_ac97_controller_info *) file->private_data;
|
|
+ unsigned long flags;
|
|
+
|
|
+ if (file->f_mode & FMODE_WRITE) {
|
|
+ controller->opened1 = 0;
|
|
+ drain_dac(controller, file->f_flags & O_NONBLOCK);
|
|
+ disable_dma(controller->dma1);
|
|
+ set_dma_count(controller->dma1, 0);
|
|
+ __ac97_disable_transmit_dma();
|
|
+ __ac97_disable_replay();
|
|
+
|
|
+ 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 (file->f_mode & FMODE_READ) {
|
|
+ controller->opened2 = 0;
|
|
+ first_record_call = 1;
|
|
+ drain_adc(controller, file->f_flags & O_NONBLOCK);
|
|
+ disable_dma(controller->dma2);
|
|
+ set_dma_count(controller->dma2, 0);
|
|
+ __ac97_disable_receive_dma();
|
|
+ __ac97_disable_record();
|
|
+ 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);
|
|
+
|
|
+ }
|
|
+ Free_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int jz_audio_open(struct inode *inode, struct file *file)
|
|
+{
|
|
+ struct jz_ac97_controller_info *controller= ac97_controller;
|
|
+
|
|
+ if (controller == NULL)
|
|
+ return -ENODEV;
|
|
+
|
|
+ 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;
|
|
+ jz_audio_set_channels(controller->dev_audio, 1);
|
|
+ jz_audio_set_format(controller->dev_audio, AFMT_U8);
|
|
+ jz_audio_set_speed(controller->dev_audio, 8000);
|
|
+ }
|
|
+
|
|
+ if (file->f_mode & FMODE_READ) {
|
|
+ if (controller->opened2 == 1)
|
|
+ return -EBUSY;
|
|
+ controller->opened2 = 1;
|
|
+ first_record_call = 1;
|
|
+ printk(KERN_DEBUG "You'd better apply 48000Hz 16-bit Stereo for record.\n");
|
|
+ //for ioctl
|
|
+ controller->total_bytes = 0;
|
|
+ jz_audio_dma_tran_count = 0;
|
|
+ controller->count = 0;
|
|
+ controller->finish = 0;
|
|
+ controller->blocks = 0;
|
|
+ controller->nextIn = 0;
|
|
+ jz_audio_set_channels(controller->dev_audio, 2);
|
|
+ jz_audio_set_format(controller->dev_audio, AFMT_S16_LE);
|
|
+ jz_audio_set_speed(controller->dev_audio, 48000);
|
|
+ }
|
|
+
|
|
+ last_jz_audio_count = jz_audio_count = 0;
|
|
+ file->private_data = controller;
|
|
+
|
|
+ jz_audio_fragsize = 8 * PAGE_SIZE;
|
|
+ jz_audio_fragstotal = 4;
|
|
+ Free_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int jz_audio_ioctl(struct inode *inode, struct file *file,
|
|
+ unsigned int cmd, unsigned long arg)
|
|
+{
|
|
+ struct jz_ac97_controller_info *controller =
|
|
+ (struct jz_ac97_controller_info *) file->private_data;
|
|
+ int val=0,fullc,busyc,unfinish;
|
|
+ unsigned int flags;
|
|
+ count_info cinfo;
|
|
+
|
|
+ switch (cmd)
|
|
+ {
|
|
+ case OSS_GETVERSION:
|
|
+ return put_user(SOUND_VERSION, (int *)arg);
|
|
+
|
|
+ case SNDCTL_DSP_RESET:
|
|
+ jz_audio_reset();
|
|
+ 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(4*PAGE_SIZE, (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:
|
|
+ if(out_dma_buf || in_dma_buf)
|
|
+ return -EBUSY;
|
|
+ get_user(val, (long *) arg);
|
|
+ jz_audio_fragsize = 1 << (val & 0xFFFF);//16 least bits
|
|
+
|
|
+ if (jz_audio_fragsize < 4 * PAGE_SIZE)
|
|
+ jz_audio_fragsize = 4 * PAGE_SIZE;
|
|
+ if (jz_audio_fragsize > (16 * PAGE_SIZE)) //16 PAGE_SIZE
|
|
+ jz_audio_fragsize = 16 * PAGE_SIZE;
|
|
+ jz_audio_fragstotal = (val >> 16) & 0x7FFF;
|
|
+ if (jz_audio_fragstotal < 2)
|
|
+ jz_audio_fragstotal = 2;
|
|
+ if (jz_audio_fragstotal > 32)
|
|
+ jz_audio_fragstotal = 32;
|
|
+
|
|
+ Free_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize);
|
|
+ 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:
|
|
+ {
|
|
+ audio_buf_info abinfo;
|
|
+ int i, bytes = 0;
|
|
+
|
|
+ if (!(file->f_mode & FMODE_WRITE))
|
|
+ return -EINVAL;
|
|
+ //unused fragment amount
|
|
+ spin_lock_irqsave(&controller->ioctllock, flags);
|
|
+ jz_audio_fragments = elements_in_queue(&out_empty_queue);
|
|
+ for (i = 0; i < jz_audio_fragments; i++)
|
|
+ bytes += jz_audio_fragsize;
|
|
+ bytes /= jz_audio_k;
|
|
+ spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
+ //bytes /= jz_audio_b;
|
|
+ abinfo.fragments = jz_audio_fragments;
|
|
+ abinfo.fragstotal = jz_audio_fragstotal;
|
|
+ abinfo.fragsize = jz_audio_fragsize;
|
|
+ abinfo.bytes = bytes;
|
|
+ return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
|
|
+ }
|
|
+
|
|
+ case SNDCTL_DSP_GETISPACE:
|
|
+ {
|
|
+ audio_buf_info abinfo;
|
|
+ int i, bytes = 0;
|
|
+
|
|
+ if (!(file->f_mode & FMODE_READ))
|
|
+ return -EINVAL;
|
|
+ //unused fragment amount
|
|
+ jz_audio_fragments = elements_in_queue(&in_empty_queue);
|
|
+ for (i = 0; i < jz_audio_fragments; i++)
|
|
+ bytes += jz_audio_fragsize;
|
|
+
|
|
+ abinfo.fragments = jz_audio_fragments;
|
|
+ abinfo.fragstotal = jz_audio_fragstotal;
|
|
+ abinfo.fragsize = jz_audio_fragsize;
|
|
+ abinfo.bytes = 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) //record is at working
|
|
+ val |= PCM_ENABLE_INPUT;
|
|
+ if (file->f_mode & FMODE_WRITE && out_dma_buf) //playback is at working
|
|
+ val |= PCM_ENABLE_OUTPUT;
|
|
+ return put_user(val, (int *)arg);
|
|
+
|
|
+ case SNDCTL_DSP_SETTRIGGER:
|
|
+ if (get_user(val, (int *)arg))
|
|
+ return -EFAULT;
|
|
+ /*if (file->f_mode & FMODE_READ) {
|
|
+ if (val & PCM_ENABLE_INPUT) {
|
|
+ if (!dmabuf->ready && (ret = prog_dmabuf(state, 1)))
|
|
+ return ret;
|
|
+ start_adc(state);
|
|
+ } else
|
|
+ stop_adc(state);
|
|
+ }
|
|
+ if (file->f_mode & FMODE_WRITE) {
|
|
+ if (val & PCM_ENABLE_OUTPUT) {
|
|
+ if (!dmabuf->ready && (ret = prog_dmabuf(state, 0)))
|
|
+ return ret;
|
|
+ start_dac(state);
|
|
+ } else
|
|
+ stop_dac(state);
|
|
+ }*/
|
|
+ return 0;
|
|
+
|
|
+ case SNDCTL_DSP_GETIPTR:
|
|
+ if (!(file->f_mode & FMODE_READ))
|
|
+ return -EINVAL;
|
|
+
|
|
+ spin_lock_irqsave(&controller->ioctllock, flags);
|
|
+ //controller->total_bytes += get_dma_residue(controller->dma2);
|
|
+ cinfo.bytes = controller->total_bytes;
|
|
+ cinfo.blocks = controller->blocks;
|
|
+ cinfo.ptr = controller->nextIn;
|
|
+ controller->blocks = 0;
|
|
+ spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
+ 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);
|
|
+ //controller->total_bytes += get_dma_residue(controller->dma1);
|
|
+ cinfo.bytes = controller->total_bytes;
|
|
+ cinfo.blocks = controller->blocks;
|
|
+ cinfo.ptr = controller->nextOut;
|
|
+ controller->blocks = 0;
|
|
+ spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
+ return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
|
|
+
|
|
+ case SNDCTL_DSP_GETODELAY:
|
|
+ {
|
|
+ int id, i;
|
|
+
|
|
+ if (!(file->f_mode & FMODE_WRITE))
|
|
+ return -EINVAL;
|
|
+ spin_lock_irqsave(&controller->ioctllock, flags);
|
|
+ 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);
|
|
+ unfinish /= jz_audio_k;//jz_audio_k is jz_audio_b
|
|
+ return put_user(val, (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_ac97_controller_info *controller =
|
|
+ (struct jz_ac97_controller_info *) file->private_data;
|
|
+ unsigned long flags;
|
|
+ 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);
|
|
+ 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);
|
|
+ return mask;
|
|
+}
|
|
+
|
|
+static ssize_t jz_audio_read(struct file *file, char *buffer,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct jz_ac97_controller_info *controller =
|
|
+ (struct jz_ac97_controller_info *) file->private_data;
|
|
+ int id, ret = 0, left_count, copy_count, cnt = 0;
|
|
+ unsigned long flags;
|
|
+
|
|
+ if (count < 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ spin_lock_irqsave(&controller->ioctllock, flags);
|
|
+ controller->nextIn = 0;
|
|
+ spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
+ Init_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize);
|
|
+
|
|
+ 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();
|
|
+ audio_start_dma(controller->dma2,file->private_data,
|
|
+ *(in_dma_pbuf + id),
|
|
+ *(in_dma_buf_data_count + id),
|
|
+ DMA_MODE_READ);
|
|
+ interruptible_sleep_on(&rx_wait_queue);
|
|
+ } else
|
|
+ BUG();
|
|
+ }
|
|
+
|
|
+ while (left_count > 0) {
|
|
+ if (elements_in_queue(&in_full_queue) <= 0) {
|
|
+ if (file->f_flags & O_NONBLOCK)
|
|
+ return ret ? ret : -EAGAIN;
|
|
+ else
|
|
+ interruptible_sleep_on(&rx_wait_queue);
|
|
+ }
|
|
+ if (signal_pending(current))
|
|
+ return ret ? ret: -ERESTARTSYS;
|
|
+
|
|
+ if ((id = get_buffer_id(&in_full_queue)) >= 0) {
|
|
+ cnt = record_filler((unsigned long)controller->tmp2+ret, copy_count, id);
|
|
+ put_buffer_id(&in_empty_queue, id);
|
|
+ } else
|
|
+ BUG();
|
|
+
|
|
+ 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();
|
|
+ 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)
|
|
+ cnt = count - ret;
|
|
+
|
|
+ if (copy_to_user(buffer+ret, controller->tmp2+ret, cnt))
|
|
+ return ret ? ret : -EFAULT;
|
|
+
|
|
+ ret += cnt;
|
|
+ spin_lock_irqsave(&controller->ioctllock, flags);
|
|
+ controller->nextIn += ret;
|
|
+ spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
+ left_count -= cnt;
|
|
+ }
|
|
+ if (ret != count)
|
|
+ printk(KERN_DEBUG "%s: count=%d ret=%d jz_audio_count=%d\n",
|
|
+ __FUNCTION__, count, ret, jz_audio_count);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static ssize_t jz_audio_write(struct file *file, const char *buffer,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct jz_ac97_controller_info *controller =
|
|
+ (struct jz_ac97_controller_info *) file->private_data;
|
|
+ int id, ret = 0, left_count, copy_count;
|
|
+ unsigned int flags;
|
|
+
|
|
+ if (count <= 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ spin_lock_irqsave(&controller->ioctllock, flags);
|
|
+ controller->nextOut = 0;
|
|
+ spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
+ Init_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize);
|
|
+
|
|
+ /* The data buffer size of the user space is always a PAGE_SIZE
|
|
+ * scale, so the process can be simplified.
|
|
+ */
|
|
+
|
|
+ if ((ac97_controller->patched) && (ac97_controller->ac97_features & 1))
|
|
+ copy_count = count;
|
|
+ else {
|
|
+ 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;
|
|
+
|
|
+ if (!(ac97_controller->patched) || !(ac97_controller->ac97_features & 1)) {
|
|
+ if (copy_from_user(controller->tmp1, buffer, count)) {
|
|
+ printk(KERN_DEBUG "%s: copy_from_user failed.\n", __FUNCTION__);
|
|
+ return ret ? ret : -EFAULT;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ while (left_count > 0) {
|
|
+ if (elements_in_queue(&out_empty_queue) == 0) {
|
|
+ if (file->f_flags & O_NONBLOCK)
|
|
+ return ret;
|
|
+ else
|
|
+ interruptible_sleep_on(&tx_wait_queue);
|
|
+ }
|
|
+
|
|
+ if (signal_pending(current))
|
|
+ return ret ? ret : -ERESTARTSYS;
|
|
+
|
|
+ /* 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) {
|
|
+ if ((ac97_controller->patched) &&
|
|
+ (ac97_controller->ac97_features & 1)) {
|
|
+ if (copy_from_user((char *)(*(out_dma_buf + id)), buffer+ret, copy_count)) {
|
|
+ printk(KERN_DEBUG "%s: copy_from_user failed.\n", __FUNCTION__);
|
|
+ return ret ? ret : -EFAULT;
|
|
+ }
|
|
+ *(out_dma_buf_data_count + id) = copy_count;
|
|
+ } else
|
|
+ replay_filler(
|
|
+ (unsigned long)controller->tmp1 + ret,
|
|
+ copy_count, id);
|
|
+ //when 0,kernel will panic
|
|
+ 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 {//when 0,i need refill in empty queue
|
|
+ put_buffer_id(&out_empty_queue, id);
|
|
+ }
|
|
+ } else
|
|
+ BUG();
|
|
+ left_count = left_count - copy_count;
|
|
+ ret += copy_count;
|
|
+
|
|
+ spin_lock_irqsave(&controller->ioctllock, flags);
|
|
+ controller->nextOut += ret;
|
|
+ spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
+
|
|
+ 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();
|
|
+ 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);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (ret != count)
|
|
+ printk(KERN_DEBUG "%s: count=%d ret=%d jz_audio_count=%d\n",
|
|
+ __FUNCTION__, count, ret, jz_audio_count);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/* this function for the other codec function */
|
|
+struct ac97_codec * find_ac97_codec(void)
|
|
+{
|
|
+ if ( ac97_controller )
|
|
+ return ac97_controller->ac97_codec[0];
|
|
+ return 0;
|
|
+
|
|
+}
|
|
+
|
|
--- /dev/null
|
|
+++ b/sound/oss/jz_i2s.c
|
|
@@ -0,0 +1,2894 @@
|
|
+/*
|
|
+ * 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;
|
|
+ unsigned int flags;
|
|
+ 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);
|
|
+ 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);
|
|
+ /* 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);
|
|
+ cinfo.bytes = controller->total_bytes;
|
|
+ cinfo.blocks = controller->blocks;
|
|
+ cinfo.ptr = controller->nextIn;
|
|
+ controller->blocks = 0;
|
|
+ spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
+
|
|
+ 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);
|
|
+ cinfo.bytes = controller->total_bytes;
|
|
+ cinfo.blocks = controller->blocks;
|
|
+ cinfo.ptr = controller->nextOut;
|
|
+ controller->blocks = 0;
|
|
+ spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
+
|
|
+ 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);
|
|
+ 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);
|
|
+
|
|
+ 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 long flags;
|
|
+ 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);
|
|
+ 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);
|
|
+
|
|
+ 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;
|
|
+ unsigned long flags;
|
|
+
|
|
+ if (count < 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ __i2s_enable_receive_dma();
|
|
+ __i2s_enable_record();
|
|
+
|
|
+ spin_lock_irqsave(&controller->ioctllock, flags);
|
|
+ controller->nextIn = 0;
|
|
+ spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
+
|
|
+ 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);
|
|
+ controller->nextIn += ret;
|
|
+ spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
+
|
|
+ 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;
|
|
+ unsigned int flags;
|
|
+ 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);
|
|
+ controller->nextOut = 0;
|
|
+ spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
+ 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);
|
|
+ controller->nextOut += ret;
|
|
+ spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
+
|
|
+ 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
|
|
--- /dev/null
|
|
+++ b/sound/oss/jz_i2s_4750.c
|
|
@@ -0,0 +1,3010 @@
|
|
+/*
|
|
+ * 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"
|
|
+
|
|
+#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 MAXDELAY 50000
|
|
+#define JZCODEC_RW_BUFFER_SIZE 2
|
|
+#define JZCODEC_RW_BUFFER_TOTAL 6
|
|
+
|
|
+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)(void) = NULL;
|
|
+void (*set_codec_replay)(void) = NULL;
|
|
+void (*set_codec_replay_record)(void);
|
|
+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 (*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;
|
|
+
|
|
+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 */
|
|
+static 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;
|
|
+
|
|
+static int codec_mic_gain;
|
|
+static int pop_dma_flag;
|
|
+static int last_dma_buffer_id;
|
|
+static int drain_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 int read_codec_file(int addr)
|
|
+{
|
|
+ while (__icdc_rgwr_ready());
|
|
+ __icdc_set_addr(addr);
|
|
+ mdelay(1);
|
|
+ return(__icdc_get_value());
|
|
+}
|
|
+
|
|
+#if 0 /* mask warning */
|
|
+static void printk_codec_files(void)
|
|
+{
|
|
+ int cnt;
|
|
+
|
|
+ printk("\n");
|
|
+
|
|
+ printk("REG_CPM_I2SCDR=0x%08x\n",REG_CPM_I2SCDR);
|
|
+ printk("REG_CPM_CLKGR=0x%08x\n",REG_CPM_CLKGR);
|
|
+ printk("REG_CPM_CPCCR=0x%08x\n",REG_CPM_CPCCR);
|
|
+ printk("REG_AIC_FR=0x%08x\n",REG_AIC_FR);
|
|
+ printk("REG_AIC_CR=0x%08x\n",REG_AIC_CR);
|
|
+ printk("REG_AIC_I2SCR=0x%08x\n",REG_AIC_I2SCR);
|
|
+ printk("REG_AIC_SR=0x%08x\n",REG_AIC_SR);
|
|
+ printk("REG_ICDC_RGDATA=0x%08x\n",REG_ICDC_RGDATA);
|
|
+
|
|
+ for (cnt = 0; cnt <= 27 ; cnt++) {
|
|
+ printk(" ( %d : 0x%x ) ",cnt ,read_codec_file(cnt));
|
|
+ }
|
|
+ printk("\n");
|
|
+}
|
|
+#endif
|
|
+
|
|
+static void write_codec_file(int addr, int val)
|
|
+{
|
|
+ while (__icdc_rgwr_ready());
|
|
+ __icdc_set_addr(addr);
|
|
+ __icdc_set_cmd(val); /* write */
|
|
+ mdelay(1);
|
|
+ __icdc_set_rgwr();
|
|
+ mdelay(1);
|
|
+}
|
|
+
|
|
+static int write_codec_file_bit(int addr, int bitval, int mask_bit)
|
|
+{
|
|
+ int val;
|
|
+ while (__icdc_rgwr_ready());
|
|
+ __icdc_set_addr(addr);
|
|
+ mdelay(1);
|
|
+ val = __icdc_get_value(); /* read */
|
|
+
|
|
+ while (__icdc_rgwr_ready());
|
|
+ __icdc_set_addr(addr);
|
|
+ val &= ~(1 << mask_bit);
|
|
+ if (bitval == 1)
|
|
+ val |= 1 << mask_bit;
|
|
+
|
|
+ __icdc_set_cmd(val); /* write */
|
|
+ mdelay(1);
|
|
+ __icdc_set_rgwr();
|
|
+ mdelay(1);
|
|
+
|
|
+ while (__icdc_rgwr_ready());
|
|
+ __icdc_set_addr(addr);
|
|
+ val = __icdc_get_value(); /* read */
|
|
+
|
|
+ if (((val >> mask_bit) & bitval) == bitval)
|
|
+ return 1;
|
|
+ else
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#if 0 /* mask warning */
|
|
+/* set Audio data replay */
|
|
+static void set_audio_data_replay(void)
|
|
+{
|
|
+ /* DAC path */
|
|
+ write_codec_file(9, 0xff);
|
|
+ //write_codec_file(8, 0x30);
|
|
+ write_codec_file(8, 0x20);
|
|
+ mdelay(10);
|
|
+ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0
|
|
+ write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1
|
|
+ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1
|
|
+
|
|
+ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0
|
|
+ write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1
|
|
+
|
|
+ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0
|
|
+ //mdelay(100);
|
|
+ //write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0
|
|
+ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1
|
|
+ //mdelay(300);
|
|
+}
|
|
+#endif
|
|
+
|
|
+/* unset Audio data replay */
|
|
+static void unset_audio_data_replay(void)
|
|
+{
|
|
+ //write_codec_file_bit(1, 1, 5);//DAC_MUTE->1
|
|
+ //mdelay(800);
|
|
+ //write_codec_file_bit(5, 1, 6);//SB_OUT->1
|
|
+ //mdelay(800);
|
|
+ write_codec_file_bit(5, 1, 7);//SB_DAC->1
|
|
+ write_codec_file_bit(5, 1, 4);//SB_MIX->1
|
|
+ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1
|
|
+ write_codec_file_bit(6, 1, 1);//SB->1
|
|
+}
|
|
+
|
|
+#if 0 /* mask warning */
|
|
+/* set Record MIC input audio without playback */
|
|
+static void set_record_mic_input_audio_without_playback(void)
|
|
+{
|
|
+ /* ADC path for MIC IN */
|
|
+ jz_mic_only = 1;
|
|
+ write_codec_file(9, 0xff);
|
|
+ write_codec_file(8, 0x3f);
|
|
+ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0
|
|
+ mdelay(10);
|
|
+ write_codec_file_bit(1, 1, 2);
|
|
+ //write_codec_file_bit(1, 1, 6);//CR1.MONO->1
|
|
+
|
|
+ write_codec_file(22, 0x40);//mic 1
|
|
+ write_codec_file_bit(3, 1, 7);//CR1.HP_DIS->1
|
|
+ write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1
|
|
+ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1
|
|
+
|
|
+ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0
|
|
+ write_codec_file_bit(1, 0, 3);//CR1.DACSEL->0
|
|
+ //write_codec_file_bit(6, 1, 3);// gain set
|
|
+
|
|
+ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0
|
|
+ mdelay(100);
|
|
+ write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0
|
|
+ write_codec_file(1, 0x4);
|
|
+}
|
|
+#endif
|
|
+
|
|
+#if 0 /* mask warning */
|
|
+/* unset Record MIC input audio without playback */
|
|
+static void unset_record_mic_input_audio_without_playback(void)
|
|
+{
|
|
+ /* ADC path for MIC IN */
|
|
+ jz_mic_only = 0;
|
|
+ write_codec_file_bit(5, 1, 4);//SB_ADC->1
|
|
+ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1
|
|
+ write_codec_file(22, 0xc0);//CR3.SB_MIC1
|
|
+ write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1
|
|
+ write_codec_file_bit(1, 1, 5);//DAC_MUTE->1
|
|
+ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1
|
|
+ write_codec_file_bit(6, 1, 1);//SB->1
|
|
+}
|
|
+#endif
|
|
+
|
|
+#if 0 /* mask warning */
|
|
+/* set Record LINE input audio without playback */
|
|
+static void set_record_line_input_audio_without_playback(void)
|
|
+{
|
|
+ /* ADC path for LINE IN */
|
|
+ jz_mic_only = 1;
|
|
+ write_codec_file(9, 0xff);
|
|
+ write_codec_file(8, 0x3f);
|
|
+ mdelay(10);
|
|
+ write_codec_file(22, 0xf6);//line in 1
|
|
+ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0
|
|
+ write_codec_file_bit(3, 1, 7);//CR1.HP_DIS->1
|
|
+ write_codec_file_bit(5, 0, 3);//PMR1.SB_LIN->0
|
|
+ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1
|
|
+
|
|
+ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0
|
|
+ write_codec_file_bit(1, 0, 3);//CR1.DACSEL->0
|
|
+ mdelay(10);
|
|
+ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0
|
|
+ mdelay(100);
|
|
+ write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0
|
|
+ write_codec_file(1, 0x4);
|
|
+}
|
|
+#endif
|
|
+
|
|
+#if 0 /* mask warning */
|
|
+/* unset Record LINE input audio without playback */
|
|
+static void unset_record_line_input_audio_without_playback(void)
|
|
+{
|
|
+ /* ADC path for LINE IN */
|
|
+ write_codec_file_bit(5, 1, 4);//SB_ADC->1
|
|
+ write_codec_file_bit(5, 1, 3);//ONR1.SB_LIN->1
|
|
+
|
|
+ write_codec_file(22, 0xc0);//CR3.SB_MIC1
|
|
+ write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1
|
|
+ write_codec_file_bit(1, 1, 5);//DAC_MUTE->1
|
|
+ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1
|
|
+ write_codec_file_bit(6, 1, 1);//SB->1
|
|
+}
|
|
+#endif
|
|
+
|
|
+#if 0 /* mask warning */
|
|
+/* set Playback LINE input audio direct only */
|
|
+static void set_playback_line_input_audio_direct_only(void)
|
|
+{
|
|
+ jz_audio_reset();//or init_codec()
|
|
+ REG_AIC_I2SCR = 0x10;
|
|
+ write_codec_file(9, 0xff);
|
|
+ write_codec_file(8, 0x3f);
|
|
+ mdelay(10);
|
|
+ write_codec_file(22, 0xf6);//line in 1
|
|
+ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0
|
|
+ mdelay(10);
|
|
+ write_codec_file_bit(1, 1, 2);//CR1.HP_BYPASS->1
|
|
+ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0
|
|
+ write_codec_file_bit(1, 0, 3);//CR1.DACSEL->0
|
|
+ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1
|
|
+ write_codec_file_bit(5, 0, 3);//PMR1.SB_LIN->0
|
|
+
|
|
+ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0
|
|
+ mdelay(100);
|
|
+ write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0
|
|
+ //write_codec_file_bit(5, 1, 7);//PMR1.SB_DAC->1
|
|
+ //write_codec_file_bit(5, 1, 4);//PMR1.SB_ADC->1
|
|
+}
|
|
+#endif
|
|
+
|
|
+#if 0 /* mask warning */
|
|
+/* unset Playback LINE input audio direct only */
|
|
+static void unset_playback_line_input_audio_direct_only(void)
|
|
+{
|
|
+ write_codec_file_bit(6, 0, 3);//GIM->0
|
|
+ write_codec_file_bit(1, 0, 2);//PMR1.BYPASS->0
|
|
+ write_codec_file_bit(5, 1, 3);//PMR1.SB_LINE->1
|
|
+ write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1
|
|
+ mdelay(100);
|
|
+ write_codec_file_bit(5, 1, 5);//PMR1.SB_MIX->1
|
|
+ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1
|
|
+ write_codec_file_bit(6, 1, 1);//SB->1
|
|
+}
|
|
+#endif
|
|
+
|
|
+#if 0 /* mask warning */
|
|
+/* set Record MIC input audio with direct playback */
|
|
+static void set_record_mic_input_audio_with_direct_playback(void)
|
|
+{
|
|
+ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0
|
|
+ jz_mic_only = 0;
|
|
+ write_codec_file(9, 0xff);
|
|
+ write_codec_file(8, 0x3f);
|
|
+ mdelay(10);
|
|
+
|
|
+ write_codec_file(22, 0x60);//mic 1
|
|
+ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0
|
|
+ write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1
|
|
+ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1
|
|
+ write_codec_file_bit(1, 0, 7);//CR1.SB_MICBIAS->0
|
|
+ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0
|
|
+
|
|
+ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0
|
|
+ write_codec_file_bit(1, 0, 3);//CR1.DACSEL->0
|
|
+ write_codec_file_bit(6, 1, 3);// gain set
|
|
+
|
|
+ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0
|
|
+ mdelay(100);
|
|
+ write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0
|
|
+ //write_codec_file(1, 0x4);
|
|
+}
|
|
+#endif
|
|
+
|
|
+#if 0 /* mask warning */
|
|
+/* unset Record MIC input audio with direct playback */
|
|
+static void unset_record_mic_input_audio_with_direct_playback(void)
|
|
+{
|
|
+ /* ADC path for MIC IN */
|
|
+ jz_mic_only = 0;
|
|
+ write_codec_file_bit(5, 1, 4);//SB_ADC->1
|
|
+ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1
|
|
+ write_codec_file(22, 0xc0);//CR3.SB_MIC1
|
|
+ write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1
|
|
+ write_codec_file_bit(1, 1, 5);//DAC_MUTE->1
|
|
+ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1
|
|
+ write_codec_file_bit(6, 1, 1);//SB->1
|
|
+}
|
|
+#endif
|
|
+
|
|
+#if 0 /* mask warning */
|
|
+/* set Record playing audio mixed with MIC input audio */
|
|
+static void set_record_playing_audio_mixed_with_mic_input_audio(void)
|
|
+{
|
|
+ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0
|
|
+ write_codec_file(9, 0xff);
|
|
+ //write_codec_file(8, 0x30);
|
|
+ write_codec_file(8, 0x20);
|
|
+ mdelay(10);
|
|
+
|
|
+ write_codec_file(22, 0x63);//mic 1
|
|
+
|
|
+ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0
|
|
+ write_codec_file_bit(6, 1, 3);// gain set
|
|
+
|
|
+ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0
|
|
+ write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1
|
|
+ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1
|
|
+ write_codec_file_bit(1, 0, 7);//CR1.SB_MICBIAS->0
|
|
+ write_codec_file_bit(22, 0, 7);//CR3.SB_MIC->0
|
|
+ write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1
|
|
+ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0
|
|
+ write_codec_file_bit(5, 0, 4);//PMR1.SB_MIX->0
|
|
+}
|
|
+#endif
|
|
+
|
|
+#if 0 /* mask warning */
|
|
+/* unset Record playing audio mixed with MIC input audio */
|
|
+static void unset_record_playing_audio_mixed_with_mic_input_audio(void)
|
|
+{
|
|
+ /* ADC path */
|
|
+ write_codec_file_bit(5, 1, 4);//SB_ADC->1
|
|
+ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1
|
|
+ //write_codec_file_bit(1, 1, 6);//CR1.MONO->1
|
|
+ write_codec_file(22, 0xc0);//CR3.SB_MIC1->1
|
|
+ //write_codec_file_bit(1, 1, 5);//DAC_MUTE->1
|
|
+ //write_codec_file_bit(5, 1, 6);//SB_OUT->1
|
|
+ write_codec_file_bit(5, 1, 7);//SB_DAC->1
|
|
+ write_codec_file_bit(5, 1, 5);//SB_MIX->1
|
|
+ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1
|
|
+ write_codec_file_bit(6, 1, 1);//SB->1
|
|
+}
|
|
+#endif
|
|
+
|
|
+#if 0 /* mask warning */
|
|
+/* set Record MIC input audio with Audio data replay (full duplex) */
|
|
+static void set_record_mic_input_audio_with_audio_data_replay(void)
|
|
+{
|
|
+ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0
|
|
+ write_codec_file(9, 0xff);
|
|
+ //write_codec_file(8, 0x30);
|
|
+ write_codec_file(8, 0x20);
|
|
+ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0
|
|
+ write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1
|
|
+ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1
|
|
+
|
|
+ write_codec_file_bit(22, 0, 7);//CR3.SB_MIC->0
|
|
+ write_codec_file_bit(1, 0, 7);//CR1.SB_MICBIAS->0
|
|
+
|
|
+ write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1
|
|
+ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0
|
|
+}
|
|
+#endif
|
|
+
|
|
+#if 0 /* mask warning */
|
|
+/* unset Record MIC input audio with Audio data replay (full duplex) */
|
|
+static void unset_record_mic_input_audio_with_audio_data_replay(void)
|
|
+{
|
|
+ /* ADC path */
|
|
+ write_codec_file_bit(5, 1, 4);//SB_ADC->1
|
|
+ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1
|
|
+ //write_codec_file_bit(1, 1, 6);//CR1.MONO->1
|
|
+ write_codec_file(22, 0xc0);//CR3.SB_MIC1->1
|
|
+ write_codec_file_bit(5, 1, 7);//SB_DAC->1
|
|
+ write_codec_file_bit(5, 1, 5);//SB_MIX->1
|
|
+ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1
|
|
+ write_codec_file_bit(6, 1, 1);//SB->1
|
|
+}
|
|
+#endif
|
|
+
|
|
+#if 0 /* mask warning */
|
|
+/* set Record LINE input audio with Audio data replay (full duplex for linein) */
|
|
+static void set_record_line_input_audio_with_audio_data_replay(void)
|
|
+{
|
|
+ write_codec_file(9, 0xff);
|
|
+ //write_codec_file(8, 0x30);
|
|
+ write_codec_file(8, 0x20);
|
|
+ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0
|
|
+ write_codec_file_bit(5, 0, 3);//PMR1.SB_LIN->0
|
|
+ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1
|
|
+ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1
|
|
+ //write_codec_file_bit(22, 1, 7);//CR3.SB_MIC->1
|
|
+ write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1
|
|
+ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0
|
|
+
|
|
+
|
|
+ //jz_mic_only = 1;
|
|
+ write_codec_file(22, 0xc6);//line in 1
|
|
+ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0
|
|
+ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0
|
|
+ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0
|
|
+}
|
|
+#endif
|
|
+
|
|
+#if 0 /* mask warning */
|
|
+/* unset Record LINE input audio with Audio data replay (full duplex for linein) */
|
|
+static void unset_record_line_input_audio_with_audio_data_replay(void)
|
|
+{
|
|
+ /* ADC path */
|
|
+ write_codec_file_bit(5, 1, 4);//SB_ADC->1
|
|
+ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1
|
|
+ //write_codec_file_bit(1, 1, 6);//CR1.MONO->1
|
|
+ write_codec_file(22, 0xc0);//CR3.SB_MIC1->1
|
|
+ write_codec_file_bit(5, 1, 7);//SB_DAC->1
|
|
+ write_codec_file_bit(5, 1, 5);//SB_MIX->1
|
|
+ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1
|
|
+ write_codec_file_bit(6, 1, 1);//SB->1
|
|
+}
|
|
+#endif
|
|
+
|
|
+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);
|
|
+ //set_dma_mode(chan, mode);
|
|
+ jz_set_oss_dma(chan, mode, jz_audio_format);
|
|
+ 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 (rate > 48000)
|
|
+ rate = 48000;
|
|
+ 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;
|
|
+ *(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;
|
|
+}
|
|
+
|
|
+#if 0
|
|
+static void replay_fill_2x16_s(signed long src_start, int count, int id)
|
|
+{
|
|
+ int cnt = 0;
|
|
+ signed short d1;
|
|
+ signed long l1;
|
|
+ int mute_cnt = 0;
|
|
+ signed long tmp1,tmp2;
|
|
+ volatile signed short *s = (signed short *)src_start;
|
|
+ volatile signed long *dp = (signed long*)(*(out_dma_buf + id));
|
|
+#if defined(CONFIG_I2S_ICDC)
|
|
+ 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;
|
|
+}
|
|
+#else
|
|
+static void replay_fill_2x16_s(signed long src_start, int count, int id)
|
|
+{
|
|
+ int cnt = 0;
|
|
+ signed short d1;
|
|
+ signed long l1;
|
|
+
|
|
+#if 0
|
|
+ volatile signed short *s = (signed short *)src_start;
|
|
+ volatile signed short *dp = (signed short*)(*(out_dma_buf + id));
|
|
+ memcpy((char*)dp, (char*)s, count);
|
|
+ *(out_dma_buf_data_count + id) = count;
|
|
+#else
|
|
+ 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;
|
|
+
|
|
+ *(dp ++) = l1;
|
|
+ }
|
|
+ cnt *= jz_audio_b;
|
|
+ *(out_dma_buf_data_count + id) = cnt;
|
|
+#endif
|
|
+}
|
|
+#endif
|
|
+
|
|
+
|
|
+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);
|
|
+ /* print all files */
|
|
+ __i2s_set_oss_sample_size(16);
|
|
+ __i2s_set_iss_sample_size(16);
|
|
+ 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;
|
|
+ if (val > 31)
|
|
+ val = 31;
|
|
+ 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;
|
|
+ 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);
|
|
+ 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;
|
|
+ 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();
|
|
+ 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 */
|
|
+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;
|
|
+}
|
|
+
|
|
+static int __init init_jz_i2s(void)
|
|
+{
|
|
+ int errno, retval;
|
|
+#if defined(CONFIG_I2S_DLV)
|
|
+
|
|
+ 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
|
|
+
|
|
+ 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 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
|
|
+ if(set_codec_volume_table)
|
|
+ set_codec_volume_table();
|
|
+
|
|
+ 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_JZ4750) || defined(CONFIG_SOC_JZ4750D)
|
|
+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
|
|
+
|
|
+#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;
|
|
+
|
|
+ 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;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ 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
|
|
+
|
|
+static int jz_audio_release(struct inode *inode, struct file *file)
|
|
+{
|
|
+ unsigned long flags;
|
|
+ struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data;
|
|
+ unsigned long tfl;
|
|
+
|
|
+ if (controller == NULL)
|
|
+ return -ENODEV;
|
|
+
|
|
+ pop_dma_flag = 0;
|
|
+ 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_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);
|
|
+ }
|
|
+#endif
|
|
+ disable_dma(controller->dma1);
|
|
+ set_dma_count(controller->dma1, 0);
|
|
+ __i2s_disable_transmit_dma();
|
|
+ __i2s_disable_replay();
|
|
+ __aic_flush_fifo();
|
|
+ if(clear_codec_replay)
|
|
+ clear_codec_replay();
|
|
+ __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(turn_off_codec)
|
|
+ turn_off_codec();
|
|
+ }
|
|
+
|
|
+ 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(clear_codec_record)
|
|
+ clear_codec_record();
|
|
+
|
|
+ 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);
|
|
+ __i2s_disable();
|
|
+ if(turn_off_codec)
|
|
+ turn_off_codec();
|
|
+ abnormal_data_count = 0;
|
|
+ }
|
|
+
|
|
+#if defined(CONFIG_I2S_DLV)
|
|
+ write_codec_file(9, 0xff);
|
|
+ write_codec_file(8, 0x3f);
|
|
+#endif
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int jz_audio_open(struct inode *inode, struct file *file)
|
|
+{
|
|
+ int i;
|
|
+ struct jz_i2s_controller_info *controller = i2s_controller;
|
|
+
|
|
+ if (controller == NULL)
|
|
+ return -ENODEV;
|
|
+
|
|
+ mdelay(2);
|
|
+ REG_DMAC_DMACKE(0) = 0x3f;
|
|
+ pop_dma_flag = 0;
|
|
+ if (controller->opened1 == 1 || controller->opened2 == 1 ) {
|
|
+ printk("\naudio is busy!\n");
|
|
+ return -EBUSY;
|
|
+ }
|
|
+ jz_codec_config = 0;
|
|
+
|
|
+ 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;
|
|
+ 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;
|
|
+ }
|
|
+
|
|
+ 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) {
|
|
+ if(set_codec_replay)
|
|
+ set_codec_replay();
|
|
+ }
|
|
+
|
|
+ if (file->f_mode & FMODE_READ) {
|
|
+ abnormal_data_count = 0;
|
|
+ if(set_codec_record)
|
|
+ set_codec_record();
|
|
+ }
|
|
+
|
|
+#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)
|
|
+ if (file->f_mode & FMODE_WRITE) {
|
|
+
|
|
+ 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
|
|
+ } else if (file->f_mode & FMODE_READ) {
|
|
+ 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
|
|
+
|
|
+ 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;
|
|
+ unsigned int flags;
|
|
+ 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, bytes = 0;
|
|
+ if (!(file->f_mode & FMODE_WRITE))
|
|
+ return -EINVAL;
|
|
+
|
|
+ spin_lock_irqsave(&controller->ioctllock, flags);
|
|
+ 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);
|
|
+ /* 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 = bytes;
|
|
+
|
|
+ return copy_to_user((void *)arg, &abinfo,
|
|
+ sizeof(abinfo)) ? -EFAULT : 0;
|
|
+ }
|
|
+ case SNDCTL_DSP_GETISPACE:
|
|
+ {
|
|
+ int i, 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 = 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);
|
|
+ cinfo.bytes = controller->total_bytes;
|
|
+ cinfo.blocks = controller->blocks;
|
|
+ cinfo.ptr = controller->nextIn;
|
|
+ controller->blocks = 0;
|
|
+ spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
+
|
|
+ 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);
|
|
+ cinfo.bytes = controller->total_bytes;
|
|
+ cinfo.blocks = controller->blocks;
|
|
+ cinfo.ptr = controller->nextOut;
|
|
+ controller->blocks = 0;
|
|
+ spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
+
|
|
+ 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);
|
|
+ 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);
|
|
+
|
|
+ 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 long flags;
|
|
+ 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);
|
|
+ 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);
|
|
+
|
|
+ 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;
|
|
+ unsigned long flags;
|
|
+
|
|
+ if (count < 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ __i2s_enable_receive_dma();
|
|
+ __i2s_enable_record();
|
|
+
|
|
+ spin_lock_irqsave(&controller->ioctllock, flags);
|
|
+ controller->nextIn = 0;
|
|
+ spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
+
|
|
+ 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);
|
|
+ controller->nextIn += ret;
|
|
+ spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
+
|
|
+ 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;
|
|
+ unsigned int flags;
|
|
+ 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);
|
|
+ controller->nextOut = 0;
|
|
+ spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
+ 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);
|
|
+ controller->nextOut += ret;
|
|
+ spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
+
|
|
+ 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 (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);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ 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
|
|
--- /dev/null
|
|
+++ b/sound/oss/jz_i2s_dlv_dma_test.c
|
|
@@ -0,0 +1,2808 @@
|
|
+/*
|
|
+ * linux/drivers/sound/jz_i2s_dlv.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"
|
|
+
|
|
+#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 MAXDELAY 50000
|
|
+#define JZCODEC_RW_BUFFER_SIZE 3
|
|
+#define JZCODEC_RW_BUFFER_TOTAL 3
|
|
+#define JZCODEC_USER_BUFFER 6
|
|
+
|
|
+#define USE_NONE 1
|
|
+#define USE_MIC 2
|
|
+#define USE_LINEIN 3
|
|
+
|
|
+static int jz_audio_rate;
|
|
+static char jz_audio_format;
|
|
+static char jz_audio_volume;
|
|
+static char 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 audio_mix_modcnt;
|
|
+static int codec_volue_shift;
|
|
+static int jz_audio_dma_tran_count;//bytes count of one DMA transfer
|
|
+static int jz_mic_only = 1;
|
|
+static int jz_codec_config = 0;
|
|
+static int use_mic_line_flag;
|
|
+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;
|
|
+
|
|
+
|
|
+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);
|
|
+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 (pop_wait_queue);
|
|
+
|
|
+struct jz_i2s_controller_info
|
|
+{
|
|
+ int io_base;
|
|
+ int dma1; /* play */
|
|
+ int dma2; /* 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 fcj add
|
|
+ void (*codec_write) (u8 reg, u16 val);//to AK4642EN,val is 8bit
|
|
+ /* Wait for codec-ready. Ok to sleep here. */
|
|
+ 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;
|
|
+
|
|
+typedef struct hpvol_shift_s
|
|
+{
|
|
+ int hpvol;
|
|
+ int shift;
|
|
+} hpvol_shift_t;
|
|
+
|
|
+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 int read_codec_file(int addr)
|
|
+{
|
|
+ while (__icdc_rgwr_ready());
|
|
+ __icdc_set_addr(addr);
|
|
+ mdelay(1);
|
|
+ return(__icdc_get_value());
|
|
+}
|
|
+
|
|
+static void printk_codec_files(void)
|
|
+{
|
|
+ int cnt;
|
|
+
|
|
+ printk("\n");
|
|
+
|
|
+ printk("REG_CPM_I2SCDR=0x%08x\n",REG_CPM_I2SCDR);
|
|
+ printk("REG_CPM_CLKGR=0x%08x\n",REG_CPM_CLKGR);
|
|
+ printk("REG_CPM_CPCCR=0x%08x\n",REG_CPM_CPCCR);
|
|
+ printk("REG_AIC_FR=0x%08x\n",REG_AIC_FR);
|
|
+ printk("REG_AIC_CR=0x%08x\n",REG_AIC_CR);
|
|
+ printk("REG_AIC_I2SCR=0x%08x\n",REG_AIC_I2SCR);
|
|
+ printk("REG_AIC_SR=0x%08x\n",REG_AIC_SR);
|
|
+ printk("REG_ICDC_RGDATA=0x%08x\n",REG_ICDC_RGDATA);
|
|
+
|
|
+ for (cnt = 0; cnt <= 27 ; cnt++) {
|
|
+ printk(" ( %d : 0x%x ) ",cnt ,read_codec_file(cnt));
|
|
+ }
|
|
+ printk("\n");
|
|
+}
|
|
+
|
|
+static void write_codec_file(int addr, int val)
|
|
+{
|
|
+ while (__icdc_rgwr_ready());
|
|
+ __icdc_set_addr(addr);
|
|
+ __icdc_set_cmd(val); /* write */
|
|
+ mdelay(1);
|
|
+ __icdc_set_rgwr();
|
|
+ mdelay(1);
|
|
+}
|
|
+
|
|
+static int write_codec_file_bit(int addr, int bitval, int mask_bit)
|
|
+{
|
|
+ int val;
|
|
+ while (__icdc_rgwr_ready());
|
|
+ __icdc_set_addr(addr);
|
|
+ mdelay(1);
|
|
+ val = __icdc_get_value(); /* read */
|
|
+
|
|
+ while (__icdc_rgwr_ready());
|
|
+ __icdc_set_addr(addr);
|
|
+ val &= ~(1 << mask_bit);
|
|
+ if (bitval == 1)
|
|
+ val |= 1 << mask_bit;
|
|
+
|
|
+ __icdc_set_cmd(val); /* write */
|
|
+ mdelay(1);
|
|
+ __icdc_set_rgwr();
|
|
+ mdelay(1);
|
|
+
|
|
+ while (__icdc_rgwr_ready());
|
|
+ __icdc_set_addr(addr);
|
|
+ val = __icdc_get_value(); /* read */
|
|
+
|
|
+ if (((val >> mask_bit) & bitval) == bitval)
|
|
+ return 1;
|
|
+ else
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* set Audio data replay */
|
|
+static void set_audio_data_replay()
|
|
+{
|
|
+ /* DAC path */
|
|
+ write_codec_file(9, 0xff);
|
|
+ //write_codec_file(8, 0x30);
|
|
+ write_codec_file(8, 0x20);
|
|
+ mdelay(10);
|
|
+ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0
|
|
+ write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1
|
|
+ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1
|
|
+
|
|
+ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0
|
|
+ write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1
|
|
+
|
|
+ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0
|
|
+ //mdelay(100);
|
|
+ //write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0
|
|
+ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1
|
|
+ //mdelay(300);
|
|
+}
|
|
+
|
|
+/* unset Audio data replay */
|
|
+static void unset_audio_data_replay(void)
|
|
+{
|
|
+ //write_codec_file_bit(1, 1, 5);//DAC_MUTE->1
|
|
+ //mdelay(800);
|
|
+ //write_codec_file_bit(5, 1, 6);//SB_OUT->1
|
|
+ //mdelay(800);
|
|
+ write_codec_file_bit(5, 1, 7);//SB_DAC->1
|
|
+ write_codec_file_bit(5, 1, 4);//SB_MIX->1
|
|
+ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1
|
|
+ write_codec_file_bit(6, 1, 1);//SB->1
|
|
+}
|
|
+
|
|
+/* set Record MIC input audio without playback */
|
|
+static void set_record_mic_input_audio_without_playback(void)
|
|
+{
|
|
+ /* ADC path for MIC IN */
|
|
+ jz_mic_only = 1;
|
|
+ write_codec_file(9, 0xff);
|
|
+ write_codec_file(8, 0x3f);
|
|
+ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0
|
|
+ mdelay(10);
|
|
+ write_codec_file_bit(1, 1, 2);
|
|
+ //write_codec_file_bit(1, 1, 6);//CR1.MONO->1
|
|
+
|
|
+ write_codec_file(22, 0x40);//mic 1
|
|
+ write_codec_file_bit(3, 1, 7);//CR1.HP_DIS->1
|
|
+ write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1
|
|
+ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1
|
|
+
|
|
+ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0
|
|
+ write_codec_file_bit(1, 0, 3);//CR1.DACSEL->0
|
|
+ //write_codec_file_bit(6, 1, 3);// gain set
|
|
+
|
|
+ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0
|
|
+ mdelay(100);
|
|
+ write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0
|
|
+ write_codec_file(1, 0x4);
|
|
+}
|
|
+
|
|
+/* unset Record MIC input audio without playback */
|
|
+static void unset_record_mic_input_audio_without_playback(void)
|
|
+{
|
|
+ /* ADC path for MIC IN */
|
|
+ jz_mic_only = 0;
|
|
+ write_codec_file_bit(5, 1, 4);//SB_ADC->1
|
|
+ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1
|
|
+ write_codec_file(22, 0xc0);//CR3.SB_MIC1
|
|
+ write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1
|
|
+ write_codec_file_bit(1, 1, 5);//DAC_MUTE->1
|
|
+ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1
|
|
+ write_codec_file_bit(6, 1, 1);//SB->1
|
|
+}
|
|
+
|
|
+/* set Record LINE input audio without playback */
|
|
+static void set_record_line_input_audio_without_playback(void)
|
|
+{
|
|
+ /* ADC path for LINE IN */
|
|
+ jz_mic_only = 1;
|
|
+ write_codec_file(9, 0xff);
|
|
+ write_codec_file(8, 0x3f);
|
|
+ mdelay(10);
|
|
+ write_codec_file(22, 0xf6);//line in 1
|
|
+ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0
|
|
+ write_codec_file_bit(3, 1, 7);//CR1.HP_DIS->1
|
|
+ write_codec_file_bit(5, 0, 3);//PMR1.SB_LIN->0
|
|
+ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1
|
|
+
|
|
+ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0
|
|
+ write_codec_file_bit(1, 0, 3);//CR1.DACSEL->0
|
|
+ mdelay(10);
|
|
+ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0
|
|
+ mdelay(100);
|
|
+ write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0
|
|
+ write_codec_file(1, 0x4);
|
|
+}
|
|
+
|
|
+/* unset Record LINE input audio without playback */
|
|
+static void unset_record_line_input_audio_without_playback(void)
|
|
+{
|
|
+ /* ADC path for LINE IN */
|
|
+ write_codec_file_bit(5, 1, 4);//SB_ADC->1
|
|
+ write_codec_file_bit(5, 1, 3);//ONR1.SB_LIN->1
|
|
+
|
|
+ write_codec_file(22, 0xc0);//CR3.SB_MIC1
|
|
+ write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1
|
|
+ write_codec_file_bit(1, 1, 5);//DAC_MUTE->1
|
|
+ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1
|
|
+ write_codec_file_bit(6, 1, 1);//SB->1
|
|
+}
|
|
+
|
|
+/* set Playback LINE input audio direct only */
|
|
+static void set_playback_line_input_audio_direct_only()
|
|
+{
|
|
+ jz_audio_reset();//or init_codec()
|
|
+ REG_AIC_I2SCR = 0x10;
|
|
+ write_codec_file(9, 0xff);
|
|
+ write_codec_file(8, 0x3f);
|
|
+ mdelay(10);
|
|
+ write_codec_file(22, 0xf6);//line in 1
|
|
+ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0
|
|
+ mdelay(10);
|
|
+ write_codec_file_bit(1, 1, 2);//CR1.HP_BYPASS->1
|
|
+ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0
|
|
+ write_codec_file_bit(1, 0, 3);//CR1.DACSEL->0
|
|
+ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1
|
|
+ write_codec_file_bit(5, 0, 3);//PMR1.SB_LIN->0
|
|
+
|
|
+ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0
|
|
+ mdelay(100);
|
|
+ write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0
|
|
+ //write_codec_file_bit(5, 1, 7);//PMR1.SB_DAC->1
|
|
+ //write_codec_file_bit(5, 1, 4);//PMR1.SB_ADC->1
|
|
+}
|
|
+
|
|
+/* unset Playback LINE input audio direct only */
|
|
+static void unset_playback_line_input_audio_direct_only()
|
|
+{
|
|
+ write_codec_file_bit(6, 0, 3);//GIM->0
|
|
+ write_codec_file_bit(1, 0, 2);//PMR1.BYPASS->0
|
|
+ write_codec_file_bit(5, 1, 3);//PMR1.SB_LINE->1
|
|
+ write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1
|
|
+ mdelay(100);
|
|
+ write_codec_file_bit(5, 1, 5);//PMR1.SB_MIX->1
|
|
+ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1
|
|
+ write_codec_file_bit(6, 1, 1);//SB->1
|
|
+}
|
|
+
|
|
+/* set Record MIC input audio with direct playback */
|
|
+static void set_record_mic_input_audio_with_direct_playback(void)
|
|
+{
|
|
+ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0
|
|
+ jz_mic_only = 0;
|
|
+ write_codec_file(9, 0xff);
|
|
+ write_codec_file(8, 0x3f);
|
|
+ mdelay(10);
|
|
+
|
|
+ write_codec_file(22, 0x60);//mic 1
|
|
+ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0
|
|
+ write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1
|
|
+ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1
|
|
+ write_codec_file_bit(1, 0, 7);//CR1.SB_MICBIAS->0
|
|
+ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0
|
|
+
|
|
+ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0
|
|
+ write_codec_file_bit(1, 0, 3);//CR1.DACSEL->0
|
|
+ write_codec_file_bit(6, 1, 3);// gain set
|
|
+
|
|
+ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0
|
|
+ mdelay(100);
|
|
+ write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0
|
|
+ //write_codec_file(1, 0x4);
|
|
+}
|
|
+
|
|
+/* unset Record MIC input audio with direct playback */
|
|
+static void unset_record_mic_input_audio_with_direct_playback(void)
|
|
+{
|
|
+ /* ADC path for MIC IN */
|
|
+ jz_mic_only = 0;
|
|
+ write_codec_file_bit(5, 1, 4);//SB_ADC->1
|
|
+ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1
|
|
+ write_codec_file(22, 0xc0);//CR3.SB_MIC1
|
|
+ write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1
|
|
+ write_codec_file_bit(1, 1, 5);//DAC_MUTE->1
|
|
+ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1
|
|
+ write_codec_file_bit(6, 1, 1);//SB->1
|
|
+}
|
|
+
|
|
+/* set Record playing audio mixed with MIC input audio */
|
|
+static void set_record_playing_audio_mixed_with_mic_input_audio(void)
|
|
+{
|
|
+ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0
|
|
+ write_codec_file(9, 0xff);
|
|
+ //write_codec_file(8, 0x30);
|
|
+ write_codec_file(8, 0x20);
|
|
+ mdelay(10);
|
|
+
|
|
+ write_codec_file(22, 0x63);//mic 1
|
|
+
|
|
+ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0
|
|
+ write_codec_file_bit(6, 1, 3);// gain set
|
|
+
|
|
+ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0
|
|
+ write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1
|
|
+ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1
|
|
+ write_codec_file_bit(1, 0, 7);//CR1.SB_MICBIAS->0
|
|
+ write_codec_file_bit(22, 0, 7);//CR3.SB_MIC->0
|
|
+ write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1
|
|
+ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0
|
|
+ write_codec_file_bit(5, 0, 4);//PMR1.SB_MIX->0
|
|
+}
|
|
+
|
|
+/* unset Record playing audio mixed with MIC input audio */
|
|
+static void unset_record_playing_audio_mixed_with_mic_input_audio(void)
|
|
+{
|
|
+ /* ADC path */
|
|
+ write_codec_file_bit(5, 1, 4);//SB_ADC->1
|
|
+ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1
|
|
+ //write_codec_file_bit(1, 1, 6);//CR1.MONO->1
|
|
+ write_codec_file(22, 0xc0);//CR3.SB_MIC1->1
|
|
+ //write_codec_file_bit(1, 1, 5);//DAC_MUTE->1
|
|
+ //write_codec_file_bit(5, 1, 6);//SB_OUT->1
|
|
+ write_codec_file_bit(5, 1, 7);//SB_DAC->1
|
|
+ write_codec_file_bit(5, 1, 5);//SB_MIX->1
|
|
+ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1
|
|
+ write_codec_file_bit(6, 1, 1);//SB->1
|
|
+}
|
|
+
|
|
+/* set Record MIC input audio with Audio data replay (full duplex) */
|
|
+static void set_record_mic_input_audio_with_audio_data_replay(void)
|
|
+{
|
|
+ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0
|
|
+ write_codec_file(9, 0xff);
|
|
+ //write_codec_file(8, 0x30);
|
|
+ write_codec_file(8, 0x20);
|
|
+ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0
|
|
+ write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1
|
|
+ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1
|
|
+
|
|
+ write_codec_file_bit(22, 0, 7);//CR3.SB_MIC->0
|
|
+ write_codec_file_bit(1, 0, 7);//CR1.SB_MICBIAS->0
|
|
+
|
|
+ write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1
|
|
+ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0
|
|
+}
|
|
+
|
|
+/* unset Record MIC input audio with Audio data replay (full duplex) */
|
|
+static void unset_record_mic_input_audio_with_audio_data_replay(void)
|
|
+{
|
|
+ /* ADC path */
|
|
+ write_codec_file_bit(5, 1, 4);//SB_ADC->1
|
|
+ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1
|
|
+ //write_codec_file_bit(1, 1, 6);//CR1.MONO->1
|
|
+ write_codec_file(22, 0xc0);//CR3.SB_MIC1->1
|
|
+ write_codec_file_bit(5, 1, 7);//SB_DAC->1
|
|
+ write_codec_file_bit(5, 1, 5);//SB_MIX->1
|
|
+ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1
|
|
+ write_codec_file_bit(6, 1, 1);//SB->1
|
|
+}
|
|
+
|
|
+/////////
|
|
+/* set Record LINE input audio with Audio data replay (full duplex for linein) */
|
|
+static void set_record_line_input_audio_with_audio_data_replay(void)
|
|
+{
|
|
+ write_codec_file(9, 0xff);
|
|
+ //write_codec_file(8, 0x30);
|
|
+ write_codec_file(8, 0x20);
|
|
+ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0
|
|
+ write_codec_file_bit(5, 0, 3);//PMR1.SB_LIN->0
|
|
+ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1
|
|
+ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1
|
|
+ //write_codec_file_bit(22, 1, 7);//CR3.SB_MIC->1
|
|
+ write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1
|
|
+ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0
|
|
+
|
|
+
|
|
+ //jz_mic_only = 1;
|
|
+ write_codec_file(22, 0xc6);//line in 1
|
|
+ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0
|
|
+ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0
|
|
+ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0
|
|
+}
|
|
+
|
|
+/* unset Record LINE input audio with Audio data replay (full duplex for linein) */
|
|
+static void unset_record_line_input_audio_with_audio_data_replay(void)
|
|
+{
|
|
+ /* ADC path */
|
|
+ write_codec_file_bit(5, 1, 4);//SB_ADC->1
|
|
+ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1
|
|
+ //write_codec_file_bit(1, 1, 6);//CR1.MONO->1
|
|
+ write_codec_file(22, 0xc0);//CR3.SB_MIC1->1
|
|
+ write_codec_file_bit(5, 1, 7);//SB_DAC->1
|
|
+ write_codec_file_bit(5, 1, 5);//SB_MIX->1
|
|
+ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1
|
|
+ write_codec_file_bit(6, 1, 1);//SB->1
|
|
+}
|
|
+/////////
|
|
+
|
|
+static inline int get_buffer_id(struct buffer_queue_s *q)
|
|
+{
|
|
+ int r;
|
|
+ unsigned long flags;
|
|
+ int i;
|
|
+ spin_lock_irqsave(&q->lock, flags);
|
|
+ //spin_lock(&q->lock);
|
|
+ if (q->count == 0) {
|
|
+ spin_unlock_irqrestore(&q->lock, flags);
|
|
+ //spin_unlock(&q->lock);
|
|
+ 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);
|
|
+ //spin_unlock(&q->lock);
|
|
+ return r;
|
|
+}
|
|
+
|
|
+static inline void put_buffer_id(struct buffer_queue_s *q, int id)
|
|
+{
|
|
+ unsigned long flags;
|
|
+ spin_lock_irqsave(&q->lock, flags);
|
|
+ //spin_lock(&q->lock);
|
|
+ *(q->id + q->count) = id;
|
|
+ q->count ++;
|
|
+ spin_unlock_irqrestore(&q->lock, flags);
|
|
+ //spin_unlock(&q->lock);
|
|
+}
|
|
+
|
|
+static inline int elements_in_queue(struct buffer_queue_s *q)
|
|
+{
|
|
+ int r;
|
|
+ unsigned long flags;
|
|
+ spin_lock_irqsave(&q->lock, flags);
|
|
+ //spin_lock(&q->lock);
|
|
+ r = q->count;
|
|
+ spin_unlock_irqrestore(&q->lock, flags);
|
|
+ //spin_unlock(&q->lock);
|
|
+ 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;
|
|
+ //for DSP_GETOPTR
|
|
+ //spin_lock_irqsave(&controller->ioctllock, flags);
|
|
+ spin_lock(&controller->ioctllock);
|
|
+ jz_audio_dma_tran_count = count;
|
|
+ //spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
+ spin_unlock(&controller->ioctllock);
|
|
+ flags = claim_dma_lock();
|
|
+ //__dmac_disable_module(0);
|
|
+ disable_dma(chan);
|
|
+ clear_dma_ff(chan);
|
|
+
|
|
+ set_dma_mode(chan, mode);
|
|
+ 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);
|
|
+ //__dmac_enable_module(0);
|
|
+ release_dma_lock(flags);
|
|
+ //dump_jz_dma_channel(chan);
|
|
+
|
|
+ //printk_codec_files();
|
|
+}
|
|
+
|
|
+static irqreturn_t jz_i2s_record_dma_irq (int irq, void *dev_id)
|
|
+{
|
|
+ int id1, id2;
|
|
+
|
|
+ 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);
|
|
+ //for DSP_GETIPTR
|
|
+ //spin_lock_irqsave(&controller->ioctllock, flags);
|
|
+ spin_lock(&controller->ioctllock);
|
|
+ controller->total_bytes += jz_audio_dma_tran_count;
|
|
+ controller->blocks ++;
|
|
+ //spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
+ spin_unlock(&controller->ioctllock);
|
|
+ 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);
|
|
+ //write back
|
|
+ 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);
|
|
+
|
|
+
|
|
+ //for DSP_GETOPTR
|
|
+ spin_lock_irqsave(&controller->ioctllock, flags);
|
|
+ //spin_lock(&controller->ioctllock);
|
|
+ controller->total_bytes += jz_audio_dma_tran_count;
|
|
+ controller->blocks ++;
|
|
+ spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
+ //spin_unlock(&controller->ioctllock);
|
|
+ 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); //very busy
|
|
+ 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);
|
|
+ }
|
|
+ } 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)
|
|
+{
|
|
+ __i2s_disable();
|
|
+ //schedule_timeout(5);
|
|
+
|
|
+#if defined(CONFIG_I2S_DLV)
|
|
+ __i2s_disable();
|
|
+ __i2s_as_slave();
|
|
+ __aic_internal_codec();
|
|
+ //__i2s_set_oss_sample_size(16);
|
|
+ //__i2s_set_iss_sample_size(16);
|
|
+
|
|
+#endif
|
|
+ __i2s_disable_record();
|
|
+ __i2s_disable_replay();
|
|
+ __i2s_disable_loopback();
|
|
+ __i2s_set_transmit_trigger(7);
|
|
+ __i2s_set_receive_trigger(8);
|
|
+}
|
|
+
|
|
+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; //release page error
|
|
+ }
|
|
+ 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; //release page error
|
|
+ }
|
|
+ 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 ? */
|
|
+ int speed = 0, val;
|
|
+
|
|
+ jz_audio_speed = rate;
|
|
+ if (rate > 96000)
|
|
+ rate = 96000;
|
|
+ if (rate < 8000)
|
|
+ rate = 8000;
|
|
+ jz_audio_rate = rate;
|
|
+
|
|
+#if defined(CONFIG_I2S_DLV)
|
|
+ speed = 0;
|
|
+ switch (rate) {
|
|
+ case 8000:
|
|
+ speed = 10;
|
|
+ break;
|
|
+ case 9600:
|
|
+ speed = 9;
|
|
+ break;
|
|
+ case 11025:
|
|
+ speed = 8;
|
|
+ break;
|
|
+ case 12000:
|
|
+ speed = 7;
|
|
+ break;
|
|
+ case 16000:
|
|
+ speed = 6;
|
|
+ break;
|
|
+ case 22050:
|
|
+ speed = 5;
|
|
+ break;
|
|
+ case 24000:
|
|
+ speed = 4;
|
|
+ break;
|
|
+ case 32000:
|
|
+ speed = 3;
|
|
+ break;
|
|
+ case 44100:
|
|
+ speed = 2;
|
|
+ break;
|
|
+ case 48000:
|
|
+ speed = 1;
|
|
+ break;
|
|
+ case 96000:
|
|
+ speed = 0;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ val = read_codec_file(4);
|
|
+ val = (speed << 4) | speed;
|
|
+ write_codec_file(4, val);
|
|
+
|
|
+#if 0
|
|
+ for (i = 0; i <= 27 ; i++) {
|
|
+ printk(" ( CCC %d : 0x%x ) ",i ,read_codec_file(i));
|
|
+ }
|
|
+#endif
|
|
+
|
|
+#endif
|
|
+ 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++);
|
|
+ *(dp ++) = (d1 << 16) >> 16;
|
|
+ d2 = *(s++);
|
|
+ *(dp ++) = (d2 << 16) >> 16;
|
|
+ }
|
|
+ return cnt;
|
|
+}
|
|
+
|
|
+static int record_fill_2x24_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 -= 4; /* count in dword */
|
|
+ cnt += 4; /* count in byte */
|
|
+ d1 = *(s++);
|
|
+ *(dp ++) = (d1 << 24) >> 24;
|
|
+ d2 = *(s++);
|
|
+ *(dp ++) = (d2 << 24) >> 24;
|
|
+ }
|
|
+ //printk("--- rec 24 ---\n");
|
|
+ return cnt;
|
|
+}
|
|
+
|
|
+static void replay_fill_1x8_u(unsigned 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;
|
|
+ }
|
|
+ cnt = cnt * 2 * jz_audio_b;
|
|
+ *(out_dma_buf_data_count + id) = cnt;
|
|
+}
|
|
+
|
|
+static void replay_fill_2x8_u(unsigned 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;
|
|
+ }
|
|
+ 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 = (unsigned long)d1;
|
|
+ *(dp ++) = l1;
|
|
+ *(dp ++) = 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));
|
|
+
|
|
+ while (count > 0) {
|
|
+ count -= 2;
|
|
+ cnt += 2;
|
|
+ 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:
|
|
+ /* DAC path and ADC path */
|
|
+ write_codec_file(2, 0x00);
|
|
+ //write_codec_file(2, 0x60);
|
|
+
|
|
+ jz_audio_format = fmt;
|
|
+ jz_update_filler(jz_audio_format, jz_audio_channels);
|
|
+
|
|
+ /* print all files */
|
|
+ __i2s_set_oss_sample_size(16);
|
|
+ __i2s_set_iss_sample_size(16);
|
|
+ break;
|
|
+ case 18:
|
|
+ /* DAC path and ADC path */
|
|
+ write_codec_file(2, 0x28);
|
|
+ //write_codec_file(2, 0x60);
|
|
+ __i2s_set_oss_sample_size(18);
|
|
+ __i2s_set_iss_sample_size(16);
|
|
+ jz_audio_format = fmt;
|
|
+ jz_update_filler(jz_audio_format,jz_audio_channels);
|
|
+ break;
|
|
+ case 24:
|
|
+ /* DAC path and ADC path */
|
|
+ write_codec_file(2, 0x78);
|
|
+ //write_codec_file(2, 0x60);
|
|
+ __i2s_set_oss_sample_size(24);
|
|
+ __i2s_set_iss_sample_size(24);
|
|
+ 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:
|
|
+ /* print all files */
|
|
+ jz_audio_channels = channels;
|
|
+ jz_update_filler(jz_audio_format, jz_audio_channels);
|
|
+ write_codec_file_bit(1, 1, 6);//CR1.MONO->1 for Mono
|
|
+ break;
|
|
+ case 2:
|
|
+ /* print all files */
|
|
+ jz_audio_channels = channels;
|
|
+ jz_update_filler(jz_audio_format, jz_audio_channels);
|
|
+ write_codec_file_bit(1, 0, 6);//CR1.MONO->0 for Stereo
|
|
+ break;
|
|
+ case 0:
|
|
+ break;
|
|
+ }
|
|
+ return jz_audio_channels;
|
|
+}
|
|
+
|
|
+static void init_codec(void)
|
|
+{
|
|
+#if defined(CONFIG_I2S_DLV)
|
|
+ /* reset DLV codec. from hibernate mode to sleep mode */
|
|
+ write_codec_file(0, 0xf);
|
|
+ write_codec_file_bit(6, 0, 0);
|
|
+ write_codec_file_bit(6, 0, 1);
|
|
+ mdelay(200);
|
|
+ //write_codec_file(0, 0xf);
|
|
+ write_codec_file_bit(5, 0, 7);//PMR1.SB_DAC->0
|
|
+ write_codec_file_bit(5, 0, 4);//PMR1.SB_ADC->0
|
|
+ mdelay(10);//wait for stability
|
|
+#endif
|
|
+}
|
|
+
|
|
+static void jz_audio_reset(void)
|
|
+{
|
|
+ __i2s_disable_replay();
|
|
+ __i2s_disable_receive_dma();
|
|
+ __i2s_disable_record();
|
|
+ __i2s_disable_transmit_dma();
|
|
+ REG_AIC_I2SCR = 0x10;
|
|
+ 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;
|
|
+ u8 cur_vol;
|
|
+ long val = 0;
|
|
+
|
|
+ switch (cmd) {
|
|
+ case SOUND_MIXER_INFO:
|
|
+ {
|
|
+ mixer_info info;
|
|
+#if defined(CONFIG_I2S_DLV)
|
|
+ strncpy(info.id, "DLV", sizeof(info.id));
|
|
+ strncpy(info.name,"Jz4750 internal codec", sizeof(info.name));
|
|
+#endif
|
|
+
|
|
+ info.modify_counter = audio_mix_modcnt;
|
|
+ return copy_to_user((void *)arg, &info, sizeof(info));
|
|
+ }
|
|
+ case SOUND_OLD_MIXER_INFO:
|
|
+ {
|
|
+ _old_mixer_info info;
|
|
+#if defined(CONFIG_I2S_DLV)
|
|
+ strncpy(info.id, "DLV", sizeof(info.id));
|
|
+ strncpy(info.name,"Jz4750 internal codec", sizeof(info.name));
|
|
+#endif
|
|
+
|
|
+ return copy_to_user((void *)arg, &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;
|
|
+
|
|
+ REG_CPM_I2SCDR = val;
|
|
+ printk_codec_files();
|
|
+ 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;
|
|
+#if defined(CONFIG_I2S_DLV)
|
|
+#endif
|
|
+ return 0;
|
|
+
|
|
+ case SOUND_MIXER_READ_BASS:
|
|
+#if defined(CONFIG_I2S_DLV)
|
|
+#endif
|
|
+ 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) {
|
|
+ //REG_CPM_CLKGR |= 0x60;
|
|
+ printk("gate clock : 0x%08x\n", REG_CPM_CLKGR);
|
|
+ return 100;
|
|
+ }
|
|
+ if(val == 99) {
|
|
+ REG_CPM_CLKGR &= ~0x60;
|
|
+ return 99;
|
|
+ }
|
|
+ if(val == 98) {
|
|
+ REG_CPM_CPCCR |= 0x80400000;
|
|
+ return 98;
|
|
+ }
|
|
+ if(val == 97) {
|
|
+ REG_CPM_CPCCR &= ~0x8000000;
|
|
+ REG_CPM_CPCCR |= 0x0040000;
|
|
+ return 97;
|
|
+ }
|
|
+ if(val > 31)
|
|
+ val = 31;
|
|
+
|
|
+ jz_audio_volume = val;
|
|
+ //printk("\njz_audio_volume=%d\n",jz_audio_volume);
|
|
+#if defined(CONFIG_I2S_DLV)
|
|
+ cur_vol = val;
|
|
+ write_codec_file(17, cur_vol | 0xc0);
|
|
+ write_codec_file(18, cur_vol);
|
|
+#endif
|
|
+ return 0;
|
|
+ case SOUND_MIXER_READ_VOLUME:
|
|
+#if defined(CONFIG_I2S_DLV)
|
|
+ val = jz_audio_volume | jz_audio_volume << 8;
|
|
+#endif
|
|
+ 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) {
|
|
+ printk("Playback LINE input audio direct only is starting...\n");
|
|
+ printk_codec_files();
|
|
+ set_playback_line_input_audio_direct_only();
|
|
+ printk_codec_files();
|
|
+ return 100;
|
|
+ }
|
|
+ if (val == 99) {
|
|
+ printk("Playback LINE input audio direct only is end\n");
|
|
+ unset_playback_line_input_audio_direct_only();
|
|
+ printk_codec_files();
|
|
+ return 99;
|
|
+ }
|
|
+
|
|
+ if(val > 31)
|
|
+ val = 31;
|
|
+#if defined(CONFIG_I2S_DLV)
|
|
+ use_mic_line_flag = USE_LINEIN;
|
|
+ /* set gain */
|
|
+ cur_vol = val;
|
|
+ cur_vol &= 0x1f;
|
|
+ write_codec_file(11, cur_vol);//GO1L
|
|
+ write_codec_file(12, cur_vol);//GO1R
|
|
+#endif
|
|
+ return 0;
|
|
+ break;
|
|
+ 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 > 0xf)
|
|
+ val = 0xf;
|
|
+#if defined(CONFIG_I2S_DLV)
|
|
+ use_mic_line_flag = USE_MIC;
|
|
+ /* set gain */
|
|
+ //write_codec_file_bit(6, 1, 3);//GIM
|
|
+ cur_vol = val;
|
|
+ cur_vol |= cur_vol << 4;
|
|
+ write_codec_file(19, cur_vol);//GIL,GIR
|
|
+#endif
|
|
+ return 0;
|
|
+
|
|
+ case SOUND_MIXER_READ_MIC:
|
|
+#if defined(CONFIG_I2S_DLV)
|
|
+#endif
|
|
+
|
|
+ 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 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 void replay_fill_2x24_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;
|
|
+ //printk("--- play 24 ---\n");
|
|
+}
|
|
+
|
|
+static void jz_update_filler(int format, int channels)
|
|
+{
|
|
+ #define TYPE(fmt,ch) (((fmt)<<2) | ((ch)&3))
|
|
+
|
|
+ switch (TYPE(format, channels))
|
|
+ {
|
|
+ default:
|
|
+ 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):
|
|
+ //2bytes * 16bits =32bits
|
|
+ jz_audio_b = 2;
|
|
+ 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;
|
|
+ case TYPE(24, 2):
|
|
+ jz_audio_b = 1;
|
|
+ replay_filler = replay_fill_2x24_s;
|
|
+ record_filler = record_fill_2x24_s;
|
|
+ //printk("--- 24 ---\n");
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+#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;
|
|
+ jz_i2s_initHw(1);//initialize AIC controller and reset it
|
|
+ /* register /dev/audio */
|
|
+ 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, JZCODEC_USER_BUFFER);
|
|
+ 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, JZCODEC_USER_BUFFER);
|
|
+ if (!controller->tmp2) {
|
|
+ printk(KERN_ERR "%s: can't allocate tmp buffers.\n", controller->name);
|
|
+ goto tmp2_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;
|
|
+ }
|
|
+ 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;
|
|
+ }
|
|
+ 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;
|
|
+
|
|
+ return;
|
|
+dma2_failed:
|
|
+ jz_free_dma(controller->dma2);
|
|
+dma1_failed:
|
|
+ jz_free_dma(controller->dma1);
|
|
+ free_pages((unsigned long)controller->tmp2, JZCODEC_USER_BUFFER);
|
|
+tmp2_failed:
|
|
+tmp1_failed:
|
|
+ free_pages((unsigned long)controller->tmp1, JZCODEC_USER_BUFFER);
|
|
+
|
|
+#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);
|
|
+
|
|
+ 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;
|
|
+
|
|
+#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, JZCODEC_USER_BUFFER);
|
|
+ free_pages((unsigned long)controller->tmp2, JZCODEC_USER_BUFFER);
|
|
+ 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)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int jz_i2s_resume(struct jz_i2s_controller_info *controller)
|
|
+{
|
|
+ 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 */
|
|
+
|
|
+
|
|
+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;
|
|
+}
|
|
+
|
|
+static int __init init_jz_i2s(void)
|
|
+{
|
|
+ int errno, retval;
|
|
+
|
|
+#if defined(CONFIG_I2S_DLV)
|
|
+ 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;
|
|
+
|
|
+ //REG_CPM_CPCCR &= ~(1 << 31);
|
|
+ //REG_CPM_CPCCR &= ~(1 << 30);
|
|
+ use_mic_line_flag = USE_NONE;
|
|
+ write_codec_file(0, 0xf);
|
|
+
|
|
+ REG_AIC_I2SCR = 0x10;
|
|
+ __i2s_internal_codec();
|
|
+ __i2s_as_slave();
|
|
+ __i2s_select_i2s();
|
|
+ __aic_select_i2s();
|
|
+ __aic_reset();
|
|
+ mdelay(10);
|
|
+ REG_AIC_I2SCR = 0x10;
|
|
+ mdelay(20);
|
|
+
|
|
+ /* power on DLV */
|
|
+ write_codec_file(9, 0xff);
|
|
+ write_codec_file(8, 0x3f);
|
|
+ mdelay(10);
|
|
+#endif
|
|
+
|
|
+ if ((errno = probe_jz_i2s(&i2s_controller)) < 0)
|
|
+ return errno;
|
|
+ attach_jz_i2s(i2s_controller);
|
|
+#if defined(CONFIG_I2S_DLV)
|
|
+ __i2s_disable_transmit_intr();
|
|
+ __i2s_disable_receive_intr();
|
|
+ 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
|
|
+
|
|
+ __cpm_start_idct();
|
|
+ __cpm_start_db();
|
|
+ __cpm_start_me();
|
|
+ __cpm_start_mc();
|
|
+ __cpm_start_ipu();
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+static void __exit cleanup_jz_i2s(void)
|
|
+{
|
|
+ unload_jz_i2s(i2s_controller);
|
|
+#if defined(CONFIG_I2S_DLV)
|
|
+ free_irq(IRQ_AIC, NULL);
|
|
+#endif
|
|
+ Free_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize);
|
|
+}
|
|
+
|
|
+module_init(init_jz_i2s);
|
|
+module_exit(cleanup_jz_i2s);
|
|
+
|
|
+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;
|
|
+}
|
|
+
|
|
+static void print_pop_duration(void)
|
|
+{
|
|
+ long diff;
|
|
+#if 0
|
|
+ printk(" ramp_up_start=0x%x\n",ramp_up_start);
|
|
+ printk(" ramp_up_end =0x%x\n",ramp_up_end);
|
|
+ printk(" gain_up_start=0x%x\n",gain_up_start);
|
|
+ printk(" gain_up_end =0x%x\n",gain_up_end);
|
|
+
|
|
+ printk("ramp_down_start=0x%x\n",ramp_down_start);
|
|
+ printk("ramp_down_end =0x%x\n",ramp_down_end);
|
|
+ printk("gain_down_start=0x%x\n",gain_down_start);
|
|
+ printk("gain_down_end =0x%x\n",gain_down_end);
|
|
+#endif
|
|
+
|
|
+ diff = (long)ramp_up_end - (long)ramp_up_start;
|
|
+ printk("ramp up duration: %d ms\n",diff * 1000 / HZ);
|
|
+ diff = (long)gain_up_end - (long)gain_up_start;
|
|
+ printk("gain up duration: %d ms\n",diff * 1000 / HZ);
|
|
+ diff = (long)gain_down_end - (long)gain_down_start;
|
|
+ printk("gain down duration: %d ms\n",diff);
|
|
+ diff = (long)ramp_down_end - (long)ramp_down_start;
|
|
+ printk("ramp down duration: %d ms\n",diff * 1000 / HZ);
|
|
+}
|
|
+
|
|
+static int jz_audio_release(struct inode *inode, struct file *file)
|
|
+{
|
|
+ struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data;
|
|
+ unsigned long tfl;
|
|
+
|
|
+ jz_codec_config = 0;
|
|
+ if (controller == NULL)
|
|
+ return -ENODEV;
|
|
+
|
|
+ if (controller->opened1 == 1 && controller->opened2 == 1) {
|
|
+ __i2s_enable_transmit_dma();
|
|
+ __i2s_enable_replay();
|
|
+
|
|
+ drain_dac(controller, file->f_flags & O_NONBLOCK);
|
|
+ /* 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);
|
|
+ disable_dma(controller->dma1);
|
|
+ set_dma_count(controller->dma1, 0);
|
|
+
|
|
+ __i2s_disable_transmit_dma();
|
|
+ __i2s_disable_replay();
|
|
+
|
|
+ //spin_lock_irqsave(&controller->ioctllock, flags);
|
|
+ spin_lock(&controller->ioctllock);
|
|
+ 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);
|
|
+ spin_unlock(&controller->ioctllock);
|
|
+
|
|
+ /////////////////////////
|
|
+ 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();
|
|
+ //__aic_flush_fifo();
|
|
+ //spin_lock_irqsave(&controller->ioctllock, flags);
|
|
+ spin_lock(&controller->ioctllock);
|
|
+ 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);
|
|
+ spin_unlock(&controller->ioctllock);
|
|
+#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
|
|
+ __i2s_disable();
|
|
+#endif
|
|
+ } else if ( controller->opened1 == 1 ) {
|
|
+ __i2s_enable_transmit_dma();
|
|
+ __i2s_enable_replay();
|
|
+ drain_dac(controller, file->f_flags & O_NONBLOCK);
|
|
+ /* 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);
|
|
+ }
|
|
+ disable_dma(controller->dma1);
|
|
+ set_dma_count(controller->dma1, 0);
|
|
+
|
|
+ __i2s_disable_transmit_dma();
|
|
+ __i2s_disable_replay();
|
|
+
|
|
+ __aic_flush_fifo();
|
|
+
|
|
+ //spin_lock_irqsave(&controller->ioctllock, flags);
|
|
+ spin_lock(&controller->ioctllock);
|
|
+ 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);
|
|
+ spin_unlock(&controller->ioctllock);
|
|
+#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();
|
|
+ } 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();
|
|
+
|
|
+ //spin_lock_irqsave(&controller->ioctllock, flags);
|
|
+ spin_lock(&controller->ioctllock);
|
|
+ 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);
|
|
+ spin_unlock(&controller->ioctllock);
|
|
+#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 (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;
|
|
+ }
|
|
+ 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();*/
|
|
+ printk("close audio and clear ipu gate : 0x%08x\n", REG_CPM_CLKGR);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int jz_audio_open(struct inode *inode, struct file *file)
|
|
+{
|
|
+ int i;
|
|
+ struct jz_i2s_controller_info *controller = i2s_controller;
|
|
+ if (controller == NULL)
|
|
+ return -ENODEV;
|
|
+ mdelay(2);
|
|
+ REG_DMAC_DMACKE(0) = 0x3f;
|
|
+ if (controller->opened1 == 1 || controller->opened2 == 1 ) {
|
|
+ printk("\naudio is busy!\n");
|
|
+ return -EBUSY;
|
|
+ }
|
|
+ jz_codec_config = 0;
|
|
+
|
|
+ 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;
|
|
+
|
|
+ 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;
|
|
+
|
|
+ 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;
|
|
+
|
|
+ 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;
|
|
+
|
|
+ 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;
|
|
+ } 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();//11.2
|
|
+
|
|
+ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
|
|
+#if defined(CONFIG_I2S_DLV)
|
|
+
|
|
+ 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_DLV)
|
|
+ //mdelay(10);
|
|
+ /* Audio data replay */
|
|
+ set_audio_data_replay();
|
|
+#endif
|
|
+ } else if (file->f_mode & FMODE_READ) {
|
|
+#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
|
|
+ }
|
|
+
|
|
+ __aic_reset();
|
|
+
|
|
+ mdelay(10);
|
|
+ REG_AIC_I2SCR = 0x10;
|
|
+ mdelay(20);
|
|
+ __aic_flush_fifo();
|
|
+ __i2s_enable();
|
|
+ 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
|
|
+ }
|
|
+
|
|
+
|
|
+ printk("open audio and set ipu gate : 0x%08x\n", REG_CPM_CLKGR);
|
|
+ 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;
|
|
+ unsigned int flags;
|
|
+ struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data;
|
|
+
|
|
+ audio_buf_info abinfo;
|
|
+ int i, bytes, id;
|
|
+ count_info cinfo;
|
|
+ val = 0;
|
|
+ bytes = 0;
|
|
+ switch (cmd) {
|
|
+ case OSS_GETVERSION:
|
|
+ return put_user(SOUND_VERSION, (int *)arg);
|
|
+ case SNDCTL_DSP_RESET:
|
|
+ return 0;
|
|
+ case SNDCTL_DSP_SYNC:
|
|
+ if (file->f_mode & FMODE_WRITE)
|
|
+ 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(4*PAGE_SIZE, (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);//16 least bits
|
|
+
|
|
+ if (newfragsize < 4 * PAGE_SIZE)
|
|
+ newfragsize = 4 * PAGE_SIZE;
|
|
+ if (newfragsize > (16 * PAGE_SIZE)) //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:
|
|
+ {
|
|
+ if (!(file->f_mode & FMODE_WRITE))
|
|
+ return -EINVAL;
|
|
+
|
|
+ //unused fragment amount
|
|
+ spin_lock_irqsave(&controller->ioctllock, flags);
|
|
+ jz_audio_fragments = elements_in_queue(&out_empty_queue);
|
|
+ for (i = 0; i < jz_audio_fragments; i++)
|
|
+ bytes += jz_audio_fragsize;
|
|
+ bytes /= jz_audio_b;
|
|
+ spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
+ abinfo.fragments = jz_audio_fragments;
|
|
+ abinfo.fragstotal = jz_audio_fragstotal;
|
|
+ abinfo.fragsize = jz_audio_fragsize;
|
|
+ abinfo.bytes = bytes;
|
|
+ return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
|
|
+ }
|
|
+ case SNDCTL_DSP_GETISPACE:
|
|
+ if (!(file->f_mode & FMODE_READ))
|
|
+ return -EINVAL;
|
|
+
|
|
+ bytes = 0;
|
|
+ //unused fragment amount
|
|
+ jz_audio_fragments = elements_in_queue(&in_empty_queue);
|
|
+ for (i = 0; i < jz_audio_fragments; i++)
|
|
+ bytes += jz_audio_fragsize;
|
|
+ bytes /= jz_audio_b;
|
|
+ abinfo.fragments = jz_audio_fragments;
|
|
+ abinfo.fragstotal = jz_audio_fragstotal;
|
|
+ abinfo.fragsize = jz_audio_fragsize;
|
|
+ abinfo.bytes = 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);
|
|
+ //controller->total_bytes += get_dma_residue(controller->dma2);
|
|
+ cinfo.bytes = controller->total_bytes;
|
|
+ cinfo.blocks = controller->blocks;
|
|
+ cinfo.ptr = controller->nextIn;
|
|
+ controller->blocks = 0;
|
|
+ spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
+ 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);
|
|
+ //controller->total_bytes += get_dma_residue(controller->dma1);
|
|
+ cinfo.bytes = controller->total_bytes;
|
|
+ cinfo.blocks = controller->blocks;
|
|
+ cinfo.ptr = controller->nextOut;
|
|
+ controller->blocks = 0;
|
|
+ spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
+ 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);
|
|
+ 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);
|
|
+ unfinish /= jz_audio_b;
|
|
+ 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 long flags;
|
|
+ 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);
|
|
+ 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);
|
|
+ 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);
|
|
+
|
|
+ spin_lock(&controller->lock);
|
|
+
|
|
+ copy_count = jz_audio_fragsize / 4;
|
|
+
|
|
+ left_count = count;
|
|
+ spin_unlock(&controller->lock);
|
|
+
|
|
+ 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();
|
|
+
|
|
+ //write back
|
|
+ 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);
|
|
+ //interruptible_sleep_on(&rx_wait_queue);
|
|
+ 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 (signal_pending(current))
|
|
+ return ret ? ret: -ERESTARTSYS; */
|
|
+ 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);
|
|
+ //write back
|
|
+ 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);
|
|
+ }//while (left_count > 0)
|
|
+ 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, val;
|
|
+ unsigned int flags;
|
|
+ struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data;
|
|
+
|
|
+ if (count <= 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ __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) {
|
|
+ // all are full
|
|
+ if (file->f_flags & O_NONBLOCK)
|
|
+ return ret;//no waiting,no block
|
|
+ else {
|
|
+ sleep_on(&tx_wait_queue);//blocked
|
|
+ }
|
|
+ }
|
|
+ /*if (signal_pending(current))
|
|
+ return ret ? ret : -ERESTARTSYS;*/
|
|
+ /* 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((unsigned long)controller->tmp1 + ret, copy_count, id);
|
|
+ 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); //busy in
|
|
+ dma_cache_wback_inv(*(out_dma_buf + id),
|
|
+ *(out_dma_buf_data_count + id));
|
|
+ } else
|
|
+ put_buffer_id(&out_empty_queue, id); //spare
|
|
+ } 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); //first once,next spare
|
|
+
|
|
+ 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);
|
|
+ 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);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
--- /dev/null
|
|
+++ b/sound/oss/jz_i2s_for_4750.c
|
|
@@ -0,0 +1,2981 @@
|
|
+/*
|
|
+ * 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"
|
|
+
|
|
+#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 MAXDELAY 50000
|
|
+#define JZCODEC_RW_BUFFER_SIZE 2
|
|
+#define JZCODEC_RW_BUFFER_TOTAL 6
|
|
+
|
|
+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)(void) = NULL;
|
|
+void (*set_codec_replay)(void) = NULL;
|
|
+void (*set_codec_replay_record)(void);
|
|
+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 (*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;
|
|
+
|
|
+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 */
|
|
+static 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;
|
|
+
|
|
+static int codec_mic_gain;
|
|
+static int pop_dma_flag;
|
|
+static int last_dma_buffer_id;
|
|
+static int drain_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 int read_codec_file(int addr)
|
|
+{
|
|
+ while (__icdc_rgwr_ready());
|
|
+ __icdc_set_addr(addr);
|
|
+ mdelay(1);
|
|
+ return(__icdc_get_value());
|
|
+}
|
|
+
|
|
+static void printk_codec_files(void)
|
|
+{
|
|
+ int cnt;
|
|
+
|
|
+ printk("\n");
|
|
+
|
|
+ printk("REG_CPM_I2SCDR=0x%08x\n",REG_CPM_I2SCDR);
|
|
+ printk("REG_CPM_CLKGR=0x%08x\n",REG_CPM_CLKGR);
|
|
+ printk("REG_CPM_CPCCR=0x%08x\n",REG_CPM_CPCCR);
|
|
+ printk("REG_AIC_FR=0x%08x\n",REG_AIC_FR);
|
|
+ printk("REG_AIC_CR=0x%08x\n",REG_AIC_CR);
|
|
+ printk("REG_AIC_I2SCR=0x%08x\n",REG_AIC_I2SCR);
|
|
+ printk("REG_AIC_SR=0x%08x\n",REG_AIC_SR);
|
|
+ printk("REG_ICDC_RGDATA=0x%08x\n",REG_ICDC_RGDATA);
|
|
+
|
|
+ for (cnt = 0; cnt <= 27 ; cnt++) {
|
|
+ printk(" ( %d : 0x%x ) ",cnt ,read_codec_file(cnt));
|
|
+ }
|
|
+ printk("\n");
|
|
+}
|
|
+
|
|
+static void write_codec_file(int addr, int val)
|
|
+{
|
|
+ while (__icdc_rgwr_ready());
|
|
+ __icdc_set_addr(addr);
|
|
+ __icdc_set_cmd(val); /* write */
|
|
+ mdelay(1);
|
|
+ __icdc_set_rgwr();
|
|
+ mdelay(1);
|
|
+}
|
|
+
|
|
+static int write_codec_file_bit(int addr, int bitval, int mask_bit)
|
|
+{
|
|
+ int val;
|
|
+ while (__icdc_rgwr_ready());
|
|
+ __icdc_set_addr(addr);
|
|
+ mdelay(1);
|
|
+ val = __icdc_get_value(); /* read */
|
|
+
|
|
+ while (__icdc_rgwr_ready());
|
|
+ __icdc_set_addr(addr);
|
|
+ val &= ~(1 << mask_bit);
|
|
+ if (bitval == 1)
|
|
+ val |= 1 << mask_bit;
|
|
+
|
|
+ __icdc_set_cmd(val); /* write */
|
|
+ mdelay(1);
|
|
+ __icdc_set_rgwr();
|
|
+ mdelay(1);
|
|
+
|
|
+ while (__icdc_rgwr_ready());
|
|
+ __icdc_set_addr(addr);
|
|
+ val = __icdc_get_value(); /* read */
|
|
+
|
|
+ if (((val >> mask_bit) & bitval) == bitval)
|
|
+ return 1;
|
|
+ else
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* set Audio data replay */
|
|
+static void set_audio_data_replay()
|
|
+{
|
|
+ /* DAC path */
|
|
+ write_codec_file(9, 0xff);
|
|
+ //write_codec_file(8, 0x30);
|
|
+ write_codec_file(8, 0x20);
|
|
+ mdelay(10);
|
|
+ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0
|
|
+ write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1
|
|
+ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1
|
|
+
|
|
+ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0
|
|
+ write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1
|
|
+
|
|
+ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0
|
|
+ //mdelay(100);
|
|
+ //write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0
|
|
+ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1
|
|
+ //mdelay(300);
|
|
+}
|
|
+
|
|
+/* unset Audio data replay */
|
|
+static void unset_audio_data_replay(void)
|
|
+{
|
|
+ //write_codec_file_bit(1, 1, 5);//DAC_MUTE->1
|
|
+ //mdelay(800);
|
|
+ //write_codec_file_bit(5, 1, 6);//SB_OUT->1
|
|
+ //mdelay(800);
|
|
+ write_codec_file_bit(5, 1, 7);//SB_DAC->1
|
|
+ write_codec_file_bit(5, 1, 4);//SB_MIX->1
|
|
+ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1
|
|
+ write_codec_file_bit(6, 1, 1);//SB->1
|
|
+}
|
|
+
|
|
+/* set Record MIC input audio without playback */
|
|
+static void set_record_mic_input_audio_without_playback(void)
|
|
+{
|
|
+ /* ADC path for MIC IN */
|
|
+ jz_mic_only = 1;
|
|
+ write_codec_file(9, 0xff);
|
|
+ write_codec_file(8, 0x3f);
|
|
+ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0
|
|
+ mdelay(10);
|
|
+ write_codec_file_bit(1, 1, 2);
|
|
+ //write_codec_file_bit(1, 1, 6);//CR1.MONO->1
|
|
+
|
|
+ write_codec_file(22, 0x40);//mic 1
|
|
+ write_codec_file_bit(3, 1, 7);//CR1.HP_DIS->1
|
|
+ write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1
|
|
+ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1
|
|
+
|
|
+ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0
|
|
+ write_codec_file_bit(1, 0, 3);//CR1.DACSEL->0
|
|
+ //write_codec_file_bit(6, 1, 3);// gain set
|
|
+
|
|
+ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0
|
|
+ mdelay(100);
|
|
+ write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0
|
|
+ write_codec_file(1, 0x4);
|
|
+}
|
|
+
|
|
+/* unset Record MIC input audio without playback */
|
|
+static void unset_record_mic_input_audio_without_playback(void)
|
|
+{
|
|
+ /* ADC path for MIC IN */
|
|
+ jz_mic_only = 0;
|
|
+ write_codec_file_bit(5, 1, 4);//SB_ADC->1
|
|
+ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1
|
|
+ write_codec_file(22, 0xc0);//CR3.SB_MIC1
|
|
+ write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1
|
|
+ write_codec_file_bit(1, 1, 5);//DAC_MUTE->1
|
|
+ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1
|
|
+ write_codec_file_bit(6, 1, 1);//SB->1
|
|
+}
|
|
+
|
|
+/* set Record LINE input audio without playback */
|
|
+static void set_record_line_input_audio_without_playback(void)
|
|
+{
|
|
+ /* ADC path for LINE IN */
|
|
+ jz_mic_only = 1;
|
|
+ write_codec_file(9, 0xff);
|
|
+ write_codec_file(8, 0x3f);
|
|
+ mdelay(10);
|
|
+ write_codec_file(22, 0xf6);//line in 1
|
|
+ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0
|
|
+ write_codec_file_bit(3, 1, 7);//CR1.HP_DIS->1
|
|
+ write_codec_file_bit(5, 0, 3);//PMR1.SB_LIN->0
|
|
+ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1
|
|
+
|
|
+ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0
|
|
+ write_codec_file_bit(1, 0, 3);//CR1.DACSEL->0
|
|
+ mdelay(10);
|
|
+ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0
|
|
+ mdelay(100);
|
|
+ write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0
|
|
+ write_codec_file(1, 0x4);
|
|
+}
|
|
+
|
|
+/* unset Record LINE input audio without playback */
|
|
+static void unset_record_line_input_audio_without_playback(void)
|
|
+{
|
|
+ /* ADC path for LINE IN */
|
|
+ write_codec_file_bit(5, 1, 4);//SB_ADC->1
|
|
+ write_codec_file_bit(5, 1, 3);//ONR1.SB_LIN->1
|
|
+
|
|
+ write_codec_file(22, 0xc0);//CR3.SB_MIC1
|
|
+ write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1
|
|
+ write_codec_file_bit(1, 1, 5);//DAC_MUTE->1
|
|
+ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1
|
|
+ write_codec_file_bit(6, 1, 1);//SB->1
|
|
+}
|
|
+
|
|
+/* set Playback LINE input audio direct only */
|
|
+static void set_playback_line_input_audio_direct_only()
|
|
+{
|
|
+ jz_audio_reset();//or init_codec()
|
|
+ REG_AIC_I2SCR = 0x10;
|
|
+ write_codec_file(9, 0xff);
|
|
+ write_codec_file(8, 0x3f);
|
|
+ mdelay(10);
|
|
+ write_codec_file(22, 0xf6);//line in 1
|
|
+ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0
|
|
+ mdelay(10);
|
|
+ write_codec_file_bit(1, 1, 2);//CR1.HP_BYPASS->1
|
|
+ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0
|
|
+ write_codec_file_bit(1, 0, 3);//CR1.DACSEL->0
|
|
+ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1
|
|
+ write_codec_file_bit(5, 0, 3);//PMR1.SB_LIN->0
|
|
+
|
|
+ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0
|
|
+ mdelay(100);
|
|
+ write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0
|
|
+ //write_codec_file_bit(5, 1, 7);//PMR1.SB_DAC->1
|
|
+ //write_codec_file_bit(5, 1, 4);//PMR1.SB_ADC->1
|
|
+}
|
|
+
|
|
+/* unset Playback LINE input audio direct only */
|
|
+static void unset_playback_line_input_audio_direct_only()
|
|
+{
|
|
+ write_codec_file_bit(6, 0, 3);//GIM->0
|
|
+ write_codec_file_bit(1, 0, 2);//PMR1.BYPASS->0
|
|
+ write_codec_file_bit(5, 1, 3);//PMR1.SB_LINE->1
|
|
+ write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1
|
|
+ mdelay(100);
|
|
+ write_codec_file_bit(5, 1, 5);//PMR1.SB_MIX->1
|
|
+ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1
|
|
+ write_codec_file_bit(6, 1, 1);//SB->1
|
|
+}
|
|
+
|
|
+/* set Record MIC input audio with direct playback */
|
|
+static void set_record_mic_input_audio_with_direct_playback(void)
|
|
+{
|
|
+ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0
|
|
+ jz_mic_only = 0;
|
|
+ write_codec_file(9, 0xff);
|
|
+ write_codec_file(8, 0x3f);
|
|
+ mdelay(10);
|
|
+
|
|
+ write_codec_file(22, 0x60);//mic 1
|
|
+ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0
|
|
+ write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1
|
|
+ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1
|
|
+ write_codec_file_bit(1, 0, 7);//CR1.SB_MICBIAS->0
|
|
+ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0
|
|
+
|
|
+ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0
|
|
+ write_codec_file_bit(1, 0, 3);//CR1.DACSEL->0
|
|
+ write_codec_file_bit(6, 1, 3);// gain set
|
|
+
|
|
+ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0
|
|
+ mdelay(100);
|
|
+ write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0
|
|
+ //write_codec_file(1, 0x4);
|
|
+}
|
|
+
|
|
+/* unset Record MIC input audio with direct playback */
|
|
+static void unset_record_mic_input_audio_with_direct_playback(void)
|
|
+{
|
|
+ /* ADC path for MIC IN */
|
|
+ jz_mic_only = 0;
|
|
+ write_codec_file_bit(5, 1, 4);//SB_ADC->1
|
|
+ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1
|
|
+ write_codec_file(22, 0xc0);//CR3.SB_MIC1
|
|
+ write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1
|
|
+ write_codec_file_bit(1, 1, 5);//DAC_MUTE->1
|
|
+ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1
|
|
+ write_codec_file_bit(6, 1, 1);//SB->1
|
|
+}
|
|
+
|
|
+/* set Record playing audio mixed with MIC input audio */
|
|
+static void set_record_playing_audio_mixed_with_mic_input_audio(void)
|
|
+{
|
|
+ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0
|
|
+ write_codec_file(9, 0xff);
|
|
+ //write_codec_file(8, 0x30);
|
|
+ write_codec_file(8, 0x20);
|
|
+ mdelay(10);
|
|
+
|
|
+ write_codec_file(22, 0x63);//mic 1
|
|
+
|
|
+ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0
|
|
+ write_codec_file_bit(6, 1, 3);// gain set
|
|
+
|
|
+ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0
|
|
+ write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1
|
|
+ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1
|
|
+ write_codec_file_bit(1, 0, 7);//CR1.SB_MICBIAS->0
|
|
+ write_codec_file_bit(22, 0, 7);//CR3.SB_MIC->0
|
|
+ write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1
|
|
+ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0
|
|
+ write_codec_file_bit(5, 0, 4);//PMR1.SB_MIX->0
|
|
+}
|
|
+
|
|
+/* unset Record playing audio mixed with MIC input audio */
|
|
+static void unset_record_playing_audio_mixed_with_mic_input_audio(void)
|
|
+{
|
|
+ /* ADC path */
|
|
+ write_codec_file_bit(5, 1, 4);//SB_ADC->1
|
|
+ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1
|
|
+ //write_codec_file_bit(1, 1, 6);//CR1.MONO->1
|
|
+ write_codec_file(22, 0xc0);//CR3.SB_MIC1->1
|
|
+ //write_codec_file_bit(1, 1, 5);//DAC_MUTE->1
|
|
+ //write_codec_file_bit(5, 1, 6);//SB_OUT->1
|
|
+ write_codec_file_bit(5, 1, 7);//SB_DAC->1
|
|
+ write_codec_file_bit(5, 1, 5);//SB_MIX->1
|
|
+ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1
|
|
+ write_codec_file_bit(6, 1, 1);//SB->1
|
|
+}
|
|
+
|
|
+/* set Record MIC input audio with Audio data replay (full duplex) */
|
|
+static void set_record_mic_input_audio_with_audio_data_replay(void)
|
|
+{
|
|
+ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0
|
|
+ write_codec_file(9, 0xff);
|
|
+ //write_codec_file(8, 0x30);
|
|
+ write_codec_file(8, 0x20);
|
|
+ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0
|
|
+ write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1
|
|
+ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1
|
|
+
|
|
+ write_codec_file_bit(22, 0, 7);//CR3.SB_MIC->0
|
|
+ write_codec_file_bit(1, 0, 7);//CR1.SB_MICBIAS->0
|
|
+
|
|
+ write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1
|
|
+ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0
|
|
+}
|
|
+
|
|
+/* unset Record MIC input audio with Audio data replay (full duplex) */
|
|
+static void unset_record_mic_input_audio_with_audio_data_replay(void)
|
|
+{
|
|
+ /* ADC path */
|
|
+ write_codec_file_bit(5, 1, 4);//SB_ADC->1
|
|
+ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1
|
|
+ //write_codec_file_bit(1, 1, 6);//CR1.MONO->1
|
|
+ write_codec_file(22, 0xc0);//CR3.SB_MIC1->1
|
|
+ write_codec_file_bit(5, 1, 7);//SB_DAC->1
|
|
+ write_codec_file_bit(5, 1, 5);//SB_MIX->1
|
|
+ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1
|
|
+ write_codec_file_bit(6, 1, 1);//SB->1
|
|
+}
|
|
+
|
|
+/////////
|
|
+/* set Record LINE input audio with Audio data replay (full duplex for linein) */
|
|
+static void set_record_line_input_audio_with_audio_data_replay(void)
|
|
+{
|
|
+ write_codec_file(9, 0xff);
|
|
+ //write_codec_file(8, 0x30);
|
|
+ write_codec_file(8, 0x20);
|
|
+ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0
|
|
+ write_codec_file_bit(5, 0, 3);//PMR1.SB_LIN->0
|
|
+ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1
|
|
+ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1
|
|
+ //write_codec_file_bit(22, 1, 7);//CR3.SB_MIC->1
|
|
+ write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1
|
|
+ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0
|
|
+
|
|
+
|
|
+ //jz_mic_only = 1;
|
|
+ write_codec_file(22, 0xc6);//line in 1
|
|
+ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0
|
|
+ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0
|
|
+ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0
|
|
+}
|
|
+
|
|
+/* unset Record LINE input audio with Audio data replay (full duplex for linein) */
|
|
+static void unset_record_line_input_audio_with_audio_data_replay(void)
|
|
+{
|
|
+ /* ADC path */
|
|
+ write_codec_file_bit(5, 1, 4);//SB_ADC->1
|
|
+ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1
|
|
+ //write_codec_file_bit(1, 1, 6);//CR1.MONO->1
|
|
+ write_codec_file(22, 0xc0);//CR3.SB_MIC1->1
|
|
+ write_codec_file_bit(5, 1, 7);//SB_DAC->1
|
|
+ write_codec_file_bit(5, 1, 5);//SB_MIX->1
|
|
+ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1
|
|
+ write_codec_file_bit(6, 1, 1);//SB->1
|
|
+}
|
|
+/////////
|
|
+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);
|
|
+ //set_dma_mode(chan, mode);
|
|
+ jz_set_oss_dma(chan, mode, jz_audio_format);
|
|
+ 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 (rate > 48000)
|
|
+ rate = 48000;
|
|
+ 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;
|
|
+ *(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;
|
|
+}
|
|
+
|
|
+#if 0
|
|
+static void replay_fill_2x16_s(signed long src_start, int count, int id)
|
|
+{
|
|
+ int cnt = 0;
|
|
+ signed short d1;
|
|
+ signed long l1;
|
|
+ int mute_cnt = 0;
|
|
+ signed long tmp1,tmp2;
|
|
+ volatile signed short *s = (signed short *)src_start;
|
|
+ volatile signed long *dp = (signed long*)(*(out_dma_buf + id));
|
|
+#if defined(CONFIG_I2S_ICDC)
|
|
+ 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;
|
|
+}
|
|
+#else
|
|
+static void replay_fill_2x16_s(signed long src_start, int count, int id)
|
|
+{
|
|
+ int cnt = 0;
|
|
+ signed short d1;
|
|
+ signed long l1;
|
|
+ int mute_cnt = 0;
|
|
+ signed long tmp1,tmp2;
|
|
+
|
|
+#if 0
|
|
+ volatile signed short *s = (signed short *)src_start;
|
|
+ volatile signed short *dp = (signed short*)(*(out_dma_buf + id));
|
|
+ memcpy((char*)dp, (char*)s, count);
|
|
+ *(out_dma_buf_data_count + id) = count;
|
|
+#else
|
|
+ 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;
|
|
+
|
|
+ *(dp ++) = l1;
|
|
+ }
|
|
+ cnt *= jz_audio_b;
|
|
+ *(out_dma_buf_data_count + id) = cnt;
|
|
+#endif
|
|
+}
|
|
+#endif
|
|
+
|
|
+
|
|
+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);
|
|
+ /* print all files */
|
|
+ __i2s_set_oss_sample_size(16);
|
|
+ __i2s_set_iss_sample_size(16);
|
|
+ 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;
|
|
+ if (val > 31)
|
|
+ val = 31;
|
|
+ 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;
|
|
+ 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);
|
|
+ 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;
|
|
+ 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();
|
|
+ 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 */
|
|
+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;
|
|
+}
|
|
+
|
|
+static int __init init_jz_i2s(void)
|
|
+{
|
|
+ int errno, retval;
|
|
+#if defined(CONFIG_I2S_DLV)
|
|
+
|
|
+ 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
|
|
+
|
|
+ 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 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
|
|
+ if(set_codec_volume_table)
|
|
+ set_codec_volume_table();
|
|
+
|
|
+ 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_JZ4750)
|
|
+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
|
|
+
|
|
+#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;
|
|
+
|
|
+ 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;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ 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
|
|
+
|
|
+static int jz_audio_release(struct inode *inode, struct file *file)
|
|
+{
|
|
+ unsigned long flags;
|
|
+ struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data;
|
|
+ unsigned long tfl;
|
|
+
|
|
+ if (controller == NULL)
|
|
+ return -ENODEV;
|
|
+
|
|
+ pop_dma_flag = 0;
|
|
+ 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_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);
|
|
+ }
|
|
+#endif
|
|
+ disable_dma(controller->dma1);
|
|
+ set_dma_count(controller->dma1, 0);
|
|
+ __i2s_disable_transmit_dma();
|
|
+ __i2s_disable_replay();
|
|
+ __aic_flush_fifo();
|
|
+ if(clear_codec_replay)
|
|
+ clear_codec_replay();
|
|
+ __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(turn_off_codec)
|
|
+ turn_off_codec();
|
|
+ }
|
|
+
|
|
+ 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(clear_codec_record)
|
|
+ clear_codec_record();
|
|
+
|
|
+ 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);
|
|
+ __i2s_disable();
|
|
+ if(turn_off_codec)
|
|
+ turn_off_codec();
|
|
+ abnormal_data_count = 0;
|
|
+ }
|
|
+
|
|
+#if defined(CONFIG_I2S_DLV)
|
|
+ write_codec_file(9, 0xff);
|
|
+ write_codec_file(8, 0x3f);
|
|
+#endif
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int jz_audio_open(struct inode *inode, struct file *file)
|
|
+{
|
|
+ int i;
|
|
+ struct jz_i2s_controller_info *controller = i2s_controller;
|
|
+
|
|
+ if (controller == NULL)
|
|
+ return -ENODEV;
|
|
+
|
|
+ mdelay(2);
|
|
+ REG_DMAC_DMACKE(0) = 0x3f;
|
|
+ pop_dma_flag = 0;
|
|
+ if (controller->opened1 == 1 || controller->opened2 == 1 ) {
|
|
+ printk("\naudio is busy!\n");
|
|
+ return -EBUSY;
|
|
+ }
|
|
+ jz_codec_config = 0;
|
|
+
|
|
+ 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;
|
|
+ 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;
|
|
+ }
|
|
+
|
|
+ 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) {
|
|
+ if(set_codec_replay)
|
|
+ set_codec_replay();
|
|
+ }
|
|
+
|
|
+ if (file->f_mode & FMODE_READ) {
|
|
+ abnormal_data_count = 0;
|
|
+ if(set_codec_record)
|
|
+ set_codec_record();
|
|
+ }
|
|
+
|
|
+#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)
|
|
+ if (file->f_mode & FMODE_WRITE) {
|
|
+
|
|
+ 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
|
|
+ } else if (file->f_mode & FMODE_READ) {
|
|
+ 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
|
|
+
|
|
+ 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;
|
|
+ unsigned int flags;
|
|
+ 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, bytes = 0;
|
|
+ if (!(file->f_mode & FMODE_WRITE))
|
|
+ return -EINVAL;
|
|
+
|
|
+ spin_lock_irqsave(&controller->ioctllock, flags);
|
|
+ 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);
|
|
+ /* 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 = bytes;
|
|
+
|
|
+ return copy_to_user((void *)arg, &abinfo,
|
|
+ sizeof(abinfo)) ? -EFAULT : 0;
|
|
+ }
|
|
+ case SNDCTL_DSP_GETISPACE:
|
|
+ {
|
|
+ int i, 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 = 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);
|
|
+ cinfo.bytes = controller->total_bytes;
|
|
+ cinfo.blocks = controller->blocks;
|
|
+ cinfo.ptr = controller->nextIn;
|
|
+ controller->blocks = 0;
|
|
+ spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
+
|
|
+ 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);
|
|
+ cinfo.bytes = controller->total_bytes;
|
|
+ cinfo.blocks = controller->blocks;
|
|
+ cinfo.ptr = controller->nextOut;
|
|
+ controller->blocks = 0;
|
|
+ spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
+
|
|
+ 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);
|
|
+ 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);
|
|
+
|
|
+ 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 long flags;
|
|
+ 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);
|
|
+ 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);
|
|
+
|
|
+ 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;
|
|
+ unsigned long flags;
|
|
+
|
|
+ if (count < 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ __i2s_enable_receive_dma();
|
|
+ __i2s_enable_record();
|
|
+
|
|
+ spin_lock_irqsave(&controller->ioctllock, flags);
|
|
+ controller->nextIn = 0;
|
|
+ spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
+
|
|
+ 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);
|
|
+ controller->nextIn += ret;
|
|
+ spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
+
|
|
+ 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;
|
|
+ unsigned int flags;
|
|
+ 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);
|
|
+ controller->nextOut = 0;
|
|
+ spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
+ 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);
|
|
+ controller->nextOut += ret;
|
|
+ spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
+
|
|
+ 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 (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);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ 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
|
|
--- /dev/null
|
|
+++ b/sound/oss/jz_pcm_tlv320aic1106_dma.c
|
|
@@ -0,0 +1,1839 @@
|
|
+/*
|
|
+ * linux/drivers/sound/jz_pcm_tlv320aic1106.c
|
|
+ *
|
|
+ * JzSOC On-Chip PCM audio driver.
|
|
+ *
|
|
+ * Copyright (C) 2005 by Ingenic 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.
|
|
+ *
|
|
+ * 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"
|
|
+
|
|
+#define NR_PCM 2
|
|
+#define MAXDELAY 50000
|
|
+#define JZCODEC_RW_BUFFER_SIZE 4
|
|
+#define JZCODEC_RW_BUFFER_TOTAL 3
|
|
+#define JZCODEC_USER_BUFFER 6
|
|
+
|
|
+#define MODE_is_8 0 // 1 is 8 bits, and 0 is 16 bits
|
|
+
|
|
+static int jz_audio_rate;
|
|
+static char jz_audio_format;
|
|
+static char jz_audio_volume;
|
|
+static char jz_audio_channels;
|
|
+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 jz_audio_dma_tran_count;//bytes count of one DMA transfer
|
|
+
|
|
+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_pcm_irq(int irqnr, void *ref);
|
|
+static irqreturn_t jz_pcm_replay_dma_irq(int irqnr, void *ref);
|
|
+static irqreturn_t jz_pcm_record_dma_irq(int irqnr, void *ref);
|
|
+static void (*replay_filler)(unsigned long src_start, int count, int id);
|
|
+static int (*record_filler)(unsigned long dst_start, int count, int id);
|
|
+static void dump_pcmc_reg(void);
|
|
+
|
|
+static struct file_operations jz_pcm_audio_fops;
|
|
+static DECLARE_WAIT_QUEUE_HEAD (rx_wait_queue);
|
|
+static DECLARE_WAIT_QUEUE_HEAD (tx_wait_queue);
|
|
+
|
|
+struct jz_pcm_controller_info {
|
|
+ int io_base;
|
|
+ int dma1; /* play */
|
|
+ int dma2; /* record */
|
|
+ char *name;
|
|
+ int dev_audio;
|
|
+ struct pcm_codec *pcm_codec[NR_PCM];
|
|
+ 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 long total_bytes; // total bytes written or read
|
|
+ unsigned long blocks;
|
|
+ unsigned long error; // over/underrun
|
|
+};
|
|
+static struct jz_pcm_controller_info *pcm_controller = NULL;
|
|
+
|
|
+struct pcm_codec {
|
|
+ /* PCM controller connected with */
|
|
+ void *private_data;
|
|
+ char *name;
|
|
+ int id;
|
|
+ int dev_mixer;
|
|
+ /* controller specific lower leverl pcm accessing routines */
|
|
+ u16 (*codec_read) (u8 reg);//the function accessing Codec REGs
|
|
+ void (*codec_write) (u8 reg, u16 val);
|
|
+ /* Wait for codec-ready. Ok to sleep here. */
|
|
+ void (*codec_wait) (struct pcm_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 pcm_codec *codec, int oss_channel);
|
|
+ void (*write_mixer)(struct pcm_codec *codec, int oss_channel,
|
|
+ unsigned int left, unsigned int right);
|
|
+ int (*recmask_io) (struct pcm_codec *codec, int rw, int mask);
|
|
+ int (*mixer_ioctl)(struct pcm_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;
|
|
+
|
|
+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 inline int get_buffer_id(struct buffer_queue_s *q)
|
|
+{
|
|
+ int r, i;
|
|
+ unsigned long flags;
|
|
+
|
|
+ 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_pcm_controller_info * controller = (struct jz_pcm_controller_info *) dev_id;
|
|
+
|
|
+ //for DSP_GETOPTR
|
|
+ //spin_lock_irqsave(&controller->ioctllock, flags);
|
|
+ spin_lock(&controller->ioctllock);
|
|
+ jz_audio_dma_tran_count = count;
|
|
+ //spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
+ spin_unlock(&controller->ioctllock);
|
|
+ flags = claim_dma_lock();
|
|
+ __dmac_disable_module(0);//!!!!!!!!!
|
|
+ disable_dma(chan);
|
|
+ clear_dma_ff(chan);
|
|
+ set_dma_mode(chan, mode);
|
|
+#if MODE_is_8
|
|
+ __dmac_channel_set_src_port_width(chan, 8);
|
|
+ __dmac_channel_set_dest_port_width(chan, 8);
|
|
+ __dmac_channel_set_transfer_unit_8bit(chan);
|
|
+#else
|
|
+ __dmac_channel_set_src_port_width(chan, 16);
|
|
+ __dmac_channel_set_dest_port_width(chan, 16);
|
|
+ __dmac_channel_set_transfer_unit_16bit(chan);
|
|
+#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);
|
|
+ __dmac_enable_module(0);
|
|
+ release_dma_lock(flags);
|
|
+#if 0
|
|
+ //for DSP_GETOPTR on FPGA
|
|
+ struct jz_dma_chan *tmpchan = &jz_dma_table[1];
|
|
+
|
|
+ spin_lock(&controller->ioctllock);
|
|
+ jz_audio_dma_tran_count = count;
|
|
+ spin_unlock(&controller->ioctllock);
|
|
+ flags = claim_dma_lock();
|
|
+ disable_dma(chan);
|
|
+ clear_dma_ff(chan);
|
|
+ set_dma_mode(chan, mode);
|
|
+#if MODE_is_8
|
|
+ __dmac_channel_set_src_port_width(chan, 8);
|
|
+ __dmac_channel_set_dest_port_width(chan, 8);
|
|
+ __dmac_channel_set_transfer_unit_8bit(chan);
|
|
+#else
|
|
+ __dmac_channel_set_src_port_width(chan, 16);
|
|
+ __dmac_channel_set_dest_port_width(chan, 16);
|
|
+ __dmac_channel_set_transfer_unit_16bit(chan);
|
|
+#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);
|
|
+#if 0
|
|
+ __pcm_enable_tfs_intr();
|
|
+ __pcm_enable_tur_intr();
|
|
+ __pcm_enable_rfs_intr();
|
|
+ __pcm_enable_ror_intr();
|
|
+#endif
|
|
+#endif
|
|
+}
|
|
+
|
|
+static irqreturn_t jz_pcm_record_dma_irq(int irq, void *dev_id)
|
|
+{
|
|
+ int id1, id2;
|
|
+ struct jz_pcm_controller_info * controller = (struct jz_pcm_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);
|
|
+ //for DSP_GETIPTR
|
|
+ spin_lock(&controller->ioctllock);
|
|
+ controller->total_bytes += jz_audio_dma_tran_count;
|
|
+ controller->blocks ++;
|
|
+ spin_unlock(&controller->ioctllock);
|
|
+ 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_pcm_irq(int irq, void *dev_id)
|
|
+{
|
|
+ struct jz_pcm_controller_info * controller = (struct jz_pcm_controller_info *) dev_id;
|
|
+ printk("pcm interrupt REG_PCM_INTS : 0x%08x\n",REG_PCM_INTS);
|
|
+
|
|
+ return IRQ_HANDLED;
|
|
+}
|
|
+
|
|
+static irqreturn_t jz_pcm_replay_dma_irq(int irq, void *dev_id)
|
|
+{
|
|
+ int id;
|
|
+ unsigned long flags;
|
|
+ struct jz_pcm_controller_info * controller = (struct jz_pcm_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);
|
|
+ //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 PCM 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); //very busy
|
|
+ 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);
|
|
+ }
|
|
+ } 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 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; //release page error
|
|
+ }
|
|
+ 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; //release page error
|
|
+ }
|
|
+ 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 int jz_audio_set_speed(int dev, int rate)
|
|
+{
|
|
+ /* 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000 */
|
|
+ long codec_speed;
|
|
+ long speed = 0;
|
|
+
|
|
+ jz_audio_speed = rate;
|
|
+ if (rate > 48000)
|
|
+ rate = 48000;
|
|
+ if (rate < 8000)
|
|
+ rate = 8000;
|
|
+ jz_audio_rate = rate;
|
|
+
|
|
+ /*switch (rate) {
|
|
+ case 8000:
|
|
+ speed = 0;
|
|
+ break;
|
|
+ case 11025:
|
|
+ speed = 1;
|
|
+ break;
|
|
+ case 12000:
|
|
+ speed = 2;
|
|
+ break;
|
|
+ case 16000:
|
|
+ speed = 3;
|
|
+ break;
|
|
+ case 22050:
|
|
+ speed = 4;
|
|
+ break;
|
|
+ case 24000:
|
|
+ speed = 5;
|
|
+ break;
|
|
+ case 32000:
|
|
+ speed = 6;
|
|
+ break;
|
|
+ case 44100:
|
|
+ speed = 7;
|
|
+ break;
|
|
+ case 48000:
|
|
+ speed = 8;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ printk("set PCMIN or PCMOUT speed.\n");
|
|
+ __pcm_set_clk_rate(speed);
|
|
+ __pcm_set_sync_rate(256);*/
|
|
+
|
|
+ return jz_audio_rate;
|
|
+}
|
|
+
|
|
+
|
|
+static int record_fill_1x8_u(unsigned long dst_start, int count, int id)
|
|
+{
|
|
+ int cnt = 0;
|
|
+ volatile unsigned char *s = (unsigned char*)(*(in_dma_buf + id));
|
|
+ volatile unsigned char *dp = (unsigned char*)dst_start;
|
|
+
|
|
+ while (count > 0) {
|
|
+ *dp = *s;
|
|
+ dp ++;
|
|
+ s++;
|
|
+ count --;
|
|
+ cnt ++;
|
|
+ }
|
|
+
|
|
+ return cnt;
|
|
+}
|
|
+
|
|
+static int record_fill_2x8_u(unsigned long dst_start, int count, int id)
|
|
+{
|
|
+ int cnt = 0;
|
|
+ volatile unsigned char *s = (unsigned char*)(*(in_dma_buf + id));
|
|
+ volatile unsigned char *dp = (unsigned char*)dst_start;
|
|
+
|
|
+#if 1
|
|
+ while (count > 0) {
|
|
+ *dp = *s;
|
|
+ s ++;
|
|
+ dp ++;
|
|
+ count --;
|
|
+ cnt ++;
|
|
+ }
|
|
+#else
|
|
+ while (count > 0) {
|
|
+ *dp = *s;
|
|
+ s += 2; //skip right sample
|
|
+ dp ++;
|
|
+ count -= 2;
|
|
+ cnt ++;
|
|
+ }
|
|
+#endif
|
|
+ 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) {
|
|
+ *dp = *s;
|
|
+ s ++;
|
|
+ dp ++;
|
|
+ count -= 2;
|
|
+ cnt += 2;
|
|
+ }
|
|
+
|
|
+ return cnt;
|
|
+}
|
|
+
|
|
+static int record_fill_2x16_s(unsigned long dst_start, int count, int id)
|
|
+{
|
|
+ int cnt = 0;
|
|
+ unsigned short *s = (unsigned short*)(*(in_dma_buf + id));
|
|
+ unsigned short *dp = (unsigned short *)dst_start;
|
|
+
|
|
+#if 1
|
|
+ while (count > 0) {
|
|
+ *dp = *s;
|
|
+ s ++;
|
|
+ dp ++;
|
|
+ count -= 2;
|
|
+ cnt += 2;
|
|
+ }
|
|
+#else
|
|
+ while (count > 0) {
|
|
+ *dp = *s;
|
|
+ s += 2; //skip right sample
|
|
+ dp ++;
|
|
+ count -= 4;
|
|
+ cnt += 2;/* count in byte */
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ return cnt;
|
|
+}
|
|
+
|
|
+static void replay_fill_1x8_u(unsigned long src_start, int count, int id)
|
|
+{
|
|
+#if 0
|
|
+ volatile unsigned char *s = (unsigned char *)src_start;
|
|
+ volatile unsigned char *dp = (unsigned char *)(*(out_dma_buf + id));
|
|
+
|
|
+ *(out_dma_buf_data_count + id) = count;
|
|
+ while (count > 0) {
|
|
+ *dp = *s;
|
|
+ dp ++;
|
|
+ s ++;
|
|
+ count --;
|
|
+ }
|
|
+#else
|
|
+ volatile u8 *s = (u8 *)src_start;
|
|
+ volatile u8 *dp = (u8 *)(*(out_dma_buf + id));
|
|
+
|
|
+ *(out_dma_buf_data_count + id) = count;
|
|
+ while (count > 0) {
|
|
+ *dp = *s;
|
|
+ dp ++;
|
|
+ s ++;
|
|
+ count --;
|
|
+ }
|
|
+#endif
|
|
+}
|
|
+
|
|
+static void replay_fill_2x8_u(unsigned long src_start, int count, int id)
|
|
+{
|
|
+ volatile unsigned char *s = (unsigned char *)src_start;
|
|
+ volatile unsigned char *dp = (unsigned char *)(*(out_dma_buf + id));
|
|
+
|
|
+#if 1
|
|
+ *(out_dma_buf_data_count + id) = count;
|
|
+ while (count > 0) {
|
|
+ *dp = *s;
|
|
+ dp ++;
|
|
+ s ++;
|
|
+ count --;
|
|
+ }
|
|
+#else
|
|
+ while (count > 0) {
|
|
+ *dp = *s;
|
|
+ s += 2; //skip right sample
|
|
+ dp ++;
|
|
+ count -= 2;
|
|
+ cnt ++;
|
|
+ }
|
|
+ *(out_dma_buf_data_count + id) = cnt;
|
|
+#endif
|
|
+}
|
|
+
|
|
+
|
|
+static void replay_fill_1x16_s(unsigned long src_start, int count, int id)
|
|
+{
|
|
+ int cnt = 0;
|
|
+ unsigned short d1, l1;
|
|
+ signed short sam_to;
|
|
+ volatile unsigned short *s = (unsigned short *)src_start;
|
|
+ volatile unsigned short *dp = (unsigned short*)(*(out_dma_buf + id));
|
|
+
|
|
+ while (count > 0) {
|
|
+ d1 = *s;
|
|
+
|
|
+ sam_to = (signed short)d1;
|
|
+ if (sam_to >= 0) {
|
|
+ sam_to = 0xfff * sam_to / 0x7fff;
|
|
+ } else {
|
|
+ sam_to = 0 - sam_to;
|
|
+ sam_to = 0xfff * sam_to / 0x7fff;
|
|
+ sam_to = 0 - sam_to;
|
|
+ }
|
|
+ d1 = (unsigned short)sam_to;
|
|
+ d1 = d1 << 3;
|
|
+ l1 = d1 | jz_audio_volume;
|
|
+
|
|
+ *dp = l1;
|
|
+ s ++;
|
|
+ dp ++;
|
|
+ count -= 2;
|
|
+ cnt += 2 ;
|
|
+ }
|
|
+ *(out_dma_buf_data_count + id) = cnt;
|
|
+}
|
|
+
|
|
+static void replay_fill_2x16_s(unsigned long src_start, int count, int id)
|
|
+{
|
|
+ int cnt = 0;
|
|
+ unsigned short d1, l1;
|
|
+ volatile unsigned short *s = (unsigned short *)src_start;
|
|
+ volatile unsigned short *dp = (unsigned short*)(*(out_dma_buf + id));
|
|
+
|
|
+#if 1
|
|
+ while (count > 0) {
|
|
+ d1 = *s;
|
|
+ l1 = (d1 & 0xfff8) | jz_audio_volume;
|
|
+ *dp = l1;
|
|
+ s ++;
|
|
+ dp ++;
|
|
+ count -= 2;
|
|
+ cnt += 2 ;
|
|
+ }
|
|
+ *(out_dma_buf_data_count + id) = cnt;
|
|
+#else
|
|
+ while (count > 0) {
|
|
+ d1 = *s;
|
|
+ l1 = (d1 & 0xfff8) | jz_audio_volume;
|
|
+ *dp = l1;
|
|
+ s += 2; //skip right sample
|
|
+ dp ++;
|
|
+ count -= 4;
|
|
+ cnt += 2;
|
|
+ }
|
|
+ *(out_dma_buf_data_count + id) = cnt;
|
|
+#endif
|
|
+}
|
|
+
|
|
+static unsigned int jz_audio_set_format(int dev, unsigned int fmt)
|
|
+{
|
|
+ switch (fmt) {
|
|
+ case AFMT_U8:
|
|
+ case AFMT_S16_LE:
|
|
+ jz_audio_format = fmt;
|
|
+ jz_update_filler(jz_audio_format, jz_audio_channels);
|
|
+ case AFMT_QUERY:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return jz_audio_format;
|
|
+}
|
|
+
|
|
+static short jz_audio_set_channels(int dev, short channels)
|
|
+{
|
|
+ switch (channels) {
|
|
+ case 1:
|
|
+ case 2:
|
|
+ jz_audio_channels = channels;
|
|
+ jz_update_filler(jz_audio_format, jz_audio_channels);
|
|
+ break;
|
|
+ default:
|
|
+ printk("channel number is wrong. %d\n",__LINE__);
|
|
+ }
|
|
+
|
|
+ return jz_audio_channels;
|
|
+}
|
|
+
|
|
+static void init_codec(void)
|
|
+{
|
|
+ printk("set PCM codec RESET pin on LOW, while MCLK occur.\n");
|
|
+ printk("set LINSEL on LOW(8bits) or HIGH(13bits+3bits for PCMOUT gain).\n");
|
|
+}
|
|
+
|
|
+static void jz_audio_reset(void)
|
|
+{
|
|
+ __pcm_flush_fifo();
|
|
+ __pcm_disable_txfifo();
|
|
+ __pcm_disable_rxfifo();
|
|
+ __pcm_set_transmit_trigger(7);
|
|
+ __pcm_set_receive_trigger(7);
|
|
+ __pcm_omsb_next_sync();
|
|
+ __pcm_imsb_next_sync();
|
|
+#if MODE_is_8
|
|
+ __pcm_set_iss(8);//8bits decided by LINSEL
|
|
+ __pcm_set_oss(8);//8bits decided by LINSEL
|
|
+#else
|
|
+ __pcm_set_iss(16);//16bits decided by LINSEL
|
|
+ __pcm_set_oss(16);
|
|
+#endif
|
|
+ __pcm_disable_tfs_intr();
|
|
+ __pcm_disable_tur_intr();
|
|
+ __pcm_disable_rfs_intr();
|
|
+ __pcm_disable_ror_intr();
|
|
+
|
|
+ __pcm_disable_receive_dma();
|
|
+ __pcm_disable_transmit_dma();
|
|
+
|
|
+ //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_pcm_audio_fops */
|
|
+static struct file_operations jz_pcm_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_pcm_open_mixdev(struct inode *inode, struct file *file)
|
|
+{
|
|
+ int i;
|
|
+ int minor = MINOR(inode->i_rdev);
|
|
+ struct jz_pcm_controller_info *controller = pcm_controller;
|
|
+
|
|
+ for (i = 0; i < NR_PCM; i++)
|
|
+ if (controller->pcm_codec[i] != NULL && controller->pcm_codec[i]->dev_mixer == minor)
|
|
+ goto match;
|
|
+
|
|
+ if (!controller)
|
|
+ return -ENODEV;
|
|
+match:
|
|
+ file->private_data = controller->pcm_codec[i];
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int jz_pcm_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
|
|
+{
|
|
+ struct pcm_codec *codec = (struct pcm_codec *)file->private_data;
|
|
+ return codec->mixer_ioctl(codec, cmd, arg);
|
|
+}
|
|
+
|
|
+static loff_t jz_pcm_llseek(struct file *file, loff_t offset, int origin)
|
|
+{
|
|
+ return -ESPIPE;
|
|
+}
|
|
+
|
|
+static struct file_operations jz_pcm_mixer_fops =
|
|
+{
|
|
+ owner: THIS_MODULE,
|
|
+ llseek: jz_pcm_llseek,
|
|
+ ioctl: jz_pcm_ioctl_mixdev,
|
|
+ open: jz_pcm_open_mixdev,
|
|
+};
|
|
+
|
|
+static int pcm_mixer_ioctl(struct pcm_codec *codec, unsigned int cmd, unsigned long arg)
|
|
+{
|
|
+ int ret;
|
|
+ long val = 0;
|
|
+
|
|
+ switch (cmd) {
|
|
+ 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_VOLUME:
|
|
+ ret = get_user(val, (long *) arg);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ val = val & 0xff;
|
|
+ if(val < 0)
|
|
+ val = 0;
|
|
+ if(val > 100)
|
|
+ val = 100;
|
|
+ if (val == 100) {
|
|
+ dump_pcmc_reg();
|
|
+ return 100;
|
|
+ }
|
|
+ val = val / 10;
|
|
+ switch (val) {
|
|
+ case 0:
|
|
+ case 1:
|
|
+ jz_audio_volume = 7;
|
|
+ break;
|
|
+ case 2:
|
|
+ jz_audio_volume = 6;
|
|
+ break;
|
|
+ case 3:
|
|
+ jz_audio_volume = 5;
|
|
+ break;
|
|
+ case 4:
|
|
+ jz_audio_volume = 4;
|
|
+ break;
|
|
+ case 5:
|
|
+ jz_audio_volume = 3;
|
|
+ break;
|
|
+ case 6:
|
|
+ jz_audio_volume = 2;
|
|
+ break;
|
|
+ case 7:
|
|
+ case 8:
|
|
+ jz_audio_volume = 1;
|
|
+ break;
|
|
+ case 9:
|
|
+ case 10:
|
|
+ jz_audio_volume = 0;
|
|
+ break;
|
|
+ }
|
|
+ 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:
|
|
+ printk("Can not justify Mic gain to the PCM codec.!\n");
|
|
+ return -ENOSYS;
|
|
+
|
|
+ case SOUND_MIXER_READ_MIC:
|
|
+ printk("Can not justify Mic gain to the PCM codec.!\n");
|
|
+ return -ENOSYS;
|
|
+ default:
|
|
+ return -ENOSYS;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+int pcm_probe_codec(struct pcm_codec *codec)
|
|
+{
|
|
+ /* generic OSS to PCM wrapper */
|
|
+ codec->mixer_ioctl = pcm_mixer_ioctl;
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+/* PCM codec initialisation. */
|
|
+static int __init jz_pcm_codec_init(struct jz_pcm_controller_info *controller)
|
|
+{
|
|
+ int num_pcm = 0;
|
|
+ struct pcm_codec *codec;
|
|
+
|
|
+ for (num_pcm = 0; num_pcm < NR_PCM; num_pcm++) {
|
|
+ if ((codec = kmalloc(sizeof(struct pcm_codec),GFP_KERNEL)) == NULL)
|
|
+ return -ENOMEM;
|
|
+ memset(codec, 0, sizeof(struct pcm_codec));
|
|
+ codec->private_data = controller;
|
|
+ codec->id = num_pcm;
|
|
+
|
|
+ if (pcm_probe_codec(codec) == 0)
|
|
+ break;
|
|
+ if ((codec->dev_mixer = register_sound_mixer(&jz_pcm_mixer_fops, -1)) < 0) {
|
|
+ printk(KERN_ERR "Jz PCM: couldn't register mixer!\n");
|
|
+ kfree(codec);
|
|
+ break;
|
|
+ }
|
|
+ controller->pcm_codec[num_pcm] = codec;
|
|
+ }
|
|
+
|
|
+ return num_pcm;
|
|
+}
|
|
+
|
|
+static void jz_update_filler(int format, int channels)
|
|
+{
|
|
+#define TYPE(fmt,ch) (((fmt)<<2) | ((ch)&3))
|
|
+
|
|
+ switch (TYPE(format, channels)) {
|
|
+ default:
|
|
+ case TYPE(AFMT_U8, 1):
|
|
+ replay_filler = replay_fill_1x8_u;
|
|
+ record_filler = record_fill_1x8_u;
|
|
+ break;
|
|
+ case TYPE(AFMT_U8, 2):
|
|
+ /*replay_filler = replay_fill_2x8_u;
|
|
+ record_filler = record_fill_2x8_u;*/
|
|
+ printk("channel is 2. Line:%d\n",__LINE__);
|
|
+ break;
|
|
+ case TYPE(AFMT_S16_LE, 1):
|
|
+ replay_filler = replay_fill_1x16_s;
|
|
+ record_filler = record_fill_1x16_s;
|
|
+ break;
|
|
+ case TYPE(AFMT_S16_LE, 2):
|
|
+ /*replay_filler = replay_fill_2x16_s;
|
|
+ record_filler = record_fill_2x16_s;*/
|
|
+ printk("channel is 2. Line:%d\n",__LINE__);
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void __init attach_jz_pcm(struct jz_pcm_controller_info *controller)
|
|
+{
|
|
+ char *name;
|
|
+ int adev;//No of Audio device.
|
|
+ int err;
|
|
+
|
|
+ name = controller->name;
|
|
+ /* register /dev/audio */
|
|
+ adev = register_sound_dsp(&jz_pcm_audio_fops, -1);
|
|
+ if (adev < 0)
|
|
+ goto audio_failed;
|
|
+ /* initialize PCM codec and register /dev/mixer */
|
|
+ if (jz_pcm_codec_init(controller) <= 0)
|
|
+ goto mixer_failed;
|
|
+
|
|
+ controller->tmp1 = (void *)__get_free_pages(GFP_KERNEL, JZCODEC_USER_BUFFER);
|
|
+ if (!controller->tmp1) {
|
|
+ printk(KERN_ERR "%s: can't allocate tmp buffers.\n", controller->name);
|
|
+ goto tmp1_failed;
|
|
+ }
|
|
+ printk("DMA_ID_PCM_TX=0x%08x DMA_ID_PCM_RX=0x%08x\n",DMA_ID_PCM_TX ,DMA_ID_PCM_RX);
|
|
+ if ((controller->dma1 = jz_request_dma(DMA_ID_PCM_TX, "audio dac", jz_pcm_replay_dma_irq, IRQF_DISABLED, controller)) < 0) {
|
|
+ printk(KERN_ERR "%s: can't reqeust DMA DAC channel.\n", name);
|
|
+ goto dma1_failed;
|
|
+ }
|
|
+ if ((controller->dma2 = jz_request_dma(DMA_ID_PCM_RX, "audio adc", jz_pcm_record_dma_irq, IRQF_DISABLED, controller)) < 0) {
|
|
+ printk(KERN_ERR "%s: can't reqeust DMA ADC channel.\n", name);
|
|
+ goto dma2_failed;
|
|
+ }
|
|
+ printk("JzSOC On-Chip PCM 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));
|
|
+
|
|
+ err = request_irq(IRQ_PCM, jz_pcm_irq, IRQF_DISABLED, "pcm irq", controller);
|
|
+ if (err < 0)
|
|
+ printk("can't allocate pcm irq.\n");
|
|
+ controller->dev_audio = adev;
|
|
+
|
|
+ return;
|
|
+dma2_failed:
|
|
+ jz_free_dma(controller->dma2);
|
|
+dma1_failed:
|
|
+ jz_free_dma(controller->dma1);
|
|
+tmp1_failed:
|
|
+ free_pages((unsigned long)controller->tmp1, JZCODEC_USER_BUFFER);
|
|
+
|
|
+mixer_failed:
|
|
+ unregister_sound_dsp(adev);
|
|
+audio_failed:
|
|
+ return;
|
|
+}
|
|
+
|
|
+
|
|
+static int __init probe_jz_pcm(struct jz_pcm_controller_info **controller)
|
|
+{
|
|
+ if ((*controller = kmalloc(sizeof(struct jz_pcm_controller_info),
|
|
+ GFP_KERNEL)) == NULL) {
|
|
+ printk(KERN_ERR "Jz PCM Controller: out of memory.\n");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ (*controller)->name = "Jz PCM 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);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void __exit unload_jz_pcm(struct jz_pcm_controller_info *controller)
|
|
+{
|
|
+ int adev = controller->dev_audio;
|
|
+
|
|
+ __pcm_reset();
|
|
+ schedule_timeout(5);
|
|
+ __pcm_disable();
|
|
+ __pcm_clk_disable();
|
|
+ __pcm_flush_fifo();
|
|
+
|
|
+ controller->dev_audio = -1;
|
|
+ jz_free_dma(controller->dma1);
|
|
+ jz_free_dma(controller->dma2);
|
|
+ free_pages((unsigned long)controller->tmp1, JZCODEC_USER_BUFFER);
|
|
+
|
|
+ if (adev >= 0)
|
|
+ unregister_sound_dsp(controller->dev_audio);
|
|
+}
|
|
+
|
|
+static int __init init_jz_pcm(void)
|
|
+{
|
|
+ int errno;
|
|
+ /* 24M OSC ---> CPCCR.ECS ---> PCMCDR.PCMS ---> cpm_pcm_sysclk(X) */
|
|
+ long X = 12000000; //in Hz /* 6.144 MHz <= X <= 264.192 MHz */
|
|
+
|
|
+#if 0
|
|
+ /* pcm_sys_clk is from PLL divsion */
|
|
+ REG_CPM_PCMCDR = 0x8000001b;
|
|
+ REG_CPM_CPCCR |= 0x00400000;
|
|
+#endif
|
|
+ /* pcm_sys_clk is from external clock */
|
|
+ /* reset codec GPF4 */
|
|
+ __gpio_as_output(32 * 5 + 4);
|
|
+ __gpio_set_pin(32 * 5 + 4);
|
|
+ mdelay(1);
|
|
+
|
|
+ __pcm_reset();
|
|
+ schedule_timeout(5);
|
|
+ __pcm_clk_disable();
|
|
+
|
|
+ /* set CPM to output cpm-pcm-sysclk ,assume cpm-pcm-sysclk is X Hz */
|
|
+ /* PCMCLK must be 2048000 Hz, it is 256 mutil of PCMSYNC */
|
|
+ //__pcm_set_clk_rate(X, 2048000);
|
|
+ __pcm_set_clk_rate(X, 2000000);
|
|
+ /* PCMSYNC must be 8000 Hz. 2048000 / 256 = 8000 */
|
|
+ __pcm_set_sync_rate(2048000, 8000);
|
|
+ //__pcm_set_sync_rate(2000000, 8000);
|
|
+ __pcm_set_sync_len(0);
|
|
+
|
|
+ __pcm_flush_fifo();
|
|
+ __pcm_disable_txfifo();
|
|
+ __pcm_disable_rxfifo();
|
|
+ __pcm_set_transmit_trigger(7);
|
|
+ __pcm_set_receive_trigger(7);
|
|
+ __pcm_omsb_next_sync();
|
|
+ __pcm_imsb_next_sync();
|
|
+
|
|
+#if MODE_is_8
|
|
+ __pcm_set_iss(8);//8bits decided by LINSEL
|
|
+ __pcm_set_oss(8);
|
|
+#else
|
|
+ __pcm_set_iss(16);//16bits decided by LINSEL
|
|
+ __pcm_set_oss(16);
|
|
+#endif
|
|
+ __pcm_set_valid_slot(1);
|
|
+
|
|
+ __pcm_disable_tfs_intr();
|
|
+ __pcm_disable_tur_intr();
|
|
+ __pcm_disable_rfs_intr();
|
|
+ __pcm_disable_ror_intr();
|
|
+
|
|
+ __pcm_disable_receive_dma();
|
|
+ __pcm_disable_transmit_dma();
|
|
+
|
|
+ __pcm_last_sample();
|
|
+ __pcm_as_master();
|
|
+
|
|
+ __pcm_enable();
|
|
+ __pcm_clk_enable();
|
|
+
|
|
+ if ((errno = probe_jz_pcm(&pcm_controller)) < 0)
|
|
+ return errno;
|
|
+ attach_jz_pcm(pcm_controller);
|
|
+
|
|
+ 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);
|
|
+
|
|
+ __gpio_clear_pin(32 * 5 + 4);
|
|
+ udelay(100);
|
|
+
|
|
+ __gpio_set_pin(32 * 5 + 4);
|
|
+ dump_pcmc_reg();
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+static void __exit cleanup_jz_pcm(void)
|
|
+{
|
|
+ unload_jz_pcm(pcm_controller);
|
|
+ Free_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize);
|
|
+}
|
|
+
|
|
+module_init(init_jz_pcm);
|
|
+module_exit(cleanup_jz_pcm);
|
|
+
|
|
+static int drain_adc(struct jz_pcm_controller_info *ctrl, int nonblock)
|
|
+{
|
|
+ unsigned long flags;
|
|
+ int count, i=0;
|
|
+
|
|
+ for (;;) {
|
|
+ if ( i < MAXDELAY ) {
|
|
+ udelay(10);
|
|
+ i++;
|
|
+ } else
|
|
+ break;
|
|
+
|
|
+ spin_lock_irqsave(&ctrl->lock, flags);
|
|
+ count = get_dma_residue(ctrl->dma2);
|
|
+ spin_unlock_irqrestore(&ctrl->lock, flags);
|
|
+ if (count <= 0)
|
|
+ break;
|
|
+
|
|
+ if (nonblock)
|
|
+ return -EBUSY;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+static int drain_dac(struct jz_pcm_controller_info *ctrl, int nonblock)
|
|
+{
|
|
+ unsigned long flags;
|
|
+ int count, ele, 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(10);
|
|
+ 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);
|
|
+
|
|
+ spin_lock_irqsave(&ctrl->lock, flags);
|
|
+ count = get_dma_residue(ctrl->dma1);
|
|
+ spin_unlock_irqrestore(&ctrl->lock, flags);
|
|
+ if (count <= 0)
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int jz_audio_release(struct inode *inode, struct file *file)
|
|
+{
|
|
+ struct jz_pcm_controller_info *controller = (struct jz_pcm_controller_info *) file->private_data;
|
|
+
|
|
+ if (controller == NULL)
|
|
+ return -ENODEV;
|
|
+
|
|
+ if ( controller->opened1 == 1 ) {
|
|
+ __pcm_enable_transmit_dma();
|
|
+ __pcm_enable_txfifo();
|
|
+ drain_dac(controller, file->f_flags & O_NONBLOCK);
|
|
+ disable_dma(controller->dma1);
|
|
+ set_dma_count(controller->dma1, 0);
|
|
+
|
|
+ __pcm_disable_transmit_dma();
|
|
+ __pcm_disable_txfifo();
|
|
+
|
|
+ spin_lock(&controller->ioctllock);
|
|
+ controller->total_bytes = 0;
|
|
+ controller->count = 0;
|
|
+ controller->finish = 0;
|
|
+ jz_audio_dma_tran_count = 0;
|
|
+ controller->blocks = 0;
|
|
+ controller->nextOut = 0;
|
|
+ spin_unlock(&controller->ioctllock);
|
|
+ //__pcm_disable();
|
|
+ controller->opened1 = 0;
|
|
+ }
|
|
+
|
|
+ if ( controller->opened2 == 1 ) {
|
|
+ first_record_call = 1;
|
|
+ __pcm_enable_receive_dma();
|
|
+ __pcm_enable_rxfifo();
|
|
+ drain_adc(controller, file->f_flags & O_NONBLOCK);
|
|
+ disable_dma(controller->dma2);
|
|
+ set_dma_count(controller->dma2, 0);
|
|
+ __pcm_disable_receive_dma();
|
|
+ __pcm_disable_rxfifo();
|
|
+
|
|
+ spin_lock(&controller->ioctllock);
|
|
+ controller->total_bytes = 0;
|
|
+ jz_audio_dma_tran_count = 0;
|
|
+ controller->count = 0;
|
|
+ controller->finish = 0;
|
|
+ controller->blocks = 0;
|
|
+ controller->nextIn = 0;
|
|
+ spin_unlock(&controller->ioctllock);
|
|
+ //__pcm_disable();
|
|
+ controller->opened2 = 0;
|
|
+ }
|
|
+ __pcm_disable_tfs_intr();
|
|
+ __pcm_disable_tur_intr();
|
|
+ __pcm_disable_rfs_intr();
|
|
+ __pcm_disable_ror_intr();
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int jz_audio_open(struct inode *inode, struct file *file)
|
|
+{
|
|
+ int i;
|
|
+ struct jz_pcm_controller_info *controller = pcm_controller;
|
|
+
|
|
+ if (controller == NULL)
|
|
+ return -ENODEV;
|
|
+
|
|
+ if (controller->opened1 == 1 || controller->opened2 == 1 ) {
|
|
+ printk("\naudio is busy!\n");
|
|
+ return -EBUSY;
|
|
+ }
|
|
+ REG_DMAC_DMACKE(0) = 0x3f;
|
|
+ REG_DMAC_DMACKE(1) = 0x3f;
|
|
+ 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;
|
|
+
|
|
+ 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;
|
|
+ /* set PCMOUT params */
|
|
+ }
|
|
+
|
|
+ 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;
|
|
+ /* set PCMIN params */
|
|
+ }
|
|
+
|
|
+ file->private_data = controller;
|
|
+ jz_audio_reset();
|
|
+ __pcm_enable();
|
|
+
|
|
+ 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;
|
|
+ unsigned int flags;
|
|
+ audio_buf_info abinfo;
|
|
+ int i, bytes, id;
|
|
+ count_info cinfo;
|
|
+ struct jz_pcm_controller_info *controller = (struct jz_pcm_controller_info *) file->private_data;
|
|
+
|
|
+ val = 0;
|
|
+ bytes = 0;
|
|
+ switch (cmd) {
|
|
+ case OSS_GETVERSION:
|
|
+ return put_user(SOUND_VERSION, (int *)arg);
|
|
+ case SNDCTL_DSP_RESET:
|
|
+ return 0;
|
|
+ case SNDCTL_DSP_SYNC:
|
|
+ if (file->f_mode & FMODE_WRITE)
|
|
+ drain_dac(controller, file->f_flags & O_NONBLOCK);
|
|
+ return 0;
|
|
+ case SNDCTL_DSP_SPEED:
|
|
+ {
|
|
+ if (get_user(val, (int *)arg))
|
|
+ return -EFAULT;
|
|
+ /* set smaple rate */
|
|
+ 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(4*PAGE_SIZE, (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:
|
|
+ {
|
|
+ if (get_user(val, (int *)arg))
|
|
+ return -EFAULT;
|
|
+ /* Select sample format */
|
|
+ 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;
|
|
+ printk("%s:%s:%d\n",__FILE__,__FUNCTION__,__LINE__);
|
|
+ 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);//16 least bits
|
|
+
|
|
+ if (newfragsize < 4 * PAGE_SIZE)
|
|
+ newfragsize = 4 * PAGE_SIZE;
|
|
+ if (newfragsize > (16 * PAGE_SIZE)) //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:
|
|
+ {
|
|
+ if (!(file->f_mode & FMODE_WRITE))
|
|
+ return -EINVAL;
|
|
+ //unused fragment amount
|
|
+ spin_lock_irqsave(&controller->ioctllock, flags);
|
|
+ jz_audio_fragments = elements_in_queue(&out_empty_queue);
|
|
+ for (i = 0; i < jz_audio_fragments; i++)
|
|
+ bytes += jz_audio_fragsize;
|
|
+
|
|
+ spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
+ abinfo.fragments = jz_audio_fragments;
|
|
+ abinfo.fragstotal = jz_audio_fragstotal;
|
|
+ abinfo.fragsize = jz_audio_fragsize;
|
|
+ abinfo.bytes = bytes;
|
|
+ return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
|
|
+ }
|
|
+ case SNDCTL_DSP_GETISPACE:
|
|
+ if (!(file->f_mode & FMODE_READ))
|
|
+ return -EINVAL;
|
|
+ bytes = 0;
|
|
+ //unused fragment amount
|
|
+ jz_audio_fragments = elements_in_queue(&in_empty_queue);
|
|
+ for (i = 0; i < jz_audio_fragments; i++)
|
|
+ bytes += jz_audio_fragsize;
|
|
+
|
|
+ abinfo.fragments = jz_audio_fragments;
|
|
+ abinfo.fragstotal = jz_audio_fragstotal;
|
|
+ abinfo.fragsize = jz_audio_fragsize;
|
|
+ abinfo.bytes = 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);
|
|
+ cinfo.bytes = controller->total_bytes;
|
|
+ cinfo.blocks = controller->blocks;
|
|
+ cinfo.ptr = controller->nextIn;
|
|
+ controller->blocks = 0;
|
|
+ spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
+ 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);
|
|
+ cinfo.bytes = controller->total_bytes;
|
|
+ cinfo.blocks = controller->blocks;
|
|
+ cinfo.ptr = controller->nextOut;
|
|
+ controller->blocks = 0;
|
|
+ spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
+ 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);
|
|
+ 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);
|
|
+ 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)
|
|
+{
|
|
+ unsigned long flags;
|
|
+ unsigned int mask = 0;
|
|
+ struct jz_pcm_controller_info *controller = (struct jz_pcm_controller_info *) file->private_data;
|
|
+
|
|
+ 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);
|
|
+ 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);
|
|
+
|
|
+ return mask;
|
|
+}
|
|
+
|
|
+static ssize_t jz_audio_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
|
|
+{
|
|
+ int id, ret = 0, left_count, copy_count, cnt = 0;
|
|
+ struct jz_pcm_controller_info *controller = (struct jz_pcm_controller_info *) file->private_data;
|
|
+
|
|
+ if (count < 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ __pcm_enable_receive_dma();
|
|
+ __pcm_enable_rxfifo();
|
|
+
|
|
+ spin_lock(&controller->ioctllock);
|
|
+ controller->nextIn = 0;
|
|
+ spin_unlock(&controller->ioctllock);
|
|
+
|
|
+ spin_lock(&controller->lock);
|
|
+
|
|
+#if 0
|
|
+ if (count < 2 * PAGE_SIZE )
|
|
+ copy_count = count * 16 / (jz_audio_channels * jz_audio_format);
|
|
+ else
|
|
+ copy_count = 2 * PAGE_SIZE ;
|
|
+#else
|
|
+ if (count <= jz_audio_fragsize)
|
|
+ copy_count = count;
|
|
+ else
|
|
+ copy_count = jz_audio_fragsize;
|
|
+#endif
|
|
+
|
|
+ left_count = count;
|
|
+ spin_unlock(&controller->lock);
|
|
+
|
|
+ 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;
|
|
+ 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);
|
|
+
|
|
+ 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->tmp1+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;
|
|
+ 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->tmp1+ret, cnt))
|
|
+ return ret ? ret : -EFAULT;
|
|
+ spin_lock(&controller->lock);
|
|
+ ret += cnt;
|
|
+ spin_unlock(&controller->lock);
|
|
+
|
|
+ spin_lock(&controller->ioctllock);
|
|
+ controller->nextIn += ret;
|
|
+ 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, left_count, copy_count;
|
|
+ unsigned int flags;
|
|
+ struct jz_pcm_controller_info *controller = (struct jz_pcm_controller_info *) file->private_data;
|
|
+
|
|
+ if (count <= 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ __pcm_enable_transmit_dma();
|
|
+ __pcm_enable_txfifo();
|
|
+
|
|
+ spin_lock_irqsave(&controller->ioctllock, flags);
|
|
+ controller->nextOut = 0;
|
|
+ spin_unlock_irqrestore(&controller->ioctllock, flags);
|
|
+
|
|
+#if 0
|
|
+ if (count < 2 * PAGE_SIZE )
|
|
+ copy_count = count;
|
|
+ else
|
|
+ copy_count = 2 * PAGE_SIZE;
|
|
+#else
|
|
+ if (count <= jz_audio_fragsize)
|
|
+ copy_count = count;
|
|
+ else
|
|
+ copy_count = jz_audio_fragsize;
|
|
+#endif
|
|
+
|
|
+ left_count = count;
|
|
+ ret = 0;
|
|
+
|
|
+ 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 (elements_in_queue(&out_empty_queue) == 0) {
|
|
+ // all are full
|
|
+ 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((unsigned long)controller->tmp1 + ret, copy_count, id);
|
|
+ if(*(out_dma_buf_data_count + id) > 0) {
|
|
+ put_buffer_id(&out_full_queue, id); //busy in
|
|
+ dma_cache_wback_inv(*(out_dma_buf + id), *(out_dma_buf_data_count + id));
|
|
+ } else
|
|
+ put_buffer_id(&out_empty_queue, id); //spare
|
|
+ } else
|
|
+ goto audio_write_back;
|
|
+
|
|
+ left_count = left_count - copy_count;
|
|
+ ret += copy_count;//all is in byte
|
|
+
|
|
+ spin_lock(&controller->ioctllock);
|
|
+ controller->nextOut += ret;
|
|
+ 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);
|
|
+
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void dump_pcmc_reg(void)
|
|
+{
|
|
+ printk("REG_DMAC_DMACKE(0) : 0x%08x\n",REG_DMAC_DMACKE(0));
|
|
+ printk("REG_DMAC_DMACKE(1) : 0x%08x\n",REG_DMAC_DMACKE(1));
|
|
+ printk("REG_CPM_CLKGR=0x%08x\n",REG_CPM_CLKGR);
|
|
+ printk("REG_CPM_CPCCR : 0x%08x\n",REG_CPM_CPCCR);
|
|
+ printk("REG_CPM_PCMCDR : 0x%08x\n",REG_CPM_PCMCDR);
|
|
+ printk("REG_PCM_CLT : 0x%08x\n",REG_PCM_CTL);
|
|
+ printk("REG_PCM_CFG : 0x%08x\n",REG_PCM_CFG);
|
|
+ printk("REG_PCM_INTC : 0x%08x\n",REG_PCM_INTC);
|
|
+ printk("REG_PCM_INTS : 0x%08x\n",REG_PCM_INTS);
|
|
+ printk("REG_PCM_DIV : 0x%08x\n",REG_PCM_DIV);
|
|
+}
|
|
--- /dev/null
|
|
+++ b/sound/oss/jzcodec.c
|
|
@@ -0,0 +1,443 @@
|
|
+/*
|
|
+ * linux/drivers/sound/jzcodec.c
|
|
+ *
|
|
+ * JzSOC internal audio driver.
|
|
+ *
|
|
+ */
|
|
+#include <linux/init.h>
|
|
+#include <linux/interrupt.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"
|
|
+
|
|
+#define USE_NONE 1
|
|
+#define USE_MIC 2
|
|
+#define USE_LINEIN 3
|
|
+
|
|
+typedef struct hpvol_shift_s
|
|
+{
|
|
+ int hpvol;
|
|
+ int shift;
|
|
+} hpvol_shift_t;
|
|
+
|
|
+extern mixer_info info;
|
|
+extern _old_mixer_info old_info;
|
|
+extern int codec_volue_shift;
|
|
+extern hpvol_shift_t hpvol_shift_table[72];
|
|
+extern int abnormal_data_count;
|
|
+
|
|
+extern void (*set_codec_mode)(void);
|
|
+extern void (*each_time_init_codec)(void);
|
|
+extern int (*set_codec_startup_param)(void);
|
|
+extern void (*set_codec_volume_table)(void);
|
|
+extern void (*set_codec_record)(int mode);
|
|
+extern void (*set_codec_replay)(void);
|
|
+extern void (*set_codec_replay_record)(int mode);
|
|
+extern void (*turn_on_codec)(void);
|
|
+extern void (*turn_off_codec)(void);
|
|
+extern void (*set_codec_speed)(int rate);
|
|
+extern void (*reset_codec)(void);
|
|
+extern void (*codec_mixer_old_info_id_name)(void);
|
|
+extern void (*codec_mixer_info_id_name)(void);
|
|
+extern void (*set_codec_bass)(int val);
|
|
+extern void (*set_codec_volume)(int val);
|
|
+extern void (*set_codec_mic)(int val);
|
|
+extern void (*set_codec_line)(int val);
|
|
+extern void (*i2s_resume_codec)(void);
|
|
+extern void (*i2s_suspend_codec)(void);
|
|
+extern void (*set_codec_direct_mode)(void);
|
|
+extern void (*clear_codec_direct_mode)(void);
|
|
+
|
|
+static int jzcodec_reg[2];
|
|
+
|
|
+void set_jzcodec_mode(void);
|
|
+void each_time_init_jzcodec(void);
|
|
+int set_jzcodec_startup_param(void);
|
|
+void set_jzcodec_volume_table(void);
|
|
+void set_jzcodec_replay(void);
|
|
+void set_jzcodec_record(int mode);
|
|
+void turn_on_jzcodec(void);
|
|
+void turn_off_jzcodec(void);
|
|
+void set_jzcodec_speed(int rate);
|
|
+void reset_jzcodec(void);
|
|
+void jzcodec_mixer_old_info_id_name(void);
|
|
+void jzcodec_mixer_info_id_name(void);
|
|
+void set_jzcodec_bass(int val);
|
|
+void set_jzcodec_volume(int val);
|
|
+void set_jzcodec_mic(int val);
|
|
+void set_jzcodec_line(int val);
|
|
+void in_codec_app1(void);
|
|
+void in_codec_app12(void);
|
|
+void HP_turn_on(void);
|
|
+void HP_turn_off(void);
|
|
+void resume_jzcodec(void);
|
|
+void suspend_jzcodec(void);
|
|
+void set_jzcodec_replay_record(int mode);
|
|
+
|
|
+void set_jzcodec_mode(void)
|
|
+{
|
|
+ __i2s_internal_codec();
|
|
+ __i2s_as_slave();
|
|
+ __i2s_select_i2s();
|
|
+ __aic_select_i2s();
|
|
+
|
|
+ REG_ICDC_CDCCR1 = 0x001b2303;
|
|
+ schedule_timeout(1);
|
|
+ REG_ICDC_CDCCR1 = 0x001b2302;
|
|
+}
|
|
+
|
|
+void each_time_init_jzcodec(void)
|
|
+{
|
|
+ __i2s_disable();
|
|
+ __i2s_as_slave();
|
|
+ __i2s_set_oss_sample_size(16);
|
|
+ __i2s_set_iss_sample_size(16);
|
|
+}
|
|
+
|
|
+int set_jzcodec_startup_param(void)
|
|
+{
|
|
+ REG_ICDC_CDCCR1 = 0x001b2300;
|
|
+ schedule_timeout(50);
|
|
+ REG_ICDC_CDCCR1 = 0x00033300;
|
|
+ schedule_timeout(5);
|
|
+ REG_ICDC_CDCCR1 = 0x17026300;
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+void set_jzcodec_volume_table(void)
|
|
+{
|
|
+ int errno,hpvol_step,sample_shift;
|
|
+
|
|
+ codec_volue_shift = 0;
|
|
+ hpvol_step = 3;
|
|
+ sample_shift = 0;
|
|
+ for(errno = 0;errno < 72;errno++) {
|
|
+ hpvol_shift_table[errno].hpvol = hpvol_step;
|
|
+ hpvol_shift_table[errno].shift = sample_shift;
|
|
+ hpvol_step --;
|
|
+ if(hpvol_step <= 0) {
|
|
+ hpvol_step = 3;
|
|
+ sample_shift ++;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+void set_jzcodec_replay(void)
|
|
+{
|
|
+ in_codec_app1();
|
|
+}
|
|
+
|
|
+void set_jzcodec_record(int mode)
|
|
+{
|
|
+ switch (mode) {
|
|
+ case USE_NONE:
|
|
+ case USE_MIC:
|
|
+ in_codec_app12();
|
|
+ abnormal_data_count = 0;
|
|
+ break;
|
|
+ case USE_LINEIN:
|
|
+ REG_ICDC_CDCCR1 = 0x27022000;//for linein
|
|
+ mdelay(300);
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+void set_jzcodec_replay_record(int mode)
|
|
+{
|
|
+ long val = 0;
|
|
+ REG_ICDC_CDCCR1 = 0x00037302;
|
|
+ mdelay(2);
|
|
+ REG_ICDC_CDCCR1 = 0x03006000;
|
|
+ mdelay(2);
|
|
+
|
|
+ switch (mode) {
|
|
+ case USE_NONE:
|
|
+ case USE_MIC:
|
|
+ REG_ICDC_CDCCR1 = 0x17022000;//for mic
|
|
+ break;
|
|
+ case USE_LINEIN:
|
|
+ REG_ICDC_CDCCR1 = 0x27022000;//for linein
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ val = REG_ICDC_CDCCR2;
|
|
+ val &= 0x0000ff00;
|
|
+ val |= 0x00170030;
|
|
+ REG_ICDC_CDCCR2 = val;
|
|
+}
|
|
+
|
|
+void turn_on_jzcodec(void)
|
|
+{
|
|
+ HP_turn_on();
|
|
+}
|
|
+
|
|
+void set_jzcodec_direct_mode(void)
|
|
+{
|
|
+ long val = 0;
|
|
+ REG_ICDC_CDCCR1 = 0x14000000;
|
|
+ val &= 0x0000ff00;
|
|
+ val |= 0x001f0033;
|
|
+ REG_ICDC_CDCCR2 = val;
|
|
+ mdelay(300);
|
|
+}
|
|
+
|
|
+void clear_jzcodec_direct_mode(void)
|
|
+{
|
|
+ HP_turn_off();
|
|
+}
|
|
+
|
|
+void turn_off_jzcodec(void)
|
|
+{
|
|
+ HP_turn_off();
|
|
+}
|
|
+
|
|
+void set_jzcodec_speed(int rate)
|
|
+{
|
|
+ long codec_speed,speed = 0;
|
|
+ switch (rate) {
|
|
+ case 8000:
|
|
+ speed = 0;
|
|
+ break;
|
|
+ case 11025:
|
|
+ speed = 1;
|
|
+ break;
|
|
+ case 12000:
|
|
+ speed = 2;
|
|
+ break;
|
|
+ case 16000:
|
|
+ speed = 3;
|
|
+ break;
|
|
+ case 22050:
|
|
+ speed = 4;
|
|
+ break;
|
|
+ case 24000:
|
|
+ speed = 5;
|
|
+ break;
|
|
+ case 32000:
|
|
+ speed = 6;
|
|
+ break;
|
|
+ case 44100:
|
|
+ speed = 7;
|
|
+ break;
|
|
+ case 48000:
|
|
+ speed = 8;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ codec_speed = REG_ICDC_CDCCR2;
|
|
+ codec_speed |= 0x00000f00;
|
|
+
|
|
+ speed = speed << 8;
|
|
+ speed |= 0xfffff0ff;
|
|
+ codec_speed &= speed;
|
|
+ REG_ICDC_CDCCR2 = codec_speed;
|
|
+}
|
|
+
|
|
+void reset_jzcodec(void)
|
|
+{
|
|
+ REG_ICDC_CDCCR1 |= 1;
|
|
+ mdelay(1);
|
|
+ REG_ICDC_CDCCR1 &= 0xfffffffe;
|
|
+}
|
|
+
|
|
+void jzcodec_mixer_old_info_id_name(void)
|
|
+{
|
|
+ strncpy(info.id, "JZCODEC", sizeof(info.id));
|
|
+ strncpy(info.name,"Jz internal codec", sizeof(info.name));
|
|
+}
|
|
+
|
|
+void jzcodec_mixer_info_id_name(void)
|
|
+{
|
|
+ strncpy(old_info.id, "JZCODEC", sizeof(old_info.id));
|
|
+ strncpy(old_info.name,"Jz internal codec", sizeof(old_info.name));
|
|
+}
|
|
+
|
|
+void set_jzcodec_bass(int val)
|
|
+{
|
|
+ int bass_gain = 0;
|
|
+ if(val < 25)
|
|
+ bass_gain = 0;
|
|
+ if(val >= 25 && val < 50)
|
|
+ bass_gain = 1;
|
|
+ if(val >= 50 && val < 75)
|
|
+ bass_gain = 2;
|
|
+ if(val >= 75 && val <= 100 )
|
|
+ bass_gain = 3;
|
|
+
|
|
+ REG_ICDC_CDCCR2 = ((REG_ICDC_CDCCR2 & ~(0x3 << 4)) | (bass_gain << 4));
|
|
+}
|
|
+
|
|
+void set_jzcodec_volume(int val)
|
|
+{
|
|
+ unsigned int sample_oss;
|
|
+ int index,shift_max,vol_scale,vol_step,codec_volume;
|
|
+ shift_max = 0;
|
|
+ sample_oss = (REG_AIC_CR & 0x00380000) >> 19;
|
|
+
|
|
+ switch(sample_oss)
|
|
+ {
|
|
+ case 0x0:
|
|
+ shift_max = 4; /* 8 bits */
|
|
+ break;
|
|
+ case 0x1:
|
|
+ shift_max = 10; /* 16 bits */
|
|
+ break;
|
|
+ case 0x2:
|
|
+ shift_max = 12; /* 18 bits */
|
|
+ break;
|
|
+ case 0x3:
|
|
+ shift_max = 15; /* 20 bits */
|
|
+ break;
|
|
+ case 0x4:
|
|
+ shift_max = 19; /* 24 bits */
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ vol_scale = 3 * (shift_max + 1);
|
|
+ vol_step = 100 / vol_scale;
|
|
+
|
|
+ for(index = 0;index <= 100;index += vol_step)
|
|
+ if( val <= index )
|
|
+ break;
|
|
+
|
|
+ if(index == 0)
|
|
+ index = vol_step;
|
|
+ index = index / vol_step;
|
|
+ if(index > vol_scale)
|
|
+ index = vol_scale;
|
|
+
|
|
+ index = vol_scale - index;
|
|
+ codec_volume = hpvol_shift_table[index].hpvol;
|
|
+ codec_volue_shift = hpvol_shift_table[index].shift;
|
|
+ codec_volume = 3;
|
|
+ REG_ICDC_CDCCR2 = ((REG_ICDC_CDCCR2 & ~(0x3)) | codec_volume);
|
|
+}
|
|
+
|
|
+void set_jzcodec_mic(int val)
|
|
+{
|
|
+ long mic_gain;
|
|
+
|
|
+ mic_gain = 31 * val /100;
|
|
+ REG_ICDC_CDCCR2 = (REG_ICDC_CDCCR2 | (0x3 << 4));
|
|
+ REG_ICDC_CDCCR2 = ((REG_ICDC_CDCCR2 & ~(0x1f << 16)) | (mic_gain << 16));
|
|
+}
|
|
+
|
|
+void set_jzcodec_line(int val)
|
|
+{
|
|
+ long line_gain;
|
|
+
|
|
+ line_gain = 31 * val /100;
|
|
+ REG_ICDC_CDCCR2 = ((REG_ICDC_CDCCR2 & ~(0x1f << 16)) | (line_gain << 16));
|
|
+}
|
|
+
|
|
+void HP_turn_on(void)
|
|
+{
|
|
+ long val = 0;
|
|
+ /* simple and slow anti-pop */
|
|
+ REG_ICDC_CDCCR1 = 0x00037302;
|
|
+ mdelay(2);
|
|
+ REG_ICDC_CDCCR1 = 0x03006000;
|
|
+ mdelay(2);
|
|
+ REG_ICDC_CDCCR1 = 0x03002000;
|
|
+
|
|
+ val = REG_ICDC_CDCCR2;
|
|
+ val &= 0x0000ff00;
|
|
+ val |= 0x001f0033;
|
|
+ REG_ICDC_CDCCR2 = val;
|
|
+}
|
|
+
|
|
+void HP_turn_off(void)
|
|
+{
|
|
+ mdelay(20);
|
|
+ REG_ICDC_CDCCR1 = 0x00033300;
|
|
+}
|
|
+
|
|
+void in_codec_app1(void)
|
|
+{
|
|
+ /* test is OK */
|
|
+ HP_turn_on();
|
|
+}
|
|
+
|
|
+void in_codec_app12(void)
|
|
+{
|
|
+ REG_ICDC_CDCCR1 = 0x14024300;
|
|
+ mdelay(300);
|
|
+}
|
|
+
|
|
+void resume_jzcodec(void)
|
|
+{
|
|
+ REG_ICDC_CDCCR2 = 0x00170800;
|
|
+ mdelay(2);
|
|
+ REG_ICDC_CDCCR1 = 0x001f2102;
|
|
+ mdelay(5);
|
|
+ REG_ICDC_CDCCR1 = 0x00033302;
|
|
+ mdelay(550);
|
|
+ REG_ICDC_CDCCR1 = 0x00033300;
|
|
+ REG_ICDC_CDCCR1 = jzcodec_reg[0];
|
|
+ REG_ICDC_CDCCR2 = jzcodec_reg[1];
|
|
+}
|
|
+
|
|
+void suspend_jzcodec(void)
|
|
+{
|
|
+
|
|
+ jzcodec_reg[0] = REG_ICDC_CDCCR1;
|
|
+ jzcodec_reg[1] = REG_ICDC_CDCCR2;
|
|
+
|
|
+ REG_ICDC_CDCCR1 = 0x001b2302;
|
|
+ mdelay(1);
|
|
+ REG_ICDC_CDCCR1 = 0x001b2102;
|
|
+}
|
|
+
|
|
+static int __init init_jzcodec(void)
|
|
+{
|
|
+ set_codec_mode = set_jzcodec_mode;
|
|
+ each_time_init_codec = each_time_init_jzcodec;
|
|
+
|
|
+ set_codec_startup_param = set_jzcodec_startup_param;
|
|
+ set_codec_volume_table = set_jzcodec_volume_table;
|
|
+ set_codec_record = set_jzcodec_record;
|
|
+ set_codec_replay = set_jzcodec_replay;
|
|
+ set_codec_replay_record = set_jzcodec_replay_record;
|
|
+ turn_on_codec = turn_on_jzcodec;
|
|
+ turn_off_codec = turn_off_jzcodec;
|
|
+ set_codec_speed = set_jzcodec_speed;
|
|
+ reset_codec = reset_jzcodec;
|
|
+ codec_mixer_old_info_id_name = jzcodec_mixer_old_info_id_name;
|
|
+ codec_mixer_info_id_name = jzcodec_mixer_info_id_name;
|
|
+ set_codec_bass = set_jzcodec_bass;
|
|
+ set_codec_volume = set_jzcodec_volume;
|
|
+ set_codec_mic = set_jzcodec_mic;
|
|
+ set_codec_line = set_jzcodec_line;
|
|
+ i2s_resume_codec = resume_jzcodec;
|
|
+ i2s_suspend_codec = suspend_jzcodec;
|
|
+ set_codec_direct_mode = set_jzcodec_direct_mode;
|
|
+ clear_codec_direct_mode = clear_jzcodec_direct_mode;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+static void __exit cleanup_jzcodec(void)
|
|
+{
|
|
+ REG_ICDC_CDCCR1 = 0x001b2302;
|
|
+
|
|
+}
|
|
+
|
|
+module_init(init_jzcodec);
|
|
+module_exit(cleanup_jzcodec);
|
|
--- /dev/null
|
|
+++ b/sound/oss/jzdlv.c
|
|
@@ -0,0 +1,644 @@
|
|
+/*
|
|
+ * linux/drivers/sound/jzcodec.c
|
|
+ *
|
|
+ * JzSOC internal audio driver.
|
|
+ *
|
|
+ */
|
|
+#include <linux/init.h>
|
|
+#include <linux/interrupt.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"
|
|
+#include "jzdlv.h"
|
|
+
|
|
+#define USE_NONE 1
|
|
+#define USE_MIC 2
|
|
+#define USE_LINEIN 3
|
|
+
|
|
+extern mixer_info info;
|
|
+extern _old_mixer_info old_info;
|
|
+extern int codec_volue_shift;
|
|
+
|
|
+extern void (*set_codec_mode)(void);
|
|
+extern void (*each_time_init_codec)(void);
|
|
+extern int (*set_codec_startup_param)(void);
|
|
+extern void (*set_codec_record)(void);
|
|
+extern void (*set_codec_replay)(void);
|
|
+extern void (*set_codec_replay_record)(void);
|
|
+extern void (*turn_on_codec)(void);
|
|
+extern void (*turn_off_codec)(void);
|
|
+extern void (*set_codec_speed)(int rate);
|
|
+extern void (*reset_codec)(void);
|
|
+extern void (*codec_mixer_old_info_id_name)(void);
|
|
+extern void (*codec_mixer_info_id_name)(void);
|
|
+extern void (*set_codec_bass)(int val);
|
|
+extern void (*set_codec_volume)(int val);
|
|
+extern void (*set_codec_mic)(int val);
|
|
+extern void (*set_codec_line)(int val);
|
|
+extern void (*i2s_resume_codec)(void);
|
|
+extern void (*i2s_suspend_codec)(void);
|
|
+extern void (*set_codec_direct_mode)(void);
|
|
+extern void (*clear_codec_direct_mode)(void);
|
|
+
|
|
+
|
|
+void set_dlv_mode(void);
|
|
+void each_time_init_jzcodec(void);
|
|
+int set_dlv_startup_param(void);
|
|
+void set_dlvjzcodec_volume_table(void);
|
|
+void set_dlv_replay(void);
|
|
+void set_dlv_record(void);
|
|
+void set_dlv_speed(int rate);
|
|
+void reset_dlv(void);
|
|
+void jzcodec_mixer_old_info_id_name(void);
|
|
+void jzcodec_mixer_info_id_name(void);
|
|
+void set_dlv_volume(int val);
|
|
+void set_dlv_mic(int val);
|
|
+
|
|
+extern int jz_mic_only;
|
|
+int read_codec_file(int addr)
|
|
+{
|
|
+ while (__icdc_rgwr_ready());
|
|
+ __icdc_set_addr(addr);
|
|
+ mdelay(1);
|
|
+ return(__icdc_get_value());
|
|
+}
|
|
+
|
|
+#if 0
|
|
+void printk_codec_files(void)
|
|
+{
|
|
+ int cnt;
|
|
+
|
|
+ printk("\n");
|
|
+
|
|
+ printk("REG_CPM_I2SCDR=0x%08x\n",REG_CPM_I2SCDR);
|
|
+ printk("REG_CPM_CLKGR=0x%08x\n",REG_CPM_CLKGR);
|
|
+ printk("REG_CPM_CPCCR=0x%08x\n",REG_CPM_CPCCR);
|
|
+ printk("REG_AIC_FR=0x%08x\n",REG_AIC_FR);
|
|
+ printk("REG_AIC_CR=0x%08x\n",REG_AIC_CR);
|
|
+ printk("REG_AIC_I2SCR=0x%08x\n",REG_AIC_I2SCR);
|
|
+ printk("REG_AIC_SR=0x%08x\n",REG_AIC_SR);
|
|
+ printk("REG_ICDC_RGDATA=0x%08x\n",REG_ICDC_RGDATA);
|
|
+
|
|
+ for (cnt = 0; cnt <= 27 ; cnt++) {
|
|
+ printk(" ( %d : 0x%x ) ",cnt ,read_codec_file(cnt));
|
|
+ }
|
|
+ printk("\n");
|
|
+}
|
|
+#endif
|
|
+
|
|
+void write_codec_file(int addr, int val)
|
|
+{
|
|
+ while (__icdc_rgwr_ready());
|
|
+ __icdc_set_addr(addr);
|
|
+ __icdc_set_cmd(val); /* write */
|
|
+ mdelay(1);
|
|
+ __icdc_set_rgwr();
|
|
+ mdelay(1);
|
|
+}
|
|
+
|
|
+int write_codec_file_bit(int addr, int bitval, int mask_bit)
|
|
+{
|
|
+ int val;
|
|
+ while (__icdc_rgwr_ready());
|
|
+ __icdc_set_addr(addr);
|
|
+ mdelay(1);
|
|
+ val = __icdc_get_value(); /* read */
|
|
+
|
|
+ while (__icdc_rgwr_ready());
|
|
+ __icdc_set_addr(addr);
|
|
+ val &= ~(1 << mask_bit);
|
|
+ if (bitval == 1)
|
|
+ val |= 1 << mask_bit;
|
|
+
|
|
+ __icdc_set_cmd(val); /* write */
|
|
+ mdelay(1);
|
|
+ __icdc_set_rgwr();
|
|
+ mdelay(1);
|
|
+
|
|
+ while (__icdc_rgwr_ready());
|
|
+ __icdc_set_addr(addr);
|
|
+ val = __icdc_get_value(); /* read */
|
|
+
|
|
+ if (((val >> mask_bit) & bitval) == bitval)
|
|
+ return 1;
|
|
+ else
|
|
+ return 0;
|
|
+}
|
|
+void set_dlv_mode(void)
|
|
+{
|
|
+ /*REG_CPM_CPCCR &= ~(1 << 31);
|
|
+ REG_CPM_CPCCR &= ~(1 << 30);*/
|
|
+ write_codec_file(0, 0xf);
|
|
+
|
|
+ REG_AIC_I2SCR = 0x10;
|
|
+ __i2s_internal_codec();
|
|
+ __i2s_as_slave();
|
|
+ __i2s_select_i2s();
|
|
+ __aic_select_i2s();
|
|
+ __aic_reset();
|
|
+ mdelay(10);
|
|
+ REG_AIC_I2SCR = 0x10;
|
|
+ mdelay(20);
|
|
+
|
|
+ /* power on DLV */
|
|
+ write_codec_file(9, 0xff);
|
|
+ write_codec_file(8, 0x3f);
|
|
+ mdelay(10);
|
|
+}
|
|
+void reset_dlv_codec(void)
|
|
+{
|
|
+ /* reset DLV codec. from hibernate mode to sleep mode */
|
|
+ write_codec_file(0, 0xf);
|
|
+ write_codec_file_bit(6, 0, 0);
|
|
+ write_codec_file_bit(6, 0, 1);
|
|
+ mdelay(200);
|
|
+ //write_codec_file(0, 0xf);
|
|
+ write_codec_file_bit(5, 0, 7);//PMR1.SB_DAC->0
|
|
+ write_codec_file_bit(5, 0, 4);//PMR1.SB_ADC->0
|
|
+ mdelay(10);//wait for stability
|
|
+}
|
|
+
|
|
+void each_time_init_dlv(void)
|
|
+{
|
|
+ __i2s_disable();
|
|
+ __i2s_as_slave();
|
|
+ __aic_internal_codec();
|
|
+ __i2s_set_oss_sample_size(16);
|
|
+ __i2s_set_iss_sample_size(16);
|
|
+}
|
|
+
|
|
+int set_dlv_startup_param(void)
|
|
+{
|
|
+ __i2s_disable_transmit_intr();
|
|
+ __i2s_disable_receive_intr();
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+/* set Audio data replay */
|
|
+void set_audio_data_replay(void)
|
|
+{
|
|
+ /* DAC path */
|
|
+ write_codec_file(9, 0xff);
|
|
+ //write_codec_file(8, 0x30);
|
|
+ write_codec_file(8, 0x20);
|
|
+ mdelay(10);
|
|
+ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0
|
|
+ write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1
|
|
+ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1
|
|
+
|
|
+ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0
|
|
+ write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1
|
|
+
|
|
+ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0
|
|
+ //mdelay(100);
|
|
+ //write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0
|
|
+ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1
|
|
+ //mdelay(300);
|
|
+}
|
|
+
|
|
+#if 1 /* mask warning */
|
|
+/* set Record MIC input audio without playback */
|
|
+void set_record_mic_input_audio_without_playback(void)
|
|
+{
|
|
+ /* ADC path for MIC IN */
|
|
+ jz_mic_only = 1;
|
|
+ write_codec_file(9, 0xff);
|
|
+ write_codec_file(8, 0x3f);
|
|
+ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0
|
|
+ mdelay(10);
|
|
+ write_codec_file_bit(1, 1, 2);
|
|
+ //write_codec_file_bit(1, 1, 6);//CR1.MONO->1
|
|
+
|
|
+ write_codec_file(22, 0x40);//mic 1
|
|
+ write_codec_file_bit(3, 1, 7);//CR1.HP_DIS->1
|
|
+ write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1
|
|
+ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1
|
|
+
|
|
+ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0
|
|
+ write_codec_file_bit(1, 0, 3);//CR1.DACSEL->0
|
|
+ //write_codec_file_bit(6, 1, 3);// gain set
|
|
+
|
|
+ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0
|
|
+ mdelay(100);
|
|
+ write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0
|
|
+ write_codec_file(1, 0x4);
|
|
+}
|
|
+#endif
|
|
+
|
|
+#if 1 /* mask warning */
|
|
+/* unset Record MIC input audio without playback */
|
|
+void unset_record_mic_input_audio_without_playback(void)
|
|
+{
|
|
+ /* ADC path for MIC IN */
|
|
+ jz_mic_only = 0;
|
|
+ write_codec_file_bit(5, 1, 4);//SB_ADC->1
|
|
+ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1
|
|
+ write_codec_file(22, 0xc0);//CR3.SB_MIC1
|
|
+ write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1
|
|
+ write_codec_file_bit(1, 1, 5);//DAC_MUTE->1
|
|
+ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1
|
|
+ write_codec_file_bit(6, 1, 1);//SB->1
|
|
+}
|
|
+#endif
|
|
+
|
|
+#if 0 /* mask warning */
|
|
+/* set Record LINE input audio without playback */
|
|
+void set_record_line_input_audio_without_playback(void)
|
|
+{
|
|
+ /* ADC path for LINE IN */
|
|
+ jz_mic_only = 1;
|
|
+ write_codec_file(9, 0xff);
|
|
+ write_codec_file(8, 0x3f);
|
|
+ mdelay(10);
|
|
+ write_codec_file(22, 0xf6);//line in 1
|
|
+ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0
|
|
+ write_codec_file_bit(3, 1, 7);//CR1.HP_DIS->1
|
|
+ write_codec_file_bit(5, 0, 3);//PMR1.SB_LIN->0
|
|
+ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1
|
|
+
|
|
+ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0
|
|
+ write_codec_file_bit(1, 0, 3);//CR1.DACSEL->0
|
|
+ mdelay(10);
|
|
+ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0
|
|
+ mdelay(100);
|
|
+ write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0
|
|
+ write_codec_file(1, 0x4);
|
|
+}
|
|
+#endif
|
|
+
|
|
+#if 0 /* mask warning */
|
|
+/* unset Record LINE input audio without playback */
|
|
+void unset_record_line_input_audio_without_playback(void)
|
|
+{
|
|
+ /* ADC path for LINE IN */
|
|
+ write_codec_file_bit(5, 1, 4);//SB_ADC->1
|
|
+ write_codec_file_bit(5, 1, 3);//ONR1.SB_LIN->1
|
|
+
|
|
+ write_codec_file(22, 0xc0);//CR3.SB_MIC1
|
|
+ write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1
|
|
+ write_codec_file_bit(1, 1, 5);//DAC_MUTE->1
|
|
+ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1
|
|
+ write_codec_file_bit(6, 1, 1);//SB->1
|
|
+}
|
|
+#endif
|
|
+
|
|
+#if 0 /* mask warning */
|
|
+/* set Playback LINE input audio direct only */
|
|
+void set_playback_line_input_audio_direct_only(void)
|
|
+{
|
|
+ jz_audio_reset();//or init_codec()
|
|
+ REG_AIC_I2SCR = 0x10;
|
|
+ write_codec_file(9, 0xff);
|
|
+ write_codec_file(8, 0x3f);
|
|
+ mdelay(10);
|
|
+ write_codec_file(22, 0xf6);//line in 1
|
|
+ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0
|
|
+ mdelay(10);
|
|
+ write_codec_file_bit(1, 1, 2);//CR1.HP_BYPASS->1
|
|
+ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0
|
|
+ write_codec_file_bit(1, 0, 3);//CR1.DACSEL->0
|
|
+ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1
|
|
+ write_codec_file_bit(5, 0, 3);//PMR1.SB_LIN->0
|
|
+
|
|
+ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0
|
|
+ mdelay(100);
|
|
+ write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0
|
|
+ //write_codec_file_bit(5, 1, 7);//PMR1.SB_DAC->1
|
|
+ //write_codec_file_bit(5, 1, 4);//PMR1.SB_ADC->1
|
|
+}
|
|
+#endif
|
|
+
|
|
+#if 0 /* mask warning */
|
|
+/* unset Playback LINE input audio direct only */
|
|
+void unset_playback_line_input_audio_direct_only(void)
|
|
+{
|
|
+ write_codec_file_bit(6, 0, 3);//GIM->0
|
|
+ write_codec_file_bit(1, 0, 2);//PMR1.BYPASS->0
|
|
+ write_codec_file_bit(5, 1, 3);//PMR1.SB_LINE->1
|
|
+ write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1
|
|
+ mdelay(100);
|
|
+ write_codec_file_bit(5, 1, 5);//PMR1.SB_MIX->1
|
|
+ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1
|
|
+ write_codec_file_bit(6, 1, 1);//SB->1
|
|
+}
|
|
+#endif
|
|
+
|
|
+#if 0 /* mask warning */
|
|
+/* set Record MIC input audio with direct playback */
|
|
+void set_record_mic_input_audio_with_direct_playback(void)
|
|
+{
|
|
+ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0
|
|
+ jz_mic_only = 0;
|
|
+ write_codec_file(9, 0xff);
|
|
+ write_codec_file(8, 0x3f);
|
|
+ mdelay(10);
|
|
+
|
|
+ write_codec_file(22, 0x60);//mic 1
|
|
+ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0
|
|
+ write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1
|
|
+ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1
|
|
+ write_codec_file_bit(1, 0, 7);//CR1.SB_MICBIAS->0
|
|
+ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0
|
|
+
|
|
+ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0
|
|
+ write_codec_file_bit(1, 0, 3);//CR1.DACSEL->0
|
|
+ write_codec_file_bit(6, 1, 3);// gain set
|
|
+
|
|
+ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0
|
|
+ mdelay(100);
|
|
+ write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0
|
|
+ //write_codec_file(1, 0x4);
|
|
+}
|
|
+#endif
|
|
+
|
|
+#if 0 /* mask warning */
|
|
+/* unset Record MIC input audio with direct playback */
|
|
+void unset_record_mic_input_audio_with_direct_playback(void)
|
|
+{
|
|
+ /* ADC path for MIC IN */
|
|
+ jz_mic_only = 0;
|
|
+ write_codec_file_bit(5, 1, 4);//SB_ADC->1
|
|
+ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1
|
|
+ write_codec_file(22, 0xc0);//CR3.SB_MIC1
|
|
+ write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1
|
|
+ write_codec_file_bit(1, 1, 5);//DAC_MUTE->1
|
|
+ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1
|
|
+ write_codec_file_bit(6, 1, 1);//SB->1
|
|
+}
|
|
+#endif
|
|
+
|
|
+#if 0 /* mask warning */
|
|
+/* set Record playing audio mixed with MIC input audio */
|
|
+void set_record_playing_audio_mixed_with_mic_input_audio(void)
|
|
+{
|
|
+ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0
|
|
+ write_codec_file(9, 0xff);
|
|
+ //write_codec_file(8, 0x30);
|
|
+ write_codec_file(8, 0x20);
|
|
+ mdelay(10);
|
|
+
|
|
+ write_codec_file(22, 0x63);//mic 1
|
|
+
|
|
+ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0
|
|
+ write_codec_file_bit(6, 1, 3);// gain set
|
|
+
|
|
+ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0
|
|
+ write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1
|
|
+ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1
|
|
+ write_codec_file_bit(1, 0, 7);//CR1.SB_MICBIAS->0
|
|
+ write_codec_file_bit(22, 0, 7);//CR3.SB_MIC->0
|
|
+ write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1
|
|
+ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0
|
|
+ write_codec_file_bit(5, 0, 4);//PMR1.SB_MIX->0
|
|
+}
|
|
+#endif
|
|
+
|
|
+#if 0 /* mask warning */
|
|
+/* unset Record playing audio mixed with MIC input audio */
|
|
+void unset_record_playing_audio_mixed_with_mic_input_audio(void)
|
|
+{
|
|
+ /* ADC path */
|
|
+ write_codec_file_bit(5, 1, 4);//SB_ADC->1
|
|
+ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1
|
|
+ //write_codec_file_bit(1, 1, 6);//CR1.MONO->1
|
|
+ write_codec_file(22, 0xc0);//CR3.SB_MIC1->1
|
|
+ //write_codec_file_bit(1, 1, 5);//DAC_MUTE->1
|
|
+ //write_codec_file_bit(5, 1, 6);//SB_OUT->1
|
|
+ write_codec_file_bit(5, 1, 7);//SB_DAC->1
|
|
+ write_codec_file_bit(5, 1, 5);//SB_MIX->1
|
|
+ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1
|
|
+ write_codec_file_bit(6, 1, 1);//SB->1
|
|
+}
|
|
+#endif
|
|
+
|
|
+#if 1 /* mask warning */
|
|
+/* set Record MIC input audio with Audio data replay (full duplex) */
|
|
+void set_record_mic_input_audio_with_audio_data_replay(void)
|
|
+{
|
|
+ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0
|
|
+ write_codec_file(9, 0xff);
|
|
+ //write_codec_file(8, 0x30);
|
|
+ write_codec_file(8, 0x20);
|
|
+ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0
|
|
+ write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1
|
|
+ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1
|
|
+
|
|
+ write_codec_file_bit(22, 0, 7);//CR3.SB_MIC->0
|
|
+ write_codec_file_bit(1, 0, 7);//CR1.SB_MICBIAS->0
|
|
+
|
|
+ write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1
|
|
+ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0
|
|
+}
|
|
+#endif
|
|
+
|
|
+#if 1 /* mask warning */
|
|
+/* unset Record MIC input audio with Audio data replay (full duplex) */
|
|
+void unset_record_mic_input_audio_with_audio_data_replay(void)
|
|
+{
|
|
+ /* ADC path */
|
|
+ write_codec_file_bit(5, 1, 4);//SB_ADC->1
|
|
+ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1
|
|
+ //write_codec_file_bit(1, 1, 6);//CR1.MONO->1
|
|
+ write_codec_file(22, 0xc0);//CR3.SB_MIC1->1
|
|
+ write_codec_file_bit(5, 1, 7);//SB_DAC->1
|
|
+ write_codec_file_bit(5, 1, 5);//SB_MIX->1
|
|
+ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1
|
|
+ write_codec_file_bit(6, 1, 1);//SB->1
|
|
+}
|
|
+#endif
|
|
+
|
|
+#if 1 /* mask warning */
|
|
+/* set Record LINE input audio with Audio data replay (full duplex for linein) */
|
|
+void set_record_line_input_audio_with_audio_data_replay(void)
|
|
+{
|
|
+ write_codec_file(9, 0xff);
|
|
+ //write_codec_file(8, 0x30);
|
|
+ write_codec_file(8, 0x20);
|
|
+ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0
|
|
+ write_codec_file_bit(5, 0, 3);//PMR1.SB_LIN->0
|
|
+ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1
|
|
+ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1
|
|
+ //write_codec_file_bit(22, 1, 7);//CR3.SB_MIC->1
|
|
+ write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1
|
|
+ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0
|
|
+
|
|
+
|
|
+ //jz_mic_only = 1;
|
|
+ write_codec_file(22, 0xc6);//line in 1
|
|
+ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0
|
|
+ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0
|
|
+ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0
|
|
+}
|
|
+#endif
|
|
+
|
|
+#if 1 /* mask warning */
|
|
+/* unset Record LINE input audio with Audio data replay (full duplex for linein) */
|
|
+void unset_record_line_input_audio_with_audio_data_replay(void)
|
|
+{
|
|
+ /* ADC path */
|
|
+ write_codec_file_bit(5, 1, 4);//SB_ADC->1
|
|
+ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1
|
|
+ //write_codec_file_bit(1, 1, 6);//CR1.MONO->1
|
|
+ write_codec_file(22, 0xc0);//CR3.SB_MIC1->1
|
|
+ write_codec_file_bit(5, 1, 7);//SB_DAC->1
|
|
+ write_codec_file_bit(5, 1, 5);//SB_MIX->1
|
|
+ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1
|
|
+ write_codec_file_bit(6, 1, 1);//SB->1
|
|
+}
|
|
+#endif
|
|
+
|
|
+#if 1
|
|
+/* unset Audio data replay */
|
|
+void unset_audio_data_replay(void)
|
|
+{
|
|
+ //write_codec_file_bit(1, 1, 5);//DAC_MUTE->1
|
|
+ //mdelay(800);
|
|
+ //write_codec_file_bit(5, 1, 6);//SB_OUT->1
|
|
+ //mdelay(800);
|
|
+ write_codec_file_bit(5, 1, 7);//SB_DAC->1
|
|
+ write_codec_file_bit(5, 1, 4);//SB_MIX->1
|
|
+ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1
|
|
+ write_codec_file_bit(6, 1, 1);//SB->1
|
|
+}
|
|
+#endif
|
|
+
|
|
+void set_dlv_replay(void)
|
|
+{
|
|
+ set_audio_data_replay();
|
|
+}
|
|
+
|
|
+void set_dlv_speed(int rate)
|
|
+{
|
|
+ int speed = 0, val;
|
|
+ speed = 0;
|
|
+ switch (rate) {
|
|
+ case 8000:
|
|
+ speed = 10;
|
|
+ break;
|
|
+ case 9600:
|
|
+ speed = 9;
|
|
+ break;
|
|
+ case 11025:
|
|
+ speed = 8;
|
|
+ break;
|
|
+ case 12000:
|
|
+ speed = 7;
|
|
+ break;
|
|
+ case 16000:
|
|
+ speed = 6;
|
|
+ break;
|
|
+ case 22050:
|
|
+ speed = 5;
|
|
+ break;
|
|
+ case 24000:
|
|
+ speed = 4;
|
|
+ break;
|
|
+ case 32000:
|
|
+ speed = 3;
|
|
+ break;
|
|
+ case 44100:
|
|
+ speed = 2;
|
|
+ break;
|
|
+ case 48000:
|
|
+ speed = 1;
|
|
+ break;
|
|
+ case 96000:
|
|
+ speed = 0;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ val = read_codec_file(4);
|
|
+ val = (speed << 4) | speed;
|
|
+ write_codec_file(4, val);
|
|
+}
|
|
+
|
|
+void reset_jzcodec(void)
|
|
+{
|
|
+
|
|
+}
|
|
+
|
|
+void dlv_mixer_old_info_id_name(void)
|
|
+{
|
|
+ strncpy(info.id, "JZDLV", sizeof(info.id));
|
|
+ strncpy(info.name,"Jz internal codec dlv on jz4750", sizeof(info.name));
|
|
+}
|
|
+
|
|
+void dlv_mixer_info_id_name(void)
|
|
+{
|
|
+ strncpy(old_info.id, "JZDLV", sizeof(old_info.id));
|
|
+ strncpy(old_info.name,"Jz internal codec dlv on jz4750", sizeof(old_info.name));
|
|
+}
|
|
+
|
|
+void set_dlv_mic(int val)
|
|
+{
|
|
+ int cur_vol ;
|
|
+ /* set gain */
|
|
+ //write_codec_file_bit(6, 1, 3);//GIM
|
|
+ cur_vol = 31 * val / 100;
|
|
+ cur_vol |= cur_vol << 4;
|
|
+ write_codec_file(19, cur_vol);//GIL,GIR
|
|
+}
|
|
+
|
|
+void set_dlv_line(int val)
|
|
+{
|
|
+ int cur_vol;
|
|
+ /* set gain */
|
|
+ cur_vol = 31 * val / 100;
|
|
+ cur_vol &= 0x1f;
|
|
+ write_codec_file(11, cur_vol);//GO1L
|
|
+ write_codec_file(12, cur_vol);//GO1R
|
|
+}
|
|
+
|
|
+void set_dlv_volume(int val)
|
|
+{
|
|
+ unsigned long cur_vol;
|
|
+ cur_vol = 31 * (100 - val) / 100;
|
|
+ write_codec_file(17, cur_vol | 0xc0);
|
|
+ write_codec_file(18, cur_vol);
|
|
+}
|
|
+
|
|
+static int __init init_dlv(void)
|
|
+{
|
|
+ set_codec_mode = set_dlv_mode;
|
|
+ each_time_init_codec = each_time_init_dlv;
|
|
+ reset_codec = reset_dlv_codec;
|
|
+ set_codec_startup_param = set_dlv_startup_param;
|
|
+
|
|
+ set_codec_replay = set_dlv_replay;
|
|
+
|
|
+ set_codec_speed = set_dlv_speed;
|
|
+
|
|
+ codec_mixer_old_info_id_name = dlv_mixer_old_info_id_name;
|
|
+ codec_mixer_info_id_name = dlv_mixer_info_id_name;
|
|
+
|
|
+ set_codec_volume = set_dlv_volume;
|
|
+ set_codec_mic = set_dlv_mic;
|
|
+ set_codec_line = set_dlv_line;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+static void __exit cleanup_dlv(void)
|
|
+{
|
|
+
|
|
+}
|
|
+
|
|
+module_init(init_dlv);
|
|
+module_exit(cleanup_dlv);
|
|
--- /dev/null
|
|
+++ b/sound/oss/jzdlv.h
|
|
@@ -0,0 +1,21 @@
|
|
+/* header file for dlv */
|
|
+void write_codec_file(int addr, int val);
|
|
+int read_codec_file(int addr);
|
|
+void printk_codec_files(void);
|
|
+int write_codec_file_bit(int addr, int bitval, int mask_bit);
|
|
+void set_audio_data_replay(void);
|
|
+void unset_audio_data_replay(void);
|
|
+void set_record_mic_input_audio_without_playback(void);
|
|
+void unset_record_mic_input_audio_without_playback(void);
|
|
+void set_record_line_input_audio_without_playback(void);
|
|
+void unset_record_line_input_audio_without_playback(void);
|
|
+void set_playback_line_input_audio_direct_only(void);
|
|
+void unset_playback_line_input_audio_direct_only(void);
|
|
+void set_record_mic_input_audio_with_direct_playback(void);
|
|
+void unset_record_mic_input_audio_with_direct_playback(void);
|
|
+void set_record_playing_audio_mixed_with_mic_input_audio(void);
|
|
+void unset_record_playing_audio_mixed_with_mic_input_audio(void);
|
|
+void set_record_mic_input_audio_with_audio_data_replay(void);
|
|
+void unset_record_mic_input_audio_with_audio_data_replay(void);
|
|
+void set_record_line_input_audio_with_audio_data_replay(void);
|
|
+void unset_record_line_input_audio_with_audio_data_replay(void);
|
|
--- a/sound/oss/os.h
|
|
+++ b/sound/oss/os.h
|
|
@@ -9,7 +9,6 @@
|
|
#ifdef __KERNEL__
|
|
#include <linux/string.h>
|
|
#include <linux/fs.h>
|
|
-#include <asm/dma.h>
|
|
#include <asm/io.h>
|
|
#include <asm/param.h>
|
|
#include <linux/sched.h>
|
|
--- a/sound/soc/Kconfig
|
|
+++ b/sound/soc/Kconfig
|
|
@@ -33,6 +33,7 @@ source "sound/soc/fsl/Kconfig"
|
|
source "sound/soc/davinci/Kconfig"
|
|
source "sound/soc/omap/Kconfig"
|
|
source "sound/soc/blackfin/Kconfig"
|
|
+source "sound/soc/jz4740/Kconfig"
|
|
|
|
# Supported codecs
|
|
source "sound/soc/codecs/Kconfig"
|
|
--- a/sound/soc/Makefile
|
|
+++ b/sound/soc/Makefile
|
|
@@ -2,4 +2,4 @@ snd-soc-core-objs := soc-core.o soc-dapm
|
|
|
|
obj-$(CONFIG_SND_SOC) += snd-soc-core.o
|
|
obj-$(CONFIG_SND_SOC) += codecs/ at32/ at91/ pxa/ s3c24xx/ sh/ fsl/ davinci/
|
|
-obj-$(CONFIG_SND_SOC) += omap/ au1x/ blackfin/
|
|
+obj-$(CONFIG_SND_SOC) += omap/ au1x/ blackfin/ jz4740/
|
|
--- a/sound/soc/codecs/Kconfig
|
|
+++ b/sound/soc/codecs/Kconfig
|
|
@@ -110,3 +110,9 @@ config SND_SOC_WM9712
|
|
|
|
config SND_SOC_WM9713
|
|
tristate
|
|
+
|
|
+config SND_SOC_ICODEC
|
|
+ tristate "Jz4740 internal codec"
|
|
+ depends on SND_SOC && SND_JZ4740_SOC_PAVO && SND_JZ4740_SOC_I2S
|
|
+ help
|
|
+ Say Y if you want to use internal codec on Ingenic Jz4740 PAVO board.
|
|
--- a/sound/soc/codecs/Makefile
|
|
+++ b/sound/soc/codecs/Makefile
|
|
@@ -19,6 +19,7 @@ snd-soc-wm8971-objs := wm8971.o
|
|
snd-soc-wm8990-objs := wm8990.o
|
|
snd-soc-wm9712-objs := wm9712.o
|
|
snd-soc-wm9713-objs := wm9713.o
|
|
+snd-soc-jzcodec-objs := jzcodec.o
|
|
|
|
obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o
|
|
obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o
|
|
@@ -41,3 +42,4 @@ obj-$(CONFIG_SND_SOC_WM8971) += snd-soc-
|
|
obj-$(CONFIG_SND_SOC_WM8990) += snd-soc-wm8990.o
|
|
obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
|
|
obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o
|
|
+obj-$(CONFIG_SND_SOC_ICODEC) += snd-soc-jzcodec.o
|
|
--- /dev/null
|
|
+++ b/sound/soc/codecs/jzcodec.c
|
|
@@ -0,0 +1,725 @@
|
|
+/*
|
|
+ * 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 <linux/module.h>
|
|
+#include <linux/moduleparam.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/pm.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <sound/driver.h>
|
|
+#include <sound/core.h>
|
|
+#include <sound/pcm.h>
|
|
+#include <sound/pcm_params.h>
|
|
+#include <sound/soc.h>
|
|
+#include <sound/soc-dapm.h>
|
|
+#include <sound/initval.h>
|
|
+
|
|
+#include "../jz4740/jz4740-pcm.h"
|
|
+#include "jzcodec.h"
|
|
+
|
|
+#define AUDIO_NAME "jzcodec"
|
|
+#define JZCODEC_VERSION "1.0"
|
|
+
|
|
+/*
|
|
+ * Debug
|
|
+ */
|
|
+
|
|
+#define JZCODEC_DEBUG 0
|
|
+
|
|
+#ifdef JZCODEC_DEBUG
|
|
+#define dbg(format, arg...) \
|
|
+ printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
|
|
+#else
|
|
+#define dbg(format, arg...) do {} while (0)
|
|
+#endif
|
|
+#define err(format, arg...) \
|
|
+ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
|
|
+#define info(format, arg...) \
|
|
+ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
|
|
+#define warn(format, arg...) \
|
|
+ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
|
|
+
|
|
+struct snd_soc_codec_device soc_codec_dev_jzcodec;
|
|
+
|
|
+/* codec private data */
|
|
+struct jzcodec_priv {
|
|
+ unsigned int sysclk;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * jzcodec register cache
|
|
+ */
|
|
+static u32 jzcodec_reg[JZCODEC_CACHEREGNUM / 2];
|
|
+
|
|
+/*
|
|
+ * codec register is 16 bits width in ALSA, so we define array to store 16 bits configure paras
|
|
+ */
|
|
+static u16 jzcodec_reg_LH[JZCODEC_CACHEREGNUM];
|
|
+
|
|
+/*
|
|
+ * read jzcodec register cache
|
|
+ */
|
|
+static inline unsigned int jzcodec_read_reg_cache(struct snd_soc_codec *codec,
|
|
+ unsigned int reg)
|
|
+{
|
|
+ u16 *cache = codec->reg_cache;
|
|
+
|
|
+ if (reg >= JZCODEC_CACHEREGNUM)
|
|
+ return -1;
|
|
+ return cache[reg];
|
|
+}
|
|
+
|
|
+/*
|
|
+ * write jzcodec register cache
|
|
+ */
|
|
+static inline void jzcodec_write_reg_cache(struct snd_soc_codec *codec,
|
|
+ unsigned int reg, u16 value)
|
|
+{
|
|
+ u16 *cache = codec->reg_cache;
|
|
+ u32 reg_val;
|
|
+
|
|
+ if (reg >= JZCODEC_CACHEREGNUM) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ cache[reg] = value;
|
|
+ /* update internal codec register value */
|
|
+ switch (reg) {
|
|
+ case 0:
|
|
+ case 1:
|
|
+ reg_val = cache[0] & 0xffff;
|
|
+ reg_val = reg_val | (cache[1] << 16);
|
|
+ jzcodec_reg[0] = reg_val;
|
|
+ break;
|
|
+ case 2:
|
|
+ case 3:
|
|
+ reg_val = cache[2] & 0xffff;
|
|
+ reg_val = reg_val | (cache[3] << 16);
|
|
+ jzcodec_reg[1] = reg_val;
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * write to the jzcodec register space
|
|
+ */
|
|
+static int jzcodec_write(struct snd_soc_codec *codec, unsigned int reg,
|
|
+ unsigned int value)
|
|
+{
|
|
+ jzcodec_write_reg_cache(codec, reg, value);
|
|
+ if(codec->hw_write)
|
|
+ codec->hw_write(&value, NULL, reg);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int jzcodec_reset(struct snd_soc_codec *codec)
|
|
+{
|
|
+ u16 val;
|
|
+
|
|
+ val = jzcodec_read_reg_cache(codec, ICODEC_1_LOW);
|
|
+ val = val | 0x1;
|
|
+ jzcodec_write(codec, ICODEC_1_LOW, val);
|
|
+ mdelay(1);
|
|
+
|
|
+ val = jzcodec_read_reg_cache(codec, ICODEC_1_LOW);
|
|
+ val = val & ~0x1;
|
|
+ jzcodec_write(codec, ICODEC_1_LOW, val);
|
|
+ mdelay(1);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct snd_kcontrol_new jzcodec_snd_controls[] = {
|
|
+
|
|
+ //SOC_DOUBLE_R("Master Playback Volume", 1, 1, 0, 3, 0),
|
|
+ SOC_DOUBLE_R("Master Playback Volume", ICODEC_2_LOW, ICODEC_2_LOW, 0, 3, 0),
|
|
+ //SOC_DOUBLE_R("MICBG", ICODEC_2_LOW, ICODEC_2_LOW, 4, 3, 0),
|
|
+ //SOC_DOUBLE_R("Line", 2, 2, 0, 31, 0),
|
|
+ SOC_DOUBLE_R("Line", ICODEC_2_HIGH, ICODEC_2_HIGH, 0, 31, 0),
|
|
+};
|
|
+
|
|
+/* add non dapm controls */
|
|
+static int jzcodec_add_controls(struct snd_soc_codec *codec)
|
|
+{
|
|
+ int err, i;
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(jzcodec_snd_controls); i++) {
|
|
+ if ((err = snd_ctl_add(codec->card,
|
|
+ snd_soc_cnew(&jzcodec_snd_controls[i], codec, NULL))) < 0)
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct snd_soc_dapm_widget jzcodec_dapm_widgets[] = {
|
|
+ SND_SOC_DAPM_OUTPUT("LOUT"),
|
|
+ SND_SOC_DAPM_OUTPUT("LHPOUT"),
|
|
+ SND_SOC_DAPM_OUTPUT("ROUT"),
|
|
+ SND_SOC_DAPM_OUTPUT("RHPOUT"),
|
|
+ SND_SOC_DAPM_INPUT("MICIN"),
|
|
+ SND_SOC_DAPM_INPUT("RLINEIN"),
|
|
+ SND_SOC_DAPM_INPUT("LLINEIN"),
|
|
+};
|
|
+
|
|
+static const char *intercon[][3] = {
|
|
+ /* output mixer */
|
|
+ {"Output Mixer", "Line Bypass Switch", "Line Input"},
|
|
+ {"Output Mixer", "HiFi Playback Switch", "DAC"},
|
|
+ {"Output Mixer", "Mic Sidetone Switch", "Mic Bias"},
|
|
+
|
|
+ /* outputs */
|
|
+ {"RHPOUT", NULL, "Output Mixer"},
|
|
+ {"ROUT", NULL, "Output Mixer"},
|
|
+ {"LHPOUT", NULL, "Output Mixer"},
|
|
+ {"LOUT", NULL, "Output Mixer"},
|
|
+
|
|
+ /* input mux */
|
|
+ {"Input Mux", "Line In", "Line Input"},
|
|
+ {"Input Mux", "Mic", "Mic Bias"},
|
|
+ {"ADC", NULL, "Input Mux"},
|
|
+
|
|
+ /* inputs */
|
|
+ {"Line Input", NULL, "LLINEIN"},
|
|
+ {"Line Input", NULL, "RLINEIN"},
|
|
+ {"Mic Bias", NULL, "MICIN"},
|
|
+
|
|
+ /* terminator */
|
|
+ {NULL, NULL, NULL},
|
|
+};
|
|
+
|
|
+static int jzcodec_add_widgets(struct snd_soc_codec *codec)
|
|
+{
|
|
+ int i,cnt;
|
|
+
|
|
+ cnt = ARRAY_SIZE(jzcodec_dapm_widgets);
|
|
+ for(i = 0; i < ARRAY_SIZE(jzcodec_dapm_widgets); i++) {
|
|
+ snd_soc_dapm_new_control(codec, &jzcodec_dapm_widgets[i]);
|
|
+ }
|
|
+
|
|
+ /* set up audio path interconnects */
|
|
+ for(i = 0; intercon[i][0] != NULL; i++) {
|
|
+ snd_soc_dapm_connect_input(codec, intercon[i][0],
|
|
+ intercon[i][1], intercon[i][2]);
|
|
+ }
|
|
+
|
|
+ snd_soc_dapm_new_widgets(codec);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int jzcodec_hw_params(struct snd_pcm_substream *substream,
|
|
+ struct snd_pcm_hw_params *params)
|
|
+{
|
|
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
+ struct snd_soc_device *socdev = rtd->socdev;
|
|
+ struct snd_soc_codec *codec = socdev->codec;
|
|
+ u16 reg_val = jzcodec_read_reg_cache(codec, ICODEC_2_LOW);
|
|
+
|
|
+ /* bit size. codec side */
|
|
+ switch (params_format(params)) {
|
|
+ case SNDRV_PCM_FORMAT_S16_LE:
|
|
+ break;
|
|
+ }
|
|
+ /* sample rate */
|
|
+ reg_val = reg_val & ~(0xf << 8);
|
|
+
|
|
+ switch (params_rate(params)) {
|
|
+ case 8000:
|
|
+ reg_val |= (0x0 << 8);
|
|
+ break;
|
|
+ case 11025:
|
|
+ reg_val |= (0x1 << 8);
|
|
+ break;
|
|
+ case 12000:
|
|
+ reg_val |= (0x2 << 8);
|
|
+ break;
|
|
+ case 16000:
|
|
+ reg_val |= (0x3 << 8);
|
|
+ break;
|
|
+ case 22050:
|
|
+ reg_val |= (0x4 << 8);
|
|
+ break;
|
|
+ case 24000:
|
|
+ reg_val |= (0x5 << 8);
|
|
+ break;
|
|
+ case 32000:
|
|
+ reg_val |= (0x6 << 8);
|
|
+ break;
|
|
+ case 44100:
|
|
+ reg_val |= (0x7 << 8);
|
|
+ break;
|
|
+ case 48000:
|
|
+ reg_val |= (0x8 << 8);
|
|
+ break;
|
|
+ default:
|
|
+ printk(" invalid rate :0x%08x\n",params_rate(params));
|
|
+ }
|
|
+
|
|
+ jzcodec_write(codec, ICODEC_2_LOW, reg_val);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int jzcodec_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
|
+{
|
|
+ int ret = 0;
|
|
+ u16 val;
|
|
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
+ struct snd_soc_device *socdev = rtd->socdev;
|
|
+ struct snd_soc_codec *codec = socdev->codec;
|
|
+
|
|
+ switch (cmd) {
|
|
+ case SNDRV_PCM_TRIGGER_START:
|
|
+ //case SNDRV_PCM_TRIGGER_RESUME:
|
|
+ //case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
|
+
|
|
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
|
+ val = 0x7302;
|
|
+ jzcodec_write(codec, ICODEC_1_LOW, val);
|
|
+ val = 0x0003;
|
|
+ jzcodec_write(codec, ICODEC_1_HIGH, val);
|
|
+ mdelay(2);
|
|
+ val = 0x6000;
|
|
+ jzcodec_write(codec, ICODEC_1_LOW, val);
|
|
+ val = 0x0300;
|
|
+ jzcodec_write(codec, ICODEC_1_HIGH, val);
|
|
+ mdelay(2);
|
|
+ val = 0x2000;
|
|
+ jzcodec_write(codec, ICODEC_1_LOW, val);
|
|
+ val = 0x0300;
|
|
+ jzcodec_write(codec, ICODEC_1_HIGH, val);
|
|
+ } else {
|
|
+ val = 0x4300;
|
|
+ jzcodec_write(codec, ICODEC_1_LOW, val);
|
|
+ val = 0x1402;
|
|
+ jzcodec_write(codec, ICODEC_1_HIGH, val);
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case SNDRV_PCM_TRIGGER_STOP:
|
|
+ //case SNDRV_PCM_TRIGGER_SUSPEND:
|
|
+ //case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
|
+
|
|
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
|
|
+ val = 0x3300;
|
|
+ jzcodec_write(codec, ICODEC_1_LOW, val);
|
|
+ val = 0x0003;
|
|
+ jzcodec_write(codec, ICODEC_1_HIGH, val);
|
|
+ } else {
|
|
+ val = 0x3300;
|
|
+ jzcodec_write(codec, ICODEC_1_LOW, val);
|
|
+ val = 0x0003;
|
|
+ jzcodec_write(codec, ICODEC_1_HIGH, val);
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ ret = -EINVAL;
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int jzcodec_pcm_prepare(struct snd_pcm_substream *substream)
|
|
+{
|
|
+ /*struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
+ struct snd_soc_device *socdev = rtd->socdev;
|
|
+ struct snd_soc_codec *codec = socdev->codec; */
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void jzcodec_shutdown(struct snd_pcm_substream *substream)
|
|
+{
|
|
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
+ struct snd_soc_device *socdev = rtd->socdev;
|
|
+ struct snd_soc_codec *codec = socdev->codec;
|
|
+
|
|
+ /* deactivate */
|
|
+ if (!codec->active) {
|
|
+ udelay(50);
|
|
+ }
|
|
+}
|
|
+
|
|
+static int jzcodec_mute(struct snd_soc_codec_dai *dai, int mute)
|
|
+{
|
|
+ struct snd_soc_codec *codec = dai->codec;
|
|
+ u16 reg_val = jzcodec_read_reg_cache(codec, ICODEC_1_LOW);
|
|
+
|
|
+ if (mute != 0)
|
|
+ mute = 1;
|
|
+ if (mute)
|
|
+ reg_val = reg_val | (0x1 << 14);
|
|
+ else
|
|
+ reg_val = reg_val & ~(0x1 << 14);
|
|
+
|
|
+ jzcodec_write(codec, ICODEC_1_LOW, reg_val);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int jzcodec_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
|
|
+ int clk_id, unsigned int freq, int dir)
|
|
+{
|
|
+ struct snd_soc_codec *codec = codec_dai->codec;
|
|
+ struct jzcodec_priv *jzcodec = codec->private_data;
|
|
+
|
|
+ jzcodec->sysclk = freq;
|
|
+ return 0;
|
|
+}
|
|
+/*
|
|
+ * Set's ADC and Voice DAC format. called by pavo_hw_params() in pavo.c
|
|
+ */
|
|
+static int jzcodec_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
|
|
+ unsigned int fmt)
|
|
+{
|
|
+ /* struct snd_soc_codec *codec = codec_dai->codec; */
|
|
+
|
|
+ /* set master/slave audio interface. codec side */
|
|
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
|
+ case SND_SOC_DAIFMT_CBM_CFM:
|
|
+ /* set master mode for codec */
|
|
+ break;
|
|
+ case SND_SOC_DAIFMT_CBS_CFS:
|
|
+ /* set slave mode for codec */
|
|
+ break;
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ /* interface format . set some parameter for codec side */
|
|
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
|
+ case SND_SOC_DAIFMT_I2S:
|
|
+ /* set I2S mode for codec */
|
|
+ break;
|
|
+ case SND_SOC_DAIFMT_RIGHT_J:
|
|
+ /* set right J mode */
|
|
+ break;
|
|
+ case SND_SOC_DAIFMT_LEFT_J:
|
|
+ /* set left J mode */
|
|
+ break;
|
|
+ case SND_SOC_DAIFMT_DSP_A:
|
|
+ /* set dsp A mode */
|
|
+ break;
|
|
+ case SND_SOC_DAIFMT_DSP_B:
|
|
+ /* set dsp B mode */
|
|
+ break;
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ /* clock inversion. codec side */
|
|
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
|
+ case SND_SOC_DAIFMT_NB_NF:
|
|
+ break;
|
|
+ case SND_SOC_DAIFMT_IB_IF:
|
|
+ break;
|
|
+ case SND_SOC_DAIFMT_IB_NF:
|
|
+ break;
|
|
+ case SND_SOC_DAIFMT_NB_IF:
|
|
+ break;
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ /* jzcodec_write(codec, 0, val); */
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int jzcodec_dapm_event(struct snd_soc_codec *codec, int event)
|
|
+{
|
|
+/* u16 reg_val; */
|
|
+
|
|
+ switch (event) {
|
|
+ case SNDRV_CTL_POWER_D0: /* full On */
|
|
+ /* vref/mid, osc on, dac unmute */
|
|
+ /* u16 reg_val = jzcodec_read_reg_cache(codec, ICODEC_1_LOW); */
|
|
+ /* jzcodec_write(codec, 0, val); */
|
|
+ break;
|
|
+ case SNDRV_CTL_POWER_D1: /* partial On */
|
|
+ case SNDRV_CTL_POWER_D2: /* partial On */
|
|
+ break;
|
|
+ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
|
|
+ /* everything off except vref/vmid, */
|
|
+ /*reg_val = 0x0800;
|
|
+ jzcodec_write_reg_cache(codec, ICODEC_1_LOW, reg_val);
|
|
+ reg_val = 0x0017;
|
|
+ jzcodec_write_reg_cache(codec, ICODEC_1_HIGH, reg_val);
|
|
+ REG_ICDC_CDCCR1 = jzcodec_reg[0];
|
|
+ mdelay(2);
|
|
+ reg_val = 0x2102;
|
|
+ jzcodec_write_reg_cache(codec, ICODEC_1_LOW, reg_val);
|
|
+ reg_val = 0x001f;
|
|
+ jzcodec_write_reg_cache(codec, ICODEC_1_HIGH, reg_val);
|
|
+ REG_ICDC_CDCCR1 = jzcodec_reg[0];
|
|
+ mdelay(2);
|
|
+ reg_val = 0x3302;
|
|
+ jzcodec_write_reg_cache(codec, ICODEC_1_LOW, reg_val);
|
|
+ reg_val = 0x0003;
|
|
+ jzcodec_write_reg_cache(codec, ICODEC_1_HIGH, reg_val);
|
|
+ REG_ICDC_CDCCR1 = jzcodec_reg[0];*/
|
|
+ break;
|
|
+ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
|
|
+ /* everything off, dac mute, inactive */
|
|
+ /*reg_val = 0x2302;
|
|
+ jzcodec_write(codec, ICODEC_1_LOW, reg_val);
|
|
+ reg_val = 0x001b;
|
|
+ jzcodec_write(codec, ICODEC_1_HIGH, reg_val);
|
|
+ mdelay(1);
|
|
+ reg_val = 0x2102;
|
|
+ jzcodec_write(codec, ICODEC_1_LOW, reg_val);
|
|
+ reg_val = 0x001b;
|
|
+ jzcodec_write(codec, ICODEC_1_HIGH, reg_val);*/
|
|
+ break;
|
|
+ }
|
|
+ codec->dapm_state = event;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#define JZCODEC_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
|
|
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
|
|
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
|
|
+ SNDRV_PCM_RATE_48000)
|
|
+
|
|
+#define JZCODEC_FORMATS (SNDRV_PCM_FORMAT_S8 | SNDRV_PCM_FMTBIT_S16_LE)
|
|
+
|
|
+struct snd_soc_codec_dai jzcodec_dai = {
|
|
+ .name = "JZCODEC",
|
|
+ .playback = {
|
|
+ .stream_name = "Playback",
|
|
+ .channels_min = 1,
|
|
+ .channels_max = 2,
|
|
+ .rates = JZCODEC_RATES,
|
|
+ .formats = JZCODEC_FORMATS,},
|
|
+ .capture = {
|
|
+ .stream_name = "Capture",
|
|
+ .channels_min = 1,
|
|
+ .channels_max = 2,
|
|
+ .rates = JZCODEC_RATES,
|
|
+ .formats = JZCODEC_FORMATS,},
|
|
+ .ops = {
|
|
+ .trigger = jzcodec_pcm_trigger,
|
|
+ .prepare = jzcodec_pcm_prepare,
|
|
+ .hw_params = jzcodec_hw_params,
|
|
+ .shutdown = jzcodec_shutdown,
|
|
+ },
|
|
+ .dai_ops = {
|
|
+ .digital_mute = jzcodec_mute,
|
|
+ .set_sysclk = jzcodec_set_dai_sysclk,
|
|
+ .set_fmt = jzcodec_set_dai_fmt,
|
|
+ }
|
|
+};
|
|
+EXPORT_SYMBOL_GPL(jzcodec_dai);
|
|
+
|
|
+#ifdef CONFIG_PM
|
|
+static u16 jzcodec_reg_pm[JZCODEC_CACHEREGNUM];
|
|
+static int jzcodec_suspend(struct platform_device *pdev, pm_message_t state)
|
|
+{
|
|
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
|
|
+ struct snd_soc_codec *codec = socdev->codec;
|
|
+
|
|
+ jzcodec_reg_pm[ICODEC_1_LOW] = jzcodec_read_reg_cache(codec, ICODEC_1_LOW);
|
|
+ jzcodec_reg_pm[ICODEC_1_HIGH] = jzcodec_read_reg_cache(codec, ICODEC_1_HIGH);
|
|
+ jzcodec_reg_pm[ICODEC_2_LOW] = jzcodec_read_reg_cache(codec, ICODEC_2_LOW);
|
|
+ jzcodec_reg_pm[ICODEC_2_HIGH] = jzcodec_read_reg_cache(codec, ICODEC_2_HIGH);
|
|
+
|
|
+ jzcodec_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int jzcodec_resume(struct platform_device *pdev)
|
|
+{
|
|
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
|
|
+ struct snd_soc_codec *codec = socdev->codec;
|
|
+ u16 reg_val;
|
|
+
|
|
+ jzcodec_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
|
|
+ reg_val = jzcodec_reg_pm[ICODEC_1_LOW];
|
|
+ jzcodec_write(codec, ICODEC_1_LOW, reg_val);
|
|
+ reg_val = jzcodec_reg_pm[ICODEC_1_HIGH];
|
|
+ jzcodec_write(codec, ICODEC_1_HIGH, reg_val);
|
|
+ reg_val = jzcodec_reg_pm[ICODEC_2_LOW];
|
|
+ jzcodec_write(codec, ICODEC_2_LOW, reg_val);
|
|
+ reg_val = jzcodec_reg_pm[ICODEC_2_HIGH];
|
|
+ jzcodec_write(codec, ICODEC_2_HIGH, reg_val);
|
|
+
|
|
+ jzcodec_dapm_event(codec, codec->suspend_dapm_state);
|
|
+ return 0;
|
|
+}
|
|
+#else
|
|
+#define jzcodec_suspend NULL
|
|
+#define jzcodec_resume NULL
|
|
+#endif
|
|
+/*
|
|
+ * initialise the JZCODEC driver
|
|
+ * register the mixer and dsp interfaces with the kernel
|
|
+ */
|
|
+static int jzcodec_init(struct snd_soc_device *socdev)
|
|
+{
|
|
+ struct snd_soc_codec *codec = socdev->codec;
|
|
+ int reg, ret = 0;
|
|
+ u16 reg_val;
|
|
+
|
|
+ for (reg = 0; reg < JZCODEC_CACHEREGNUM / 2; reg++) {
|
|
+ switch (reg) {
|
|
+ case 0:
|
|
+ jzcodec_reg[reg] = REG_ICDC_CDCCR1;
|
|
+ jzcodec_reg_LH[ICODEC_1_LOW] = jzcodec_reg[reg] & 0xffff;
|
|
+ jzcodec_reg_LH[ICODEC_1_HIGH] = (jzcodec_reg[reg] & 0xffff0000) >> 16;
|
|
+ break;
|
|
+ case 1:
|
|
+ jzcodec_reg[reg] = REG_ICDC_CDCCR2;
|
|
+ jzcodec_reg_LH[ICODEC_2_LOW] = jzcodec_reg[reg] & 0xffff;
|
|
+ jzcodec_reg_LH[ICODEC_2_HIGH] = (jzcodec_reg[reg] & 0xffff0000) >> 16;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ codec->name = "JZCODEC";
|
|
+ codec->owner = THIS_MODULE;
|
|
+ codec->read = jzcodec_read_reg_cache;
|
|
+ codec->write = jzcodec_write;
|
|
+ codec->dapm_event = jzcodec_dapm_event;
|
|
+ codec->dai = &jzcodec_dai;
|
|
+ codec->num_dai = 1;
|
|
+ codec->reg_cache_size = sizeof(jzcodec_reg_LH);
|
|
+ codec->reg_cache = kmemdup(jzcodec_reg_LH, sizeof(jzcodec_reg_LH), GFP_KERNEL);
|
|
+ if (codec->reg_cache == NULL)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ jzcodec_reset(codec);
|
|
+ /* register pcms */
|
|
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
|
|
+ if (ret < 0) {
|
|
+ printk(KERN_ERR "jzcodec: failed to create pcms\n");
|
|
+ goto pcm_err;
|
|
+ }
|
|
+
|
|
+ /* power on device */
|
|
+ jzcodec_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
|
|
+
|
|
+ /* clear suspend bit of jz4740 internal codec */
|
|
+ reg_val = jzcodec_read_reg_cache(codec, ICODEC_1_LOW);
|
|
+ reg_val = reg_val & ~(0x2);
|
|
+ jzcodec_write(codec, ICODEC_1_LOW, reg_val);
|
|
+ /* set vol bits */
|
|
+ reg_val = jzcodec_read_reg_cache(codec, ICODEC_2_LOW);
|
|
+ reg_val = reg_val | 0x3;
|
|
+ jzcodec_write(codec, ICODEC_2_LOW, reg_val);
|
|
+ /* set line in capture gain bits */
|
|
+ reg_val = jzcodec_read_reg_cache(codec, ICODEC_2_HIGH);
|
|
+ reg_val = reg_val | 0x1f;
|
|
+ jzcodec_write(codec, ICODEC_2_HIGH, reg_val);
|
|
+ /* set mic boost gain bits */
|
|
+ reg_val = jzcodec_read_reg_cache(codec, ICODEC_2_LOW);
|
|
+ reg_val = reg_val | (0x3 << 4);
|
|
+ jzcodec_write(codec, ICODEC_2_LOW, reg_val);
|
|
+ mdelay(5);
|
|
+ reg_val = 0x3300;
|
|
+ jzcodec_write(codec, ICODEC_1_LOW, reg_val);
|
|
+ reg_val = 0x0003;
|
|
+ jzcodec_write(codec, ICODEC_1_HIGH, reg_val);
|
|
+ jzcodec_add_controls(codec);
|
|
+ jzcodec_add_widgets(codec);
|
|
+
|
|
+ ret = snd_soc_register_card(socdev);
|
|
+ if (ret < 0) {
|
|
+ printk(KERN_ERR "jzcodec: failed to register card\n");
|
|
+ goto card_err;
|
|
+ }
|
|
+ return ret;
|
|
+
|
|
+card_err:
|
|
+ snd_soc_free_pcms(socdev);
|
|
+ snd_soc_dapm_free(socdev);
|
|
+pcm_err:
|
|
+ kfree(codec->reg_cache);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static struct snd_soc_device *jzcodec_socdev;
|
|
+
|
|
+static int write_codec_reg(u16 * add, char * name, int reg)
|
|
+{
|
|
+ switch (reg) {
|
|
+ case 0:
|
|
+ case 1:
|
|
+ REG_ICDC_CDCCR1 = jzcodec_reg[0];
|
|
+ break;
|
|
+ case 2:
|
|
+ case 3:
|
|
+ REG_ICDC_CDCCR2 = jzcodec_reg[1];
|
|
+ break;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int jzcodec_probe(struct platform_device *pdev)
|
|
+{
|
|
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
|
|
+ struct snd_soc_codec *codec;
|
|
+ struct jzcodec_priv *jzcodec;
|
|
+ int ret = 0;
|
|
+
|
|
+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
|
|
+ if (codec == NULL)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ jzcodec = kzalloc(sizeof(struct jzcodec_priv), GFP_KERNEL);
|
|
+ if (jzcodec == NULL) {
|
|
+ kfree(codec);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ codec->private_data = jzcodec;
|
|
+ socdev->codec = codec;
|
|
+ mutex_init(&codec->mutex);
|
|
+ INIT_LIST_HEAD(&codec->dapm_widgets);
|
|
+ INIT_LIST_HEAD(&codec->dapm_paths);
|
|
+
|
|
+ jzcodec_socdev = socdev;
|
|
+
|
|
+ /* Add other interfaces here ,no I2C connection */
|
|
+ codec->hw_write = (hw_write_t)write_codec_reg;
|
|
+ ret = jzcodec_init(jzcodec_socdev);
|
|
+
|
|
+ if (ret < 0) {
|
|
+ codec = jzcodec_socdev->codec;
|
|
+ err("failed to initialise jzcodec\n");
|
|
+ kfree(codec);
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/* power down chip */
|
|
+static int jzcodec_remove(struct platform_device *pdev)
|
|
+{
|
|
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
|
|
+ struct snd_soc_codec *codec = socdev->codec;
|
|
+
|
|
+ if (codec->control_data)
|
|
+ jzcodec_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
|
|
+
|
|
+ snd_soc_free_pcms(socdev);
|
|
+ snd_soc_dapm_free(socdev);
|
|
+ kfree(codec->private_data);
|
|
+ kfree(codec);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+struct snd_soc_codec_device soc_codec_dev_jzcodec = {
|
|
+ .probe = jzcodec_probe,
|
|
+ .remove = jzcodec_remove,
|
|
+ .suspend = jzcodec_suspend,
|
|
+ .resume = jzcodec_resume,
|
|
+};
|
|
+
|
|
+EXPORT_SYMBOL_GPL(soc_codec_dev_jzcodec);
|
|
+
|
|
+MODULE_DESCRIPTION("ASoC JZCODEC driver");
|
|
+MODULE_AUTHOR("Richard");
|
|
+MODULE_LICENSE("GPL");
|
|
--- /dev/null
|
|
+++ b/sound/soc/codecs/jzcodec.h
|
|
@@ -0,0 +1,22 @@
|
|
+/*
|
|
+ * 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.
|
|
+ */
|
|
+
|
|
+#ifndef _ICODEC_H
|
|
+#define _ICODEC_H
|
|
+
|
|
+/* jzcodec register space */
|
|
+#define ICODEC_1_LOW 0x00 /* bit0 -- bit15 in CDCCR1 */
|
|
+#define ICODEC_1_HIGH 0x01 /* bit16 -- bit31 in CDCCR1 */
|
|
+#define ICODEC_2_LOW 0x02 /* bit0 -- bit16 in CDCCR2 */
|
|
+#define ICODEC_2_HIGH 0x03 /* bit16 -- bit31 in CDCCR2 */
|
|
+
|
|
+#define JZCODEC_CACHEREGNUM 4
|
|
+#define JZCODEC_SYSCLK 0
|
|
+
|
|
+extern struct snd_soc_codec_dai jzcodec_dai;
|
|
+extern struct snd_soc_codec_device soc_codec_dev_jzcodec;
|
|
+
|
|
+#endif
|
|
--- /dev/null
|
|
+++ b/sound/soc/jz4740/Kconfig
|
|
@@ -0,0 +1,34 @@
|
|
+config SND_JZ4740_SOC
|
|
+ tristate "SoC Audio for Ingenic jz4740 chip"
|
|
+ depends on (JZ4740_PAVO || JZ4725_DIPPER || JZ4720_VIRGO) && SND_SOC
|
|
+ help
|
|
+ Say Y or M if you want to add support for codecs attached to
|
|
+ the Jz4740 AC97, I2S or SSP interface. You will also need
|
|
+ to select the audio interfaces to support below.
|
|
+
|
|
+config SND_JZ4740_SOC_PAVO
|
|
+ tristate "SoC Audio support for Ingenic Jz4740 PAVO board"
|
|
+ depends on SND_JZ4740_SOC
|
|
+ help
|
|
+ Say Y if you want to add support for SoC audio of internal codec on Ingenic Jz4740 PAVO board.
|
|
+
|
|
+config SND_JZ4740_AC97
|
|
+ tristate "select AC97 protocol and AC97 codec pcm core support"
|
|
+ depends on SND_JZ4740_SOC && SND_JZ4740_SOC_PAVO
|
|
+ select SND_AC97_CODEC
|
|
+ help
|
|
+ Say Y if you want to add AC97 protocol support for pcm core.
|
|
+
|
|
+config SND_JZ4740_SOC_AC97
|
|
+ tristate "SoC Audio (AC97 protocol) for Ingenic jz4740 chip"
|
|
+ depends on SND_JZ4740_SOC && SND_JZ4740_AC97 && SND_JZ4740_SOC_PAVO
|
|
+ select AC97_BUS
|
|
+ select SND_SOC_AC97_BUS
|
|
+ help
|
|
+ Say Y if you want to use AC97 protocol and ac97 codec on Ingenic Jz4740 PAVO board.
|
|
+
|
|
+config SND_JZ4740_SOC_I2S
|
|
+ depends on SND_JZ4740_SOC && SND_JZ4740_SOC_PAVO
|
|
+ tristate "SoC Audio (I2S protocol) for Ingenic jz4740 chip"
|
|
+ help
|
|
+ Say Y if you want to use I2S protocol and I2S codec on Ingenic Jz4740 PAVO board.
|
|
--- /dev/null
|
|
+++ b/sound/soc/jz4740/Makefile
|
|
@@ -0,0 +1,15 @@
|
|
+#
|
|
+# Jz4740 Platform Support
|
|
+#
|
|
+snd-soc-jz4740-objs := jz4740-pcm.o
|
|
+snd-soc-jz4740-ac97-objs := jz4740-ac97.o
|
|
+snd-soc-jz4740-i2s-objs := jz4740-i2s.o
|
|
+
|
|
+obj-$(CONFIG_SND_JZ4740_SOC) += snd-soc-jz4740.o
|
|
+obj-$(CONFIG_SND_JZ4740_SOC_AC97) += snd-soc-jz4740-ac97.o
|
|
+obj-$(CONFIG_SND_JZ4740_SOC_I2S) += snd-soc-jz4740-i2s.o
|
|
+
|
|
+# Jz4740 Machine Support
|
|
+snd-soc-pavo-objs := pavo.o
|
|
+
|
|
+obj-$(CONFIG_SND_JZ4740_SOC_PAVO) += snd-soc-pavo.o
|
|
--- /dev/null
|
|
+++ b/sound/soc/jz4740/jz4740-ac97.c
|
|
@@ -0,0 +1,261 @@
|
|
+/*
|
|
+ * linux/sound/jz4740-ac97.c -- AC97 support for the Ingenic jz4740 chip.
|
|
+ *
|
|
+ * Author: Richard
|
|
+ * Created: Dec 02, 2007
|
|
+ * Copyright: Ingenic Semiconductor Inc.
|
|
+ *
|
|
+ * 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 <linux/init.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/interrupt.h>
|
|
+#include <linux/wait.h>
|
|
+#include <linux/delay.h>
|
|
+
|
|
+#include <sound/driver.h>
|
|
+#include <sound/core.h>
|
|
+#include <sound/pcm.h>
|
|
+#include <sound/ac97_codec.h>
|
|
+#include <sound/initval.h>
|
|
+#include <sound/soc.h>
|
|
+
|
|
+#include <asm/irq.h>
|
|
+#include <linux/mutex.h>
|
|
+#include <asm/hardware.h>
|
|
+#include <asm/arch/audio.h>
|
|
+
|
|
+#include "jz4740-pcm.h"
|
|
+#include "jz4740-ac97.h"
|
|
+
|
|
+static DEFINE_MUTEX(car_mutex);
|
|
+static DECLARE_WAIT_QUEUE_HEAD(gsr_wq);
|
|
+static volatile long gsr_bits;
|
|
+
|
|
+static unsigned short jz4740_ac97_read(struct snd_ac97 *ac97,
|
|
+ unsigned short reg)
|
|
+{
|
|
+ unsigned short val = -1;
|
|
+ volatile u32 *reg_addr;
|
|
+
|
|
+ mutex_lock(&car_mutex);
|
|
+
|
|
+out: mutex_unlock(&car_mutex);
|
|
+ return val;
|
|
+}
|
|
+
|
|
+static void jz4740_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
|
|
+ unsigned short val)
|
|
+{
|
|
+ volatile u32 *reg_addr;
|
|
+
|
|
+ mutex_lock(&car_mutex);
|
|
+
|
|
+ mutex_unlock(&car_mutex);
|
|
+}
|
|
+
|
|
+static void jz4740_ac97_warm_reset(struct snd_ac97 *ac97)
|
|
+{
|
|
+ gsr_bits = 0;
|
|
+}
|
|
+
|
|
+static void jz4740_ac97_cold_reset(struct snd_ac97 *ac97)
|
|
+{
|
|
+}
|
|
+
|
|
+static irqreturn_t jz4740_ac97_irq(int irq, void *dev_id)
|
|
+{
|
|
+ long status;
|
|
+ return IRQ_NONE;
|
|
+}
|
|
+
|
|
+struct snd_ac97_bus_ops soc_ac97_ops = {
|
|
+ .read = jz4740_ac97_read,
|
|
+ .write = jz4740_ac97_write,
|
|
+ .warm_reset = jz4740_ac97_warm_reset,
|
|
+ .reset = jz4740_ac97_cold_reset,
|
|
+};
|
|
+
|
|
+static struct jz4740_pcm_dma_params jz4740_ac97_pcm_stereo_out = {
|
|
+ .name = "AC97 PCM Stereo out",
|
|
+ .dev_addr = __PREG(PCDR),
|
|
+ .drcmr = &DRCMRTXPCDR,
|
|
+ .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
|
|
+ DCMD_BURST32 | DCMD_WIDTH4,
|
|
+};
|
|
+
|
|
+static struct jz4740_pcm_dma_params jz4740_ac97_pcm_stereo_in = {
|
|
+ .name = "AC97 PCM Stereo in",
|
|
+ .dev_addr = __PREG(PCDR),
|
|
+ .drcmr = &DRCMRRXPCDR,
|
|
+ .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
|
|
+ DCMD_BURST32 | DCMD_WIDTH4,
|
|
+};
|
|
+
|
|
+static struct jz4740_pcm_dma_params jz4740_ac97_pcm_aux_mono_out = {
|
|
+ .name = "AC97 Aux PCM (Slot 5) Mono out",
|
|
+ .dev_addr = __PREG(MODR),
|
|
+ .drcmr = &DRCMRTXMODR,
|
|
+ .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
|
|
+ DCMD_BURST16 | DCMD_WIDTH2,
|
|
+};
|
|
+
|
|
+static struct jz4740_pcm_dma_params jz4740_ac97_pcm_aux_mono_in = {
|
|
+ .name = "AC97 Aux PCM (Slot 5) Mono in",
|
|
+ .dev_addr = __PREG(MODR),
|
|
+ .drcmr = &DRCMRRXMODR,
|
|
+ .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
|
|
+ DCMD_BURST16 | DCMD_WIDTH2,
|
|
+};
|
|
+
|
|
+static struct jz4740_pcm_dma_params jz4740_ac97_pcm_mic_mono_in = {
|
|
+ .name = "AC97 Mic PCM (Slot 6) Mono in",
|
|
+ .dev_addr = __PREG(MCDR),
|
|
+ .drcmr = &DRCMRRXMCDR,
|
|
+ .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
|
|
+ DCMD_BURST16 | DCMD_WIDTH2,
|
|
+};
|
|
+
|
|
+#ifdef CONFIG_PM
|
|
+static int jz4740_ac97_suspend(struct platform_device *pdev,
|
|
+ struct snd_soc_cpu_dai *dai)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int jz4740_ac97_resume(struct platform_device *pdev,
|
|
+ struct snd_soc_cpu_dai *dai)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#else
|
|
+#define jz4740_ac97_suspend NULL
|
|
+#define jz4740_ac97_resume NULL
|
|
+#endif
|
|
+
|
|
+static int jz4740_ac97_probe(struct platform_device *pdev)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void jz4740_ac97_remove(struct platform_device *pdev)
|
|
+{
|
|
+}
|
|
+
|
|
+static int jz4740_ac97_hw_params(struct snd_pcm_substream *substream,
|
|
+ struct snd_pcm_hw_params *params)
|
|
+{
|
|
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
+ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
|
|
+
|
|
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
|
+ cpu_dai->dma_data = &jz4740_ac97_pcm_stereo_out;
|
|
+ else
|
|
+ cpu_dai->dma_data = &jz4740_ac97_pcm_stereo_in;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int jz4740_ac97_hw_aux_params(struct snd_pcm_substream *substream,
|
|
+ struct snd_pcm_hw_params *params)
|
|
+{
|
|
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
+ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
|
|
+
|
|
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
|
+ cpu_dai->dma_data = &jz4740_ac97_pcm_aux_mono_out;
|
|
+ else
|
|
+ cpu_dai->dma_data = &jz4740_ac97_pcm_aux_mono_in;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int jz4740_ac97_hw_mic_params(struct snd_pcm_substream *substream,
|
|
+ struct snd_pcm_hw_params *params)
|
|
+{
|
|
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
+ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
|
|
+
|
|
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
|
+ return -ENODEV;
|
|
+ else
|
|
+ cpu_dai->dma_data = &jz4740_ac97_pcm_mic_mono_in;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#define JZ4740_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
|
|
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
|
|
+ SNDRV_PCM_RATE_48000)
|
|
+
|
|
+struct snd_soc_cpu_dai jz4740_ac97_dai[] = {
|
|
+{
|
|
+ .name = "jz4740-ac97",
|
|
+ .id = 0,
|
|
+ .type = SND_SOC_DAI_AC97,
|
|
+ .probe = jz4740_ac97_probe,
|
|
+ .remove = jz4740_ac97_remove,
|
|
+ .suspend = jz4740_ac97_suspend,
|
|
+ .resume = jz4740_ac97_resume,
|
|
+ .playback = {
|
|
+ .stream_name = "AC97 Playback",
|
|
+ .channels_min = 2,
|
|
+ .channels_max = 2,
|
|
+ .rates = JZ4740_AC97_RATES,
|
|
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
|
|
+ .capture = {
|
|
+ .stream_name = "AC97 Capture",
|
|
+ .channels_min = 2,
|
|
+ .channels_max = 2,
|
|
+ .rates = JZ4740_AC97_RATES,
|
|
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
|
|
+ .ops = {
|
|
+ .hw_params = jz4740_ac97_hw_params,},
|
|
+},
|
|
+{
|
|
+ .name = "jz4740-ac97-aux",
|
|
+ .id = 1,
|
|
+ .type = SND_SOC_DAI_AC97,
|
|
+ .playback = {
|
|
+ .stream_name = "AC97 Aux Playback",
|
|
+ .channels_min = 1,
|
|
+ .channels_max = 1,
|
|
+ .rates = JZ4740_AC97_RATES,
|
|
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
|
|
+ .capture = {
|
|
+ .stream_name = "AC97 Aux Capture",
|
|
+ .channels_min = 1,
|
|
+ .channels_max = 1,
|
|
+ .rates = JZ4740_AC97_RATES,
|
|
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
|
|
+ .ops = {
|
|
+ .hw_params = jz4740_ac97_hw_aux_params,},
|
|
+},
|
|
+{
|
|
+ .name = "jz4740-ac97-mic",
|
|
+ .id = 2,
|
|
+ .type = SND_SOC_DAI_AC97,
|
|
+ .capture = {
|
|
+ .stream_name = "AC97 Mic Capture",
|
|
+ .channels_min = 1,
|
|
+ .channels_max = 1,
|
|
+ .rates = JZ4740_AC97_RATES,
|
|
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
|
|
+ .ops = {
|
|
+ .hw_params = jz4740_ac97_hw_mic_params,},
|
|
+},
|
|
+};
|
|
+
|
|
+EXPORT_SYMBOL_GPL(jz4740_ac97_dai);
|
|
+EXPORT_SYMBOL_GPL(soc_ac97_ops);
|
|
+
|
|
+MODULE_AUTHOR("Richard");
|
|
+MODULE_DESCRIPTION("AC97 driver for the Ingenic jz4740 chip");
|
|
+MODULE_LICENSE("GPL");
|
|
--- /dev/null
|
|
+++ b/sound/soc/jz4740/jz4740-ac97.h
|
|
@@ -0,0 +1,21 @@
|
|
+/*
|
|
+ * linux/sound/soc/jz4740/jz4740-ac97.h
|
|
+ *
|
|
+ * 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.
|
|
+ */
|
|
+
|
|
+#ifndef _JZ4740_AC97_H
|
|
+#define _JZ4740_AC97_H
|
|
+
|
|
+#define JZ4740_DAI_AC97_HIFI 0
|
|
+#define JZ4740_DAI_AC97_AUX 1
|
|
+#define JZ4740_DAI_AC97_MIC 2
|
|
+
|
|
+extern struct snd_soc_cpu_dai jz4740_ac97_dai[3];
|
|
+
|
|
+/* platform data */
|
|
+extern struct snd_ac97_bus_ops jz4740_ac97_ops;
|
|
+
|
|
+#endif
|
|
--- /dev/null
|
|
+++ b/sound/soc/jz4740/jz4740-i2s.c
|
|
@@ -0,0 +1,296 @@
|
|
+/*
|
|
+ * This program is free software; you can redistribute it and/or modify it
|
|
+ * under the terms of the GNU General Public License as published by the
|
|
+ * Free Software Foundation; either version 2 of the License, or (at your
|
|
+ * option) any later version.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include <linux/init.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/device.h>
|
|
+#include <linux/delay.h>
|
|
+#include <sound/driver.h>
|
|
+#include <sound/core.h>
|
|
+#include <sound/pcm.h>
|
|
+#include <sound/pcm_params.h>
|
|
+#include <sound/initval.h>
|
|
+#include <sound/soc.h>
|
|
+
|
|
+#include "jz4740-pcm.h"
|
|
+#include "jz4740-i2s.h"
|
|
+
|
|
+static struct jz4740_dma_client jz4740_dma_client_out = {
|
|
+ .name = "I2S PCM Stereo out"
|
|
+};
|
|
+
|
|
+static struct jz4740_dma_client jz4740_dma_client_in = {
|
|
+ .name = "I2S PCM Stereo in"
|
|
+};
|
|
+
|
|
+static struct jz4740_pcm_dma_params jz4740_i2s_pcm_stereo_out = {
|
|
+ .client = &jz4740_dma_client_out,
|
|
+ .channel = DMA_ID_AIC_TX,
|
|
+ .dma_addr = AIC_DR,
|
|
+ .dma_size = 2,
|
|
+};
|
|
+
|
|
+static struct jz4740_pcm_dma_params jz4740_i2s_pcm_stereo_in = {
|
|
+ .client = &jz4740_dma_client_in,
|
|
+ .channel = DMA_ID_AIC_RX,
|
|
+ .dma_addr = AIC_DR,
|
|
+ .dma_size = 2,
|
|
+};
|
|
+
|
|
+static int jz4740_i2s_startup(struct snd_pcm_substream *substream)
|
|
+{
|
|
+ /*struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
+ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;*/
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int jz4740_i2s_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai,
|
|
+ unsigned int fmt)
|
|
+{
|
|
+ /* interface format */
|
|
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
|
+ case SND_SOC_DAIFMT_I2S:
|
|
+ /* 1 : ac97 , 0 : i2s */
|
|
+ break;
|
|
+ case SND_SOC_DAIFMT_LEFT_J:
|
|
+ break;
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
|
+ case SND_SOC_DAIFMT_CBS_CFS:
|
|
+ /* 0 : slave */
|
|
+ break;
|
|
+ case SND_SOC_DAIFMT_CBM_CFS:
|
|
+ /* 1 : master */
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+* Set Jz4740 Clock source
|
|
+*/
|
|
+static int jz4740_i2s_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai,
|
|
+ int clk_id, unsigned int freq, int dir)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void jz4740_snd_tx_ctrl(int on)
|
|
+{
|
|
+ if (on) {
|
|
+ /* enable replay */
|
|
+ __i2s_enable_transmit_dma();
|
|
+ __i2s_enable_replay();
|
|
+ __i2s_enable();
|
|
+
|
|
+ } else {
|
|
+ /* disable replay & capture */
|
|
+ __i2s_disable_replay();
|
|
+ __i2s_disable_record();
|
|
+ __i2s_disable_receive_dma();
|
|
+ __i2s_disable_transmit_dma();
|
|
+ __i2s_disable();
|
|
+ }
|
|
+}
|
|
+
|
|
+static void jz4740_snd_rx_ctrl(int on)
|
|
+{
|
|
+ if (on) {
|
|
+ /* enable capture */
|
|
+ __i2s_enable_receive_dma();
|
|
+ __i2s_enable_record();
|
|
+ __i2s_enable();
|
|
+
|
|
+ } else {
|
|
+ /* disable replay & capture */
|
|
+ __i2s_disable_replay();
|
|
+ __i2s_disable_record();
|
|
+ __i2s_disable_receive_dma();
|
|
+ __i2s_disable_transmit_dma();
|
|
+ __i2s_disable();
|
|
+ }
|
|
+}
|
|
+
|
|
+static int jz4740_i2s_hw_params(struct snd_pcm_substream *substream,
|
|
+ struct snd_pcm_hw_params *params)
|
|
+{
|
|
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
+ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
|
|
+ int channels = params_channels(params);
|
|
+
|
|
+ jz4740_snd_rx_ctrl(0);
|
|
+ jz4740_snd_rx_ctrl(0);
|
|
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
|
+ cpu_dai->dma_data = &jz4740_i2s_pcm_stereo_out;
|
|
+ if (channels == 1)
|
|
+ __aic_enable_mono2stereo();
|
|
+ else
|
|
+ __aic_disable_mono2stereo();
|
|
+ } else
|
|
+ cpu_dai->dma_data = &jz4740_i2s_pcm_stereo_in;
|
|
+
|
|
+ switch (params_format(params)) {
|
|
+ case SNDRV_PCM_FORMAT_S8:
|
|
+ __i2s_set_transmit_trigger(4);
|
|
+ __i2s_set_receive_trigger(3);
|
|
+ __i2s_set_oss_sample_size(8);
|
|
+ __i2s_set_iss_sample_size(8);
|
|
+ break;
|
|
+ case SNDRV_PCM_FORMAT_S16_LE:
|
|
+ /* playback sample:16 bits, burst:16 bytes */
|
|
+ __i2s_set_transmit_trigger(4);
|
|
+ /* capture sample:16 bits, burst:16 bytes */
|
|
+ __i2s_set_receive_trigger(3);
|
|
+ __i2s_set_oss_sample_size(16);
|
|
+ __i2s_set_iss_sample_size(16);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int jz4740_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
|
|
+{
|
|
+ int ret = 0;
|
|
+ switch (cmd) {
|
|
+ case SNDRV_PCM_TRIGGER_START:
|
|
+ case SNDRV_PCM_TRIGGER_RESUME:
|
|
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
|
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
|
+ jz4740_snd_rx_ctrl(1);
|
|
+ else
|
|
+ jz4740_snd_tx_ctrl(1);
|
|
+ break;
|
|
+ case SNDRV_PCM_TRIGGER_STOP:
|
|
+ case SNDRV_PCM_TRIGGER_SUSPEND:
|
|
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
|
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
|
+ jz4740_snd_rx_ctrl(0);
|
|
+ else
|
|
+ jz4740_snd_tx_ctrl(0);
|
|
+ break;
|
|
+ default:
|
|
+ ret = -EINVAL;
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void jz4740_i2s_shutdown(struct snd_pcm_substream *substream)
|
|
+{
|
|
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
|
+ } else {
|
|
+ }
|
|
+
|
|
+ return;
|
|
+}
|
|
+
|
|
+static int jz4740_i2s_probe(struct platform_device *pdev)
|
|
+{
|
|
+ __i2s_internal_codec();
|
|
+ __i2s_as_slave();
|
|
+ __i2s_select_i2s();
|
|
+ __aic_select_i2s();
|
|
+ mdelay(2);
|
|
+
|
|
+ __i2s_disable();
|
|
+ __i2s_reset();
|
|
+ mdelay(2);
|
|
+
|
|
+ __i2s_disable();
|
|
+ __i2s_internal_codec();
|
|
+ __i2s_as_slave();
|
|
+ __i2s_select_i2s();
|
|
+ __aic_select_i2s();
|
|
+ __i2s_set_oss_sample_size(16);
|
|
+ __i2s_set_iss_sample_size(16);
|
|
+ __aic_play_lastsample();
|
|
+
|
|
+ __i2s_disable_record();
|
|
+ __i2s_disable_replay();
|
|
+ __i2s_disable_loopback();
|
|
+ __i2s_set_transmit_trigger(7);
|
|
+ __i2s_set_receive_trigger(7);
|
|
+
|
|
+ jz4740_snd_tx_ctrl(0);
|
|
+ jz4740_snd_rx_ctrl(0);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_PM
|
|
+static int jz4740_i2s_suspend(struct platform_device *dev,
|
|
+ struct snd_soc_cpu_dai *dai)
|
|
+{
|
|
+ if (!dai->active)
|
|
+ return 0;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int jz4740_i2s_resume(struct platform_device *pdev,
|
|
+ struct snd_soc_cpu_dai *dai)
|
|
+{
|
|
+ if (!dai->active)
|
|
+ return 0;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#else
|
|
+#define jz4740_i2s_suspend NULL
|
|
+#define jz4740_i2s_resume NULL
|
|
+#endif
|
|
+
|
|
+#define JZ4740_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
|
|
+ SNDRV_PCM_RATE_12000 | SNDRV_PCM_RATE_16000 |\
|
|
+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_24000 |\
|
|
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
|
|
+ SNDRV_PCM_RATE_48000)
|
|
+
|
|
+struct snd_soc_cpu_dai jz4740_i2s_dai = {
|
|
+ .name = "jz4740-i2s",
|
|
+ .id = 0,
|
|
+ .type = SND_SOC_DAI_I2S,
|
|
+ .probe = jz4740_i2s_probe,
|
|
+ .suspend = jz4740_i2s_suspend,
|
|
+ .resume = jz4740_i2s_resume,
|
|
+ .playback = {
|
|
+ .channels_min = 1,
|
|
+ .channels_max = 2,
|
|
+ .rates = JZ4740_I2S_RATES,
|
|
+ .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
|
|
+ .capture = {
|
|
+ .channels_min = 1,
|
|
+ .channels_max = 2,
|
|
+ .rates = JZ4740_I2S_RATES,
|
|
+ .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
|
|
+ .ops = {
|
|
+ .startup = jz4740_i2s_startup,
|
|
+ .shutdown = jz4740_i2s_shutdown,
|
|
+ .trigger = jz4740_i2s_trigger,
|
|
+ .hw_params = jz4740_i2s_hw_params,},
|
|
+ .dai_ops = {
|
|
+ .set_fmt = jz4740_i2s_set_dai_fmt,
|
|
+ .set_sysclk = jz4740_i2s_set_dai_sysclk,
|
|
+ },
|
|
+};
|
|
+
|
|
+EXPORT_SYMBOL_GPL(jz4740_i2s_dai);
|
|
+
|
|
+/* Module information */
|
|
+MODULE_AUTHOR("Richard, cjfeng@ingenic.cn, www.ingenic.cn");
|
|
+MODULE_DESCRIPTION("jz4740 I2S SoC Interface");
|
|
+MODULE_LICENSE("GPL");
|
|
--- /dev/null
|
|
+++ b/sound/soc/jz4740/jz4740-i2s.h
|
|
@@ -0,0 +1,18 @@
|
|
+/*
|
|
+ * 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.
|
|
+ */
|
|
+
|
|
+#ifndef _JZ4740_I2S_H
|
|
+#define _JZ4740_I2S_H
|
|
+
|
|
+/* jz4740 DAI ID's */
|
|
+#define JZ4740_DAI_I2S 0
|
|
+
|
|
+/* I2S clock */
|
|
+#define JZ4740_I2S_SYSCLK 0
|
|
+
|
|
+extern struct snd_soc_cpu_dai jz4740_i2s_dai;
|
|
+
|
|
+#endif
|
|
--- /dev/null
|
|
+++ b/sound/soc/jz4740/jz4740-pcm.c
|
|
@@ -0,0 +1,689 @@
|
|
+/*
|
|
+ *
|
|
+ * 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 <linux/module.h>
|
|
+#include <linux/interrupt.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/dma-mapping.h>
|
|
+
|
|
+#include <sound/driver.h>
|
|
+#include <sound/core.h>
|
|
+#include <sound/pcm.h>
|
|
+#include <sound/pcm_params.h>
|
|
+#include <sound/soc.h>
|
|
+
|
|
+#include <asm/io.h>
|
|
+#include "jz4740-pcm.h"
|
|
+
|
|
+static long sum_bytes = 0;
|
|
+static int first_transfer = 0;
|
|
+static int printk_flag = 0;
|
|
+static int tran_bit = 0;
|
|
+#ifdef CONFIG_SND_OSSEMUL
|
|
+static int hw_params_cnt = 0;
|
|
+#endif
|
|
+
|
|
+struct jz4740_dma_buf_aic {
|
|
+ struct jz4740_dma_buf_aic *next;
|
|
+ int size; /* buffer size in bytes */
|
|
+ dma_addr_t data; /* start of DMA data */
|
|
+ dma_addr_t ptr; /* where the DMA got to [1] */
|
|
+ void *id; /* client's id */
|
|
+};
|
|
+
|
|
+struct jz4740_runtime_data {
|
|
+ spinlock_t lock;
|
|
+ int state;
|
|
+ int aic_dma_flag; /* start dma transfer or not */
|
|
+ unsigned int dma_loaded;
|
|
+ unsigned int dma_limit;
|
|
+ unsigned int dma_period;
|
|
+ dma_addr_t dma_start;
|
|
+ dma_addr_t dma_pos;
|
|
+ dma_addr_t dma_end;
|
|
+ struct jz4740_pcm_dma_params *params;
|
|
+
|
|
+ dma_addr_t user_cur_addr; /* user current write buffer start address */
|
|
+ unsigned int user_cur_len; /* user current write buffer length */
|
|
+
|
|
+ /* buffer list and information */
|
|
+ struct jz4740_dma_buf_aic *curr; /* current dma buffer */
|
|
+ struct jz4740_dma_buf_aic *next; /* next buffer to load */
|
|
+ struct jz4740_dma_buf_aic *end; /* end of queue */
|
|
+
|
|
+};
|
|
+
|
|
+/* identify hardware playback capabilities */
|
|
+static const struct snd_pcm_hardware jz4740_pcm_hardware = {
|
|
+ .info = SNDRV_PCM_INFO_MMAP |
|
|
+ SNDRV_PCM_INFO_MMAP_VALID |
|
|
+ SNDRV_PCM_INFO_INTERLEAVED |
|
|
+ SNDRV_PCM_INFO_BLOCK_TRANSFER,
|
|
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
|
|
+ SNDRV_PCM_FMTBIT_U16_LE |
|
|
+ SNDRV_PCM_FMTBIT_U8 |
|
|
+ SNDRV_PCM_FMTBIT_S8,
|
|
+ .rates = SNDRV_PCM_RATE_8000_48000/*0x3fe*/,
|
|
+ .rate_min = 8000,
|
|
+ .rate_min = 48000,
|
|
+ .channels_min = 1,//2
|
|
+ .channels_max = 2,
|
|
+ .buffer_bytes_max = 128 * 1024,//16 * 1024
|
|
+ .period_bytes_min = PAGE_SIZE,
|
|
+ .period_bytes_max = PAGE_SIZE * 2,
|
|
+ .periods_min = 2,
|
|
+ .periods_max = 128,//16,
|
|
+ .fifo_size = 32,
|
|
+};
|
|
+
|
|
+/* jz4740__dma_buf_enqueue
|
|
+ *
|
|
+ * queue an given buffer for dma transfer.
|
|
+ *
|
|
+ * data the physical address of the buffer data
|
|
+ * size the size of the buffer in bytes
|
|
+ *
|
|
+*/
|
|
+static int jz4740_dma_buf_enqueue(struct jz4740_runtime_data *prtd, dma_addr_t data, int size)
|
|
+{
|
|
+ struct jz4740_dma_buf_aic *aic_buf;
|
|
+
|
|
+ aic_buf = kzalloc(sizeof(struct jz4740_dma_buf_aic), GFP_KERNEL);
|
|
+ if (aic_buf == NULL) {
|
|
+ printk("aic buffer allocate failed,no memory!\n");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ aic_buf->next = NULL;
|
|
+ aic_buf->data = aic_buf->ptr = data;
|
|
+ aic_buf->size = size;
|
|
+ if( prtd->curr == NULL) {
|
|
+ prtd->curr = aic_buf;
|
|
+ prtd->end = aic_buf;
|
|
+ prtd->next = NULL;
|
|
+ } else {
|
|
+ if (prtd->end == NULL)
|
|
+ printk("prtd->end is NULL\n");
|
|
+ prtd->end->next = aic_buf;
|
|
+ prtd->end = aic_buf;
|
|
+ }
|
|
+
|
|
+ /* if necessary, update the next buffer field */
|
|
+ if (prtd->next == NULL)
|
|
+ prtd->next = aic_buf;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+void audio_start_dma(struct jz4740_runtime_data *prtd, int mode)
|
|
+{
|
|
+ unsigned long flags;
|
|
+ struct jz4740_dma_buf_aic *aic_buf;
|
|
+ int channel;
|
|
+
|
|
+ switch (mode) {
|
|
+ case DMA_MODE_WRITE:
|
|
+ /* free cur aic_buf */
|
|
+ if (first_transfer == 1) {
|
|
+ first_transfer = 0;
|
|
+ } else {
|
|
+ aic_buf = prtd->curr;
|
|
+ if (aic_buf != NULL) {
|
|
+ prtd->curr = aic_buf->next;
|
|
+ prtd->next = aic_buf->next;
|
|
+ aic_buf->next = NULL;
|
|
+ kfree(aic_buf);
|
|
+ aic_buf = NULL;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ aic_buf = prtd->next;
|
|
+ channel = prtd->params->channel;
|
|
+ if (aic_buf) {
|
|
+ flags = claim_dma_lock();
|
|
+ disable_dma(channel);
|
|
+ jz_set_alsa_dma(channel, mode, tran_bit);
|
|
+ set_dma_addr(channel, aic_buf->data);
|
|
+ set_dma_count(channel, aic_buf->size);
|
|
+ enable_dma(channel);
|
|
+ release_dma_lock(flags);
|
|
+ prtd->aic_dma_flag |= AIC_START_DMA;
|
|
+ } else {
|
|
+ printk("next buffer is NULL for playback\n");
|
|
+ prtd->aic_dma_flag &= ~AIC_START_DMA;
|
|
+ return;
|
|
+ }
|
|
+ break;
|
|
+ case DMA_MODE_READ:
|
|
+ /* free cur aic_buf */
|
|
+ if (first_transfer == 1) {
|
|
+ first_transfer = 0;
|
|
+ } else {
|
|
+ aic_buf = prtd->curr;
|
|
+ if (aic_buf != NULL) {
|
|
+ prtd->curr = aic_buf->next;
|
|
+ prtd->next = aic_buf->next;
|
|
+ aic_buf->next = NULL;
|
|
+ kfree(aic_buf);
|
|
+ aic_buf = NULL;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ aic_buf = prtd->next;
|
|
+ channel = prtd->params->channel;
|
|
+
|
|
+ if (aic_buf) {
|
|
+ flags = claim_dma_lock();
|
|
+ disable_dma(channel);
|
|
+ jz_set_alsa_dma(channel, mode, tran_bit);
|
|
+ set_dma_addr(channel, aic_buf->data);
|
|
+ set_dma_count(channel, aic_buf->size);
|
|
+ enable_dma(channel);
|
|
+ release_dma_lock(flags);
|
|
+ prtd->aic_dma_flag |= AIC_START_DMA;
|
|
+ } else {
|
|
+ printk("next buffer is NULL for capture\n");
|
|
+ prtd->aic_dma_flag &= ~AIC_START_DMA;
|
|
+ return;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ /* dump_jz_dma_channel(channel); */
|
|
+}
|
|
+
|
|
+/*
|
|
+ * place a dma buffer onto the queue for the dma system to handle.
|
|
+*/
|
|
+static void jz4740_pcm_enqueue(struct snd_pcm_substream *substream)
|
|
+{
|
|
+ struct snd_pcm_runtime *runtime = substream->runtime;
|
|
+ struct jz4740_runtime_data *prtd = runtime->private_data;
|
|
+ /*struct snd_dma_buffer *buf = &substream->dma_buffer;*/
|
|
+ dma_addr_t pos = prtd->dma_pos;
|
|
+ int ret;
|
|
+
|
|
+ while (prtd->dma_loaded < prtd->dma_limit) {
|
|
+ unsigned long len = prtd->dma_period;
|
|
+
|
|
+ if ((pos + len) > prtd->dma_end) {
|
|
+ len = prtd->dma_end - pos;
|
|
+ }
|
|
+ ret = jz4740_dma_buf_enqueue(prtd, pos, len);
|
|
+ if (ret == 0) {
|
|
+ prtd->dma_loaded++;
|
|
+ pos += prtd->dma_period;
|
|
+ if (pos >= prtd->dma_end)
|
|
+ pos = prtd->dma_start;
|
|
+ } else
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ prtd->dma_pos = pos;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * call the function:jz4740_pcm_dma_irq() after DMA has transfered the current buffer
|
|
+ */
|
|
+static irqreturn_t jz4740_pcm_dma_irq(int dma_ch, void *dev_id)
|
|
+{
|
|
+ struct snd_pcm_substream *substream = dev_id;
|
|
+ struct snd_pcm_runtime *runtime = substream->runtime;
|
|
+ struct jz4740_runtime_data *prtd = runtime->private_data;
|
|
+ /*struct jz4740_dma_buf_aic *aic_buf = prtd->curr;*/
|
|
+ int channel = prtd->params->channel;
|
|
+ unsigned long flags;
|
|
+
|
|
+ disable_dma(channel);
|
|
+ prtd->aic_dma_flag &= ~AIC_START_DMA;
|
|
+ /* must clear TT bit in DCCSR to avoid interrupt again */
|
|
+ if (__dmac_channel_transmit_end_detected(channel)) {
|
|
+ __dmac_channel_clear_transmit_end(channel);
|
|
+ }
|
|
+ if (__dmac_channel_transmit_halt_detected(channel)) {
|
|
+ __dmac_channel_clear_transmit_halt(channel);
|
|
+ }
|
|
+
|
|
+ if (__dmac_channel_address_error_detected(channel)) {
|
|
+ __dmac_channel_clear_address_error(channel);
|
|
+ }
|
|
+
|
|
+ if (substream)
|
|
+ snd_pcm_period_elapsed(substream);
|
|
+
|
|
+ spin_lock(&prtd->lock);
|
|
+ prtd->dma_loaded--;
|
|
+ if (prtd->state & ST_RUNNING) {
|
|
+ jz4740_pcm_enqueue(substream);
|
|
+ }
|
|
+ spin_unlock(&prtd->lock);
|
|
+
|
|
+ local_irq_save(flags);
|
|
+ if (prtd->state & ST_RUNNING) {
|
|
+ if (prtd->dma_loaded) {
|
|
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
|
+ audio_start_dma(prtd, DMA_MODE_WRITE);
|
|
+ else
|
|
+ audio_start_dma(prtd, DMA_MODE_READ);
|
|
+ }
|
|
+ }
|
|
+ local_irq_restore(flags);
|
|
+ return IRQ_HANDLED;
|
|
+}
|
|
+
|
|
+/* some parameter about DMA operation */
|
|
+static int jz4740_pcm_hw_params(struct snd_pcm_substream *substream,
|
|
+ struct snd_pcm_hw_params *params)
|
|
+{
|
|
+ struct snd_pcm_runtime *runtime = substream->runtime;
|
|
+ struct jz4740_runtime_data *prtd = runtime->private_data;
|
|
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
+ struct jz4740_pcm_dma_params *dma = rtd->dai->cpu_dai->dma_data;
|
|
+ size_t totbytes = params_buffer_bytes(params);
|
|
+ int ret;
|
|
+
|
|
+#ifdef CONFIG_SND_OSSEMUL
|
|
+ if (hw_params_cnt)
|
|
+ return 0;
|
|
+ else
|
|
+ hw_params_cnt++ ;
|
|
+#endif
|
|
+
|
|
+ if (!dma)
|
|
+ return 0;
|
|
+
|
|
+ switch (params_format(params)) {
|
|
+ case SNDRV_PCM_FORMAT_S8:
|
|
+ tran_bit = 8;
|
|
+ break;
|
|
+ case SNDRV_PCM_FORMAT_S16_LE:
|
|
+ tran_bit = 16;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* prepare DMA */
|
|
+ prtd->params = dma;
|
|
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
|
+ ret = jz_request_dma(DMA_ID_AIC_TX, prtd->params->client->name,
|
|
+ jz4740_pcm_dma_irq, IRQF_DISABLED, substream);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ prtd->params->channel = ret;
|
|
+ } else {
|
|
+ ret = jz_request_dma(DMA_ID_AIC_RX, prtd->params->client->name,
|
|
+ jz4740_pcm_dma_irq, IRQF_DISABLED, substream);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ prtd->params->channel = ret;
|
|
+ }
|
|
+
|
|
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
|
|
+ runtime->dma_bytes = totbytes;
|
|
+
|
|
+ spin_lock_irq(&prtd->lock);
|
|
+ prtd->dma_loaded = 0;
|
|
+ prtd->aic_dma_flag = 0;
|
|
+ prtd->dma_limit = runtime->hw.periods_min;
|
|
+ prtd->dma_period = params_period_bytes(params);
|
|
+ prtd->dma_start = runtime->dma_addr;
|
|
+ prtd->dma_pos = prtd->dma_start;
|
|
+ prtd->dma_end = prtd->dma_start + totbytes;
|
|
+ prtd->curr = NULL;
|
|
+ prtd->next = NULL;
|
|
+ prtd->end = NULL;
|
|
+ sum_bytes = 0;
|
|
+ first_transfer = 1;
|
|
+ printk_flag = 0;
|
|
+
|
|
+ __dmac_disable_descriptor(prtd->params->channel);
|
|
+ __dmac_channel_disable_irq(prtd->params->channel);
|
|
+ spin_unlock_irq(&prtd->lock);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int jz4740_pcm_hw_free(struct snd_pcm_substream *substream)
|
|
+{
|
|
+ struct jz4740_runtime_data *prtd = substream->runtime->private_data;
|
|
+
|
|
+ snd_pcm_set_runtime_buffer(substream, NULL);
|
|
+ if (prtd->params) {
|
|
+ jz_free_dma(prtd->params->channel);
|
|
+ prtd->params = NULL;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* set some dma para for playback/capture */
|
|
+static int jz4740_dma_ctrl(int channel)
|
|
+{
|
|
+
|
|
+ disable_dma(channel);
|
|
+
|
|
+ /* must clear TT bit in DCCSR to avoid interrupt again */
|
|
+ if (__dmac_channel_transmit_end_detected(channel)) {
|
|
+ __dmac_channel_clear_transmit_end(channel);
|
|
+ }
|
|
+ if (__dmac_channel_transmit_halt_detected(channel)) {
|
|
+ __dmac_channel_clear_transmit_halt(channel);
|
|
+ }
|
|
+
|
|
+ if (__dmac_channel_address_error_detected(channel)) {
|
|
+ __dmac_channel_clear_address_error(channel);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+
|
|
+}
|
|
+
|
|
+static int jz4740_pcm_prepare(struct snd_pcm_substream *substream)
|
|
+{
|
|
+ struct jz4740_runtime_data *prtd = substream->runtime->private_data;
|
|
+ int ret = 0;
|
|
+
|
|
+ /* return if this is a bufferless transfer e.g */
|
|
+ if (!prtd->params)
|
|
+ return 0;
|
|
+
|
|
+ /* flush the DMA channel and DMA channel bit check */
|
|
+ jz4740_dma_ctrl(prtd->params->channel);
|
|
+ prtd->dma_loaded = 0;
|
|
+ prtd->dma_pos = prtd->dma_start;
|
|
+
|
|
+ /* enqueue dma buffers */
|
|
+ jz4740_pcm_enqueue(substream);
|
|
+
|
|
+ return ret;
|
|
+
|
|
+}
|
|
+
|
|
+static int jz4740_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
|
+{
|
|
+ struct snd_pcm_runtime *runtime = substream->runtime;
|
|
+ struct jz4740_runtime_data *prtd = runtime->private_data;
|
|
+
|
|
+ int ret = 0;
|
|
+
|
|
+ switch (cmd) {
|
|
+ case SNDRV_PCM_TRIGGER_START:
|
|
+ prtd->state |= ST_RUNNING;
|
|
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
|
+ audio_start_dma(prtd, DMA_MODE_WRITE);
|
|
+ } else {
|
|
+ audio_start_dma(prtd, DMA_MODE_READ);
|
|
+ }
|
|
+
|
|
+ break;
|
|
+
|
|
+ case SNDRV_PCM_TRIGGER_STOP:
|
|
+ case SNDRV_PCM_TRIGGER_SUSPEND:
|
|
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
|
+ prtd->state &= ~ST_RUNNING;
|
|
+ break;
|
|
+
|
|
+ case SNDRV_PCM_TRIGGER_RESUME:
|
|
+ printk(" RESUME \n");
|
|
+ break;
|
|
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
|
+ printk(" RESTART \n");
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ ret = -EINVAL;
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static snd_pcm_uframes_t
|
|
+jz4740_pcm_pointer(struct snd_pcm_substream *substream)
|
|
+{
|
|
+ struct snd_pcm_runtime *runtime = substream->runtime;
|
|
+ struct jz4740_runtime_data *prtd = runtime->private_data;
|
|
+ struct jz4740_dma_buf_aic *aic_buf = prtd->curr;
|
|
+ long count,res;
|
|
+
|
|
+ dma_addr_t ptr;
|
|
+ snd_pcm_uframes_t x;
|
|
+ int channel = prtd->params->channel;
|
|
+
|
|
+ spin_lock(&prtd->lock);
|
|
+#if 1
|
|
+
|
|
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
|
+ count = get_dma_residue(channel);
|
|
+ count = aic_buf->size - count;
|
|
+ ptr = aic_buf->data + count;
|
|
+ res = ptr - prtd->dma_start;
|
|
+ } else {
|
|
+ count = get_dma_residue(channel);
|
|
+ count = aic_buf->size - count;
|
|
+ ptr = aic_buf->data + count;
|
|
+ res = ptr - prtd->dma_start;
|
|
+ }
|
|
+
|
|
+# else
|
|
+
|
|
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
|
+ if ((prtd->aic_dma_flag & AIC_START_DMA) == 0) {
|
|
+ count = get_dma_residue(channel);
|
|
+ count = aic_buf->size - count;
|
|
+ ptr = aic_buf->data + count;
|
|
+ REG_DMAC_DSAR(channel) = ptr;
|
|
+ res = ptr - prtd->dma_start;
|
|
+ } else {
|
|
+ ptr = REG_DMAC_DSAR(channel);
|
|
+ if (ptr == 0x0)
|
|
+ printk("\ndma address is 00000000 in running!\n");
|
|
+ res = ptr - prtd->dma_start;
|
|
+ }
|
|
+ } else {
|
|
+ if ((prtd->aic_dma_flag & AIC_START_DMA) == 0) {
|
|
+ count = get_dma_residue(channel);
|
|
+ count = aic_buf->size - count;
|
|
+ ptr = aic_buf->data + count;
|
|
+ REG_DMAC_DTAR(channel) = ptr;
|
|
+ res = ptr - prtd->dma_start;
|
|
+ } else {
|
|
+ ptr = REG_DMAC_DTAR(channel);
|
|
+ if (ptr == 0x0)
|
|
+ printk("\ndma address is 00000000 in running!\n");
|
|
+ res = ptr - prtd->dma_start;
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
+ spin_unlock(&prtd->lock);
|
|
+ x = bytes_to_frames(runtime, res);
|
|
+ if (x == runtime->buffer_size)
|
|
+ x = 0;
|
|
+
|
|
+ return x;
|
|
+}
|
|
+
|
|
+static int jz4740_pcm_open(struct snd_pcm_substream *substream)
|
|
+{
|
|
+ struct snd_pcm_runtime *runtime = substream->runtime;
|
|
+ struct jz4740_runtime_data *prtd;
|
|
+
|
|
+#ifdef CONFIG_SND_OSSEMUL
|
|
+ hw_params_cnt = 0;
|
|
+#endif
|
|
+ snd_soc_set_runtime_hwparams(substream, &jz4740_pcm_hardware);
|
|
+ prtd = kzalloc(sizeof(struct jz4740_runtime_data), GFP_KERNEL);
|
|
+ if (prtd == NULL)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ spin_lock_init(&prtd->lock);
|
|
+
|
|
+ runtime->private_data = prtd;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int jz4740_pcm_close(struct snd_pcm_substream *substream)
|
|
+{
|
|
+ struct snd_pcm_runtime *runtime = substream->runtime;
|
|
+ struct jz4740_runtime_data *prtd = runtime->private_data;
|
|
+ struct jz4740_dma_buf_aic *aic_buf = NULL;
|
|
+
|
|
+#ifdef CONFIG_SND_OSSEMUL
|
|
+ hw_params_cnt = 0;
|
|
+#endif
|
|
+
|
|
+ if (prtd)
|
|
+ aic_buf = prtd->curr;
|
|
+
|
|
+ while (aic_buf != NULL) {
|
|
+ prtd->curr = aic_buf->next;
|
|
+ prtd->next = aic_buf->next;
|
|
+ aic_buf->next = NULL;
|
|
+ kfree(aic_buf);
|
|
+ aic_buf = NULL;
|
|
+ aic_buf = prtd->curr;
|
|
+ }
|
|
+
|
|
+ if (prtd) {
|
|
+ prtd->curr = NULL;
|
|
+ prtd->next = NULL;
|
|
+ prtd->end = NULL;
|
|
+ kfree(prtd);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int jz4740_pcm_mmap(struct snd_pcm_substream *substream,
|
|
+ struct vm_area_struct *vma)//include/linux/mm.h
|
|
+{
|
|
+ struct snd_pcm_runtime *runtime = substream->runtime;
|
|
+ unsigned long start;
|
|
+ unsigned long off;
|
|
+ u32 len;
|
|
+ int ret = -ENXIO;
|
|
+
|
|
+ off = vma->vm_pgoff << PAGE_SHIFT;
|
|
+ start = runtime->dma_addr;
|
|
+
|
|
+ len = PAGE_ALIGN((start & ~PAGE_MASK) + runtime->dma_bytes);
|
|
+ start &= PAGE_MASK;
|
|
+
|
|
+ if ((vma->vm_end - vma->vm_start + off) > len) {
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ off += start;
|
|
+ vma->vm_pgoff = off >> PAGE_SHIFT;
|
|
+ vma->vm_flags |= VM_IO;
|
|
+
|
|
+#if defined(CONFIG_MIPS32)
|
|
+ pgprot_val(vma->vm_page_prot) &= ~_CACHE_MASK;
|
|
+ pgprot_val(vma->vm_page_prot) |= _CACHE_UNCACHED;
|
|
+ /* pgprot_val(vma->vm_page_prot) |= _CACHE_CACHABLE_NONCOHERENT; */
|
|
+#endif
|
|
+ ret = io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
|
|
+ vma->vm_end - vma->vm_start,
|
|
+ vma->vm_page_prot);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+struct snd_pcm_ops jz4740_pcm_ops = {
|
|
+ .open = jz4740_pcm_open,
|
|
+ .close = jz4740_pcm_close,
|
|
+ .ioctl = snd_pcm_lib_ioctl,
|
|
+ .hw_params = jz4740_pcm_hw_params,
|
|
+ .hw_free = jz4740_pcm_hw_free,
|
|
+ .prepare = jz4740_pcm_prepare,
|
|
+ .trigger = jz4740_pcm_trigger,
|
|
+ .pointer = jz4740_pcm_pointer,
|
|
+ .mmap = jz4740_pcm_mmap,
|
|
+};
|
|
+
|
|
+static int jz4740_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
|
|
+{
|
|
+ struct snd_pcm_substream *substream = pcm->streams[stream].substream;
|
|
+ struct snd_dma_buffer *buf = &substream->dma_buffer;
|
|
+ size_t size = jz4740_pcm_hardware.buffer_bytes_max;
|
|
+ buf->dev.type = SNDRV_DMA_TYPE_DEV;
|
|
+ buf->dev.dev = pcm->card->dev;
|
|
+ buf->private_data = NULL;
|
|
+
|
|
+ /*buf->area = dma_alloc_coherent(pcm->card->dev, size,
|
|
+ &buf->addr, GFP_KERNEL);*/
|
|
+ buf->area = dma_alloc_noncoherent(pcm->card->dev, size,
|
|
+ &buf->addr, GFP_KERNEL);
|
|
+ if (!buf->area)
|
|
+ return -ENOMEM;
|
|
+ buf->bytes = size;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void jz4740_pcm_free_dma_buffers(struct snd_pcm *pcm)
|
|
+{
|
|
+ struct snd_pcm_substream *substream;
|
|
+ struct snd_dma_buffer *buf;
|
|
+ int stream;
|
|
+
|
|
+ for (stream = 0; stream < 2; stream++) {
|
|
+ substream = pcm->streams[stream].substream;
|
|
+ if (!substream)
|
|
+ continue;
|
|
+
|
|
+ buf = &substream->dma_buffer;
|
|
+ if (!buf->area)
|
|
+ continue;
|
|
+
|
|
+ dma_free_noncoherent(pcm->card->dev, buf->bytes,
|
|
+ buf->area, buf->addr);
|
|
+ buf->area = NULL;
|
|
+ }
|
|
+}
|
|
+
|
|
+static u64 jz4740_pcm_dmamask = DMA_32BIT_MASK;
|
|
+
|
|
+int jz4740_pcm_new(struct snd_card *card, struct snd_soc_codec_dai *dai,
|
|
+ struct snd_pcm *pcm)
|
|
+{
|
|
+ int ret = 0;
|
|
+
|
|
+ if (!card->dev->dma_mask)
|
|
+ card->dev->dma_mask = &jz4740_pcm_dmamask;
|
|
+ if (!card->dev->coherent_dma_mask)
|
|
+ card->dev->coherent_dma_mask = DMA_32BIT_MASK;
|
|
+
|
|
+ if (dai->playback.channels_min) {
|
|
+ ret = jz4740_pcm_preallocate_dma_buffer(pcm,
|
|
+ SNDRV_PCM_STREAM_PLAYBACK);
|
|
+ if (ret)
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ if (dai->capture.channels_min) {
|
|
+ ret = jz4740_pcm_preallocate_dma_buffer(pcm,
|
|
+ SNDRV_PCM_STREAM_CAPTURE);
|
|
+ if (ret)
|
|
+ goto out;
|
|
+ }
|
|
+ out:
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+struct snd_soc_platform jz4740_soc_platform = {
|
|
+ .name = "jz4740-audio",
|
|
+ .pcm_ops = &jz4740_pcm_ops,
|
|
+ .pcm_new = jz4740_pcm_new,
|
|
+ .pcm_free = jz4740_pcm_free_dma_buffers,
|
|
+};
|
|
+
|
|
+EXPORT_SYMBOL_GPL(jz4740_soc_platform);
|
|
+
|
|
+MODULE_AUTHOR("Richard");
|
|
+MODULE_DESCRIPTION("Ingenic Jz4740 PCM DMA module");
|
|
+MODULE_LICENSE("GPL");
|
|
--- /dev/null
|
|
+++ b/sound/soc/jz4740/jz4740-pcm.h
|
|
@@ -0,0 +1,33 @@
|
|
+/*
|
|
+ *
|
|
+ * 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.
|
|
+ */
|
|
+
|
|
+#ifndef _JZ4740_PCM_H
|
|
+#define _JZ4740_PCM_H
|
|
+
|
|
+#include <asm/jzsoc.h>
|
|
+
|
|
+#define ST_RUNNING (1<<0)
|
|
+#define ST_OPENED (1<<1)
|
|
+
|
|
+#define AIC_START_DMA (1<<0)
|
|
+#define AIC_END_DMA (1<<1)
|
|
+
|
|
+struct jz4740_dma_client {
|
|
+ char *name;
|
|
+};
|
|
+
|
|
+struct jz4740_pcm_dma_params {
|
|
+ struct jz4740_dma_client *client; /* stream identifier */
|
|
+ int channel; /* Channel ID */
|
|
+ dma_addr_t dma_addr;
|
|
+ int dma_size; /* Size of the DMA transfer */
|
|
+};
|
|
+
|
|
+/* platform data */
|
|
+extern struct snd_soc_platform jz4740_soc_platform;
|
|
+
|
|
+#endif
|
|
--- /dev/null
|
|
+++ b/sound/soc/jz4740/pavo.c
|
|
@@ -0,0 +1,360 @@
|
|
+/*
|
|
+ * pavo.c -- SoC audio for PAVO
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify it
|
|
+ * under the terms of the GNU General Public License as published by the
|
|
+ * Free Software Foundation; either version 2 of the License, or (at your
|
|
+ * option) any later version.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include <linux/module.h>
|
|
+#include <linux/moduleparam.h>
|
|
+#include <linux/timer.h>
|
|
+#include <linux/interrupt.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <sound/driver.h>
|
|
+#include <sound/core.h>
|
|
+#include <sound/pcm.h>
|
|
+#include <sound/soc.h>
|
|
+#include <sound/soc-dapm.h>
|
|
+
|
|
+#include "../codecs/jzcodec.h"
|
|
+#include "jz4740-pcm.h"
|
|
+#include "jz4740-i2s.h"
|
|
+
|
|
+#define PAVO_HP 0
|
|
+#define PAVO_MIC 1
|
|
+#define PAVO_LINE 2
|
|
+#define PAVO_HEADSET 3
|
|
+#define PAVO_HP_OFF 4
|
|
+#define PAVO_SPK_ON 0
|
|
+#define PAVO_SPK_OFF 1
|
|
+
|
|
+ /* audio clock in Hz - rounded from 12.235MHz */
|
|
+#define PAVO_AUDIO_CLOCK 12288000
|
|
+
|
|
+static int pavo_jack_func;
|
|
+static int pavo_spk_func;
|
|
+
|
|
+unsigned short set_scoop_gpio(struct device *dev, unsigned short bit)
|
|
+{
|
|
+ unsigned short gpio_bit = 0;
|
|
+
|
|
+ return gpio_bit;
|
|
+}
|
|
+
|
|
+unsigned short reset_scoop_gpio(struct device *dev, unsigned short bit)
|
|
+{
|
|
+ unsigned short gpio_bit = 0;
|
|
+
|
|
+ return gpio_bit;
|
|
+}
|
|
+
|
|
+static void pavo_ext_control(struct snd_soc_codec *codec)
|
|
+{
|
|
+ int spk = 0, mic = 0, line = 0, hp = 0, hs = 0;
|
|
+
|
|
+ /* set up jack connection */
|
|
+ switch (pavo_jack_func) {
|
|
+ case PAVO_HP:
|
|
+ hp = 1;
|
|
+ /* set = unmute headphone */
|
|
+ //set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L);
|
|
+ //set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R);
|
|
+ break;
|
|
+ case PAVO_MIC:
|
|
+ mic = 1;
|
|
+ /* reset = mute headphone */
|
|
+ //reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L);
|
|
+ //reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R);
|
|
+ break;
|
|
+ case PAVO_LINE:
|
|
+ line = 1;
|
|
+ //reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L);
|
|
+ //reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R);
|
|
+ break;
|
|
+ case PAVO_HEADSET:
|
|
+ hs = 1;
|
|
+ mic = 1;
|
|
+ //reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L);
|
|
+ //set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (pavo_spk_func == PAVO_SPK_ON)
|
|
+ spk = 1;
|
|
+
|
|
+ /* set the enpoints to their new connetion states */
|
|
+ snd_soc_dapm_set_endpoint(codec, "Ext Spk", spk);
|
|
+ snd_soc_dapm_set_endpoint(codec, "Mic Jack", mic);
|
|
+ snd_soc_dapm_set_endpoint(codec, "Line Jack", line);
|
|
+ snd_soc_dapm_set_endpoint(codec, "Headphone Jack", hp);
|
|
+ snd_soc_dapm_set_endpoint(codec, "Headset Jack", hs);
|
|
+
|
|
+ /* signal a DAPM event */
|
|
+ snd_soc_dapm_sync_endpoints(codec);
|
|
+}
|
|
+
|
|
+static int pavo_startup(struct snd_pcm_substream *substream)
|
|
+{
|
|
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
+ struct snd_soc_codec *codec = rtd->socdev->codec;
|
|
+
|
|
+ /* check the jack status at stream startup */
|
|
+ pavo_ext_control(codec);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* we need to unmute the HP at shutdown as the mute burns power on pavo */
|
|
+static void pavo_shutdown(struct snd_pcm_substream *substream)
|
|
+{
|
|
+ /*struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
+ struct snd_soc_codec *codec = rtd->socdev->codec;*/
|
|
+
|
|
+ return;
|
|
+}
|
|
+
|
|
+static int pavo_hw_params(struct snd_pcm_substream *substream,
|
|
+ struct snd_pcm_hw_params *params)
|
|
+{
|
|
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
+ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
|
|
+ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
|
|
+ int ret = 0;
|
|
+
|
|
+ /* set codec DAI configuration */
|
|
+ ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
|
|
+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ /* set cpu DAI configuration */
|
|
+ ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
|
|
+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ /* set the codec system clock for DAC and ADC */
|
|
+ ret = codec_dai->dai_ops.set_sysclk(codec_dai, JZCODEC_SYSCLK, 111,
|
|
+ SND_SOC_CLOCK_IN);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ /* set the I2S system clock as input (unused) */
|
|
+ ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, JZ4740_I2S_SYSCLK, 0,
|
|
+ SND_SOC_CLOCK_IN);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct snd_soc_ops pavo_ops = {
|
|
+ .startup = pavo_startup,
|
|
+ .hw_params = pavo_hw_params,
|
|
+ .shutdown = pavo_shutdown,
|
|
+};
|
|
+
|
|
+static int pavo_get_jack(struct snd_kcontrol *kcontrol,
|
|
+ struct snd_ctl_elem_value *ucontrol)
|
|
+{
|
|
+ ucontrol->value.integer.value[0] = pavo_jack_func;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int pavo_set_jack(struct snd_kcontrol *kcontrol,
|
|
+ struct snd_ctl_elem_value *ucontrol)
|
|
+{
|
|
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
|
|
+
|
|
+ if (pavo_jack_func == ucontrol->value.integer.value[0])
|
|
+ return 0;
|
|
+
|
|
+ pavo_jack_func = ucontrol->value.integer.value[0];
|
|
+ pavo_ext_control(codec);
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static int pavo_get_spk(struct snd_kcontrol *kcontrol,
|
|
+ struct snd_ctl_elem_value *ucontrol)
|
|
+{
|
|
+ ucontrol->value.integer.value[0] = pavo_spk_func;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int pavo_set_spk(struct snd_kcontrol *kcontrol,
|
|
+ struct snd_ctl_elem_value *ucontrol)
|
|
+{
|
|
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
|
|
+
|
|
+ if (pavo_spk_func == ucontrol->value.integer.value[0])
|
|
+ return 0;
|
|
+
|
|
+ pavo_spk_func = ucontrol->value.integer.value[0];
|
|
+ pavo_ext_control(codec);
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static int pavo_amp_event(struct snd_soc_dapm_widget *w, int event)
|
|
+{
|
|
+ if (SND_SOC_DAPM_EVENT_ON(event))
|
|
+ //set_scoop_gpio(&corgiscoop_device.dev, PAVO_SCP_APM_ON);
|
|
+ ;
|
|
+ else
|
|
+ //reset_scoop_gpio(&corgiscoop_device.dev, PAVO_SCP_APM_ON);
|
|
+ ;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int pavo_mic_event(struct snd_soc_dapm_widget *w, int event)
|
|
+{
|
|
+ if (SND_SOC_DAPM_EVENT_ON(event))
|
|
+ //set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MIC_BIAS);
|
|
+ ;
|
|
+ else
|
|
+ //reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MIC_BIAS);
|
|
+ ;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* pavo machine dapm widgets */
|
|
+static const struct snd_soc_dapm_widget jzcodec_dapm_widgets[] = {
|
|
+SND_SOC_DAPM_HP("Headphone Jack", NULL),
|
|
+SND_SOC_DAPM_MIC("Mic Jack",pavo_mic_event),
|
|
+SND_SOC_DAPM_SPK("Ext Spk", pavo_amp_event),
|
|
+SND_SOC_DAPM_LINE("Line Jack", NULL),
|
|
+SND_SOC_DAPM_HP("Headset Jack", NULL),
|
|
+};
|
|
+
|
|
+/* pavo machine audio map (connections to the codec pins) */
|
|
+static const char *audio_map[][3] = {
|
|
+
|
|
+ /* headset Jack - in = micin, out = LHPOUT*/
|
|
+ {"Headset Jack", NULL, "LHPOUT"},
|
|
+
|
|
+ /* headphone connected to LHPOUT1, RHPOUT1 */
|
|
+ {"Headphone Jack", NULL, "LHPOUT"},
|
|
+ {"Headphone Jack", NULL, "RHPOUT"},
|
|
+
|
|
+ /* speaker connected to LOUT, ROUT */
|
|
+ {"Ext Spk", NULL, "ROUT"},
|
|
+ {"Ext Spk", NULL, "LOUT"},
|
|
+
|
|
+ /* mic is connected to MICIN (via right channel of headphone jack) */
|
|
+ {"MICIN", NULL, "Mic Jack"},
|
|
+
|
|
+ /* Same as the above but no mic bias for line signals */
|
|
+ {"MICIN", NULL, "Line Jack"},
|
|
+
|
|
+ {NULL, NULL, NULL},
|
|
+};
|
|
+
|
|
+static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset",
|
|
+ "Off"};
|
|
+static const char *spk_function[] = {"On", "Off"};
|
|
+static const struct soc_enum pavo_enum[] = {
|
|
+ SOC_ENUM_SINGLE_EXT(5, jack_function),
|
|
+ SOC_ENUM_SINGLE_EXT(2, spk_function),
|
|
+};
|
|
+
|
|
+static const struct snd_kcontrol_new jzcodec_pavo_controls[] = {
|
|
+ SOC_ENUM_EXT("Jack Function", pavo_enum[0], pavo_get_jack,
|
|
+ pavo_set_jack),
|
|
+ SOC_ENUM_EXT("Speaker Function", pavo_enum[1], pavo_get_spk,
|
|
+ pavo_set_spk),
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Pavo for a jzcodec as connected on jz4740 Device
|
|
+ */
|
|
+static int pavo_jzcodec_init(struct snd_soc_codec *codec)
|
|
+{
|
|
+ int i, err;
|
|
+
|
|
+ snd_soc_dapm_set_endpoint(codec, "LLINEIN", 0);
|
|
+ snd_soc_dapm_set_endpoint(codec, "RLINEIN", 0);
|
|
+
|
|
+ /* Add pavo specific controls */
|
|
+ for (i = 0; i < ARRAY_SIZE(jzcodec_pavo_controls); i++) {
|
|
+ err = snd_ctl_add(codec->card,
|
|
+ snd_soc_cnew(&jzcodec_pavo_controls[i],codec, NULL));
|
|
+ if (err < 0)
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ /* Add pavo specific widgets */
|
|
+ for(i = 0; i < ARRAY_SIZE(jzcodec_dapm_widgets); i++) {
|
|
+ snd_soc_dapm_new_control(codec, &jzcodec_dapm_widgets[i]);
|
|
+ }
|
|
+
|
|
+ /* Set up pavo specific audio path audio_map */
|
|
+ for(i = 0; audio_map[i][0] != NULL; i++) {
|
|
+ snd_soc_dapm_connect_input(codec, audio_map[i][0],
|
|
+ audio_map[i][1], audio_map[i][2]);
|
|
+ }
|
|
+
|
|
+ snd_soc_dapm_sync_endpoints(codec);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* pavo digital audio interface glue - connects codec <--> CPU */
|
|
+static struct snd_soc_dai_link pavo_dai = {
|
|
+ .name = "JZCODEC",
|
|
+ .stream_name = "JZCODEC",
|
|
+ .cpu_dai = &jz4740_i2s_dai,
|
|
+ .codec_dai = &jzcodec_dai,
|
|
+ .init = pavo_jzcodec_init,
|
|
+ .ops = &pavo_ops,
|
|
+};
|
|
+
|
|
+/* pavo audio machine driver */
|
|
+static struct snd_soc_machine snd_soc_machine_pavo = {
|
|
+ .name = "Pavo",
|
|
+ .dai_link = &pavo_dai,
|
|
+ .num_links = 1,
|
|
+};
|
|
+
|
|
+/* pavo audio subsystem */
|
|
+static struct snd_soc_device pavo_snd_devdata = {
|
|
+ .machine = &snd_soc_machine_pavo,
|
|
+ .platform = &jz4740_soc_platform,
|
|
+ .codec_dev = &soc_codec_dev_jzcodec,
|
|
+ //.codec_data
|
|
+};
|
|
+
|
|
+static struct platform_device *pavo_snd_device;
|
|
+
|
|
+static int __init pavo_init(void)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ pavo_snd_device = platform_device_alloc("soc-audio", -1);
|
|
+
|
|
+ if (!pavo_snd_device)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ platform_set_drvdata(pavo_snd_device, &pavo_snd_devdata);
|
|
+ pavo_snd_devdata.dev = &pavo_snd_device->dev;
|
|
+ ret = platform_device_add(pavo_snd_device);
|
|
+
|
|
+ if (ret)
|
|
+ platform_device_put(pavo_snd_device);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void __exit pavo_exit(void)
|
|
+{
|
|
+ platform_device_unregister(pavo_snd_device);
|
|
+}
|
|
+
|
|
+module_init(pavo_init);
|
|
+module_exit(pavo_exit);
|
|
+
|
|
+/* Module information */
|
|
+MODULE_AUTHOR("Richard");
|
|
+MODULE_DESCRIPTION("ALSA SoC Pavo");
|
|
+MODULE_LICENSE("GPL");
|