--- 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 + * + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 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 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 . + comments at the top of . 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=,,,,," to the kernel + command line. + + See 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 and the head of - as well as + as well as 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: + * + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#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 0) { + count -= 2; + jz_audio_count += 2; + cnt += 2 * jz_audio_k; + d1 = *(s++) - 0x80; + d2 = *(s++) - 0x80; + for (i=0;i= jz_audio_q * 2) { + cnt += 2 * jz_audio_k; + last_jz_audio_count = jz_audio_count; + for (i=0;i= 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 #include -#include #include #include #include --- 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#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 + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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");