1
0
mirror of git://projects.qi-hardware.com/openwrt-xburst.git synced 2024-11-25 04:01:52 +02:00
openwrt-xburst/target/linux/xburst/patches-2.6.28/500-sound.patch

23913 lines
650 KiB
Diff

--- linux-2.6.24.7.old/sound/core/pcm_native.c 2008-05-07 01:22:34.000000000 +0200
+++ linux-2.6.24.7/sound/core/pcm_native.c 2009-04-12 18:13:57.000000000 +0200
@@ -1780,12 +1780,13 @@
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),
@@ -1796,9 +1797,17 @@
struct snd_pcm_hw_rule *rule)
{
struct snd_pcm_hardware *hw = rule->private;
+#if 0
return snd_interval_list(hw_param_interval(params, rule->var),
snd_pcm_known_rates.count,
snd_pcm_known_rates.list, hw->rates);
+#else
+ //printk("hw->rates=0x%08x\n",hw->rates);//0x3b6
+ 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);
+#endif
}
static int snd_pcm_hw_rule_buffer_bytes_max(struct snd_pcm_hw_params *params,
--- linux-2.6.24.7.old/sound/core/pcm_native.c.org 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.24.7/sound/core/pcm_native.c.org 2009-04-12 18:13:57.000000000 +0200
@@ -0,0 +1,3451 @@
+/*
+ * Digital Audio (PCM) abstract layer
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/mm.h>
+#include <linux/file.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/latency.h>
+#include <linux/uio.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/info.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/timer.h>
+#include <sound/minors.h>
+#include <asm/io.h>
+
+/*
+ * Compatibility
+ */
+
+struct snd_pcm_hw_params_old {
+ unsigned int flags;
+ unsigned int masks[SNDRV_PCM_HW_PARAM_SUBFORMAT -
+ SNDRV_PCM_HW_PARAM_ACCESS + 1];
+ struct snd_interval intervals[SNDRV_PCM_HW_PARAM_TICK_TIME -
+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS + 1];
+ unsigned int rmask;
+ unsigned int cmask;
+ unsigned int info;
+ unsigned int msbits;
+ unsigned int rate_num;
+ unsigned int rate_den;
+ snd_pcm_uframes_t fifo_size;
+ unsigned char reserved[64];
+};
+
+#ifdef CONFIG_SND_SUPPORT_OLD_API
+#define SNDRV_PCM_IOCTL_HW_REFINE_OLD _IOWR('A', 0x10, struct snd_pcm_hw_params_old)
+#define SNDRV_PCM_IOCTL_HW_PARAMS_OLD _IOWR('A', 0x11, struct snd_pcm_hw_params_old)
+
+static int snd_pcm_hw_refine_old_user(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params_old __user * _oparams);
+static int snd_pcm_hw_params_old_user(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params_old __user * _oparams);
+#endif
+static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream);
+
+/*
+ *
+ */
+
+DEFINE_RWLOCK(snd_pcm_link_rwlock);
+EXPORT_SYMBOL(snd_pcm_link_rwlock);
+
+static DECLARE_RWSEM(snd_pcm_link_rwsem);
+
+static inline mm_segment_t snd_enter_user(void)
+{
+ mm_segment_t fs = get_fs();
+ set_fs(get_ds());
+ return fs;
+}
+
+static inline void snd_leave_user(mm_segment_t fs)
+{
+ set_fs(fs);
+}
+
+
+
+int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info)
+{
+ struct snd_pcm_runtime *runtime;
+ struct snd_pcm *pcm = substream->pcm;
+ struct snd_pcm_str *pstr = substream->pstr;
+
+ snd_assert(substream != NULL, return -ENXIO);
+ memset(info, 0, sizeof(*info));
+ info->card = pcm->card->number;
+ info->device = pcm->device;
+ info->stream = substream->stream;
+ info->subdevice = substream->number;
+ strlcpy(info->id, pcm->id, sizeof(info->id));
+ strlcpy(info->name, pcm->name, sizeof(info->name));
+ info->dev_class = pcm->dev_class;
+ info->dev_subclass = pcm->dev_subclass;
+ info->subdevices_count = pstr->substream_count;
+ info->subdevices_avail = pstr->substream_count - pstr->substream_opened;
+ strlcpy(info->subname, substream->name, sizeof(info->subname));
+ runtime = substream->runtime;
+ /* AB: FIXME!!! This is definitely nonsense */
+ if (runtime) {
+ info->sync = runtime->sync;
+ substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_INFO, info);
+ }
+ return 0;
+}
+
+int snd_pcm_info_user(struct snd_pcm_substream *substream,
+ struct snd_pcm_info __user * _info)
+{
+ struct snd_pcm_info *info;
+ int err;
+
+ info = kmalloc(sizeof(*info), GFP_KERNEL);
+ if (! info)
+ return -ENOMEM;
+ err = snd_pcm_info(substream, info);
+ if (err >= 0) {
+ if (copy_to_user(_info, info, sizeof(*info)))
+ err = -EFAULT;
+ }
+ kfree(info);
+ return err;
+}
+
+#undef RULES_DEBUG
+
+#ifdef RULES_DEBUG
+#define HW_PARAM(v) [SNDRV_PCM_HW_PARAM_##v] = #v
+char *snd_pcm_hw_param_names[] = {
+ HW_PARAM(ACCESS),
+ HW_PARAM(FORMAT),
+ HW_PARAM(SUBFORMAT),
+ HW_PARAM(SAMPLE_BITS),
+ HW_PARAM(FRAME_BITS),
+ HW_PARAM(CHANNELS),
+ HW_PARAM(RATE),
+ HW_PARAM(PERIOD_TIME),
+ HW_PARAM(PERIOD_SIZE),
+ HW_PARAM(PERIOD_BYTES),
+ HW_PARAM(PERIODS),
+ HW_PARAM(BUFFER_TIME),
+ HW_PARAM(BUFFER_SIZE),
+ HW_PARAM(BUFFER_BYTES),
+ HW_PARAM(TICK_TIME),
+};
+#endif
+
+int snd_pcm_hw_refine(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ unsigned int k;
+ struct snd_pcm_hardware *hw;
+ struct snd_interval *i = NULL;
+ struct snd_mask *m = NULL;
+ struct snd_pcm_hw_constraints *constrs = &substream->runtime->hw_constraints;
+ unsigned int rstamps[constrs->rules_num];
+ unsigned int vstamps[SNDRV_PCM_HW_PARAM_LAST_INTERVAL + 1];
+ unsigned int stamp = 2;
+ int changed, again;
+
+ params->info = 0;
+ params->fifo_size = 0;
+ if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_SAMPLE_BITS))
+ params->msbits = 0;
+ if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_RATE)) {
+ params->rate_num = 0;
+ params->rate_den = 0;
+ }
+
+ for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) {
+ m = hw_param_mask(params, k);
+ if (snd_mask_empty(m))
+ return -EINVAL;
+ if (!(params->rmask & (1 << k)))
+ continue;
+#ifdef RULES_DEBUG
+ printk("%s = ", snd_pcm_hw_param_names[k]);
+ printk("%04x%04x%04x%04x -> ", m->bits[3], m->bits[2], m->bits[1], m->bits[0]);
+#endif
+ changed = snd_mask_refine(m, constrs_mask(constrs, k));
+#ifdef RULES_DEBUG
+ printk("%04x%04x%04x%04x\n", m->bits[3], m->bits[2], m->bits[1], m->bits[0]);
+#endif
+ if (changed)
+ params->cmask |= 1 << k;
+ if (changed < 0)
+ return changed;
+ }
+
+ for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) {
+ i = hw_param_interval(params, k);
+ if (snd_interval_empty(i))
+ return -EINVAL;
+ if (!(params->rmask & (1 << k)))
+ continue;
+#ifdef RULES_DEBUG
+ printk("%s = ", snd_pcm_hw_param_names[k]);
+ if (i->empty)
+ printk("empty");
+ else
+ printk("%c%u %u%c",
+ i->openmin ? '(' : '[', i->min,
+ i->max, i->openmax ? ')' : ']');
+ printk(" -> ");
+#endif
+ changed = snd_interval_refine(i, constrs_interval(constrs, k));
+#ifdef RULES_DEBUG
+ if (i->empty)
+ printk("empty\n");
+ else
+ printk("%c%u %u%c\n",
+ i->openmin ? '(' : '[', i->min,
+ i->max, i->openmax ? ')' : ']');
+#endif
+ if (changed)
+ params->cmask |= 1 << k;
+ if (changed < 0)
+ return changed;
+ }
+
+ for (k = 0; k < constrs->rules_num; k++)
+ rstamps[k] = 0;
+ for (k = 0; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++)
+ vstamps[k] = (params->rmask & (1 << k)) ? 1 : 0;
+ do {
+ again = 0;
+ for (k = 0; k < constrs->rules_num; k++) {
+ struct snd_pcm_hw_rule *r = &constrs->rules[k];
+ unsigned int d;
+ int doit = 0;
+ if (r->cond && !(r->cond & params->flags))
+ continue;
+ for (d = 0; r->deps[d] >= 0; d++) {
+ if (vstamps[r->deps[d]] > rstamps[k]) {
+ doit = 1;
+ break;
+ }
+ }
+ if (!doit)
+ continue;
+#ifdef RULES_DEBUG
+ printk("Rule %d [%p]: ", k, r->func);
+ if (r->var >= 0) {
+ printk("%s = ", snd_pcm_hw_param_names[r->var]);
+ if (hw_is_mask(r->var)) {
+ m = hw_param_mask(params, r->var);
+ printk("%x", *m->bits);
+ } else {
+ i = hw_param_interval(params, r->var);
+ if (i->empty)
+ printk("empty");
+ else
+ printk("%c%u %u%c",
+ i->openmin ? '(' : '[', i->min,
+ i->max, i->openmax ? ')' : ']');
+ }
+ }
+#endif
+ changed = r->func(params, r);
+#ifdef RULES_DEBUG
+ if (r->var >= 0) {
+ printk(" -> ");
+ if (hw_is_mask(r->var))
+ printk("%x", *m->bits);
+ else {
+ if (i->empty)
+ printk("empty");
+ else
+ printk("%c%u %u%c",
+ i->openmin ? '(' : '[', i->min,
+ i->max, i->openmax ? ')' : ']');
+ }
+ }
+ printk("\n");
+#endif
+ rstamps[k] = stamp;
+ if (changed && r->var >= 0) {
+ params->cmask |= (1 << r->var);
+ vstamps[r->var] = stamp;
+ again = 1;
+ }
+ if (changed < 0)
+ return changed;
+ stamp++;
+ }
+ } while (again);
+ if (!params->msbits) {
+ i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
+ if (snd_interval_single(i))
+ params->msbits = snd_interval_value(i);
+ }
+
+ if (!params->rate_den) {
+ i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ if (snd_interval_single(i)) {
+ params->rate_num = snd_interval_value(i);
+ params->rate_den = 1;
+ }
+ }
+
+ hw = &substream->runtime->hw;
+ if (!params->info)
+ params->info = hw->info;
+ if (!params->fifo_size)
+ params->fifo_size = hw->fifo_size;
+ params->rmask = 0;
+ return 0;
+}
+
+EXPORT_SYMBOL(snd_pcm_hw_refine);
+
+static int snd_pcm_hw_refine_user(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params __user * _params)
+{
+ struct snd_pcm_hw_params *params;
+ int err;
+
+ params = kmalloc(sizeof(*params), GFP_KERNEL);
+ if (!params) {
+ err = -ENOMEM;
+ goto out;
+ }
+ if (copy_from_user(params, _params, sizeof(*params))) {
+ err = -EFAULT;
+ goto out;
+ }
+ err = snd_pcm_hw_refine(substream, params);
+ if (copy_to_user(_params, params, sizeof(*params))) {
+ if (!err)
+ err = -EFAULT;
+ }
+out:
+ kfree(params);
+ return err;
+}
+
+static int period_to_usecs(struct snd_pcm_runtime *runtime)
+{
+ int usecs;
+
+ if (! runtime->rate)
+ return -1; /* invalid */
+
+ /* take 75% of period time as the deadline */
+ usecs = (750000 / runtime->rate) * runtime->period_size;
+ usecs += ((750000 % runtime->rate) * runtime->period_size) /
+ runtime->rate;
+
+ return usecs;
+}
+
+static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime;
+ int err, usecs;
+ unsigned int bits;
+ snd_pcm_uframes_t frames;
+
+ snd_assert(substream != NULL, return -ENXIO);
+ runtime = substream->runtime;
+ snd_assert(runtime != NULL, return -ENXIO);
+ snd_pcm_stream_lock_irq(substream);
+ switch (runtime->status->state) {
+ case SNDRV_PCM_STATE_OPEN:
+ case SNDRV_PCM_STATE_SETUP:
+ case SNDRV_PCM_STATE_PREPARED:
+ break;
+ default:
+ snd_pcm_stream_unlock_irq(substream);
+ return -EBADFD;
+ }
+ snd_pcm_stream_unlock_irq(substream);
+#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
+ if (!substream->oss.oss)
+#endif
+ if (atomic_read(&substream->mmap_count))
+ return -EBADFD;
+
+ params->rmask = ~0U;
+ err = snd_pcm_hw_refine(substream, params);
+ if (err < 0)
+ goto _error;
+
+ err = snd_pcm_hw_params_choose(substream, params);
+ if (err < 0)
+ goto _error;
+
+ if (substream->ops->hw_params != NULL) {
+ err = substream->ops->hw_params(substream, params);
+ if (err < 0)
+ goto _error;
+ }
+
+ runtime->access = params_access(params);
+ runtime->format = params_format(params);
+ runtime->subformat = params_subformat(params);
+ runtime->channels = params_channels(params);
+ runtime->rate = params_rate(params);
+ runtime->period_size = params_period_size(params);
+ runtime->periods = params_periods(params);
+ runtime->buffer_size = params_buffer_size(params);
+ runtime->tick_time = params_tick_time(params);
+ runtime->info = params->info;
+ runtime->rate_num = params->rate_num;
+ runtime->rate_den = params->rate_den;
+
+ bits = snd_pcm_format_physical_width(runtime->format);
+ runtime->sample_bits = bits;
+ bits *= runtime->channels;
+ runtime->frame_bits = bits;
+ frames = 1;
+ while (bits % 8 != 0) {
+ bits *= 2;
+ frames *= 2;
+ }
+ runtime->byte_align = bits / 8;
+ runtime->min_align = frames;
+
+ /* Default sw params */
+ runtime->tstamp_mode = SNDRV_PCM_TSTAMP_NONE;
+ runtime->period_step = 1;
+ runtime->sleep_min = 0;
+ runtime->control->avail_min = runtime->period_size;
+ runtime->xfer_align = runtime->period_size;
+ runtime->start_threshold = 1;
+ runtime->stop_threshold = runtime->buffer_size;
+ runtime->silence_threshold = 0;
+ runtime->silence_size = 0;
+ runtime->boundary = runtime->buffer_size;
+ while (runtime->boundary * 2 <= LONG_MAX - runtime->buffer_size)
+ runtime->boundary *= 2;
+
+ snd_pcm_timer_resolution_change(substream);
+ runtime->status->state = SNDRV_PCM_STATE_SETUP;
+
+ remove_acceptable_latency(substream->latency_id);
+ if ((usecs = period_to_usecs(runtime)) >= 0)
+ set_acceptable_latency(substream->latency_id, usecs);
+ return 0;
+ _error:
+ /* hardware might be unuseable from this time,
+ so we force application to retry to set
+ the correct hardware parameter settings */
+ runtime->status->state = SNDRV_PCM_STATE_OPEN;
+ if (substream->ops->hw_free != NULL)
+ substream->ops->hw_free(substream);
+ return err;
+}
+
+static int snd_pcm_hw_params_user(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params __user * _params)
+{
+ struct snd_pcm_hw_params *params;
+ int err;
+
+ params = kmalloc(sizeof(*params), GFP_KERNEL);
+ if (!params) {
+ err = -ENOMEM;
+ goto out;
+ }
+ if (copy_from_user(params, _params, sizeof(*params))) {
+ err = -EFAULT;
+ goto out;
+ }
+ err = snd_pcm_hw_params(substream, params);
+ if (copy_to_user(_params, params, sizeof(*params))) {
+ if (!err)
+ err = -EFAULT;
+ }
+out:
+ kfree(params);
+ return err;
+}
+
+static int snd_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime;
+ int result = 0;
+
+ snd_assert(substream != NULL, return -ENXIO);
+ runtime = substream->runtime;
+ snd_assert(runtime != NULL, return -ENXIO);
+ snd_pcm_stream_lock_irq(substream);
+ switch (runtime->status->state) {
+ case SNDRV_PCM_STATE_SETUP:
+ case SNDRV_PCM_STATE_PREPARED:
+ break;
+ default:
+ snd_pcm_stream_unlock_irq(substream);
+ return -EBADFD;
+ }
+ snd_pcm_stream_unlock_irq(substream);
+ if (atomic_read(&substream->mmap_count))
+ return -EBADFD;
+ if (substream->ops->hw_free)
+ result = substream->ops->hw_free(substream);
+ runtime->status->state = SNDRV_PCM_STATE_OPEN;
+ remove_acceptable_latency(substream->latency_id);
+ return result;
+}
+
+static int snd_pcm_sw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_sw_params *params)
+{
+ struct snd_pcm_runtime *runtime;
+
+ snd_assert(substream != NULL, return -ENXIO);
+ runtime = substream->runtime;
+ snd_assert(runtime != NULL, return -ENXIO);
+ snd_pcm_stream_lock_irq(substream);
+ if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+ snd_pcm_stream_unlock_irq(substream);
+ return -EBADFD;
+ }
+ snd_pcm_stream_unlock_irq(substream);
+
+ if (params->tstamp_mode > SNDRV_PCM_TSTAMP_LAST)
+ return -EINVAL;
+ if (params->avail_min == 0)
+ return -EINVAL;
+ if (params->xfer_align == 0 ||
+ params->xfer_align % runtime->min_align != 0)
+ return -EINVAL;
+ if (params->silence_size >= runtime->boundary) {
+ if (params->silence_threshold != 0)
+ return -EINVAL;
+ } else {
+ if (params->silence_size > params->silence_threshold)
+ return -EINVAL;
+ if (params->silence_threshold > runtime->buffer_size)
+ return -EINVAL;
+ }
+ snd_pcm_stream_lock_irq(substream);
+ runtime->tstamp_mode = params->tstamp_mode;
+ runtime->sleep_min = params->sleep_min;
+ runtime->period_step = params->period_step;
+ runtime->control->avail_min = params->avail_min;
+ runtime->start_threshold = params->start_threshold;
+ runtime->stop_threshold = params->stop_threshold;
+ runtime->silence_threshold = params->silence_threshold;
+ runtime->silence_size = params->silence_size;
+ runtime->xfer_align = params->xfer_align;
+ params->boundary = runtime->boundary;
+ if (snd_pcm_running(substream)) {
+ if (runtime->sleep_min)
+ snd_pcm_tick_prepare(substream);
+ else
+ snd_pcm_tick_set(substream, 0);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+ runtime->silence_size > 0)
+ snd_pcm_playback_silence(substream, ULONG_MAX);
+ wake_up(&runtime->sleep);
+ }
+ snd_pcm_stream_unlock_irq(substream);
+ return 0;
+}
+
+static int snd_pcm_sw_params_user(struct snd_pcm_substream *substream,
+ struct snd_pcm_sw_params __user * _params)
+{
+ struct snd_pcm_sw_params params;
+ int err;
+ if (copy_from_user(&params, _params, sizeof(params)))
+ return -EFAULT;
+ err = snd_pcm_sw_params(substream, &params);
+ if (copy_to_user(_params, &params, sizeof(params)))
+ return -EFAULT;
+ return err;
+}
+
+int snd_pcm_status(struct snd_pcm_substream *substream,
+ struct snd_pcm_status *status)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ snd_pcm_stream_lock_irq(substream);
+ status->state = runtime->status->state;
+ status->suspended_state = runtime->status->suspended_state;
+ if (status->state == SNDRV_PCM_STATE_OPEN)
+ goto _end;
+ status->trigger_tstamp = runtime->trigger_tstamp;
+ if (snd_pcm_running(substream)) {
+ snd_pcm_update_hw_ptr(substream);
+ if (runtime->tstamp_mode & SNDRV_PCM_TSTAMP_MMAP)
+ status->tstamp = runtime->status->tstamp;
+ else
+ getnstimeofday(&status->tstamp);
+ } else
+ getnstimeofday(&status->tstamp);
+ status->appl_ptr = runtime->control->appl_ptr;
+ status->hw_ptr = runtime->status->hw_ptr;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ status->avail = snd_pcm_playback_avail(runtime);
+ if (runtime->status->state == SNDRV_PCM_STATE_RUNNING ||
+ runtime->status->state == SNDRV_PCM_STATE_DRAINING)
+ status->delay = runtime->buffer_size - status->avail;
+ else
+ status->delay = 0;
+ } else {
+ status->avail = snd_pcm_capture_avail(runtime);
+ if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
+ status->delay = status->avail;
+ else
+ status->delay = 0;
+ }
+ status->avail_max = runtime->avail_max;
+ status->overrange = runtime->overrange;
+ runtime->avail_max = 0;
+ runtime->overrange = 0;
+ _end:
+ snd_pcm_stream_unlock_irq(substream);
+ return 0;
+}
+
+static int snd_pcm_status_user(struct snd_pcm_substream *substream,
+ struct snd_pcm_status __user * _status)
+{
+ struct snd_pcm_status status;
+ struct snd_pcm_runtime *runtime;
+ int res;
+
+ snd_assert(substream != NULL, return -ENXIO);
+ runtime = substream->runtime;
+ memset(&status, 0, sizeof(status));
+ res = snd_pcm_status(substream, &status);
+ if (res < 0)
+ return res;
+ if (copy_to_user(_status, &status, sizeof(status)))
+ return -EFAULT;
+ return 0;
+}
+
+static int snd_pcm_channel_info(struct snd_pcm_substream *substream,
+ struct snd_pcm_channel_info * info)
+{
+ struct snd_pcm_runtime *runtime;
+ unsigned int channel;
+
+ snd_assert(substream != NULL, return -ENXIO);
+ channel = info->channel;
+ runtime = substream->runtime;
+ snd_pcm_stream_lock_irq(substream);
+ if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+ snd_pcm_stream_unlock_irq(substream);
+ return -EBADFD;
+ }
+ snd_pcm_stream_unlock_irq(substream);
+ if (channel >= runtime->channels)
+ return -EINVAL;
+ memset(info, 0, sizeof(*info));
+ info->channel = channel;
+ return substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_CHANNEL_INFO, info);
+}
+
+static int snd_pcm_channel_info_user(struct snd_pcm_substream *substream,
+ struct snd_pcm_channel_info __user * _info)
+{
+ struct snd_pcm_channel_info info;
+ int res;
+
+ if (copy_from_user(&info, _info, sizeof(info)))
+ return -EFAULT;
+ res = snd_pcm_channel_info(substream, &info);
+ if (res < 0)
+ return res;
+ if (copy_to_user(_info, &info, sizeof(info)))
+ return -EFAULT;
+ return 0;
+}
+
+static void snd_pcm_trigger_tstamp(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ if (runtime->trigger_master == NULL)
+ return;
+ if (runtime->trigger_master == substream) {
+ getnstimeofday(&runtime->trigger_tstamp);
+ } else {
+ snd_pcm_trigger_tstamp(runtime->trigger_master);
+ runtime->trigger_tstamp = runtime->trigger_master->runtime->trigger_tstamp;
+ }
+ runtime->trigger_master = NULL;
+}
+
+struct action_ops {
+ int (*pre_action)(struct snd_pcm_substream *substream, int state);
+ int (*do_action)(struct snd_pcm_substream *substream, int state);
+ void (*undo_action)(struct snd_pcm_substream *substream, int state);
+ void (*post_action)(struct snd_pcm_substream *substream, int state);
+};
+
+/*
+ * this functions is core for handling of linked stream
+ * Note: the stream state might be changed also on failure
+ * Note2: call with calling stream lock + link lock
+ */
+static int snd_pcm_action_group(struct action_ops *ops,
+ struct snd_pcm_substream *substream,
+ int state, int do_lock)
+{
+ struct snd_pcm_substream *s = NULL;
+ struct snd_pcm_substream *s1;
+ int res = 0;
+
+ snd_pcm_group_for_each_entry(s, substream) {
+ if (do_lock && s != substream)
+ spin_lock_nested(&s->self_group.lock,
+ SINGLE_DEPTH_NESTING);
+ res = ops->pre_action(s, state);
+ if (res < 0)
+ goto _unlock;
+ }
+ snd_pcm_group_for_each_entry(s, substream) {
+ res = ops->do_action(s, state);
+ if (res < 0) {
+ if (ops->undo_action) {
+ snd_pcm_group_for_each_entry(s1, substream) {
+ if (s1 == s) /* failed stream */
+ break;
+ ops->undo_action(s1, state);
+ }
+ }
+ s = NULL; /* unlock all */
+ goto _unlock;
+ }
+ }
+ snd_pcm_group_for_each_entry(s, substream) {
+ ops->post_action(s, state);
+ }
+ _unlock:
+ if (do_lock) {
+ /* unlock streams */
+ snd_pcm_group_for_each_entry(s1, substream) {
+ if (s1 != substream)
+ spin_unlock(&s1->self_group.lock);
+ if (s1 == s) /* end */
+ break;
+ }
+ }
+ return res;
+}
+
+/*
+ * Note: call with stream lock
+ */
+static int snd_pcm_action_single(struct action_ops *ops,
+ struct snd_pcm_substream *substream,
+ int state)
+{
+ int res;
+
+ res = ops->pre_action(substream, state);
+ if (res < 0)
+ return res;
+ res = ops->do_action(substream, state);
+ if (res == 0)
+ ops->post_action(substream, state);
+ else if (ops->undo_action)
+ ops->undo_action(substream, state);
+ return res;
+}
+
+/*
+ * Note: call with stream lock
+ */
+static int snd_pcm_action(struct action_ops *ops,
+ struct snd_pcm_substream *substream,
+ int state)
+{
+ int res;
+
+ if (snd_pcm_stream_linked(substream)) {
+ if (!spin_trylock(&substream->group->lock)) {
+ spin_unlock(&substream->self_group.lock);
+ spin_lock(&substream->group->lock);
+ spin_lock(&substream->self_group.lock);
+ }
+ res = snd_pcm_action_group(ops, substream, state, 1);
+ spin_unlock(&substream->group->lock);
+ } else {
+ res = snd_pcm_action_single(ops, substream, state);
+ }
+ return res;
+}
+
+/*
+ * Note: don't use any locks before
+ */
+static int snd_pcm_action_lock_irq(struct action_ops *ops,
+ struct snd_pcm_substream *substream,
+ int state)
+{
+ int res;
+
+ read_lock_irq(&snd_pcm_link_rwlock);
+ if (snd_pcm_stream_linked(substream)) {
+ spin_lock(&substream->group->lock);
+ spin_lock(&substream->self_group.lock);
+ res = snd_pcm_action_group(ops, substream, state, 1);
+ spin_unlock(&substream->self_group.lock);
+ spin_unlock(&substream->group->lock);
+ } else {
+ spin_lock(&substream->self_group.lock);
+ res = snd_pcm_action_single(ops, substream, state);
+ spin_unlock(&substream->self_group.lock);
+ }
+ read_unlock_irq(&snd_pcm_link_rwlock);
+ return res;
+}
+
+/*
+ */
+static int snd_pcm_action_nonatomic(struct action_ops *ops,
+ struct snd_pcm_substream *substream,
+ int state)
+{
+ int res;
+
+ down_read(&snd_pcm_link_rwsem);
+ if (snd_pcm_stream_linked(substream))
+ res = snd_pcm_action_group(ops, substream, state, 0);
+ else
+ res = snd_pcm_action_single(ops, substream, state);
+ up_read(&snd_pcm_link_rwsem);
+ return res;
+}
+
+/*
+ * start callbacks
+ */
+static int snd_pcm_pre_start(struct snd_pcm_substream *substream, int state)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ if (runtime->status->state != SNDRV_PCM_STATE_PREPARED)
+ return -EBADFD;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+ !snd_pcm_playback_data(substream))
+ return -EPIPE;
+ runtime->trigger_master = substream;
+ return 0;
+}
+
+static int snd_pcm_do_start(struct snd_pcm_substream *substream, int state)
+{
+ if (substream->runtime->trigger_master != substream)
+ return 0;
+ return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_START);
+}
+
+static void snd_pcm_undo_start(struct snd_pcm_substream *substream, int state)
+{
+ if (substream->runtime->trigger_master == substream)
+ substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_STOP);
+}
+
+static void snd_pcm_post_start(struct snd_pcm_substream *substream, int state)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_pcm_trigger_tstamp(substream);
+ runtime->status->state = state;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+ runtime->silence_size > 0)
+ snd_pcm_playback_silence(substream, ULONG_MAX);
+ if (runtime->sleep_min)
+ snd_pcm_tick_prepare(substream);
+ if (substream->timer)
+ snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTART,
+ &runtime->trigger_tstamp);
+}
+
+static struct action_ops snd_pcm_action_start = {
+ .pre_action = snd_pcm_pre_start,
+ .do_action = snd_pcm_do_start,
+ .undo_action = snd_pcm_undo_start,
+ .post_action = snd_pcm_post_start
+};
+
+/**
+ * snd_pcm_start
+ * @substream: the PCM substream instance
+ *
+ * Start all linked streams.
+ */
+int snd_pcm_start(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_action(&snd_pcm_action_start, substream,
+ SNDRV_PCM_STATE_RUNNING);
+}
+
+/*
+ * stop callbacks
+ */
+static int snd_pcm_pre_stop(struct snd_pcm_substream *substream, int state)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ return -EBADFD;
+ runtime->trigger_master = substream;
+ return 0;
+}
+
+static int snd_pcm_do_stop(struct snd_pcm_substream *substream, int state)
+{
+ if (substream->runtime->trigger_master == substream &&
+ snd_pcm_running(substream))
+ substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_STOP);
+ return 0; /* unconditonally stop all substreams */
+}
+
+static void snd_pcm_post_stop(struct snd_pcm_substream *substream, int state)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ if (runtime->status->state != state) {
+ snd_pcm_trigger_tstamp(substream);
+ if (substream->timer)
+ snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTOP,
+ &runtime->trigger_tstamp);
+ runtime->status->state = state;
+ snd_pcm_tick_set(substream, 0);
+ }
+ wake_up(&runtime->sleep);
+}
+
+static struct action_ops snd_pcm_action_stop = {
+ .pre_action = snd_pcm_pre_stop,
+ .do_action = snd_pcm_do_stop,
+ .post_action = snd_pcm_post_stop
+};
+
+/**
+ * snd_pcm_stop
+ * @substream: the PCM substream instance
+ * @state: PCM state after stopping the stream
+ *
+ * Try to stop all running streams in the substream group.
+ * The state of each stream is changed to the given value after that unconditionally.
+ */
+int snd_pcm_stop(struct snd_pcm_substream *substream, int state)
+{
+ return snd_pcm_action(&snd_pcm_action_stop, substream, state);
+}
+
+EXPORT_SYMBOL(snd_pcm_stop);
+
+/**
+ * snd_pcm_drain_done
+ * @substream: the PCM substream
+ *
+ * Stop the DMA only when the given stream is playback.
+ * The state is changed to SETUP.
+ * Unlike snd_pcm_stop(), this affects only the given stream.
+ */
+int snd_pcm_drain_done(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_action_single(&snd_pcm_action_stop, substream,
+ SNDRV_PCM_STATE_SETUP);
+}
+
+/*
+ * pause callbacks
+ */
+static int snd_pcm_pre_pause(struct snd_pcm_substream *substream, int push)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ if (!(runtime->info & SNDRV_PCM_INFO_PAUSE))
+ return -ENOSYS;
+ if (push) {
+ if (runtime->status->state != SNDRV_PCM_STATE_RUNNING)
+ return -EBADFD;
+ } else if (runtime->status->state != SNDRV_PCM_STATE_PAUSED)
+ return -EBADFD;
+ runtime->trigger_master = substream;
+ return 0;
+}
+
+static int snd_pcm_do_pause(struct snd_pcm_substream *substream, int push)
+{
+ if (substream->runtime->trigger_master != substream)
+ return 0;
+ return substream->ops->trigger(substream,
+ push ? SNDRV_PCM_TRIGGER_PAUSE_PUSH :
+ SNDRV_PCM_TRIGGER_PAUSE_RELEASE);
+}
+
+static void snd_pcm_undo_pause(struct snd_pcm_substream *substream, int push)
+{
+ if (substream->runtime->trigger_master == substream)
+ substream->ops->trigger(substream,
+ push ? SNDRV_PCM_TRIGGER_PAUSE_RELEASE :
+ SNDRV_PCM_TRIGGER_PAUSE_PUSH);
+}
+
+static void snd_pcm_post_pause(struct snd_pcm_substream *substream, int push)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_pcm_trigger_tstamp(substream);
+ if (push) {
+ runtime->status->state = SNDRV_PCM_STATE_PAUSED;
+ if (substream->timer)
+ snd_timer_notify(substream->timer,
+ SNDRV_TIMER_EVENT_MPAUSE,
+ &runtime->trigger_tstamp);
+ snd_pcm_tick_set(substream, 0);
+ wake_up(&runtime->sleep);
+ } else {
+ runtime->status->state = SNDRV_PCM_STATE_RUNNING;
+ if (runtime->sleep_min)
+ snd_pcm_tick_prepare(substream);
+ if (substream->timer)
+ snd_timer_notify(substream->timer,
+ SNDRV_TIMER_EVENT_MCONTINUE,
+ &runtime->trigger_tstamp);
+ }
+}
+
+static struct action_ops snd_pcm_action_pause = {
+ .pre_action = snd_pcm_pre_pause,
+ .do_action = snd_pcm_do_pause,
+ .undo_action = snd_pcm_undo_pause,
+ .post_action = snd_pcm_post_pause
+};
+
+/*
+ * Push/release the pause for all linked streams.
+ */
+static int snd_pcm_pause(struct snd_pcm_substream *substream, int push)
+{
+ return snd_pcm_action(&snd_pcm_action_pause, substream, push);
+}
+
+#ifdef CONFIG_PM
+/* suspend */
+
+static int snd_pcm_pre_suspend(struct snd_pcm_substream *substream, int state)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED)
+ return -EBUSY;
+ runtime->trigger_master = substream;
+ return 0;
+}
+
+static int snd_pcm_do_suspend(struct snd_pcm_substream *substream, int state)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ if (runtime->trigger_master != substream)
+ return 0;
+ if (! snd_pcm_running(substream))
+ return 0;
+ substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_SUSPEND);
+ return 0; /* suspend unconditionally */
+}
+
+static void snd_pcm_post_suspend(struct snd_pcm_substream *substream, int state)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_pcm_trigger_tstamp(substream);
+ if (substream->timer)
+ snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSUSPEND,
+ &runtime->trigger_tstamp);
+ runtime->status->suspended_state = runtime->status->state;
+ runtime->status->state = SNDRV_PCM_STATE_SUSPENDED;
+ snd_pcm_tick_set(substream, 0);
+ wake_up(&runtime->sleep);
+}
+
+static struct action_ops snd_pcm_action_suspend = {
+ .pre_action = snd_pcm_pre_suspend,
+ .do_action = snd_pcm_do_suspend,
+ .post_action = snd_pcm_post_suspend
+};
+
+/**
+ * snd_pcm_suspend
+ * @substream: the PCM substream
+ *
+ * Trigger SUSPEND to all linked streams.
+ * After this call, all streams are changed to SUSPENDED state.
+ */
+int snd_pcm_suspend(struct snd_pcm_substream *substream)
+{
+ int err;
+ unsigned long flags;
+
+ if (! substream)
+ return 0;
+
+ snd_pcm_stream_lock_irqsave(substream, flags);
+ err = snd_pcm_action(&snd_pcm_action_suspend, substream, 0);
+ snd_pcm_stream_unlock_irqrestore(substream, flags);
+ return err;
+}
+
+EXPORT_SYMBOL(snd_pcm_suspend);
+
+/**
+ * snd_pcm_suspend_all
+ * @pcm: the PCM instance
+ *
+ * Trigger SUSPEND to all substreams in the given pcm.
+ * After this call, all streams are changed to SUSPENDED state.
+ */
+int snd_pcm_suspend_all(struct snd_pcm *pcm)
+{
+ struct snd_pcm_substream *substream;
+ int stream, err = 0;
+
+ if (! pcm)
+ return 0;
+
+ for (stream = 0; stream < 2; stream++) {
+ for (substream = pcm->streams[stream].substream;
+ substream; substream = substream->next) {
+ /* FIXME: the open/close code should lock this as well */
+ if (substream->runtime == NULL)
+ continue;
+ err = snd_pcm_suspend(substream);
+ if (err < 0 && err != -EBUSY)
+ return err;
+ }
+ }
+ return 0;
+}
+
+EXPORT_SYMBOL(snd_pcm_suspend_all);
+
+/* resume */
+
+static int snd_pcm_pre_resume(struct snd_pcm_substream *substream, int state)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ if (!(runtime->info & SNDRV_PCM_INFO_RESUME))
+ return -ENOSYS;
+ runtime->trigger_master = substream;
+ return 0;
+}
+
+static int snd_pcm_do_resume(struct snd_pcm_substream *substream, int state)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ if (runtime->trigger_master != substream)
+ return 0;
+ /* DMA not running previously? */
+ if (runtime->status->suspended_state != SNDRV_PCM_STATE_RUNNING &&
+ (runtime->status->suspended_state != SNDRV_PCM_STATE_DRAINING ||
+ substream->stream != SNDRV_PCM_STREAM_PLAYBACK))
+ return 0;
+ return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_RESUME);
+}
+
+static void snd_pcm_undo_resume(struct snd_pcm_substream *substream, int state)
+{
+ if (substream->runtime->trigger_master == substream &&
+ snd_pcm_running(substream))
+ substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_SUSPEND);
+}
+
+static void snd_pcm_post_resume(struct snd_pcm_substream *substream, int state)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_pcm_trigger_tstamp(substream);
+ if (substream->timer)
+ snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MRESUME,
+ &runtime->trigger_tstamp);
+ runtime->status->state = runtime->status->suspended_state;
+ if (runtime->sleep_min)
+ snd_pcm_tick_prepare(substream);
+}
+
+static struct action_ops snd_pcm_action_resume = {
+ .pre_action = snd_pcm_pre_resume,
+ .do_action = snd_pcm_do_resume,
+ .undo_action = snd_pcm_undo_resume,
+ .post_action = snd_pcm_post_resume
+};
+
+static int snd_pcm_resume(struct snd_pcm_substream *substream)
+{
+ struct snd_card *card = substream->pcm->card;
+ int res;
+
+ snd_power_lock(card);
+ if ((res = snd_power_wait(card, SNDRV_CTL_POWER_D0)) >= 0)
+ res = snd_pcm_action_lock_irq(&snd_pcm_action_resume, substream, 0);
+ snd_power_unlock(card);
+ return res;
+}
+
+#else
+
+static int snd_pcm_resume(struct snd_pcm_substream *substream)
+{
+ return -ENOSYS;
+}
+
+#endif /* CONFIG_PM */
+
+/*
+ * xrun ioctl
+ *
+ * Change the RUNNING stream(s) to XRUN state.
+ */
+static int snd_pcm_xrun(struct snd_pcm_substream *substream)
+{
+ struct snd_card *card = substream->pcm->card;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int result;
+
+ snd_power_lock(card);
+ if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
+ result = snd_power_wait(card, SNDRV_CTL_POWER_D0);
+ if (result < 0)
+ goto _unlock;
+ }
+
+ snd_pcm_stream_lock_irq(substream);
+ switch (runtime->status->state) {
+ case SNDRV_PCM_STATE_XRUN:
+ result = 0; /* already there */
+ break;
+ case SNDRV_PCM_STATE_RUNNING:
+ result = snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+ break;
+ default:
+ result = -EBADFD;
+ }
+ snd_pcm_stream_unlock_irq(substream);
+ _unlock:
+ snd_power_unlock(card);
+ return result;
+}
+
+/*
+ * reset ioctl
+ */
+static int snd_pcm_pre_reset(struct snd_pcm_substream *substream, int state)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ switch (runtime->status->state) {
+ case SNDRV_PCM_STATE_RUNNING:
+ case SNDRV_PCM_STATE_PREPARED:
+ case SNDRV_PCM_STATE_PAUSED:
+ case SNDRV_PCM_STATE_SUSPENDED:
+ return 0;
+ default:
+ return -EBADFD;
+ }
+}
+
+static int snd_pcm_do_reset(struct snd_pcm_substream *substream, int state)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err = substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_RESET, NULL);
+ if (err < 0)
+ return err;
+ // snd_assert(runtime->status->hw_ptr < runtime->buffer_size, );
+ runtime->hw_ptr_base = 0;
+ runtime->hw_ptr_interrupt = runtime->status->hw_ptr -
+ runtime->status->hw_ptr % runtime->period_size;
+ runtime->silence_start = runtime->status->hw_ptr;
+ runtime->silence_filled = 0;
+ return 0;
+}
+
+static void snd_pcm_post_reset(struct snd_pcm_substream *substream, int state)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ runtime->control->appl_ptr = runtime->status->hw_ptr;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+ runtime->silence_size > 0)
+ snd_pcm_playback_silence(substream, ULONG_MAX);
+}
+
+static struct action_ops snd_pcm_action_reset = {
+ .pre_action = snd_pcm_pre_reset,
+ .do_action = snd_pcm_do_reset,
+ .post_action = snd_pcm_post_reset
+};
+
+static int snd_pcm_reset(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_action_nonatomic(&snd_pcm_action_reset, substream, 0);
+}
+
+/*
+ * prepare ioctl
+ */
+/* we use the second argument for updating f_flags */
+static int snd_pcm_pre_prepare(struct snd_pcm_substream *substream,
+ int f_flags)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ if (runtime->status->state == SNDRV_PCM_STATE_OPEN ||
+ runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED)
+ return -EBADFD;
+ if (snd_pcm_running(substream))
+ return -EBUSY;
+ substream->f_flags = f_flags;
+ return 0;
+}
+
+static int snd_pcm_do_prepare(struct snd_pcm_substream *substream, int state)
+{
+ int err;
+ err = substream->ops->prepare(substream);
+ if (err < 0)
+ return err;
+ return snd_pcm_do_reset(substream, 0);
+}
+
+static void snd_pcm_post_prepare(struct snd_pcm_substream *substream, int state)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ runtime->control->appl_ptr = runtime->status->hw_ptr;
+ runtime->status->state = SNDRV_PCM_STATE_PREPARED;
+}
+
+static struct action_ops snd_pcm_action_prepare = {
+ .pre_action = snd_pcm_pre_prepare,
+ .do_action = snd_pcm_do_prepare,
+ .post_action = snd_pcm_post_prepare
+};
+
+/**
+ * snd_pcm_prepare
+ * @substream: the PCM substream instance
+ * @file: file to refer f_flags
+ *
+ * Prepare the PCM substream to be triggerable.
+ */
+static int snd_pcm_prepare(struct snd_pcm_substream *substream,
+ struct file *file)
+{
+ int res;
+ struct snd_card *card = substream->pcm->card;
+ int f_flags;
+
+ if (file)
+ f_flags = file->f_flags;
+ else
+ f_flags = substream->f_flags;
+
+ snd_power_lock(card);
+ if ((res = snd_power_wait(card, SNDRV_CTL_POWER_D0)) >= 0)
+ res = snd_pcm_action_nonatomic(&snd_pcm_action_prepare,
+ substream, f_flags);
+ snd_power_unlock(card);
+ return res;
+}
+
+/*
+ * drain ioctl
+ */
+
+static int snd_pcm_pre_drain_init(struct snd_pcm_substream *substream, int state)
+{
+ if (substream->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ substream->runtime->trigger_master = substream;
+ return 0;
+}
+
+static int snd_pcm_do_drain_init(struct snd_pcm_substream *substream, int state)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (runtime->status->state) {
+ case SNDRV_PCM_STATE_PREPARED:
+ /* start playback stream if possible */
+ if (! snd_pcm_playback_empty(substream)) {
+ snd_pcm_do_start(substream, SNDRV_PCM_STATE_DRAINING);
+ snd_pcm_post_start(substream, SNDRV_PCM_STATE_DRAINING);
+ }
+ break;
+ case SNDRV_PCM_STATE_RUNNING:
+ runtime->status->state = SNDRV_PCM_STATE_DRAINING;
+ break;
+ default:
+ break;
+ }
+ } else {
+ /* stop running stream */
+ if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) {
+ int state = snd_pcm_capture_avail(runtime) > 0 ?
+ SNDRV_PCM_STATE_DRAINING : SNDRV_PCM_STATE_SETUP;
+ snd_pcm_do_stop(substream, state);
+ snd_pcm_post_stop(substream, state);
+ }
+ }
+ return 0;
+}
+
+static void snd_pcm_post_drain_init(struct snd_pcm_substream *substream, int state)
+{
+}
+
+static struct action_ops snd_pcm_action_drain_init = {
+ .pre_action = snd_pcm_pre_drain_init,
+ .do_action = snd_pcm_do_drain_init,
+ .post_action = snd_pcm_post_drain_init
+};
+
+struct drain_rec {
+ struct snd_pcm_substream *substream;
+ wait_queue_t wait;
+ snd_pcm_uframes_t stop_threshold;
+};
+
+static int snd_pcm_drop(struct snd_pcm_substream *substream);
+
+/*
+ * Drain the stream(s).
+ * When the substream is linked, sync until the draining of all playback streams
+ * is finished.
+ * After this call, all streams are supposed to be either SETUP or DRAINING
+ * (capture only) state.
+ */
+static int snd_pcm_drain(struct snd_pcm_substream *substream)
+{
+ struct snd_card *card;
+ struct snd_pcm_runtime *runtime;
+ struct snd_pcm_substream *s;
+ int result = 0;
+ int i, num_drecs;
+ struct drain_rec *drec, drec_tmp, *d;
+
+ snd_assert(substream != NULL, return -ENXIO);
+ card = substream->pcm->card;
+ runtime = substream->runtime;
+
+ if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ return -EBADFD;
+
+ snd_power_lock(card);
+ if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
+ result = snd_power_wait(card, SNDRV_CTL_POWER_D0);
+ if (result < 0) {
+ snd_power_unlock(card);
+ return result;
+ }
+ }
+
+ /* allocate temporary record for drain sync */
+ down_read(&snd_pcm_link_rwsem);
+ if (snd_pcm_stream_linked(substream)) {
+ drec = kmalloc(substream->group->count * sizeof(*drec), GFP_KERNEL);
+ if (! drec) {
+ up_read(&snd_pcm_link_rwsem);
+ snd_power_unlock(card);
+ return -ENOMEM;
+ }
+ } else
+ drec = &drec_tmp;
+
+ /* count only playback streams */
+ num_drecs = 0;
+ snd_pcm_group_for_each_entry(s, substream) {
+ runtime = s->runtime;
+ if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ d = &drec[num_drecs++];
+ d->substream = s;
+ init_waitqueue_entry(&d->wait, current);
+ add_wait_queue(&runtime->sleep, &d->wait);
+ /* stop_threshold fixup to avoid endless loop when
+ * stop_threshold > buffer_size
+ */
+ d->stop_threshold = runtime->stop_threshold;
+ if (runtime->stop_threshold > runtime->buffer_size)
+ runtime->stop_threshold = runtime->buffer_size;
+ }
+ }
+ up_read(&snd_pcm_link_rwsem);
+
+ snd_pcm_stream_lock_irq(substream);
+ /* resume pause */
+ if (substream->runtime->status->state == SNDRV_PCM_STATE_PAUSED)
+ snd_pcm_pause(substream, 0);
+
+ /* pre-start/stop - all running streams are changed to DRAINING state */
+ result = snd_pcm_action(&snd_pcm_action_drain_init, substream, 0);
+ if (result < 0) {
+ snd_pcm_stream_unlock_irq(substream);
+ goto _error;
+ }
+
+ for (;;) {
+ long tout;
+ if (signal_pending(current)) {
+ result = -ERESTARTSYS;
+ break;
+ }
+ /* all finished? */
+ for (i = 0; i < num_drecs; i++) {
+ runtime = drec[i].substream->runtime;
+ if (runtime->status->state == SNDRV_PCM_STATE_DRAINING)
+ break;
+ }
+ if (i == num_drecs)
+ break; /* yes, all drained */
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ snd_pcm_stream_unlock_irq(substream);
+ snd_power_unlock(card);
+ tout = schedule_timeout(10 * HZ);
+ snd_power_lock(card);
+ snd_pcm_stream_lock_irq(substream);
+ if (tout == 0) {
+ if (substream->runtime->status->state == SNDRV_PCM_STATE_SUSPENDED)
+ result = -ESTRPIPE;
+ else {
+ snd_printd("playback drain error (DMA or IRQ trouble?)\n");
+ snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
+ result = -EIO;
+ }
+ break;
+ }
+ }
+
+ snd_pcm_stream_unlock_irq(substream);
+
+ _error:
+ for (i = 0; i < num_drecs; i++) {
+ d = &drec[i];
+ runtime = d->substream->runtime;
+ remove_wait_queue(&runtime->sleep, &d->wait);
+ runtime->stop_threshold = d->stop_threshold;
+ }
+
+ if (drec != &drec_tmp)
+ kfree(drec);
+ snd_power_unlock(card);
+
+ return result;
+}
+
+/*
+ * drop ioctl
+ *
+ * Immediately put all linked substreams into SETUP state.
+ */
+static int snd_pcm_drop(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime;
+ struct snd_card *card;
+ int result = 0;
+
+ snd_assert(substream != NULL, return -ENXIO);
+ runtime = substream->runtime;
+ card = substream->pcm->card;
+
+ if (runtime->status->state == SNDRV_PCM_STATE_OPEN ||
+ runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED)
+ return -EBADFD;
+
+ snd_power_lock(card);
+ if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
+ result = snd_power_wait(card, SNDRV_CTL_POWER_D0);
+ if (result < 0)
+ goto _unlock;
+ }
+
+ snd_pcm_stream_lock_irq(substream);
+ /* resume pause */
+ if (runtime->status->state == SNDRV_PCM_STATE_PAUSED)
+ snd_pcm_pause(substream, 0);
+
+ snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
+ /* runtime->control->appl_ptr = runtime->status->hw_ptr; */
+ snd_pcm_stream_unlock_irq(substream);
+ _unlock:
+ snd_power_unlock(card);
+ return result;
+}
+
+
+/* WARNING: Don't forget to fput back the file */
+static struct file *snd_pcm_file_fd(int fd)
+{
+ struct file *file;
+ struct inode *inode;
+ unsigned int minor;
+
+ file = fget(fd);
+ if (!file)
+ return NULL;
+ inode = file->f_path.dentry->d_inode;
+ if (!S_ISCHR(inode->i_mode) ||
+ imajor(inode) != snd_major) {
+ fput(file);
+ return NULL;
+ }
+ minor = iminor(inode);
+ if (!snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_PLAYBACK) &&
+ !snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_CAPTURE)) {
+ fput(file);
+ return NULL;
+ }
+ return file;
+}
+
+/*
+ * PCM link handling
+ */
+static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
+{
+ int res = 0;
+ struct file *file;
+ struct snd_pcm_file *pcm_file;
+ struct snd_pcm_substream *substream1;
+
+ file = snd_pcm_file_fd(fd);
+ if (!file)
+ return -EBADFD;
+ pcm_file = file->private_data;
+ substream1 = pcm_file->substream;
+ down_write(&snd_pcm_link_rwsem);
+ write_lock_irq(&snd_pcm_link_rwlock);
+ if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN ||
+ substream->runtime->status->state != substream1->runtime->status->state) {
+ res = -EBADFD;
+ goto _end;
+ }
+ if (snd_pcm_stream_linked(substream1)) {
+ res = -EALREADY;
+ goto _end;
+ }
+ if (!snd_pcm_stream_linked(substream)) {
+ substream->group = kmalloc(sizeof(struct snd_pcm_group), GFP_ATOMIC);
+ if (substream->group == NULL) {
+ res = -ENOMEM;
+ goto _end;
+ }
+ spin_lock_init(&substream->group->lock);
+ INIT_LIST_HEAD(&substream->group->substreams);
+ list_add_tail(&substream->link_list, &substream->group->substreams);
+ substream->group->count = 1;
+ }
+ list_add_tail(&substream1->link_list, &substream->group->substreams);
+ substream->group->count++;
+ substream1->group = substream->group;
+ _end:
+ write_unlock_irq(&snd_pcm_link_rwlock);
+ up_write(&snd_pcm_link_rwsem);
+ fput(file);
+ return res;
+}
+
+static void relink_to_local(struct snd_pcm_substream *substream)
+{
+ substream->group = &substream->self_group;
+ INIT_LIST_HEAD(&substream->self_group.substreams);
+ list_add_tail(&substream->link_list, &substream->self_group.substreams);
+}
+
+static int snd_pcm_unlink(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_substream *s;
+ int res = 0;
+
+ down_write(&snd_pcm_link_rwsem);
+ write_lock_irq(&snd_pcm_link_rwlock);
+ if (!snd_pcm_stream_linked(substream)) {
+ res = -EALREADY;
+ goto _end;
+ }
+ list_del(&substream->link_list);
+ substream->group->count--;
+ if (substream->group->count == 1) { /* detach the last stream, too */
+ snd_pcm_group_for_each_entry(s, substream) {
+ relink_to_local(s);
+ break;
+ }
+ kfree(substream->group);
+ }
+ relink_to_local(substream);
+ _end:
+ write_unlock_irq(&snd_pcm_link_rwlock);
+ up_write(&snd_pcm_link_rwsem);
+ return res;
+}
+
+/*
+ * hw configurator
+ */
+static int snd_pcm_hw_rule_mul(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval t;
+ snd_interval_mul(hw_param_interval_c(params, rule->deps[0]),
+ hw_param_interval_c(params, rule->deps[1]), &t);
+ return snd_interval_refine(hw_param_interval(params, rule->var), &t);
+}
+
+static int snd_pcm_hw_rule_div(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval t;
+ snd_interval_div(hw_param_interval_c(params, rule->deps[0]),
+ hw_param_interval_c(params, rule->deps[1]), &t);
+ return snd_interval_refine(hw_param_interval(params, rule->var), &t);
+}
+
+static int snd_pcm_hw_rule_muldivk(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval t;
+ snd_interval_muldivk(hw_param_interval_c(params, rule->deps[0]),
+ hw_param_interval_c(params, rule->deps[1]),
+ (unsigned long) rule->private, &t);
+ return snd_interval_refine(hw_param_interval(params, rule->var), &t);
+}
+
+static int snd_pcm_hw_rule_mulkdiv(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval t;
+ snd_interval_mulkdiv(hw_param_interval_c(params, rule->deps[0]),
+ (unsigned long) rule->private,
+ hw_param_interval_c(params, rule->deps[1]), &t);
+ return snd_interval_refine(hw_param_interval(params, rule->var), &t);
+}
+
+static int snd_pcm_hw_rule_format(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ unsigned int k;
+ struct snd_interval *i = hw_param_interval(params, rule->deps[0]);
+ struct snd_mask m;
+ struct snd_mask *mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+ snd_mask_any(&m);
+ for (k = 0; k <= SNDRV_PCM_FORMAT_LAST; ++k) {
+ int bits;
+ if (! snd_mask_test(mask, k))
+ continue;
+ bits = snd_pcm_format_physical_width(k);
+ if (bits <= 0)
+ continue; /* ignore invalid formats */
+ if ((unsigned)bits < i->min || (unsigned)bits > i->max)
+ snd_mask_reset(&m, k);
+ }
+ return snd_mask_refine(mask, &m);
+}
+
+static int snd_pcm_hw_rule_sample_bits(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval t;
+ unsigned int k;
+ t.min = UINT_MAX;
+ t.max = 0;
+ t.openmin = 0;
+ t.openmax = 0;
+ for (k = 0; k <= SNDRV_PCM_FORMAT_LAST; ++k) {
+ int bits;
+ if (! snd_mask_test(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), k))
+ continue;
+ bits = snd_pcm_format_physical_width(k);
+ if (bits <= 0)
+ continue; /* ignore invalid formats */
+ if (t.min > (unsigned)bits)
+ t.min = bits;
+ if (t.max < (unsigned)bits)
+ t.max = bits;
+ }
+ t.integer = 1;
+ return snd_interval_refine(hw_param_interval(params, rule->var), &t);
+}
+
+#if SNDRV_PCM_RATE_5512 != 1 << 0 || SNDRV_PCM_RATE_192000 != 1 << 12
+#error "Change this table"
+#endif
+
+static unsigned int rates[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100,
+ 48000, 64000, 88200, 96000, 176400, 192000 };
+
+const struct snd_pcm_hw_constraint_list snd_pcm_known_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+};
+
+static int snd_pcm_hw_rule_rate(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_pcm_hardware *hw = rule->private;
+ return snd_interval_list(hw_param_interval(params, rule->var),
+ snd_pcm_known_rates.count,
+ snd_pcm_known_rates.list, hw->rates);
+}
+
+static int snd_pcm_hw_rule_buffer_bytes_max(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval t;
+ struct snd_pcm_substream *substream = rule->private;
+ t.min = 0;
+ t.max = substream->buffer_bytes_max;
+ t.openmin = 0;
+ t.openmax = 0;
+ t.integer = 1;
+ return snd_interval_refine(hw_param_interval(params, rule->var), &t);
+}
+
+int snd_pcm_hw_constraints_init(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints;
+ int k, err;
+
+ for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) {
+ snd_mask_any(constrs_mask(constrs, k));
+ }
+
+ for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) {
+ snd_interval_any(constrs_interval(constrs, k));
+ }
+
+ snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_CHANNELS));
+ snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_BUFFER_SIZE));
+ snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_BUFFER_BYTES));
+ snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_SAMPLE_BITS));
+ snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_FRAME_BITS));
+
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
+ snd_pcm_hw_rule_format, NULL,
+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1);
+ if (err < 0)
+ return err;
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+ snd_pcm_hw_rule_sample_bits, NULL,
+ SNDRV_PCM_HW_PARAM_FORMAT,
+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1);
+ if (err < 0)
+ return err;
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+ snd_pcm_hw_rule_div, NULL,
+ SNDRV_PCM_HW_PARAM_FRAME_BITS, SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (err < 0)
+ return err;
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FRAME_BITS,
+ snd_pcm_hw_rule_mul, NULL,
+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS, SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (err < 0)
+ return err;
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FRAME_BITS,
+ snd_pcm_hw_rule_mulkdiv, (void*) 8,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1);
+ if (err < 0)
+ return err;
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FRAME_BITS,
+ snd_pcm_hw_rule_mulkdiv, (void*) 8,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, -1);
+ if (err < 0)
+ return err;
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ snd_pcm_hw_rule_div, NULL,
+ SNDRV_PCM_HW_PARAM_FRAME_BITS, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1);
+ if (err < 0)
+ return err;
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ snd_pcm_hw_rule_mulkdiv, (void*) 1000000,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_PERIOD_TIME, -1);
+ if (err < 0)
+ return err;
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ snd_pcm_hw_rule_mulkdiv, (void*) 1000000,
+ SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_BUFFER_TIME, -1);
+ if (err < 0)
+ return err;
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIODS,
+ snd_pcm_hw_rule_div, NULL,
+ SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1);
+ if (err < 0)
+ return err;
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ snd_pcm_hw_rule_div, NULL,
+ SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_PERIODS, -1);
+ if (err < 0)
+ return err;
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ snd_pcm_hw_rule_mulkdiv, (void*) 8,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1);
+ if (err < 0)
+ return err;
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ snd_pcm_hw_rule_muldivk, (void*) 1000000,
+ SNDRV_PCM_HW_PARAM_PERIOD_TIME, SNDRV_PCM_HW_PARAM_RATE, -1);
+ if (err < 0)
+ return err;
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+ snd_pcm_hw_rule_mul, NULL,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_PERIODS, -1);
+ if (err < 0)
+ return err;
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+ snd_pcm_hw_rule_mulkdiv, (void*) 8,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1);
+ if (err < 0)
+ return err;
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+ snd_pcm_hw_rule_muldivk, (void*) 1000000,
+ SNDRV_PCM_HW_PARAM_BUFFER_TIME, SNDRV_PCM_HW_PARAM_RATE, -1);
+ if (err < 0)
+ return err;
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+ snd_pcm_hw_rule_muldivk, (void*) 8,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1);
+ if (err < 0)
+ return err;
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ snd_pcm_hw_rule_muldivk, (void*) 8,
+ SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1);
+ if (err < 0)
+ return err;
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_TIME,
+ snd_pcm_hw_rule_mulkdiv, (void*) 1000000,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_RATE, -1);
+ if (err < 0)
+ return err;
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_TIME,
+ snd_pcm_hw_rule_mulkdiv, (void*) 1000000,
+ SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_RATE, -1);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_pcm_hardware *hw = &runtime->hw;
+ int err;
+ unsigned int mask = 0;
+
+ if (hw->info & SNDRV_PCM_INFO_INTERLEAVED)
+ mask |= 1 << SNDRV_PCM_ACCESS_RW_INTERLEAVED;
+ if (hw->info & SNDRV_PCM_INFO_NONINTERLEAVED)
+ mask |= 1 << SNDRV_PCM_ACCESS_RW_NONINTERLEAVED;
+ if (hw->info & SNDRV_PCM_INFO_MMAP) {
+ if (hw->info & SNDRV_PCM_INFO_INTERLEAVED)
+ mask |= 1 << SNDRV_PCM_ACCESS_MMAP_INTERLEAVED;
+ if (hw->info & SNDRV_PCM_INFO_NONINTERLEAVED)
+ mask |= 1 << SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED;
+ if (hw->info & SNDRV_PCM_INFO_COMPLEX)
+ mask |= 1 << SNDRV_PCM_ACCESS_MMAP_COMPLEX;
+ }
+ err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_ACCESS, mask);
+ snd_assert(err >= 0, return -EINVAL);
+
+ err = snd_pcm_hw_constraint_mask64(runtime, SNDRV_PCM_HW_PARAM_FORMAT, hw->formats);
+ snd_assert(err >= 0, return -EINVAL);
+
+ err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_SUBFORMAT, 1 << SNDRV_PCM_SUBFORMAT_STD);
+ snd_assert(err >= 0, return -EINVAL);
+
+ err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_CHANNELS,
+ hw->channels_min, hw->channels_max);
+ snd_assert(err >= 0, return -EINVAL);
+
+ err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE,
+ hw->rate_min, hw->rate_max);
+ snd_assert(err >= 0, return -EINVAL);
+
+ err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+ hw->period_bytes_min, hw->period_bytes_max);
+ snd_assert(err >= 0, return -EINVAL);
+
+ err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIODS,
+ hw->periods_min, hw->periods_max);
+ snd_assert(err >= 0, return -EINVAL);
+
+ err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ hw->period_bytes_min, hw->buffer_bytes_max);
+ snd_assert(err >= 0, return -EINVAL);
+
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ snd_pcm_hw_rule_buffer_bytes_max, substream,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES, -1);
+ if (err < 0)
+ return err;
+
+ /* FIXME: remove */
+ if (runtime->dma_bytes) {
+ err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 0, runtime->dma_bytes);
+ snd_assert(err >= 0, return -EINVAL);
+ }
+
+ if (!(hw->rates & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))) {
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ snd_pcm_hw_rule_rate, hw,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+ if (err < 0)
+ return err;
+ }
+
+ /* FIXME: this belong to lowlevel */
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_TICK_TIME,
+ 1000000 / HZ, 1000000 / HZ);
+ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
+
+ return 0;
+}
+
+static void pcm_release_private(struct snd_pcm_substream *substream)
+{
+ snd_pcm_unlink(substream);
+}
+
+void snd_pcm_release_substream(struct snd_pcm_substream *substream)
+{
+ substream->ref_count--;
+ if (substream->ref_count > 0)
+ return;
+
+ snd_pcm_drop(substream);
+ if (substream->hw_opened) {
+ if (substream->ops->hw_free != NULL)
+ substream->ops->hw_free(substream);
+ substream->ops->close(substream);
+ substream->hw_opened = 0;
+ }
+ if (substream->pcm_release) {
+ substream->pcm_release(substream);
+ substream->pcm_release = NULL;
+ }
+ snd_pcm_detach_substream(substream);
+}
+
+EXPORT_SYMBOL(snd_pcm_release_substream);
+
+int snd_pcm_open_substream(struct snd_pcm *pcm, int stream,
+ struct file *file,
+ struct snd_pcm_substream **rsubstream)
+{
+ struct snd_pcm_substream *substream;
+ int err;
+
+ err = snd_pcm_attach_substream(pcm, stream, file, &substream);
+ if (err < 0)
+ return err;
+ if (substream->ref_count > 1) {
+ *rsubstream = substream;
+ return 0;
+ }
+
+ err = snd_pcm_hw_constraints_init(substream);
+ if (err < 0) {
+ snd_printd("snd_pcm_hw_constraints_init failed\n");
+ goto error;
+ }
+
+ if ((err = substream->ops->open(substream)) < 0)
+ goto error;
+
+ substream->hw_opened = 1;
+
+ err = snd_pcm_hw_constraints_complete(substream);
+ if (err < 0) {
+ snd_printd("snd_pcm_hw_constraints_complete failed\n");
+ goto error;
+ }
+
+ *rsubstream = substream;
+ return 0;
+
+ error:
+ snd_pcm_release_substream(substream);
+ return err;
+}
+
+EXPORT_SYMBOL(snd_pcm_open_substream);
+
+static int snd_pcm_open_file(struct file *file,
+ struct snd_pcm *pcm,
+ int stream,
+ struct snd_pcm_file **rpcm_file)
+{
+ struct snd_pcm_file *pcm_file;
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_str *str;
+ int err;
+
+ snd_assert(rpcm_file != NULL, return -EINVAL);
+ *rpcm_file = NULL;
+
+ err = snd_pcm_open_substream(pcm, stream, file, &substream);
+ if (err < 0)
+ return err;
+
+ pcm_file = kzalloc(sizeof(*pcm_file), GFP_KERNEL);
+ if (pcm_file == NULL) {
+ snd_pcm_release_substream(substream);
+ return -ENOMEM;
+ }
+ pcm_file->substream = substream;
+ if (substream->ref_count == 1) {
+ str = substream->pstr;
+ substream->file = pcm_file;
+ substream->pcm_release = pcm_release_private;
+ }
+ file->private_data = pcm_file;
+ *rpcm_file = pcm_file;
+ return 0;
+}
+
+static int snd_pcm_playback_open(struct inode *inode, struct file *file)
+{
+ struct snd_pcm *pcm;
+
+ pcm = snd_lookup_minor_data(iminor(inode),
+ SNDRV_DEVICE_TYPE_PCM_PLAYBACK);
+ return snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_PLAYBACK);
+}
+
+static int snd_pcm_capture_open(struct inode *inode, struct file *file)
+{
+ struct snd_pcm *pcm;
+
+ pcm = snd_lookup_minor_data(iminor(inode),
+ SNDRV_DEVICE_TYPE_PCM_CAPTURE);
+ return snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_CAPTURE);
+}
+
+static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream)
+{
+ int err;
+ struct snd_pcm_file *pcm_file;
+ wait_queue_t wait;
+
+ if (pcm == NULL) {
+ err = -ENODEV;
+ goto __error1;
+ }
+ err = snd_card_file_add(pcm->card, file);
+ if (err < 0)
+ goto __error1;
+ if (!try_module_get(pcm->card->module)) {
+ err = -EFAULT;
+ goto __error2;
+ }
+ init_waitqueue_entry(&wait, current);
+ add_wait_queue(&pcm->open_wait, &wait);
+ mutex_lock(&pcm->open_mutex);
+ while (1) {
+ err = snd_pcm_open_file(file, pcm, stream, &pcm_file);
+ if (err >= 0)
+ break;
+ if (err == -EAGAIN) {
+ if (file->f_flags & O_NONBLOCK) {
+ err = -EBUSY;
+ break;
+ }
+ } else
+ break;
+ set_current_state(TASK_INTERRUPTIBLE);
+ mutex_unlock(&pcm->open_mutex);
+ schedule();
+ mutex_lock(&pcm->open_mutex);
+ if (signal_pending(current)) {
+ err = -ERESTARTSYS;
+ break;
+ }
+ }
+ remove_wait_queue(&pcm->open_wait, &wait);
+ mutex_unlock(&pcm->open_mutex);
+ if (err < 0)
+ goto __error;
+ return err;
+
+ __error:
+ module_put(pcm->card->module);
+ __error2:
+ snd_card_file_remove(pcm->card, file);
+ __error1:
+ return err;
+}
+
+static int snd_pcm_release(struct inode *inode, struct file *file)
+{
+ struct snd_pcm *pcm;
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_file *pcm_file;
+
+ pcm_file = file->private_data;
+ substream = pcm_file->substream;
+ snd_assert(substream != NULL, return -ENXIO);
+ pcm = substream->pcm;
+ fasync_helper(-1, file, 0, &substream->runtime->fasync);
+ mutex_lock(&pcm->open_mutex);
+ snd_pcm_release_substream(substream);
+ kfree(pcm_file);
+ mutex_unlock(&pcm->open_mutex);
+ wake_up(&pcm->open_wait);
+ module_put(pcm->card->module);
+ snd_card_file_remove(pcm->card, file);
+ return 0;
+}
+
+static snd_pcm_sframes_t snd_pcm_playback_rewind(struct snd_pcm_substream *substream,
+ snd_pcm_uframes_t frames)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_pcm_sframes_t appl_ptr;
+ snd_pcm_sframes_t ret;
+ snd_pcm_sframes_t hw_avail;
+
+ if (frames == 0)
+ return 0;
+
+ snd_pcm_stream_lock_irq(substream);
+ switch (runtime->status->state) {
+ case SNDRV_PCM_STATE_PREPARED:
+ break;
+ case SNDRV_PCM_STATE_DRAINING:
+ case SNDRV_PCM_STATE_RUNNING:
+ if (snd_pcm_update_hw_ptr(substream) >= 0)
+ break;
+ /* Fall through */
+ case SNDRV_PCM_STATE_XRUN:
+ ret = -EPIPE;
+ goto __end;
+ default:
+ ret = -EBADFD;
+ goto __end;
+ }
+
+ hw_avail = snd_pcm_playback_hw_avail(runtime);
+ if (hw_avail <= 0) {
+ ret = 0;
+ goto __end;
+ }
+ if (frames > (snd_pcm_uframes_t)hw_avail)
+ frames = hw_avail;
+ else
+ frames -= frames % runtime->xfer_align;
+ appl_ptr = runtime->control->appl_ptr - frames;
+ if (appl_ptr < 0)
+ appl_ptr += runtime->boundary;
+ runtime->control->appl_ptr = appl_ptr;
+ if (runtime->status->state == SNDRV_PCM_STATE_RUNNING &&
+ runtime->sleep_min)
+ snd_pcm_tick_prepare(substream);
+ ret = frames;
+ __end:
+ snd_pcm_stream_unlock_irq(substream);
+ return ret;
+}
+
+static snd_pcm_sframes_t snd_pcm_capture_rewind(struct snd_pcm_substream *substream,
+ snd_pcm_uframes_t frames)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_pcm_sframes_t appl_ptr;
+ snd_pcm_sframes_t ret;
+ snd_pcm_sframes_t hw_avail;
+
+ if (frames == 0)
+ return 0;
+
+ snd_pcm_stream_lock_irq(substream);
+ switch (runtime->status->state) {
+ case SNDRV_PCM_STATE_PREPARED:
+ case SNDRV_PCM_STATE_DRAINING:
+ break;
+ case SNDRV_PCM_STATE_RUNNING:
+ if (snd_pcm_update_hw_ptr(substream) >= 0)
+ break;
+ /* Fall through */
+ case SNDRV_PCM_STATE_XRUN:
+ ret = -EPIPE;
+ goto __end;
+ default:
+ ret = -EBADFD;
+ goto __end;
+ }
+
+ hw_avail = snd_pcm_capture_hw_avail(runtime);
+ if (hw_avail <= 0) {
+ ret = 0;
+ goto __end;
+ }
+ if (frames > (snd_pcm_uframes_t)hw_avail)
+ frames = hw_avail;
+ else
+ frames -= frames % runtime->xfer_align;
+ appl_ptr = runtime->control->appl_ptr - frames;
+ if (appl_ptr < 0)
+ appl_ptr += runtime->boundary;
+ runtime->control->appl_ptr = appl_ptr;
+ if (runtime->status->state == SNDRV_PCM_STATE_RUNNING &&
+ runtime->sleep_min)
+ snd_pcm_tick_prepare(substream);
+ ret = frames;
+ __end:
+ snd_pcm_stream_unlock_irq(substream);
+ return ret;
+}
+
+static snd_pcm_sframes_t snd_pcm_playback_forward(struct snd_pcm_substream *substream,
+ snd_pcm_uframes_t frames)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_pcm_sframes_t appl_ptr;
+ snd_pcm_sframes_t ret;
+ snd_pcm_sframes_t avail;
+
+ if (frames == 0)
+ return 0;
+
+ snd_pcm_stream_lock_irq(substream);
+ switch (runtime->status->state) {
+ case SNDRV_PCM_STATE_PREPARED:
+ case SNDRV_PCM_STATE_PAUSED:
+ break;
+ case SNDRV_PCM_STATE_DRAINING:
+ case SNDRV_PCM_STATE_RUNNING:
+ if (snd_pcm_update_hw_ptr(substream) >= 0)
+ break;
+ /* Fall through */
+ case SNDRV_PCM_STATE_XRUN:
+ ret = -EPIPE;
+ goto __end;
+ default:
+ ret = -EBADFD;
+ goto __end;
+ }
+
+ avail = snd_pcm_playback_avail(runtime);
+ if (avail <= 0) {
+ ret = 0;
+ goto __end;
+ }
+ if (frames > (snd_pcm_uframes_t)avail)
+ frames = avail;
+ else
+ frames -= frames % runtime->xfer_align;
+ appl_ptr = runtime->control->appl_ptr + frames;
+ if (appl_ptr >= (snd_pcm_sframes_t)runtime->boundary)
+ appl_ptr -= runtime->boundary;
+ runtime->control->appl_ptr = appl_ptr;
+ if (runtime->status->state == SNDRV_PCM_STATE_RUNNING &&
+ runtime->sleep_min)
+ snd_pcm_tick_prepare(substream);
+ ret = frames;
+ __end:
+ snd_pcm_stream_unlock_irq(substream);
+ return ret;
+}
+
+static snd_pcm_sframes_t snd_pcm_capture_forward(struct snd_pcm_substream *substream,
+ snd_pcm_uframes_t frames)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_pcm_sframes_t appl_ptr;
+ snd_pcm_sframes_t ret;
+ snd_pcm_sframes_t avail;
+
+ if (frames == 0)
+ return 0;
+
+ snd_pcm_stream_lock_irq(substream);
+ switch (runtime->status->state) {
+ case SNDRV_PCM_STATE_PREPARED:
+ case SNDRV_PCM_STATE_DRAINING:
+ case SNDRV_PCM_STATE_PAUSED:
+ break;
+ case SNDRV_PCM_STATE_RUNNING:
+ if (snd_pcm_update_hw_ptr(substream) >= 0)
+ break;
+ /* Fall through */
+ case SNDRV_PCM_STATE_XRUN:
+ ret = -EPIPE;
+ goto __end;
+ default:
+ ret = -EBADFD;
+ goto __end;
+ }
+
+ avail = snd_pcm_capture_avail(runtime);
+ if (avail <= 0) {
+ ret = 0;
+ goto __end;
+ }
+ if (frames > (snd_pcm_uframes_t)avail)
+ frames = avail;
+ else
+ frames -= frames % runtime->xfer_align;
+ appl_ptr = runtime->control->appl_ptr + frames;
+ if (appl_ptr >= (snd_pcm_sframes_t)runtime->boundary)
+ appl_ptr -= runtime->boundary;
+ runtime->control->appl_ptr = appl_ptr;
+ if (runtime->status->state == SNDRV_PCM_STATE_RUNNING &&
+ runtime->sleep_min)
+ snd_pcm_tick_prepare(substream);
+ ret = frames;
+ __end:
+ snd_pcm_stream_unlock_irq(substream);
+ return ret;
+}
+
+static int snd_pcm_hwsync(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ snd_pcm_stream_lock_irq(substream);
+ switch (runtime->status->state) {
+ case SNDRV_PCM_STATE_DRAINING:
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ goto __badfd;
+ case SNDRV_PCM_STATE_RUNNING:
+ if ((err = snd_pcm_update_hw_ptr(substream)) < 0)
+ break;
+ /* Fall through */
+ case SNDRV_PCM_STATE_PREPARED:
+ case SNDRV_PCM_STATE_SUSPENDED:
+ err = 0;
+ break;
+ case SNDRV_PCM_STATE_XRUN:
+ err = -EPIPE;
+ break;
+ default:
+ __badfd:
+ err = -EBADFD;
+ break;
+ }
+ snd_pcm_stream_unlock_irq(substream);
+ return err;
+}
+
+static int snd_pcm_delay(struct snd_pcm_substream *substream,
+ snd_pcm_sframes_t __user *res)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+ snd_pcm_sframes_t n = 0;
+
+ snd_pcm_stream_lock_irq(substream);
+ switch (runtime->status->state) {
+ case SNDRV_PCM_STATE_DRAINING:
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ goto __badfd;
+ case SNDRV_PCM_STATE_RUNNING:
+ if ((err = snd_pcm_update_hw_ptr(substream)) < 0)
+ break;
+ /* Fall through */
+ case SNDRV_PCM_STATE_PREPARED:
+ case SNDRV_PCM_STATE_SUSPENDED:
+ err = 0;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ n = snd_pcm_playback_hw_avail(runtime);
+ else
+ n = snd_pcm_capture_avail(runtime);
+ break;
+ case SNDRV_PCM_STATE_XRUN:
+ err = -EPIPE;
+ break;
+ default:
+ __badfd:
+ err = -EBADFD;
+ break;
+ }
+ snd_pcm_stream_unlock_irq(substream);
+ if (!err)
+ if (put_user(n, res))
+ err = -EFAULT;
+ return err;
+}
+
+static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream,
+ struct snd_pcm_sync_ptr __user *_sync_ptr)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_pcm_sync_ptr sync_ptr;
+ volatile struct snd_pcm_mmap_status *status;
+ volatile struct snd_pcm_mmap_control *control;
+ int err;
+
+ memset(&sync_ptr, 0, sizeof(sync_ptr));
+ if (get_user(sync_ptr.flags, (unsigned __user *)&(_sync_ptr->flags)))
+ return -EFAULT;
+ if (copy_from_user(&sync_ptr.c.control, &(_sync_ptr->c.control), sizeof(struct snd_pcm_mmap_control)))
+ return -EFAULT;
+ status = runtime->status;
+ control = runtime->control;
+ if (sync_ptr.flags & SNDRV_PCM_SYNC_PTR_HWSYNC) {
+ err = snd_pcm_hwsync(substream);
+ if (err < 0)
+ return err;
+ }
+ snd_pcm_stream_lock_irq(substream);
+ if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL))
+ control->appl_ptr = sync_ptr.c.control.appl_ptr;
+ else
+ sync_ptr.c.control.appl_ptr = control->appl_ptr;
+ if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN))
+ control->avail_min = sync_ptr.c.control.avail_min;
+ else
+ sync_ptr.c.control.avail_min = control->avail_min;
+ sync_ptr.s.status.state = status->state;
+ sync_ptr.s.status.hw_ptr = status->hw_ptr;
+ sync_ptr.s.status.tstamp = status->tstamp;
+ sync_ptr.s.status.suspended_state = status->suspended_state;
+ snd_pcm_stream_unlock_irq(substream);
+ if (copy_to_user(_sync_ptr, &sync_ptr, sizeof(sync_ptr)))
+ return -EFAULT;
+ return 0;
+}
+
+static int snd_pcm_common_ioctl1(struct file *file,
+ struct snd_pcm_substream *substream,
+ unsigned int cmd, void __user *arg)
+{
+ snd_assert(substream != NULL, return -ENXIO);
+
+ switch (cmd) {
+ case SNDRV_PCM_IOCTL_PVERSION:
+ return put_user(SNDRV_PCM_VERSION, (int __user *)arg) ? -EFAULT : 0;
+ case SNDRV_PCM_IOCTL_INFO:
+ return snd_pcm_info_user(substream, arg);
+ case SNDRV_PCM_IOCTL_TSTAMP: /* just for compatibility */
+ return 0;
+ case SNDRV_PCM_IOCTL_HW_REFINE:
+ return snd_pcm_hw_refine_user(substream, arg);
+ case SNDRV_PCM_IOCTL_HW_PARAMS:
+ return snd_pcm_hw_params_user(substream, arg);
+ case SNDRV_PCM_IOCTL_HW_FREE:
+ return snd_pcm_hw_free(substream);
+ case SNDRV_PCM_IOCTL_SW_PARAMS:
+ return snd_pcm_sw_params_user(substream, arg);
+ case SNDRV_PCM_IOCTL_STATUS:
+ return snd_pcm_status_user(substream, arg);
+ case SNDRV_PCM_IOCTL_CHANNEL_INFO:
+ return snd_pcm_channel_info_user(substream, arg);
+ case SNDRV_PCM_IOCTL_PREPARE:
+ return snd_pcm_prepare(substream, file);
+ case SNDRV_PCM_IOCTL_RESET:
+ return snd_pcm_reset(substream);
+ case SNDRV_PCM_IOCTL_START:
+ return snd_pcm_action_lock_irq(&snd_pcm_action_start, substream, SNDRV_PCM_STATE_RUNNING);
+ case SNDRV_PCM_IOCTL_LINK:
+ return snd_pcm_link(substream, (int)(unsigned long) arg);
+ case SNDRV_PCM_IOCTL_UNLINK:
+ return snd_pcm_unlink(substream);
+ case SNDRV_PCM_IOCTL_RESUME:
+ return snd_pcm_resume(substream);
+ case SNDRV_PCM_IOCTL_XRUN:
+ return snd_pcm_xrun(substream);
+ case SNDRV_PCM_IOCTL_HWSYNC:
+ return snd_pcm_hwsync(substream);
+ case SNDRV_PCM_IOCTL_DELAY:
+ return snd_pcm_delay(substream, arg);
+ case SNDRV_PCM_IOCTL_SYNC_PTR:
+ return snd_pcm_sync_ptr(substream, arg);
+#ifdef CONFIG_SND_SUPPORT_OLD_API
+ case SNDRV_PCM_IOCTL_HW_REFINE_OLD:
+ return snd_pcm_hw_refine_old_user(substream, arg);
+ case SNDRV_PCM_IOCTL_HW_PARAMS_OLD:
+ return snd_pcm_hw_params_old_user(substream, arg);
+#endif
+ case SNDRV_PCM_IOCTL_DRAIN:
+ return snd_pcm_drain(substream);
+ case SNDRV_PCM_IOCTL_DROP:
+ return snd_pcm_drop(substream);
+ case SNDRV_PCM_IOCTL_PAUSE:
+ {
+ int res;
+ snd_pcm_stream_lock_irq(substream);
+ res = snd_pcm_pause(substream, (int)(unsigned long)arg);
+ snd_pcm_stream_unlock_irq(substream);
+ return res;
+ }
+ }
+ snd_printd("unknown ioctl = 0x%x\n", cmd);
+ return -ENOTTY;
+}
+
+static int snd_pcm_playback_ioctl1(struct file *file,
+ struct snd_pcm_substream *substream,
+ unsigned int cmd, void __user *arg)
+{
+ snd_assert(substream != NULL, return -ENXIO);
+ snd_assert(substream->stream == SNDRV_PCM_STREAM_PLAYBACK, return -EINVAL);
+ switch (cmd) {
+ case SNDRV_PCM_IOCTL_WRITEI_FRAMES:
+ {
+ struct snd_xferi xferi;
+ struct snd_xferi __user *_xferi = arg;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_pcm_sframes_t result;
+ if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ return -EBADFD;
+ if (put_user(0, &_xferi->result))
+ return -EFAULT;
+ if (copy_from_user(&xferi, _xferi, sizeof(xferi)))
+ return -EFAULT;
+ result = snd_pcm_lib_write(substream, xferi.buf, xferi.frames);
+ __put_user(result, &_xferi->result);
+ return result < 0 ? result : 0;
+ }
+ case SNDRV_PCM_IOCTL_WRITEN_FRAMES:
+ {
+ struct snd_xfern xfern;
+ struct snd_xfern __user *_xfern = arg;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ void __user **bufs;
+ snd_pcm_sframes_t result;
+ if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ return -EBADFD;
+ if (runtime->channels > 128)
+ return -EINVAL;
+ if (put_user(0, &_xfern->result))
+ return -EFAULT;
+ if (copy_from_user(&xfern, _xfern, sizeof(xfern)))
+ return -EFAULT;
+ bufs = kmalloc(sizeof(void *) * runtime->channels, GFP_KERNEL);
+ if (bufs == NULL)
+ return -ENOMEM;
+ if (copy_from_user(bufs, xfern.bufs, sizeof(void *) * runtime->channels)) {
+ kfree(bufs);
+ return -EFAULT;
+ }
+ result = snd_pcm_lib_writev(substream, bufs, xfern.frames);
+ kfree(bufs);
+ __put_user(result, &_xfern->result);
+ return result < 0 ? result : 0;
+ }
+ case SNDRV_PCM_IOCTL_REWIND:
+ {
+ snd_pcm_uframes_t frames;
+ snd_pcm_uframes_t __user *_frames = arg;
+ snd_pcm_sframes_t result;
+ if (get_user(frames, _frames))
+ return -EFAULT;
+ if (put_user(0, _frames))
+ return -EFAULT;
+ result = snd_pcm_playback_rewind(substream, frames);
+ __put_user(result, _frames);
+ return result < 0 ? result : 0;
+ }
+ case SNDRV_PCM_IOCTL_FORWARD:
+ {
+ snd_pcm_uframes_t frames;
+ snd_pcm_uframes_t __user *_frames = arg;
+ snd_pcm_sframes_t result;
+ if (get_user(frames, _frames))
+ return -EFAULT;
+ if (put_user(0, _frames))
+ return -EFAULT;
+ result = snd_pcm_playback_forward(substream, frames);
+ __put_user(result, _frames);
+ return result < 0 ? result : 0;
+ }
+ }
+ return snd_pcm_common_ioctl1(file, substream, cmd, arg);
+}
+
+static int snd_pcm_capture_ioctl1(struct file *file,
+ struct snd_pcm_substream *substream,
+ unsigned int cmd, void __user *arg)
+{
+ snd_assert(substream != NULL, return -ENXIO);
+ snd_assert(substream->stream == SNDRV_PCM_STREAM_CAPTURE, return -EINVAL);
+ switch (cmd) {
+ case SNDRV_PCM_IOCTL_READI_FRAMES:
+ {
+ struct snd_xferi xferi;
+ struct snd_xferi __user *_xferi = arg;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_pcm_sframes_t result;
+ if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ return -EBADFD;
+ if (put_user(0, &_xferi->result))
+ return -EFAULT;
+ if (copy_from_user(&xferi, _xferi, sizeof(xferi)))
+ return -EFAULT;
+ result = snd_pcm_lib_read(substream, xferi.buf, xferi.frames);
+ __put_user(result, &_xferi->result);
+ return result < 0 ? result : 0;
+ }
+ case SNDRV_PCM_IOCTL_READN_FRAMES:
+ {
+ struct snd_xfern xfern;
+ struct snd_xfern __user *_xfern = arg;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ void *bufs;
+ snd_pcm_sframes_t result;
+ if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ return -EBADFD;
+ if (runtime->channels > 128)
+ return -EINVAL;
+ if (put_user(0, &_xfern->result))
+ return -EFAULT;
+ if (copy_from_user(&xfern, _xfern, sizeof(xfern)))
+ return -EFAULT;
+ bufs = kmalloc(sizeof(void *) * runtime->channels, GFP_KERNEL);
+ if (bufs == NULL)
+ return -ENOMEM;
+ if (copy_from_user(bufs, xfern.bufs, sizeof(void *) * runtime->channels)) {
+ kfree(bufs);
+ return -EFAULT;
+ }
+ result = snd_pcm_lib_readv(substream, bufs, xfern.frames);
+ kfree(bufs);
+ __put_user(result, &_xfern->result);
+ return result < 0 ? result : 0;
+ }
+ case SNDRV_PCM_IOCTL_REWIND:
+ {
+ snd_pcm_uframes_t frames;
+ snd_pcm_uframes_t __user *_frames = arg;
+ snd_pcm_sframes_t result;
+ if (get_user(frames, _frames))
+ return -EFAULT;
+ if (put_user(0, _frames))
+ return -EFAULT;
+ result = snd_pcm_capture_rewind(substream, frames);
+ __put_user(result, _frames);
+ return result < 0 ? result : 0;
+ }
+ case SNDRV_PCM_IOCTL_FORWARD:
+ {
+ snd_pcm_uframes_t frames;
+ snd_pcm_uframes_t __user *_frames = arg;
+ snd_pcm_sframes_t result;
+ if (get_user(frames, _frames))
+ return -EFAULT;
+ if (put_user(0, _frames))
+ return -EFAULT;
+ result = snd_pcm_capture_forward(substream, frames);
+ __put_user(result, _frames);
+ return result < 0 ? result : 0;
+ }
+ }
+ return snd_pcm_common_ioctl1(file, substream, cmd, arg);
+}
+
+static long snd_pcm_playback_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct snd_pcm_file *pcm_file;
+
+ pcm_file = file->private_data;
+
+ if (((cmd >> 8) & 0xff) != 'A')
+ return -ENOTTY;
+
+ return snd_pcm_playback_ioctl1(file, pcm_file->substream, cmd,
+ (void __user *)arg);
+}
+
+static long snd_pcm_capture_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct snd_pcm_file *pcm_file;
+
+ pcm_file = file->private_data;
+
+ if (((cmd >> 8) & 0xff) != 'A')
+ return -ENOTTY;
+
+ return snd_pcm_capture_ioctl1(file, pcm_file->substream, cmd,
+ (void __user *)arg);
+}
+
+int snd_pcm_kernel_ioctl(struct snd_pcm_substream *substream,
+ unsigned int cmd, void *arg)
+{
+ mm_segment_t fs;
+ int result;
+
+ fs = snd_enter_user();
+ switch (substream->stream) {
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ result = snd_pcm_playback_ioctl1(NULL, substream, cmd,
+ (void __user *)arg);
+ break;
+ case SNDRV_PCM_STREAM_CAPTURE:
+ result = snd_pcm_capture_ioctl1(NULL, substream, cmd,
+ (void __user *)arg);
+ break;
+ default:
+ result = -EINVAL;
+ break;
+ }
+ snd_leave_user(fs);
+ return result;
+}
+
+EXPORT_SYMBOL(snd_pcm_kernel_ioctl);
+
+static ssize_t snd_pcm_read(struct file *file, char __user *buf, size_t count,
+ loff_t * offset)
+{
+ struct snd_pcm_file *pcm_file;
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+ snd_pcm_sframes_t result;
+
+ pcm_file = file->private_data;
+ substream = pcm_file->substream;
+ snd_assert(substream != NULL, return -ENXIO);
+ runtime = substream->runtime;
+ if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ return -EBADFD;
+ if (!frame_aligned(runtime, count))
+ return -EINVAL;
+ count = bytes_to_frames(runtime, count);
+ result = snd_pcm_lib_read(substream, buf, count);
+ if (result > 0)
+ result = frames_to_bytes(runtime, result);
+ return result;
+}
+
+static ssize_t snd_pcm_write(struct file *file, const char __user *buf,
+ size_t count, loff_t * offset)
+{
+ struct snd_pcm_file *pcm_file;
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+ snd_pcm_sframes_t result;
+
+ pcm_file = file->private_data;
+ substream = pcm_file->substream;
+ snd_assert(substream != NULL, result = -ENXIO; goto end);
+ runtime = substream->runtime;
+ if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+ result = -EBADFD;
+ goto end;
+ }
+ if (!frame_aligned(runtime, count)) {
+ result = -EINVAL;
+ goto end;
+ }
+ count = bytes_to_frames(runtime, count);
+ result = snd_pcm_lib_write(substream, buf, count);
+ if (result > 0)
+ result = frames_to_bytes(runtime, result);
+ end:
+ return result;
+}
+
+static ssize_t snd_pcm_aio_read(struct kiocb *iocb, const struct iovec *iov,
+ unsigned long nr_segs, loff_t pos)
+
+{
+ struct snd_pcm_file *pcm_file;
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+ snd_pcm_sframes_t result;
+ unsigned long i;
+ void __user **bufs;
+ snd_pcm_uframes_t frames;
+
+ pcm_file = iocb->ki_filp->private_data;
+ substream = pcm_file->substream;
+ snd_assert(substream != NULL, return -ENXIO);
+ runtime = substream->runtime;
+ if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ return -EBADFD;
+ if (nr_segs > 1024 || nr_segs != runtime->channels)
+ return -EINVAL;
+ if (!frame_aligned(runtime, iov->iov_len))
+ return -EINVAL;
+ frames = bytes_to_samples(runtime, iov->iov_len);
+ bufs = kmalloc(sizeof(void *) * nr_segs, GFP_KERNEL);
+ if (bufs == NULL)
+ return -ENOMEM;
+ for (i = 0; i < nr_segs; ++i)
+ bufs[i] = iov[i].iov_base;
+ result = snd_pcm_lib_readv(substream, bufs, frames);
+ if (result > 0)
+ result = frames_to_bytes(runtime, result);
+ kfree(bufs);
+ return result;
+}
+
+static ssize_t snd_pcm_aio_write(struct kiocb *iocb, const struct iovec *iov,
+ unsigned long nr_segs, loff_t pos)
+{
+ struct snd_pcm_file *pcm_file;
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+ snd_pcm_sframes_t result;
+ unsigned long i;
+ void __user **bufs;
+ snd_pcm_uframes_t frames;
+
+ pcm_file = iocb->ki_filp->private_data;
+ substream = pcm_file->substream;
+ snd_assert(substream != NULL, result = -ENXIO; goto end);
+ runtime = substream->runtime;
+ if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+ result = -EBADFD;
+ goto end;
+ }
+ if (nr_segs > 128 || nr_segs != runtime->channels ||
+ !frame_aligned(runtime, iov->iov_len)) {
+ result = -EINVAL;
+ goto end;
+ }
+ frames = bytes_to_samples(runtime, iov->iov_len);
+ bufs = kmalloc(sizeof(void *) * nr_segs, GFP_KERNEL);
+ if (bufs == NULL)
+ return -ENOMEM;
+ for (i = 0; i < nr_segs; ++i)
+ bufs[i] = iov[i].iov_base;
+ result = snd_pcm_lib_writev(substream, bufs, frames);
+ if (result > 0)
+ result = frames_to_bytes(runtime, result);
+ kfree(bufs);
+ end:
+ return result;
+}
+
+static unsigned int snd_pcm_playback_poll(struct file *file, poll_table * wait)
+{
+ struct snd_pcm_file *pcm_file;
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+ unsigned int mask;
+ snd_pcm_uframes_t avail;
+
+ pcm_file = file->private_data;
+
+ substream = pcm_file->substream;
+ snd_assert(substream != NULL, return -ENXIO);
+ runtime = substream->runtime;
+
+ poll_wait(file, &runtime->sleep, wait);
+
+ snd_pcm_stream_lock_irq(substream);
+ avail = snd_pcm_playback_avail(runtime);
+ switch (runtime->status->state) {
+ case SNDRV_PCM_STATE_RUNNING:
+ case SNDRV_PCM_STATE_PREPARED:
+ case SNDRV_PCM_STATE_PAUSED:
+ if (avail >= runtime->control->avail_min) {
+ mask = POLLOUT | POLLWRNORM;
+ break;
+ }
+ /* Fall through */
+ case SNDRV_PCM_STATE_DRAINING:
+ mask = 0;
+ break;
+ default:
+ mask = POLLOUT | POLLWRNORM | POLLERR;
+ break;
+ }
+ snd_pcm_stream_unlock_irq(substream);
+ return mask;
+}
+
+static unsigned int snd_pcm_capture_poll(struct file *file, poll_table * wait)
+{
+ struct snd_pcm_file *pcm_file;
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+ unsigned int mask;
+ snd_pcm_uframes_t avail;
+
+ pcm_file = file->private_data;
+
+ substream = pcm_file->substream;
+ snd_assert(substream != NULL, return -ENXIO);
+ runtime = substream->runtime;
+
+ poll_wait(file, &runtime->sleep, wait);
+
+ snd_pcm_stream_lock_irq(substream);
+ avail = snd_pcm_capture_avail(runtime);
+ switch (runtime->status->state) {
+ case SNDRV_PCM_STATE_RUNNING:
+ case SNDRV_PCM_STATE_PREPARED:
+ case SNDRV_PCM_STATE_PAUSED:
+ if (avail >= runtime->control->avail_min) {
+ mask = POLLIN | POLLRDNORM;
+ break;
+ }
+ mask = 0;
+ break;
+ case SNDRV_PCM_STATE_DRAINING:
+ if (avail > 0) {
+ mask = POLLIN | POLLRDNORM;
+ break;
+ }
+ /* Fall through */
+ default:
+ mask = POLLIN | POLLRDNORM | POLLERR;
+ break;
+ }
+ snd_pcm_stream_unlock_irq(substream);
+ return mask;
+}
+
+/*
+ * mmap support
+ */
+
+/*
+ * Only on coherent architectures, we can mmap the status and the control records
+ * for effcient data transfer. On others, we have to use HWSYNC ioctl...
+ */
+#if defined(CONFIG_X86) || defined(CONFIG_PPC) || defined(CONFIG_ALPHA)
+/*
+ * mmap status record
+ */
+static struct page * snd_pcm_mmap_status_nopage(struct vm_area_struct *area,
+ unsigned long address, int *type)
+{
+ struct snd_pcm_substream *substream = area->vm_private_data;
+ struct snd_pcm_runtime *runtime;
+ struct page * page;
+
+ if (substream == NULL)
+ return NOPAGE_SIGBUS;
+ runtime = substream->runtime;
+ page = virt_to_page(runtime->status);
+ get_page(page);
+ if (type)
+ *type = VM_FAULT_MINOR;
+ return page;
+}
+
+static struct vm_operations_struct snd_pcm_vm_ops_status =
+{
+ .nopage = snd_pcm_mmap_status_nopage,
+};
+
+static int snd_pcm_mmap_status(struct snd_pcm_substream *substream, struct file *file,
+ struct vm_area_struct *area)
+{
+ struct snd_pcm_runtime *runtime;
+ long size;
+ if (!(area->vm_flags & VM_READ))
+ return -EINVAL;
+ runtime = substream->runtime;
+ snd_assert(runtime != NULL, return -EAGAIN);
+ size = area->vm_end - area->vm_start;
+ if (size != PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status)))
+ return -EINVAL;
+ area->vm_ops = &snd_pcm_vm_ops_status;
+ area->vm_private_data = substream;
+ area->vm_flags |= VM_RESERVED;
+ return 0;
+}
+
+/*
+ * mmap control record
+ */
+static struct page * snd_pcm_mmap_control_nopage(struct vm_area_struct *area,
+ unsigned long address, int *type)
+{
+ struct snd_pcm_substream *substream = area->vm_private_data;
+ struct snd_pcm_runtime *runtime;
+ struct page * page;
+
+ if (substream == NULL)
+ return NOPAGE_SIGBUS;
+ runtime = substream->runtime;
+ page = virt_to_page(runtime->control);
+ get_page(page);
+ if (type)
+ *type = VM_FAULT_MINOR;
+ return page;
+}
+
+static struct vm_operations_struct snd_pcm_vm_ops_control =
+{
+ .nopage = snd_pcm_mmap_control_nopage,
+};
+
+static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file *file,
+ struct vm_area_struct *area)
+{
+ struct snd_pcm_runtime *runtime;
+ long size;
+ if (!(area->vm_flags & VM_READ))
+ return -EINVAL;
+ runtime = substream->runtime;
+ snd_assert(runtime != NULL, return -EAGAIN);
+ size = area->vm_end - area->vm_start;
+ if (size != PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control)))
+ return -EINVAL;
+ area->vm_ops = &snd_pcm_vm_ops_control;
+ area->vm_private_data = substream;
+ area->vm_flags |= VM_RESERVED;
+ return 0;
+}
+#else /* ! coherent mmap */
+/*
+ * don't support mmap for status and control records.
+ */
+static int snd_pcm_mmap_status(struct snd_pcm_substream *substream, struct file *file,
+ struct vm_area_struct *area)
+{
+ return -ENXIO;
+}
+static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file *file,
+ struct vm_area_struct *area)
+{
+ return -ENXIO;
+}
+#endif /* coherent mmap */
+
+/*
+ * nopage callback for mmapping a RAM page
+ */
+static struct page *snd_pcm_mmap_data_nopage(struct vm_area_struct *area,
+ unsigned long address, int *type)
+{
+ struct snd_pcm_substream *substream = area->vm_private_data;
+ struct snd_pcm_runtime *runtime;
+ unsigned long offset;
+ struct page * page;
+ void *vaddr;
+ size_t dma_bytes;
+
+ if (substream == NULL)
+ return NOPAGE_SIGBUS;
+ runtime = substream->runtime;
+ offset = area->vm_pgoff << PAGE_SHIFT;
+ offset += address - area->vm_start;
+ snd_assert((offset % PAGE_SIZE) == 0, return NOPAGE_SIGBUS);
+ dma_bytes = PAGE_ALIGN(runtime->dma_bytes);
+ if (offset > dma_bytes - PAGE_SIZE)
+ return NOPAGE_SIGBUS;
+ if (substream->ops->page) {
+ page = substream->ops->page(substream, offset);
+ if (! page)
+ return NOPAGE_OOM; /* XXX: is this really due to OOM? */
+ } else {
+ vaddr = runtime->dma_area + offset;
+ page = virt_to_page(vaddr);
+ }
+ get_page(page);
+ if (type)
+ *type = VM_FAULT_MINOR;
+ return page;
+}
+
+static struct vm_operations_struct snd_pcm_vm_ops_data =
+{
+ .open = snd_pcm_mmap_data_open,
+ .close = snd_pcm_mmap_data_close,
+ .nopage = snd_pcm_mmap_data_nopage,
+};
+
+/*
+ * mmap the DMA buffer on RAM
+ */
+static int snd_pcm_default_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *area)
+{
+ area->vm_ops = &snd_pcm_vm_ops_data;
+ area->vm_private_data = substream;
+ area->vm_flags |= VM_RESERVED;
+ atomic_inc(&substream->mmap_count);
+ return 0;
+}
+
+/*
+ * mmap the DMA buffer on I/O memory area
+ */
+#if SNDRV_PCM_INFO_MMAP_IOMEM
+static struct vm_operations_struct snd_pcm_vm_ops_data_mmio =
+{
+ .open = snd_pcm_mmap_data_open,
+ .close = snd_pcm_mmap_data_close,
+};
+
+int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream,
+ struct vm_area_struct *area)
+{
+ long size;
+ unsigned long offset;
+
+#ifdef pgprot_noncached
+ area->vm_page_prot = pgprot_noncached(area->vm_page_prot);
+#endif
+ area->vm_ops = &snd_pcm_vm_ops_data_mmio;
+ area->vm_private_data = substream;
+ area->vm_flags |= VM_IO;
+ size = area->vm_end - area->vm_start;
+ offset = area->vm_pgoff << PAGE_SHIFT;
+ if (io_remap_pfn_range(area, area->vm_start,
+ (substream->runtime->dma_addr + offset) >> PAGE_SHIFT,
+ size, area->vm_page_prot))
+ return -EAGAIN;
+ atomic_inc(&substream->mmap_count);
+ return 0;
+}
+
+EXPORT_SYMBOL(snd_pcm_lib_mmap_iomem);
+#endif /* SNDRV_PCM_INFO_MMAP */
+
+/*
+ * mmap DMA buffer
+ */
+int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file,
+ struct vm_area_struct *area)
+{
+ struct snd_pcm_runtime *runtime;
+ long size;
+ unsigned long offset;
+ size_t dma_bytes;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (!(area->vm_flags & (VM_WRITE|VM_READ)))
+ return -EINVAL;
+ } else {
+ if (!(area->vm_flags & VM_READ))
+ return -EINVAL;
+ }
+ runtime = substream->runtime;
+ snd_assert(runtime != NULL, return -EAGAIN);
+ if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ return -EBADFD;
+ if (!(runtime->info & SNDRV_PCM_INFO_MMAP))
+ return -ENXIO;
+ if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED ||
+ runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)
+ return -EINVAL;
+ size = area->vm_end - area->vm_start;
+ offset = area->vm_pgoff << PAGE_SHIFT;
+ dma_bytes = PAGE_ALIGN(runtime->dma_bytes);
+ if ((size_t)size > dma_bytes)
+ return -EINVAL;
+ if (offset > dma_bytes - size)
+ return -EINVAL;
+
+ if (substream->ops->mmap)
+ return substream->ops->mmap(substream, area);
+ else
+ return snd_pcm_default_mmap(substream, area);
+}
+
+EXPORT_SYMBOL(snd_pcm_mmap_data);
+
+static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area)
+{
+ struct snd_pcm_file * pcm_file;
+ struct snd_pcm_substream *substream;
+ unsigned long offset;
+
+ pcm_file = file->private_data;
+ substream = pcm_file->substream;
+ snd_assert(substream != NULL, return -ENXIO);
+
+ offset = area->vm_pgoff << PAGE_SHIFT;
+ switch (offset) {
+ case SNDRV_PCM_MMAP_OFFSET_STATUS:
+ if (pcm_file->no_compat_mmap)
+ return -ENXIO;
+ return snd_pcm_mmap_status(substream, file, area);
+ case SNDRV_PCM_MMAP_OFFSET_CONTROL:
+ if (pcm_file->no_compat_mmap)
+ return -ENXIO;
+ return snd_pcm_mmap_control(substream, file, area);
+ default:
+ return snd_pcm_mmap_data(substream, file, area);
+ }
+ return 0;
+}
+
+static int snd_pcm_fasync(int fd, struct file * file, int on)
+{
+ struct snd_pcm_file * pcm_file;
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+ int err;
+
+ pcm_file = file->private_data;
+ substream = pcm_file->substream;
+ snd_assert(substream != NULL, return -ENXIO);
+ runtime = substream->runtime;
+
+ err = fasync_helper(fd, file, on, &runtime->fasync);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+/*
+ * ioctl32 compat
+ */
+#ifdef CONFIG_COMPAT
+#include "pcm_compat.c"
+#else
+#define snd_pcm_ioctl_compat NULL
+#endif
+
+/*
+ * To be removed helpers to keep binary compatibility
+ */
+
+#ifdef CONFIG_SND_SUPPORT_OLD_API
+#define __OLD_TO_NEW_MASK(x) ((x&7)|((x&0x07fffff8)<<5))
+#define __NEW_TO_OLD_MASK(x) ((x&7)|((x&0xffffff00)>>5))
+
+static void snd_pcm_hw_convert_from_old_params(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_params_old *oparams)
+{
+ unsigned int i;
+
+ memset(params, 0, sizeof(*params));
+ params->flags = oparams->flags;
+ for (i = 0; i < ARRAY_SIZE(oparams->masks); i++)
+ params->masks[i].bits[0] = oparams->masks[i];
+ memcpy(params->intervals, oparams->intervals, sizeof(oparams->intervals));
+ params->rmask = __OLD_TO_NEW_MASK(oparams->rmask);
+ params->cmask = __OLD_TO_NEW_MASK(oparams->cmask);
+ params->info = oparams->info;
+ params->msbits = oparams->msbits;
+ params->rate_num = oparams->rate_num;
+ params->rate_den = oparams->rate_den;
+ params->fifo_size = oparams->fifo_size;
+}
+
+static void snd_pcm_hw_convert_to_old_params(struct snd_pcm_hw_params_old *oparams,
+ struct snd_pcm_hw_params *params)
+{
+ unsigned int i;
+
+ memset(oparams, 0, sizeof(*oparams));
+ oparams->flags = params->flags;
+ for (i = 0; i < ARRAY_SIZE(oparams->masks); i++)
+ oparams->masks[i] = params->masks[i].bits[0];
+ memcpy(oparams->intervals, params->intervals, sizeof(oparams->intervals));
+ oparams->rmask = __NEW_TO_OLD_MASK(params->rmask);
+ oparams->cmask = __NEW_TO_OLD_MASK(params->cmask);
+ oparams->info = params->info;
+ oparams->msbits = params->msbits;
+ oparams->rate_num = params->rate_num;
+ oparams->rate_den = params->rate_den;
+ oparams->fifo_size = params->fifo_size;
+}
+
+static int snd_pcm_hw_refine_old_user(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params_old __user * _oparams)
+{
+ struct snd_pcm_hw_params *params;
+ struct snd_pcm_hw_params_old *oparams = NULL;
+ int err;
+
+ params = kmalloc(sizeof(*params), GFP_KERNEL);
+ if (!params) {
+ err = -ENOMEM;
+ goto out;
+ }
+ oparams = kmalloc(sizeof(*oparams), GFP_KERNEL);
+ if (!oparams) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ if (copy_from_user(oparams, _oparams, sizeof(*oparams))) {
+ err = -EFAULT;
+ goto out;
+ }
+ snd_pcm_hw_convert_from_old_params(params, oparams);
+ err = snd_pcm_hw_refine(substream, params);
+ snd_pcm_hw_convert_to_old_params(oparams, params);
+ if (copy_to_user(_oparams, oparams, sizeof(*oparams))) {
+ if (!err)
+ err = -EFAULT;
+ }
+out:
+ kfree(params);
+ kfree(oparams);
+ return err;
+}
+
+static int snd_pcm_hw_params_old_user(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params_old __user * _oparams)
+{
+ struct snd_pcm_hw_params *params;
+ struct snd_pcm_hw_params_old *oparams = NULL;
+ int err;
+
+ params = kmalloc(sizeof(*params), GFP_KERNEL);
+ if (!params) {
+ err = -ENOMEM;
+ goto out;
+ }
+ oparams = kmalloc(sizeof(*oparams), GFP_KERNEL);
+ if (!oparams) {
+ err = -ENOMEM;
+ goto out;
+ }
+ if (copy_from_user(oparams, _oparams, sizeof(*oparams))) {
+ err = -EFAULT;
+ goto out;
+ }
+ snd_pcm_hw_convert_from_old_params(params, oparams);
+ err = snd_pcm_hw_params(substream, params);
+ snd_pcm_hw_convert_to_old_params(oparams, params);
+ if (copy_to_user(_oparams, oparams, sizeof(*oparams))) {
+ if (!err)
+ err = -EFAULT;
+ }
+out:
+ kfree(params);
+ kfree(oparams);
+ return err;
+}
+#endif /* CONFIG_SND_SUPPORT_OLD_API */
+
+/*
+ * Register section
+ */
+
+const struct file_operations snd_pcm_f_ops[2] = {
+ {
+ .owner = THIS_MODULE,
+ .write = snd_pcm_write,
+ .aio_write = snd_pcm_aio_write,
+ .open = snd_pcm_playback_open,
+ .release = snd_pcm_release,
+ .poll = snd_pcm_playback_poll,
+ .unlocked_ioctl = snd_pcm_playback_ioctl,
+ .compat_ioctl = snd_pcm_ioctl_compat,
+ .mmap = snd_pcm_mmap,
+ .fasync = snd_pcm_fasync,
+ },
+ {
+ .owner = THIS_MODULE,
+ .read = snd_pcm_read,
+ .aio_read = snd_pcm_aio_read,
+ .open = snd_pcm_capture_open,
+ .release = snd_pcm_release,
+ .poll = snd_pcm_capture_poll,
+ .unlocked_ioctl = snd_pcm_capture_ioctl,
+ .compat_ioctl = snd_pcm_ioctl_compat,
+ .mmap = snd_pcm_mmap,
+ .fasync = snd_pcm_fasync,
+ }
+};
--- linux-2.6.24.7.old/sound/oss/Kconfig 2008-05-07 01:22:34.000000000 +0200
+++ linux-2.6.24.7/sound/oss/Kconfig 2009-04-12 18:13:57.000000000 +0200
@@ -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 SOUND_PRIME && SIBYTE_SWARM
@@ -15,6 +100,13 @@
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 SOUND_PRIME && X86_VISWS
@@ -31,6 +123,14 @@
Say Y or M if you have an SGI Indy or Indigo2 system and want to be able to
use its on-board A2 audio system.
+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"
select SND_AC97_CODEC
@@ -75,7 +175,7 @@
This driver differs slightly from OSS/Free, so PLEASE READ the
- comments at the top of <file:sound/oss/trident.c>.
+ comments at the top of <file:drivers/sound/trident.c>.
config SOUND_MSNDCLAS
tristate "Support for Turtle Beach MultiSound Classic, Tahiti, Monterey"
@@ -302,9 +402,29 @@
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.
+
config SOUND_OSS
tristate "OSS sound modules"
- depends on SOUND_PRIME && 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
@@ -336,10 +456,23 @@
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"
depends on SOUND_OSS
- depends on VIRT_TO_BUS
help
Answer Y if you have a sound card based on the Ensoniq SoundScape
chipset. Such cards are being manufactured at least by Ensoniq, Spea
@@ -564,7 +697,7 @@
questions.
Read the <file:Documentation/sound/oss/README.OSS> file and the head of
- <file:sound/oss/aedsp16.c> as well as
+ <file:drivers/sound/aedsp16.c> as well as
<file:Documentation/sound/oss/AudioExcelDSP16> to get more information
about this driver and its configuration.
@@ -642,6 +775,13 @@
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
--- linux-2.6.24.7.old/sound/oss/Makefile 2008-05-07 01:22:34.000000000 +0200
+++ linux-2.6.24.7/sound/oss/Makefile 2009-04-12 18:13:57.000000000 +0200
@@ -10,6 +10,12 @@
# 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_HAL2) += hal2.o
obj-$(CONFIG_SOUND_AEDSP16) += aedsp16.o
--- linux-2.6.24.7.old/sound/oss/ak4642en.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.24.7/sound/oss/ak4642en.c 2009-04-12 18:13:57.000000000 +0200
@@ -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);
--- linux-2.6.24.7.old/sound/oss/jz_ac97.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.24.7/sound/oss/jz_ac97.c 2009-04-12 18:13:57.000000000 +0200
@@ -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;
+
+}
+
--- linux-2.6.24.7.old/sound/oss/jz_i2s.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.24.7/sound/oss/jz_i2s.c 2009-04-12 18:13:57.000000000 +0200
@@ -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
--- linux-2.6.24.7.old/sound/oss/jz_i2s_4750.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.24.7/sound/oss/jz_i2s_4750.c 2009-04-12 18:13:57.000000000 +0200
@@ -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
--- linux-2.6.24.7.old/sound/oss/jz_i2s_dlv_dma_test.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.24.7/sound/oss/jz_i2s_dlv_dma_test.c 2009-04-12 18:13:57.000000000 +0200
@@ -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;
+}
--- linux-2.6.24.7.old/sound/oss/jz_i2s_for_4750.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.24.7/sound/oss/jz_i2s_for_4750.c 2009-04-12 18:13:57.000000000 +0200
@@ -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
--- linux-2.6.24.7.old/sound/oss/jz_pcm_tlv320aic1106_dma.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.24.7/sound/oss/jz_pcm_tlv320aic1106_dma.c 2009-04-12 18:13:57.000000000 +0200
@@ -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);
+}
--- linux-2.6.24.7.old/sound/oss/jzcodec.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.24.7/sound/oss/jzcodec.c 2009-04-12 18:13:57.000000000 +0200
@@ -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);
--- linux-2.6.24.7.old/sound/oss/jzdlv.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.24.7/sound/oss/jzdlv.c 2009-04-12 18:13:57.000000000 +0200
@@ -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);
--- linux-2.6.24.7.old/sound/oss/jzdlv.h 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.24.7/sound/oss/jzdlv.h 2009-04-12 18:13:57.000000000 +0200
@@ -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);
--- linux-2.6.24.7.old/sound/oss/os.h 2008-05-07 01:22:34.000000000 +0200
+++ linux-2.6.24.7/sound/oss/os.h 2009-04-12 18:13:57.000000000 +0200
@@ -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>
--- linux-2.6.24.7.old/sound/soc/Kconfig 2008-05-07 01:22:34.000000000 +0200
+++ linux-2.6.24.7/sound/soc/Kconfig 2009-04-12 18:13:57.000000000 +0200
@@ -28,6 +28,7 @@
source "sound/soc/pxa/Kconfig"
source "sound/soc/s3c24xx/Kconfig"
source "sound/soc/sh/Kconfig"
+source "sound/soc/jz4740/Kconfig"
# Supported codecs
source "sound/soc/codecs/Kconfig"
--- linux-2.6.24.7.old/sound/soc/Makefile 2008-05-07 01:22:34.000000000 +0200
+++ linux-2.6.24.7/sound/soc/Makefile 2009-04-12 18:13:57.000000000 +0200
@@ -1,4 +1,4 @@
snd-soc-core-objs := soc-core.o soc-dapm.o
obj-$(CONFIG_SND_SOC) += snd-soc-core.o
-obj-$(CONFIG_SND_SOC) += codecs/ at91/ pxa/ s3c24xx/ sh/
+obj-$(CONFIG_SND_SOC) += codecs/ at91/ pxa/ s3c24xx/ sh/ jz4740/
--- linux-2.6.24.7.old/sound/soc/codecs/Kconfig 2008-05-07 01:22:34.000000000 +0200
+++ linux-2.6.24.7/sound/soc/codecs/Kconfig 2009-04-12 18:13:57.000000000 +0200
@@ -37,3 +37,8 @@
bool
depends on SND_SOC_CS4270
+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.
--- linux-2.6.24.7.old/sound/soc/codecs/Makefile 2008-05-07 01:22:34.000000000 +0200
+++ linux-2.6.24.7/sound/soc/codecs/Makefile 2009-04-12 18:13:57.000000000 +0200
@@ -4,6 +4,7 @@
snd-soc-wm8753-objs := wm8753.o
snd-soc-wm9712-objs := wm9712.o
snd-soc-cs4270-objs := cs4270.o
+snd-soc-jzcodec-objs := jzcodec.o
obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o
obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o
@@ -11,3 +12,4 @@
obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o
obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o
+obj-$(CONFIG_SND_SOC_ICODEC) += snd-soc-jzcodec.o
--- linux-2.6.24.7.old/sound/soc/codecs/jzcodec.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.24.7/sound/soc/codecs/jzcodec.c 2009-04-12 18:13:57.000000000 +0200
@@ -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");
--- linux-2.6.24.7.old/sound/soc/codecs/jzcodec.h 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.24.7/sound/soc/codecs/jzcodec.h 2009-04-12 18:13:57.000000000 +0200
@@ -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
--- linux-2.6.24.7.old/sound/soc/jz4740/Kconfig 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.24.7/sound/soc/jz4740/Kconfig 2009-04-12 18:13:57.000000000 +0200
@@ -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.
--- linux-2.6.24.7.old/sound/soc/jz4740/Makefile 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.24.7/sound/soc/jz4740/Makefile 2009-04-12 18:13:57.000000000 +0200
@@ -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
--- linux-2.6.24.7.old/sound/soc/jz4740/jz4740-ac97.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.24.7/sound/soc/jz4740/jz4740-ac97.c 2009-04-12 18:13:57.000000000 +0200
@@ -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");
--- linux-2.6.24.7.old/sound/soc/jz4740/jz4740-ac97.h 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.24.7/sound/soc/jz4740/jz4740-ac97.h 2009-04-12 18:13:57.000000000 +0200
@@ -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
--- linux-2.6.24.7.old/sound/soc/jz4740/jz4740-i2s.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.24.7/sound/soc/jz4740/jz4740-i2s.c 2009-04-12 18:13:57.000000000 +0200
@@ -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");
--- linux-2.6.24.7.old/sound/soc/jz4740/jz4740-i2s.h 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.24.7/sound/soc/jz4740/jz4740-i2s.h 2009-04-12 18:13:57.000000000 +0200
@@ -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
--- linux-2.6.24.7.old/sound/soc/jz4740/jz4740-pcm.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.24.7/sound/soc/jz4740/jz4740-pcm.c 2009-04-12 18:13:57.000000000 +0200
@@ -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");
--- linux-2.6.24.7.old/sound/soc/jz4740/jz4740-pcm.h 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.24.7/sound/soc/jz4740/jz4740-pcm.h 2009-04-12 18:13:57.000000000 +0200
@@ -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
--- linux-2.6.24.7.old/sound/soc/jz4740/pavo.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.24.7/sound/soc/jz4740/pavo.c 2009-04-12 18:13:57.000000000 +0200
@@ -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");