blob: f66164bfc62f5a194e14a4a215afeed7ba670e34 [file] [log] [blame]
/*
* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* 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/msm_audio.h>
#include <linux/compat.h>
#include "q6audio_common.h"
#include <dsp/msm-audio-effects-q6-v2.h>
#include "audio_utils_aio.h"
#define MAX_CHANNELS_SUPPORTED 8
#define WAIT_TIMEDOUT_DURATION_SECS 1
struct q6audio_effects {
wait_queue_head_t read_wait;
wait_queue_head_t write_wait;
struct audio_client *ac;
struct msm_hwacc_effects_config config;
struct mutex lock;
atomic_t in_count;
atomic_t out_count;
int opened;
int started;
int buf_alloc;
struct msm_nt_eff_all_config audio_effects;
};
static void audio_effects_init_pp(struct audio_client *ac)
{
int ret = 0;
struct asm_softvolume_params softvol = {
.period = SOFT_VOLUME_PERIOD,
.step = SOFT_VOLUME_STEP,
.rampingcurve = SOFT_VOLUME_CURVE_LINEAR,
};
if (!ac) {
pr_err("%s: audio client null to init pp\n", __func__);
return;
}
ret = q6asm_set_softvolume_v2(ac, &softvol,
SOFT_VOLUME_INSTANCE_1);
if (ret < 0)
pr_err("%s: Send SoftVolume Param failed ret=%d\n",
__func__, ret);
}
static void audio_effects_deinit_pp(struct audio_client *ac)
{
if (!ac) {
pr_err("%s: audio client null to deinit pp\n", __func__);
return;
}
}
static void audio_effects_event_handler(uint32_t opcode, uint32_t token,
uint32_t *payload, void *priv)
{
struct q6audio_effects *effects;
if (!payload || !priv) {
pr_err("%s: invalid data to handle events, payload: %pK, priv: %pK\n",
__func__, payload, priv);
return;
}
effects = (struct q6audio_effects *)priv;
switch (opcode) {
case ASM_DATA_EVENT_WRITE_DONE_V2: {
atomic_inc(&effects->out_count);
wake_up(&effects->write_wait);
break;
}
case ASM_DATA_EVENT_READ_DONE_V2: {
atomic_inc(&effects->in_count);
wake_up(&effects->read_wait);
break;
}
case APR_BASIC_RSP_RESULT: {
pr_debug("%s: APR_BASIC_RSP_RESULT Cmd[0x%x] Status[0x%x]\n",
__func__, payload[0], payload[1]);
switch (payload[0]) {
case ASM_SESSION_CMD_RUN_V2:
pr_debug("ASM_SESSION_CMD_RUN_V2\n");
break;
default:
pr_debug("%s: Payload = [0x%x] stat[0x%x]\n",
__func__, payload[0], payload[1]);
break;
}
break;
}
default:
pr_debug("%s: Unhandled Event 0x%x token = 0x%x\n",
__func__, opcode, token);
break;
}
}
static int audio_effects_shared_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct q6audio_effects *effects = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START: {
pr_debug("%s: AUDIO_START\n", __func__);
mutex_lock(&effects->lock);
rc = q6asm_open_read_write_v2(effects->ac,
FORMAT_LINEAR_PCM,
FORMAT_MULTI_CHANNEL_LINEAR_PCM,
effects->config.meta_mode_enabled,
effects->config.output.bits_per_sample,
true /*overwrite topology*/,
ASM_STREAM_POSTPROC_TOPO_ID_HPX_MASTER);
if (rc < 0) {
pr_err("%s: Open failed for hw accelerated effects:rc=%d\n",
__func__, rc);
rc = -EINVAL;
mutex_unlock(&effects->lock);
goto ioctl_fail;
}
effects->opened = 1;
pr_debug("%s: dec buf size: %d, num_buf: %d, enc buf size: %d, num_buf: %d\n",
__func__, effects->config.output.buf_size,
effects->config.output.num_buf,
effects->config.input.buf_size,
effects->config.input.num_buf);
rc = q6asm_audio_client_buf_alloc_contiguous(IN, effects->ac,
effects->config.output.buf_size,
effects->config.output.num_buf);
if (rc < 0) {
pr_err("%s: Write buffer Allocation failed rc = %d\n",
__func__, rc);
rc = -ENOMEM;
mutex_unlock(&effects->lock);
goto ioctl_fail;
}
atomic_set(&effects->in_count, effects->config.input.num_buf);
rc = q6asm_audio_client_buf_alloc_contiguous(OUT, effects->ac,
effects->config.input.buf_size,
effects->config.input.num_buf);
if (rc < 0) {
pr_err("%s: Read buffer Allocation failed rc = %d\n",
__func__, rc);
rc = -ENOMEM;
goto readbuf_fail;
}
atomic_set(&effects->out_count, effects->config.output.num_buf);
effects->buf_alloc = 1;
pr_debug("%s: enc: sample_rate: %d, num_channels: %d\n",
__func__, effects->config.input.sample_rate,
effects->config.input.num_channels);
rc = q6asm_enc_cfg_blk_pcm(effects->ac,
effects->config.input.sample_rate,
effects->config.input.num_channels);
if (rc < 0) {
pr_err("%s: pcm read block config failed\n", __func__);
rc = -EINVAL;
goto cfg_fail;
}
pr_debug("%s: dec: sample_rate: %d, num_channels: %d, bit_width: %d\n",
__func__, effects->config.output.sample_rate,
effects->config.output.num_channels,
effects->config.output.bits_per_sample);
rc = q6asm_media_format_block_pcm_format_support(
effects->ac, effects->config.output.sample_rate,
effects->config.output.num_channels,
effects->config.output.bits_per_sample);
if (rc < 0) {
pr_err("%s: pcm write format block config failed\n",
__func__);
rc = -EINVAL;
goto cfg_fail;
}
audio_effects_init_pp(effects->ac);
rc = q6asm_run(effects->ac, 0x00, 0x00, 0x00);
if (!rc)
effects->started = 1;
else {
effects->started = 0;
pr_err("%s: ASM run state failed\n", __func__);
}
mutex_unlock(&effects->lock);
break;
}
case AUDIO_EFFECTS_WRITE: {
char *bufptr = NULL;
uint32_t idx = 0;
uint32_t size = 0;
mutex_lock(&effects->lock);
if (!effects->started) {
rc = -EFAULT;
mutex_unlock(&effects->lock);
goto ioctl_fail;
}
rc = wait_event_timeout(effects->write_wait,
atomic_read(&effects->out_count),
WAIT_TIMEDOUT_DURATION_SECS * HZ);
if (!rc) {
pr_err("%s: write wait_event_timeout\n", __func__);
rc = -EFAULT;
mutex_unlock(&effects->lock);
goto ioctl_fail;
}
if (!atomic_read(&effects->out_count)) {
pr_err("%s: pcm stopped out_count 0\n", __func__);
rc = -EFAULT;
mutex_unlock(&effects->lock);
goto ioctl_fail;
}
bufptr = q6asm_is_cpu_buf_avail(IN, effects->ac, &size, &idx);
if (bufptr) {
if ((effects->config.buf_cfg.output_len > size) ||
copy_from_user(bufptr, (void *)arg,
effects->config.buf_cfg.output_len)) {
rc = -EFAULT;
mutex_unlock(&effects->lock);
goto ioctl_fail;
}
rc = q6asm_write(effects->ac,
effects->config.buf_cfg.output_len,
0, 0, NO_TIMESTAMP);
if (rc < 0) {
rc = -EFAULT;
mutex_unlock(&effects->lock);
goto ioctl_fail;
}
atomic_dec(&effects->out_count);
} else {
pr_err("%s: AUDIO_EFFECTS_WRITE: Buffer dropped\n",
__func__);
}
mutex_unlock(&effects->lock);
break;
}
case AUDIO_EFFECTS_READ: {
char *bufptr = NULL;
uint32_t idx = 0;
uint32_t size = 0;
mutex_lock(&effects->lock);
if (!effects->started) {
rc = -EFAULT;
mutex_unlock(&effects->lock);
goto ioctl_fail;
}
atomic_set(&effects->in_count, 0);
q6asm_read_v2(effects->ac, effects->config.buf_cfg.input_len);
/* Read might fail initially, don't error out */
if (rc < 0)
pr_err("%s: read failed\n", __func__);
rc = wait_event_timeout(effects->read_wait,
atomic_read(&effects->in_count),
WAIT_TIMEDOUT_DURATION_SECS * HZ);
if (!rc) {
pr_err("%s: read wait_event_timeout\n", __func__);
rc = -EFAULT;
mutex_unlock(&effects->lock);
goto ioctl_fail;
}
if (!atomic_read(&effects->in_count)) {
pr_err("%s: pcm stopped in_count 0\n", __func__);
rc = -EFAULT;
mutex_unlock(&effects->lock);
goto ioctl_fail;
}
bufptr = q6asm_is_cpu_buf_avail(OUT, effects->ac, &size, &idx);
if (bufptr) {
if (!((void *)arg)) {
rc = -EFAULT;
mutex_unlock(&effects->lock);
goto ioctl_fail;
}
if ((effects->config.buf_cfg.input_len > size) ||
copy_to_user((void *)arg, bufptr,
effects->config.buf_cfg.input_len)) {
rc = -EFAULT;
mutex_unlock(&effects->lock);
goto ioctl_fail;
}
}
mutex_unlock(&effects->lock);
break;
}
default:
pr_err("%s: Invalid effects config module\n", __func__);
rc = -EINVAL;
break;
}
ioctl_fail:
return rc;
readbuf_fail:
q6asm_audio_client_buf_free_contiguous(IN,
effects->ac);
mutex_unlock(&effects->lock);
return rc;
cfg_fail:
q6asm_audio_client_buf_free_contiguous(IN,
effects->ac);
q6asm_audio_client_buf_free_contiguous(OUT,
effects->ac);
effects->buf_alloc = 0;
mutex_unlock(&effects->lock);
return rc;
}
static long audio_effects_set_pp_param(struct q6audio_effects *effects,
long *values)
{
int rc = 0;
int effects_module = values[0];
switch (effects_module) {
case VIRTUALIZER_MODULE:
pr_debug("%s: VIRTUALIZER_MODULE\n", __func__);
if (msm_audio_effects_is_effmodule_supp_in_top(
effects_module, effects->ac->topology))
msm_audio_effects_virtualizer_handler(
effects->ac,
&(effects->audio_effects.virtualizer),
(long *)&values[1]);
break;
case REVERB_MODULE:
pr_debug("%s: REVERB_MODULE\n", __func__);
if (msm_audio_effects_is_effmodule_supp_in_top(
effects_module, effects->ac->topology))
msm_audio_effects_reverb_handler(effects->ac,
&(effects->audio_effects.reverb),
(long *)&values[1]);
break;
case BASS_BOOST_MODULE:
pr_debug("%s: BASS_BOOST_MODULE\n", __func__);
if (msm_audio_effects_is_effmodule_supp_in_top(
effects_module, effects->ac->topology))
msm_audio_effects_bass_boost_handler(
effects->ac,
&(effects->audio_effects.bass_boost),
(long *)&values[1]);
break;
case PBE_MODULE:
pr_debug("%s: PBE_MODULE\n", __func__);
if (msm_audio_effects_is_effmodule_supp_in_top(
effects_module, effects->ac->topology))
msm_audio_effects_pbe_handler(
effects->ac,
&(effects->audio_effects.pbe),
(long *)&values[1]);
break;
case EQ_MODULE:
pr_debug("%s: EQ_MODULE\n", __func__);
if (msm_audio_effects_is_effmodule_supp_in_top(
effects_module, effects->ac->topology))
msm_audio_effects_popless_eq_handler(
effects->ac,
&(effects->audio_effects.equalizer),
(long *)&values[1]);
break;
case SOFT_VOLUME_MODULE:
pr_debug("%s: SA PLUS VOLUME_MODULE\n", __func__);
msm_audio_effects_volume_handler_v2(effects->ac,
&(effects->audio_effects.saplus_vol),
(long *)&values[1], SOFT_VOLUME_INSTANCE_1);
break;
case SOFT_VOLUME2_MODULE:
pr_debug("%s: TOPOLOGY SWITCH VOLUME MODULE\n",
__func__);
if (msm_audio_effects_is_effmodule_supp_in_top(
effects_module, effects->ac->topology))
msm_audio_effects_volume_handler_v2(effects->ac,
&(effects->audio_effects.topo_switch_vol),
(long *)&values[1], SOFT_VOLUME_INSTANCE_2);
break;
default:
pr_err("%s: Invalid effects config module\n", __func__);
rc = -EINVAL;
}
return rc;
}
static long audio_effects_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct q6audio_effects *effects = file->private_data;
int rc = 0;
long argvalues[MAX_PP_PARAMS_SZ] = {0};
switch (cmd) {
case AUDIO_SET_EFFECTS_CONFIG: {
pr_debug("%s: AUDIO_SET_EFFECTS_CONFIG\n", __func__);
mutex_lock(&effects->lock);
memset(&effects->config, 0, sizeof(effects->config));
if (copy_from_user(&effects->config, (void *)arg,
sizeof(effects->config))) {
pr_err("%s: copy from user for AUDIO_SET_EFFECTS_CONFIG failed\n",
__func__);
rc = -EFAULT;
}
pr_debug("%s: write buf_size: %d, num_buf: %d, sample_rate: %d, channel: %d\n",
__func__, effects->config.output.buf_size,
effects->config.output.num_buf,
effects->config.output.sample_rate,
effects->config.output.num_channels);
pr_debug("%s: read buf_size: %d, num_buf: %d, sample_rate: %d, channel: %d\n",
__func__, effects->config.input.buf_size,
effects->config.input.num_buf,
effects->config.input.sample_rate,
effects->config.input.num_channels);
mutex_unlock(&effects->lock);
break;
}
case AUDIO_EFFECTS_SET_BUF_LEN: {
mutex_lock(&effects->lock);
if (copy_from_user(&effects->config.buf_cfg, (void *)arg,
sizeof(effects->config.buf_cfg))) {
pr_err("%s: copy from user for AUDIO_EFFECTS_SET_BUF_LEN failed\n",
__func__);
rc = -EFAULT;
}
pr_debug("%s: write buf len: %d, read buf len: %d\n",
__func__, effects->config.buf_cfg.output_len,
effects->config.buf_cfg.input_len);
mutex_unlock(&effects->lock);
break;
}
case AUDIO_EFFECTS_GET_BUF_AVAIL: {
struct msm_hwacc_buf_avail buf_avail;
buf_avail.input_num_avail = atomic_read(&effects->in_count);
buf_avail.output_num_avail = atomic_read(&effects->out_count);
mutex_lock(&effects->lock);
pr_debug("%s: write buf avail: %d, read buf avail: %d\n",
__func__, buf_avail.output_num_avail,
buf_avail.input_num_avail);
if (copy_to_user((void *)arg, &buf_avail,
sizeof(buf_avail))) {
pr_err("%s: copy to user for AUDIO_EFFECTS_GET_NUM_BUF_AVAIL failed\n",
__func__);
rc = -EFAULT;
}
mutex_unlock(&effects->lock);
break;
}
case AUDIO_EFFECTS_SET_PP_PARAMS: {
mutex_lock(&effects->lock);
if (copy_from_user(argvalues, (void *)arg,
MAX_PP_PARAMS_SZ*sizeof(long))) {
pr_err("%s: copy from user for pp params failed\n",
__func__);
mutex_unlock(&effects->lock);
return -EFAULT;
}
rc = audio_effects_set_pp_param(effects, argvalues);
mutex_unlock(&effects->lock);
break;
}
default:
pr_debug("%s: Calling shared ioctl\n", __func__);
rc = audio_effects_shared_ioctl(file, cmd, arg);
break;
}
if (rc)
pr_err("%s: cmd 0x%x failed\n", __func__, cmd);
return rc;
}
#ifdef CONFIG_COMPAT
struct msm_hwacc_data_config32 {
__u32 buf_size;
__u32 num_buf;
__u32 num_channels;
__u8 channel_map[MAX_CHANNELS_SUPPORTED];
__u32 sample_rate;
__u32 bits_per_sample;
};
struct msm_hwacc_buf_cfg32 {
__u32 input_len;
__u32 output_len;
};
struct msm_hwacc_buf_avail32 {
__u32 input_num_avail;
__u32 output_num_avail;
};
struct msm_hwacc_effects_config32 {
struct msm_hwacc_data_config32 input;
struct msm_hwacc_data_config32 output;
struct msm_hwacc_buf_cfg32 buf_cfg;
__u32 meta_mode_enabled;
__u32 overwrite_topology;
__s32 topology;
};
enum {
AUDIO_SET_EFFECTS_CONFIG32 = _IOW(AUDIO_IOCTL_MAGIC, 99,
struct msm_hwacc_effects_config32),
AUDIO_EFFECTS_SET_BUF_LEN32 = _IOW(AUDIO_IOCTL_MAGIC, 100,
struct msm_hwacc_buf_cfg32),
AUDIO_EFFECTS_GET_BUF_AVAIL32 = _IOW(AUDIO_IOCTL_MAGIC, 101,
struct msm_hwacc_buf_avail32),
AUDIO_EFFECTS_WRITE32 = _IOW(AUDIO_IOCTL_MAGIC, 102, compat_uptr_t),
AUDIO_EFFECTS_READ32 = _IOWR(AUDIO_IOCTL_MAGIC, 103, compat_uptr_t),
AUDIO_EFFECTS_SET_PP_PARAMS32 = _IOW(AUDIO_IOCTL_MAGIC, 104,
compat_uptr_t),
AUDIO_START32 = _IOW(AUDIO_IOCTL_MAGIC, 0, unsigned int),
};
static long audio_effects_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct q6audio_effects *effects = file->private_data;
int rc = 0, i;
switch (cmd) {
case AUDIO_SET_EFFECTS_CONFIG32: {
struct msm_hwacc_effects_config32 config32;
struct msm_hwacc_effects_config *config = &effects->config;
mutex_lock(&effects->lock);
memset(&effects->config, 0, sizeof(effects->config));
if (copy_from_user(&config32, (void *)arg,
sizeof(config32))) {
pr_err("%s: copy to user for AUDIO_SET_EFFECTS_CONFIG failed\n",
__func__);
rc = -EFAULT;
mutex_unlock(&effects->lock);
break;
}
config->input.buf_size = config32.input.buf_size;
config->input.num_buf = config32.input.num_buf;
config->input.num_channels = config32.input.num_channels;
config->input.sample_rate = config32.input.sample_rate;
config->input.bits_per_sample = config32.input.bits_per_sample;
config->input.buf_size = config32.input.buf_size;
for (i = 0; i < MAX_CHANNELS_SUPPORTED; i++)
config->input.channel_map[i] =
config32.input.channel_map[i];
config->output.buf_size = config32.output.buf_size;
config->output.num_buf = config32.output.num_buf;
config->output.num_channels = config32.output.num_channels;
config->output.sample_rate = config32.output.sample_rate;
config->output.bits_per_sample =
config32.output.bits_per_sample;
config->output.buf_size = config32.output.buf_size;
for (i = 0; i < MAX_CHANNELS_SUPPORTED; i++)
config->output.channel_map[i] =
config32.output.channel_map[i];
config->buf_cfg.input_len = config32.buf_cfg.input_len;
config->buf_cfg.output_len = config32.buf_cfg.output_len;
config->meta_mode_enabled = config32.meta_mode_enabled;
config->overwrite_topology = config32.overwrite_topology;
config->topology = config32.topology;
pr_debug("%s: write buf_size: %d, num_buf: %d, sample_rate: %d, channels: %d\n",
__func__, effects->config.output.buf_size,
effects->config.output.num_buf,
effects->config.output.sample_rate,
effects->config.output.num_channels);
pr_debug("%s: read buf_size: %d, num_buf: %d, sample_rate: %d, channels: %d\n",
__func__, effects->config.input.buf_size,
effects->config.input.num_buf,
effects->config.input.sample_rate,
effects->config.input.num_channels);
mutex_unlock(&effects->lock);
break;
}
case AUDIO_EFFECTS_SET_BUF_LEN32: {
struct msm_hwacc_buf_cfg32 buf_cfg32;
struct msm_hwacc_effects_config *config = &effects->config;
mutex_lock(&effects->lock);
if (copy_from_user(&buf_cfg32, (void *)arg,
sizeof(buf_cfg32))) {
pr_err("%s: copy from user for AUDIO_EFFECTS_SET_BUF_LEN failed\n",
__func__);
rc = -EFAULT;
mutex_unlock(&effects->lock);
break;
}
config->buf_cfg.input_len = buf_cfg32.input_len;
config->buf_cfg.output_len = buf_cfg32.output_len;
pr_debug("%s: write buf len: %d, read buf len: %d\n",
__func__, effects->config.buf_cfg.output_len,
effects->config.buf_cfg.input_len);
mutex_unlock(&effects->lock);
break;
}
case AUDIO_EFFECTS_GET_BUF_AVAIL32: {
struct msm_hwacc_buf_avail32 buf_avail;
memset(&buf_avail, 0, sizeof(buf_avail));
mutex_lock(&effects->lock);
buf_avail.input_num_avail = atomic_read(&effects->in_count);
buf_avail.output_num_avail = atomic_read(&effects->out_count);
pr_debug("%s: write buf avail: %d, read buf avail: %d\n",
__func__, buf_avail.output_num_avail,
buf_avail.input_num_avail);
if (copy_to_user((void *)arg, &buf_avail,
sizeof(buf_avail))) {
pr_err("%s: copy to user for AUDIO_EFFECTS_GET_NUM_BUF_AVAIL failed\n",
__func__);
rc = -EFAULT;
}
mutex_unlock(&effects->lock);
break;
}
case AUDIO_EFFECTS_SET_PP_PARAMS32: {
long argvalues[MAX_PP_PARAMS_SZ] = {0};
int argvalues32[MAX_PP_PARAMS_SZ] = {0};
mutex_lock(&effects->lock);
if (copy_from_user(argvalues32, (void *)arg,
MAX_PP_PARAMS_SZ*sizeof(int))) {
pr_err("%s: copy from user failed for pp params\n",
__func__);
mutex_unlock(&effects->lock);
return -EFAULT;
}
for (i = 0; i < MAX_PP_PARAMS_SZ; i++)
argvalues[i] = argvalues32[i];
rc = audio_effects_set_pp_param(effects, argvalues);
mutex_unlock(&effects->lock);
break;
}
case AUDIO_START32: {
rc = audio_effects_shared_ioctl(file, AUDIO_START, arg);
break;
}
case AUDIO_EFFECTS_WRITE32: {
rc = audio_effects_shared_ioctl(file, AUDIO_EFFECTS_WRITE, arg);
break;
}
case AUDIO_EFFECTS_READ32: {
rc = audio_effects_shared_ioctl(file, AUDIO_EFFECTS_READ, arg);
break;
}
default:
pr_debug("%s: unhandled ioctl\n", __func__);
rc = -EINVAL;
break;
}
return rc;
}
#endif
static int audio_effects_release(struct inode *inode, struct file *file)
{
struct q6audio_effects *effects = file->private_data;
int rc = 0;
if (!effects) {
pr_err("%s: effect is NULL\n", __func__);
return -EINVAL;
}
if (effects->opened) {
rc = wait_event_timeout(effects->write_wait,
atomic_read(&effects->out_count),
WAIT_TIMEDOUT_DURATION_SECS * HZ);
if (!rc)
pr_err("%s: write wait_event_timeout failed\n",
__func__);
rc = wait_event_timeout(effects->read_wait,
atomic_read(&effects->in_count),
WAIT_TIMEDOUT_DURATION_SECS * HZ);
if (!rc)
pr_err("%s: read wait_event_timeout failed\n",
__func__);
rc = q6asm_cmd(effects->ac, CMD_CLOSE);
if (rc < 0)
pr_err("%s[%pK]:Failed to close the session rc=%d\n",
__func__, effects, rc);
effects->opened = 0;
effects->started = 0;
audio_effects_deinit_pp(effects->ac);
}
if (effects->buf_alloc) {
q6asm_audio_client_buf_free_contiguous(IN, effects->ac);
q6asm_audio_client_buf_free_contiguous(OUT, effects->ac);
}
q6asm_audio_client_free(effects->ac);
mutex_destroy(&effects->lock);
kfree(effects);
pr_debug("%s: close session success\n", __func__);
return rc;
}
static int audio_effects_open(struct inode *inode, struct file *file)
{
struct q6audio_effects *effects;
int rc = 0;
effects = kzalloc(sizeof(struct q6audio_effects), GFP_KERNEL);
if (!effects)
return -ENOMEM;
effects->ac = q6asm_audio_client_alloc(
(app_cb)audio_effects_event_handler,
(void *)effects);
if (!effects->ac) {
pr_err("%s: Could not allocate memory for audio client\n",
__func__);
kfree(effects);
return -ENOMEM;
}
init_waitqueue_head(&effects->read_wait);
init_waitqueue_head(&effects->write_wait);
mutex_init(&effects->lock);
effects->opened = 0;
effects->started = 0;
effects->buf_alloc = 0;
file->private_data = effects;
pr_debug("%s: open session success\n", __func__);
return rc;
}
static const struct file_operations audio_effects_fops = {
.owner = THIS_MODULE,
.open = audio_effects_open,
.release = audio_effects_release,
.unlocked_ioctl = audio_effects_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = audio_effects_compat_ioctl,
#endif
};
struct miscdevice audio_effects_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "msm_hweffects",
.fops = &audio_effects_fops,
};
int __init audio_effects_init(void)
{
return misc_register(&audio_effects_misc);
}
void audio_effects_exit(void)
{
misc_deregister(&audio_effects_misc);
}
MODULE_DESCRIPTION("Audio hardware accelerated effects driver");
MODULE_LICENSE("GPL v2");