diff options
author | Frederic Weisbecker | 2009-10-18 01:09:09 +0200 |
---|---|---|
committer | Frederic Weisbecker | 2009-10-18 01:12:33 +0200 |
commit | 0f8f86c7bdd1c954fbe153af437a0d91a6c5721a (patch) | |
tree | 94a8d419a470a4f9852ca397bb9bbe48db92ff5c /drivers/staging/dream/qdsp5/audio_out.c | |
parent | Merge branch 'linus' into tracing/hw-breakpoints (diff) | |
parent | perf tools: Move dereference after NULL test (diff) | |
download | kernel-qcow2-linux-0f8f86c7bdd1c954fbe153af437a0d91a6c5721a.tar.gz kernel-qcow2-linux-0f8f86c7bdd1c954fbe153af437a0d91a6c5721a.tar.xz kernel-qcow2-linux-0f8f86c7bdd1c954fbe153af437a0d91a6c5721a.zip |
Merge commit 'perf/core' into perf/hw-breakpoint
Conflicts:
kernel/Makefile
kernel/trace/Makefile
kernel/trace/trace.h
samples/Makefile
Merge reason: We need to be uptodate with the perf events development
branch because we plan to rewrite the breakpoints API on top of
perf events.
Diffstat (limited to 'drivers/staging/dream/qdsp5/audio_out.c')
-rw-r--r-- | drivers/staging/dream/qdsp5/audio_out.c | 851 |
1 files changed, 851 insertions, 0 deletions
diff --git a/drivers/staging/dream/qdsp5/audio_out.c b/drivers/staging/dream/qdsp5/audio_out.c new file mode 100644 index 000000000000..d1adcf65f2bd --- /dev/null +++ b/drivers/staging/dream/qdsp5/audio_out.c @@ -0,0 +1,851 @@ +/* arch/arm/mach-msm/qdsp5/audio_out.c + * + * pcm audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/miscdevice.h> +#include <linux/uaccess.h> +#include <linux/kthread.h> +#include <linux/wait.h> +#include <linux/dma-mapping.h> +#include <linux/debugfs.h> +#include <linux/delay.h> +#include <linux/wakelock.h> + +#include <linux/msm_audio.h> + +#include <asm/atomic.h> +#include <asm/ioctls.h> +#include <mach/msm_adsp.h> + +#include "audmgr.h" + +#include <mach/qdsp5/qdsp5audppcmdi.h> +#include <mach/qdsp5/qdsp5audppmsg.h> + +#include <mach/htc_pwrsink.h> + +#include "evlog.h" + +#define LOG_AUDIO_EVENTS 1 +#define LOG_AUDIO_FAULTS 0 + +enum { + EV_NULL, + EV_OPEN, + EV_WRITE, + EV_RETURN, + EV_IOCTL, + EV_WRITE_WAIT, + EV_WAIT_EVENT, + EV_FILL_BUFFER, + EV_SEND_BUFFER, + EV_DSP_EVENT, + EV_ENABLE, +}; + +#if (LOG_AUDIO_EVENTS != 1) +static inline void LOG(unsigned id, unsigned arg) {} +#else +static const char *pcm_log_strings[] = { + "NULL", + "OPEN", + "WRITE", + "RETURN", + "IOCTL", + "WRITE_WAIT", + "WAIT_EVENT", + "FILL_BUFFER", + "SEND_BUFFER", + "DSP_EVENT", + "ENABLE", +}; + +DECLARE_LOG(pcm_log, 64, pcm_log_strings); + +static int __init _pcm_log_init(void) +{ + return ev_log_init(&pcm_log); +} +module_init(_pcm_log_init); + +#define LOG(id,arg) ev_log_write(&pcm_log, id, arg) +#endif + + + + + +#define BUFSZ (960 * 5) +#define DMASZ (BUFSZ * 2) + +#define AUDPP_CMD_CFG_OBJ_UPDATE 0x8000 +#define AUDPP_CMD_EQ_FLAG_DIS 0x0000 +#define AUDPP_CMD_EQ_FLAG_ENA -1 +#define AUDPP_CMD_IIR_FLAG_DIS 0x0000 +#define AUDPP_CMD_IIR_FLAG_ENA -1 + +#define AUDPP_CMD_IIR_TUNING_FILTER 1 +#define AUDPP_CMD_EQUALIZER 2 +#define AUDPP_CMD_ADRC 3 + +#define ADRC_ENABLE 0x0001 +#define EQ_ENABLE 0x0002 +#define IIR_ENABLE 0x0004 + +struct adrc_filter { + uint16_t compression_th; + uint16_t compression_slope; + uint16_t rms_time; + uint16_t attack_const_lsw; + uint16_t attack_const_msw; + uint16_t release_const_lsw; + uint16_t release_const_msw; + uint16_t adrc_system_delay; +}; + +struct eqalizer { + uint16_t num_bands; + uint16_t eq_params[132]; +}; + +struct rx_iir_filter { + uint16_t num_bands; + uint16_t iir_params[48]; +}; + +typedef struct { + audpp_cmd_cfg_object_params_common common; + uint16_t eq_flag; + uint16_t num_bands; + uint16_t eq_params[132]; +} audpp_cmd_cfg_object_params_eq; + +typedef struct { + audpp_cmd_cfg_object_params_common common; + uint16_t active_flag; + uint16_t num_bands; + uint16_t iir_params[48]; +} audpp_cmd_cfg_object_params_rx_iir; + +struct buffer { + void *data; + unsigned size; + unsigned used; + unsigned addr; +}; + +struct audio { + struct buffer out[2]; + + spinlock_t dsp_lock; + + uint8_t out_head; + uint8_t out_tail; + uint8_t out_needed; /* number of buffers the dsp is waiting for */ + + atomic_t out_bytes; + + struct mutex lock; + struct mutex write_lock; + wait_queue_head_t wait; + + /* configuration to use on next enable */ + uint32_t out_sample_rate; + uint32_t out_channel_mode; + uint32_t out_weight; + uint32_t out_buffer_size; + + struct audmgr audmgr; + + /* data allocated for various buffers */ + char *data; + dma_addr_t phys; + + int opened; + int enabled; + int running; + int stopped; /* set when stopped, cleared on flush */ + unsigned volume; + + struct wake_lock wakelock; + struct wake_lock idlelock; + + int adrc_enable; + struct adrc_filter adrc; + + int eq_enable; + struct eqalizer eq; + + int rx_iir_enable; + struct rx_iir_filter iir; +}; + +static void audio_prevent_sleep(struct audio *audio) +{ + printk(KERN_INFO "++++++++++++++++++++++++++++++\n"); + wake_lock(&audio->wakelock); + wake_lock(&audio->idlelock); +} + +static void audio_allow_sleep(struct audio *audio) +{ + wake_unlock(&audio->wakelock); + wake_unlock(&audio->idlelock); + printk(KERN_INFO "------------------------------\n"); +} + +static int audio_dsp_out_enable(struct audio *audio, int yes); +static int audio_dsp_send_buffer(struct audio *audio, unsigned id, unsigned len); +static int audio_dsp_set_adrc(struct audio *audio); +static int audio_dsp_set_eq(struct audio *audio); +static int audio_dsp_set_rx_iir(struct audio *audio); + +static void audio_dsp_event(void *private, unsigned id, uint16_t *msg); + +/* must be called with audio->lock held */ +static int audio_enable(struct audio *audio) +{ + struct audmgr_config cfg; + int rc; + + pr_info("audio_enable()\n"); + + if (audio->enabled) + return 0; + + /* refuse to start if we're not ready */ + if (!audio->out[0].used || !audio->out[1].used) + return -EIO; + + /* we start buffers 0 and 1, so buffer 0 will be the + * next one the dsp will want + */ + audio->out_tail = 0; + audio->out_needed = 0; + + cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE; + cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000; + cfg.def_method = RPC_AUD_DEF_METHOD_HOST_PCM; + cfg.codec = RPC_AUD_DEF_CODEC_PCM; + cfg.snd_method = RPC_SND_METHOD_MIDI; + + audio_prevent_sleep(audio); + rc = audmgr_enable(&audio->audmgr, &cfg); + if (rc < 0) { + audio_allow_sleep(audio); + return rc; + } + + if (audpp_enable(-1, audio_dsp_event, audio)) { + pr_err("audio: audpp_enable() failed\n"); + audmgr_disable(&audio->audmgr); + audio_allow_sleep(audio); + return -ENODEV; + } + + audio->enabled = 1; + htc_pwrsink_set(PWRSINK_AUDIO, 100); + return 0; +} + +/* must be called with audio->lock held */ +static int audio_disable(struct audio *audio) +{ + pr_info("audio_disable()\n"); + if (audio->enabled) { + audio->enabled = 0; + audio_dsp_out_enable(audio, 0); + + audpp_disable(-1, audio); + + wake_up(&audio->wait); + audmgr_disable(&audio->audmgr); + audio->out_needed = 0; + audio_allow_sleep(audio); + } + return 0; +} + +/* ------------------- dsp --------------------- */ +static void audio_dsp_event(void *private, unsigned id, uint16_t *msg) +{ + struct audio *audio = private; + struct buffer *frame; + unsigned long flags; + + LOG(EV_DSP_EVENT, id); + switch (id) { + case AUDPP_MSG_HOST_PCM_INTF_MSG: { + unsigned id = msg[2]; + unsigned idx = msg[3] - 1; + + /* pr_info("audio_dsp_event: HOST_PCM id %d idx %d\n", id, idx); */ + if (id != AUDPP_MSG_HOSTPCM_ID_ARM_RX) { + pr_err("bogus id\n"); + break; + } + if (idx > 1) { + pr_err("bogus buffer idx\n"); + break; + } + + spin_lock_irqsave(&audio->dsp_lock, flags); + if (audio->running) { + atomic_add(audio->out[idx].used, &audio->out_bytes); + audio->out[idx].used = 0; + + frame = audio->out + audio->out_tail; + if (frame->used) { + audio_dsp_send_buffer( + audio, audio->out_tail, frame->used); + audio->out_tail ^= 1; + } else { + audio->out_needed++; + } + wake_up(&audio->wait); + } + spin_unlock_irqrestore(&audio->dsp_lock, flags); + break; + } + case AUDPP_MSG_PCMDMAMISSED: + pr_info("audio_dsp_event: PCMDMAMISSED %d\n", msg[0]); + break; + case AUDPP_MSG_CFG_MSG: + if (msg[0] == AUDPP_MSG_ENA_ENA) { + LOG(EV_ENABLE, 1); + pr_info("audio_dsp_event: CFG_MSG ENABLE\n"); + audio->out_needed = 0; + audio->running = 1; + audpp_set_volume_and_pan(5, audio->volume, 0); + audio_dsp_set_adrc(audio); + audio_dsp_set_eq(audio); + audio_dsp_set_rx_iir(audio); + audio_dsp_out_enable(audio, 1); + } else if (msg[0] == AUDPP_MSG_ENA_DIS) { + LOG(EV_ENABLE, 0); + pr_info("audio_dsp_event: CFG_MSG DISABLE\n"); + audio->running = 0; + } else { + pr_err("audio_dsp_event: CFG_MSG %d?\n", msg[0]); + } + break; + default: + pr_err("audio_dsp_event: UNKNOWN (%d)\n", id); + } +} + +static int audio_dsp_out_enable(struct audio *audio, int yes) +{ + audpp_cmd_pcm_intf cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPP_CMD_PCM_INTF_2; + cmd.object_num = AUDPP_CMD_PCM_INTF_OBJECT_NUM; + cmd.config = AUDPP_CMD_PCM_INTF_CONFIG_CMD_V; + cmd.intf_type = AUDPP_CMD_PCM_INTF_RX_ENA_ARMTODSP_V; + + if (yes) { + cmd.write_buf1LSW = audio->out[0].addr; + cmd.write_buf1MSW = audio->out[0].addr >> 16; + cmd.write_buf1_len = audio->out[0].size; + cmd.write_buf2LSW = audio->out[1].addr; + cmd.write_buf2MSW = audio->out[1].addr >> 16; + cmd.write_buf2_len = audio->out[1].size; + cmd.arm_to_rx_flag = AUDPP_CMD_PCM_INTF_ENA_V; + cmd.weight_decoder_to_rx = audio->out_weight; + cmd.weight_arm_to_rx = 1; + cmd.partition_number_arm_to_dsp = 0; + cmd.sample_rate = audio->out_sample_rate; + cmd.channel_mode = audio->out_channel_mode; + } + + return audpp_send_queue2(&cmd, sizeof(cmd)); +} + +static int audio_dsp_send_buffer(struct audio *audio, unsigned idx, unsigned len) +{ + audpp_cmd_pcm_intf_send_buffer cmd; + + cmd.cmd_id = AUDPP_CMD_PCM_INTF_2; + cmd.host_pcm_object = AUDPP_CMD_PCM_INTF_OBJECT_NUM; + cmd.config = AUDPP_CMD_PCM_INTF_BUFFER_CMD_V; + cmd.intf_type = AUDPP_CMD_PCM_INTF_RX_ENA_ARMTODSP_V; + cmd.dsp_to_arm_buf_id = 0; + cmd.arm_to_dsp_buf_id = idx + 1; + cmd.arm_to_dsp_buf_len = len; + + LOG(EV_SEND_BUFFER, idx); + return audpp_send_queue2(&cmd, sizeof(cmd)); +} + +static int audio_dsp_set_adrc(struct audio *audio) +{ + audpp_cmd_cfg_object_params_adrc cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.comman_cfg = AUDPP_CMD_CFG_OBJ_UPDATE; + cmd.common.command_type = AUDPP_CMD_ADRC; + + if (audio->adrc_enable) { + cmd.adrc_flag = AUDPP_CMD_ADRC_FLAG_ENA; + cmd.compression_th = audio->adrc.compression_th; + cmd.compression_slope = audio->adrc.compression_slope; + cmd.rms_time = audio->adrc.rms_time; + cmd.attack_const_lsw = audio->adrc.attack_const_lsw; + cmd.attack_const_msw = audio->adrc.attack_const_msw; + cmd.release_const_lsw = audio->adrc.release_const_lsw; + cmd.release_const_msw = audio->adrc.release_const_msw; + cmd.adrc_system_delay = audio->adrc.adrc_system_delay; + } else { + cmd.adrc_flag = AUDPP_CMD_ADRC_FLAG_DIS; + } + return audpp_send_queue3(&cmd, sizeof(cmd)); +} + +static int audio_dsp_set_eq(struct audio *audio) +{ + audpp_cmd_cfg_object_params_eq cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.comman_cfg = AUDPP_CMD_CFG_OBJ_UPDATE; + cmd.common.command_type = AUDPP_CMD_EQUALIZER; + + if (audio->eq_enable) { + cmd.eq_flag = AUDPP_CMD_EQ_FLAG_ENA; + cmd.num_bands = audio->eq.num_bands; + memcpy(&cmd.eq_params, audio->eq.eq_params, + sizeof(audio->eq.eq_params)); + } else { + cmd.eq_flag = AUDPP_CMD_EQ_FLAG_DIS; + } + return audpp_send_queue3(&cmd, sizeof(cmd)); +} + +static int audio_dsp_set_rx_iir(struct audio *audio) +{ + audpp_cmd_cfg_object_params_rx_iir cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.comman_cfg = AUDPP_CMD_CFG_OBJ_UPDATE; + cmd.common.command_type = AUDPP_CMD_IIR_TUNING_FILTER; + + if (audio->rx_iir_enable) { + cmd.active_flag = AUDPP_CMD_IIR_FLAG_ENA; + cmd.num_bands = audio->iir.num_bands; + memcpy(&cmd.iir_params, audio->iir.iir_params, + sizeof(audio->iir.iir_params)); + } else { + cmd.active_flag = AUDPP_CMD_IIR_FLAG_DIS; + } + + return audpp_send_queue3(&cmd, sizeof(cmd)); +} + +/* ------------------- device --------------------- */ + +static int audio_enable_adrc(struct audio *audio, int enable) +{ + if (audio->adrc_enable != enable) { + audio->adrc_enable = enable; + if (audio->running) + audio_dsp_set_adrc(audio); + } + return 0; +} + +static int audio_enable_eq(struct audio *audio, int enable) +{ + if (audio->eq_enable != enable) { + audio->eq_enable = enable; + if (audio->running) + audio_dsp_set_eq(audio); + } + return 0; +} + +static int audio_enable_rx_iir(struct audio *audio, int enable) +{ + if (audio->rx_iir_enable != enable) { + audio->rx_iir_enable = enable; + if (audio->running) + audio_dsp_set_rx_iir(audio); + } + return 0; +} + +static void audio_flush(struct audio *audio) +{ + audio->out[0].used = 0; + audio->out[1].used = 0; + audio->out_head = 0; + audio->out_tail = 0; + audio->stopped = 0; +} + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct audio *audio = file->private_data; + int rc; + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + stats.byte_count = atomic_read(&audio->out_bytes); + if (copy_to_user((void*) arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } + if (cmd == AUDIO_SET_VOLUME) { + unsigned long flags; + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->volume = arg; + if (audio->running) + audpp_set_volume_and_pan(6, arg, 0); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + } + + LOG(EV_IOCTL, cmd); + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: + rc = audio_enable(audio); + break; + case AUDIO_STOP: + rc = audio_disable(audio); + audio->stopped = 1; + break; + case AUDIO_FLUSH: + if (audio->stopped) { + /* Make sure we're stopped and we wake any threads + * that might be blocked holding the write_lock. + * While audio->stopped write threads will always + * exit immediately. + */ + wake_up(&audio->wait); + mutex_lock(&audio->write_lock); + audio_flush(audio); + mutex_unlock(&audio->write_lock); + } + case AUDIO_SET_CONFIG: { + struct msm_audio_config config; + if (copy_from_user(&config, (void*) arg, sizeof(config))) { + rc = -EFAULT; + break; + } + if (config.channel_count == 1) { + config.channel_count = AUDPP_CMD_PCM_INTF_MONO_V; + } else if (config.channel_count == 2) { + config.channel_count= AUDPP_CMD_PCM_INTF_STEREO_V; + } else { + rc = -EINVAL; + break; + } + audio->out_sample_rate = config.sample_rate; + audio->out_channel_mode = config.channel_count; + rc = 0; + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config config; + config.buffer_size = BUFSZ; + config.buffer_count = 2; + config.sample_rate = audio->out_sample_rate; + if (audio->out_channel_mode == AUDPP_CMD_PCM_INTF_MONO_V) { + config.channel_count = 1; + } else { + config.channel_count = 2; + } + config.unused[0] = 0; + config.unused[1] = 0; + config.unused[2] = 0; + config.unused[3] = 0; + if (copy_to_user((void*) arg, &config, sizeof(config))) { + rc = -EFAULT; + } else { + rc = 0; + } + break; + } + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +static ssize_t audio_read(struct file *file, char __user *buf, size_t count, loff_t *pos) +{ + return -EINVAL; +} + +static inline int rt_policy(int policy) +{ + if (unlikely(policy == SCHED_FIFO) || unlikely(policy == SCHED_RR)) + return 1; + return 0; +} + +static inline int task_has_rt_policy(struct task_struct *p) +{ + return rt_policy(p->policy); +} + +static ssize_t audio_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct sched_param s = { .sched_priority = 1 }; + struct audio *audio = file->private_data; + unsigned long flags; + const char __user *start = buf; + struct buffer *frame; + size_t xfer; + int old_prio = current->rt_priority; + int old_policy = current->policy; + int cap_nice = cap_raised(current_cap(), CAP_SYS_NICE); + int rc = 0; + + LOG(EV_WRITE, count | (audio->running << 28) | (audio->stopped << 24)); + + /* just for this write, set us real-time */ + if (!task_has_rt_policy(current)) { + struct cred *new = prepare_creds(); + cap_raise(new->cap_effective, CAP_SYS_NICE); + commit_creds(new); + sched_setscheduler(current, SCHED_RR, &s); + } + + mutex_lock(&audio->write_lock); + while (count > 0) { + frame = audio->out + audio->out_head; + + LOG(EV_WAIT_EVENT, 0); + rc = wait_event_interruptible(audio->wait, + (frame->used == 0) || (audio->stopped)); + LOG(EV_WAIT_EVENT, 1); + + if (rc < 0) + break; + if (audio->stopped) { + rc = -EBUSY; + break; + } + xfer = count > frame->size ? frame->size : count; + if (copy_from_user(frame->data, buf, xfer)) { + rc = -EFAULT; + break; + } + frame->used = xfer; + audio->out_head ^= 1; + count -= xfer; + buf += xfer; + + spin_lock_irqsave(&audio->dsp_lock, flags); + LOG(EV_FILL_BUFFER, audio->out_head ^ 1); + frame = audio->out + audio->out_tail; + if (frame->used && audio->out_needed) { + audio_dsp_send_buffer(audio, audio->out_tail, frame->used); + audio->out_tail ^= 1; + audio->out_needed--; + } + spin_unlock_irqrestore(&audio->dsp_lock, flags); + } + + mutex_unlock(&audio->write_lock); + + /* restore scheduling policy and priority */ + if (!rt_policy(old_policy)) { + struct sched_param v = { .sched_priority = old_prio }; + sched_setscheduler(current, old_policy, &v); + if (likely(!cap_nice)) { + struct cred *new = prepare_creds(); + cap_lower(new->cap_effective, CAP_SYS_NICE); + commit_creds(new); + sched_setscheduler(current, SCHED_RR, &s); + } + } + + LOG(EV_RETURN,(buf > start) ? (buf - start) : rc); + if (buf > start) + return buf - start; + return rc; +} + +static int audio_release(struct inode *inode, struct file *file) +{ + struct audio *audio = file->private_data; + + LOG(EV_OPEN, 0); + mutex_lock(&audio->lock); + audio_disable(audio); + audio_flush(audio); + audio->opened = 0; + mutex_unlock(&audio->lock); + htc_pwrsink_set(PWRSINK_AUDIO, 0); + return 0; +} + +static struct audio the_audio; + +static int audio_open(struct inode *inode, struct file *file) +{ + struct audio *audio = &the_audio; + int rc; + + mutex_lock(&audio->lock); + + if (audio->opened) { + pr_err("audio: busy\n"); + rc = -EBUSY; + goto done; + } + + if (!audio->data) { + audio->data = dma_alloc_coherent(NULL, DMASZ, + &audio->phys, GFP_KERNEL); + if (!audio->data) { + pr_err("audio: could not allocate DMA buffers\n"); + rc = -ENOMEM; + goto done; + } + } + + rc = audmgr_open(&audio->audmgr); + if (rc) + goto done; + + audio->out_buffer_size = BUFSZ; + audio->out_sample_rate = 44100; + audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V; + audio->out_weight = 100; + + audio->out[0].data = audio->data + 0; + audio->out[0].addr = audio->phys + 0; + audio->out[0].size = BUFSZ; + + audio->out[1].data = audio->data + BUFSZ; + audio->out[1].addr = audio->phys + BUFSZ; + audio->out[1].size = BUFSZ; + + audio->volume = 0x2000; + + audio_flush(audio); + + file->private_data = audio; + audio->opened = 1; + rc = 0; + LOG(EV_OPEN, 1); +done: + mutex_unlock(&audio->lock); + return rc; +} + +static long audpp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct audio *audio = file->private_data; + int rc = 0, enable; + uint16_t enable_mask; + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_ENABLE_AUDPP: + if (copy_from_user(&enable_mask, (void *) arg, sizeof(enable_mask))) + goto out_fault; + + enable = (enable_mask & ADRC_ENABLE)? 1 : 0; + audio_enable_adrc(audio, enable); + enable = (enable_mask & EQ_ENABLE)? 1 : 0; + audio_enable_eq(audio, enable); + enable = (enable_mask & IIR_ENABLE)? 1 : 0; + audio_enable_rx_iir(audio, enable); + break; + + case AUDIO_SET_ADRC: + if (copy_from_user(&audio->adrc, (void*) arg, sizeof(audio->adrc))) + goto out_fault; + break; + + case AUDIO_SET_EQ: + if (copy_from_user(&audio->eq, (void*) arg, sizeof(audio->eq))) + goto out_fault; + break; + + case AUDIO_SET_RX_IIR: + if (copy_from_user(&audio->iir, (void*) arg, sizeof(audio->iir))) + goto out_fault; + break; + + default: + rc = -EINVAL; + } + + goto out; + + out_fault: + rc = -EFAULT; + out: + mutex_unlock(&audio->lock); + return rc; +} + +static int audpp_open(struct inode *inode, struct file *file) +{ + struct audio *audio = &the_audio; + + file->private_data = audio; + return 0; +} + +static struct file_operations audio_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_release, + .read = audio_read, + .write = audio_write, + .unlocked_ioctl = audio_ioctl, +}; + +static struct file_operations audpp_fops = { + .owner = THIS_MODULE, + .open = audpp_open, + .unlocked_ioctl = audpp_ioctl, +}; + +struct miscdevice audio_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_pcm_out", + .fops = &audio_fops, +}; + +struct miscdevice audpp_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_pcm_ctl", + .fops = &audpp_fops, +}; + +static int __init audio_init(void) +{ + mutex_init(&the_audio.lock); + mutex_init(&the_audio.write_lock); + spin_lock_init(&the_audio.dsp_lock); + init_waitqueue_head(&the_audio.wait); + wake_lock_init(&the_audio.wakelock, WAKE_LOCK_SUSPEND, "audio_pcm"); + wake_lock_init(&the_audio.idlelock, WAKE_LOCK_IDLE, "audio_pcm_idle"); + return (misc_register(&audio_misc) || misc_register(&audpp_misc)); +} + +device_initcall(audio_init); |