mirror of
git://projects.qi-hardware.com/openwrt-xburst.git
synced 2024-11-25 04:01:52 +02:00
23913 lines
650 KiB
Diff
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(¶ms, _params, sizeof(params)))
|
|
+ return -EFAULT;
|
|
+ err = snd_pcm_sw_params(substream, ¶ms);
|
|
+ if (copy_to_user(_params, ¶ms, 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");
|