
spin_lock is acquired in process context and trying do the operation in process context,while the interrupt came and the same lock is trying to take in interrupt context which leads to deadlock.To avoid this we are using spin_lock_irqsave instead of spin_lock. Change-Id: I9c4a3ac65d92b0612d7c4845212647c51a72065b Signed-off-by: Prasad Kumpatla <nkumpat@codeaurora.org>
955 lines
25 KiB
C
955 lines
25 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/* Copyright (c) 2010-2020, The Linux Foundation. All rights reserved.
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/miscdevice.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/wait.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/atomic.h>
|
|
#include <linux/compat.h>
|
|
#include <asm/ioctls.h>
|
|
#include "audio_utils.h"
|
|
|
|
/*
|
|
* Define maximum buffer size. Below values are chosen considering the higher
|
|
* values used among all native drivers.
|
|
*/
|
|
#define MAX_FRAME_SIZE 1536
|
|
#define MAX_FRAMES 5
|
|
#define META_SIZE (sizeof(struct meta_out_dsp))
|
|
#define MAX_BUFFER_SIZE (1 + ((MAX_FRAME_SIZE + META_SIZE) * MAX_FRAMES))
|
|
|
|
static int audio_in_pause(struct q6audio_in *audio)
|
|
{
|
|
int rc;
|
|
|
|
rc = q6asm_cmd(audio->ac, CMD_PAUSE);
|
|
if (rc < 0)
|
|
pr_err("%s:session id %d: pause cmd failed rc=%d\n", __func__,
|
|
audio->ac->session, rc);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int audio_in_flush(struct q6audio_in *audio)
|
|
{
|
|
int rc;
|
|
|
|
pr_debug("%s:session id %d: flush\n", __func__, audio->ac->session);
|
|
/* Flush if session running */
|
|
if (audio->enabled) {
|
|
/* Implicitly issue a pause to the encoder before flushing */
|
|
rc = audio_in_pause(audio);
|
|
if (rc < 0) {
|
|
pr_err("%s:session id %d: pause cmd failed rc=%d\n",
|
|
__func__, audio->ac->session, rc);
|
|
return rc;
|
|
}
|
|
|
|
rc = q6asm_cmd(audio->ac, CMD_FLUSH);
|
|
if (rc < 0) {
|
|
pr_err("%s:session id %d: flush cmd failed rc=%d\n",
|
|
__func__, audio->ac->session, rc);
|
|
return rc;
|
|
}
|
|
/* 2nd arg: 0 -> run immediately
|
|
* 3rd arg: 0 -> msw_ts,
|
|
* 4th arg: 0 ->lsw_ts
|
|
*/
|
|
q6asm_run(audio->ac, 0x00, 0x00, 0x00);
|
|
pr_debug("Rerun the session\n");
|
|
}
|
|
audio->rflush = 1;
|
|
audio->wflush = 1;
|
|
memset(audio->out_frame_info, 0, sizeof(audio->out_frame_info));
|
|
wake_up(&audio->read_wait);
|
|
/* get read_lock to ensure no more waiting read thread */
|
|
mutex_lock(&audio->read_lock);
|
|
audio->rflush = 0;
|
|
mutex_unlock(&audio->read_lock);
|
|
wake_up(&audio->write_wait);
|
|
/* get write_lock to ensure no more waiting write thread */
|
|
mutex_lock(&audio->write_lock);
|
|
audio->wflush = 0;
|
|
mutex_unlock(&audio->write_lock);
|
|
pr_debug("%s:session id %d: in_bytes %d\n", __func__,
|
|
audio->ac->session, atomic_read(&audio->in_bytes));
|
|
pr_debug("%s:session id %d: in_samples %d\n", __func__,
|
|
audio->ac->session, atomic_read(&audio->in_samples));
|
|
atomic_set(&audio->in_bytes, 0);
|
|
atomic_set(&audio->in_samples, 0);
|
|
atomic_set(&audio->out_count, 0);
|
|
return 0;
|
|
}
|
|
|
|
/* must be called with audio->lock held */
|
|
int audio_in_enable(struct q6audio_in *audio)
|
|
{
|
|
if (audio->enabled)
|
|
return 0;
|
|
|
|
/* 2nd arg: 0 -> run immediately
|
|
* 3rd arg: 0 -> msw_ts,
|
|
* 4th arg: 0 ->lsw_ts
|
|
*/
|
|
return q6asm_run(audio->ac, 0x00, 0x00, 0x00);
|
|
}
|
|
|
|
/* must be called with audio->lock held */
|
|
int audio_in_disable(struct q6audio_in *audio)
|
|
{
|
|
int rc = 0;
|
|
|
|
if (!audio->stopped) {
|
|
audio->enabled = 0;
|
|
audio->opened = 0;
|
|
pr_debug("%s:session id %d: inbytes[%d] insamples[%d]\n",
|
|
__func__, audio->ac->session,
|
|
atomic_read(&audio->in_bytes),
|
|
atomic_read(&audio->in_samples));
|
|
|
|
rc = q6asm_cmd(audio->ac, CMD_CLOSE);
|
|
if (rc < 0)
|
|
pr_err("%s:session id %d: Failed to close the session rc=%d\n",
|
|
__func__, audio->ac->session,
|
|
rc);
|
|
audio->stopped = 1;
|
|
memset(audio->out_frame_info, 0,
|
|
sizeof(audio->out_frame_info));
|
|
wake_up(&audio->read_wait);
|
|
wake_up(&audio->write_wait);
|
|
}
|
|
pr_debug("%s:session id %d: enabled[%d]\n", __func__,
|
|
audio->ac->session, audio->enabled);
|
|
return rc;
|
|
}
|
|
|
|
int audio_in_buf_alloc(struct q6audio_in *audio)
|
|
{
|
|
int rc = 0;
|
|
|
|
switch (audio->buf_alloc) {
|
|
case NO_BUF_ALLOC:
|
|
if (audio->feedback == NON_TUNNEL_MODE) {
|
|
rc = q6asm_audio_client_buf_alloc(IN,
|
|
audio->ac,
|
|
ALIGN_BUF_SIZE(audio->pcm_cfg.buffer_size),
|
|
audio->pcm_cfg.buffer_count);
|
|
if (rc < 0) {
|
|
pr_err("%s:session id %d: Buffer Alloc failed\n",
|
|
__func__,
|
|
audio->ac->session);
|
|
rc = -ENOMEM;
|
|
break;
|
|
}
|
|
audio->buf_alloc |= BUF_ALLOC_IN;
|
|
}
|
|
rc = q6asm_audio_client_buf_alloc(OUT, audio->ac,
|
|
ALIGN_BUF_SIZE(audio->str_cfg.buffer_size),
|
|
audio->str_cfg.buffer_count);
|
|
if (rc < 0) {
|
|
pr_err("%s:session id %d: Buffer Alloc failed rc=%d\n",
|
|
__func__, audio->ac->session, rc);
|
|
rc = -ENOMEM;
|
|
break;
|
|
}
|
|
audio->buf_alloc |= BUF_ALLOC_OUT;
|
|
break;
|
|
case BUF_ALLOC_IN:
|
|
rc = q6asm_audio_client_buf_alloc(OUT, audio->ac,
|
|
ALIGN_BUF_SIZE(audio->str_cfg.buffer_size),
|
|
audio->str_cfg.buffer_count);
|
|
if (rc < 0) {
|
|
pr_err("%s:session id %d: Buffer Alloc failed rc=%d\n",
|
|
__func__, audio->ac->session, rc);
|
|
rc = -ENOMEM;
|
|
break;
|
|
}
|
|
audio->buf_alloc |= BUF_ALLOC_OUT;
|
|
break;
|
|
case BUF_ALLOC_OUT:
|
|
if (audio->feedback == NON_TUNNEL_MODE) {
|
|
rc = q6asm_audio_client_buf_alloc(IN, audio->ac,
|
|
ALIGN_BUF_SIZE(audio->pcm_cfg.buffer_size),
|
|
audio->pcm_cfg.buffer_count);
|
|
if (rc < 0) {
|
|
pr_err("%s:session id %d: Buffer Alloc failed\n",
|
|
__func__,
|
|
audio->ac->session);
|
|
rc = -ENOMEM;
|
|
break;
|
|
}
|
|
audio->buf_alloc |= BUF_ALLOC_IN;
|
|
}
|
|
break;
|
|
default:
|
|
pr_debug("%s:session id %d: buf[%d]\n", __func__,
|
|
audio->ac->session, audio->buf_alloc);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
int audio_in_set_config(struct file *file,
|
|
struct msm_audio_config *cfg)
|
|
{
|
|
int rc = 0;
|
|
struct q6audio_in *audio = file->private_data;
|
|
|
|
if (audio->feedback != NON_TUNNEL_MODE) {
|
|
pr_err("%s:session id %d: Not sufficient permission to change the record mode\n",
|
|
__func__, audio->ac->session);
|
|
rc = -EACCES;
|
|
goto ret;
|
|
}
|
|
if ((cfg->buffer_count > PCM_BUF_COUNT) ||
|
|
(cfg->buffer_count == 1))
|
|
cfg->buffer_count = PCM_BUF_COUNT;
|
|
|
|
audio->pcm_cfg.buffer_count = cfg->buffer_count;
|
|
audio->pcm_cfg.buffer_size = cfg->buffer_size;
|
|
audio->pcm_cfg.channel_count = cfg->channel_count;
|
|
audio->pcm_cfg.sample_rate = cfg->sample_rate;
|
|
if (audio->opened && audio->feedback == NON_TUNNEL_MODE) {
|
|
rc = q6asm_audio_client_buf_alloc(IN, audio->ac,
|
|
ALIGN_BUF_SIZE(audio->pcm_cfg.buffer_size),
|
|
audio->pcm_cfg.buffer_count);
|
|
if (rc < 0) {
|
|
pr_err("%s:session id %d: Buffer Alloc failed\n",
|
|
__func__, audio->ac->session);
|
|
rc = -ENOMEM;
|
|
goto ret;
|
|
}
|
|
}
|
|
audio->buf_alloc |= BUF_ALLOC_IN;
|
|
rc = 0;
|
|
pr_debug("%s:session id %d: AUDIO_SET_CONFIG %d %d\n", __func__,
|
|
audio->ac->session, audio->pcm_cfg.buffer_count,
|
|
audio->pcm_cfg.buffer_size);
|
|
ret:
|
|
return rc;
|
|
}
|
|
/* ------------------- device --------------------- */
|
|
static long audio_in_ioctl_shared(struct file *file,
|
|
unsigned int cmd, unsigned long arg)
|
|
{
|
|
struct q6audio_in *audio = file->private_data;
|
|
int rc = 0;
|
|
|
|
switch (cmd) {
|
|
case AUDIO_FLUSH: {
|
|
/* Make sure we're stopped and we wake any threads
|
|
* that might be blocked holding the read_lock.
|
|
* While audio->stopped read threads will always
|
|
* exit immediately.
|
|
*/
|
|
rc = audio_in_flush(audio);
|
|
if (rc < 0)
|
|
pr_err("%s:session id %d: Flush Fail rc=%d\n",
|
|
__func__, audio->ac->session, rc);
|
|
else { /* Register back the flushed read buffer with DSP */
|
|
int cnt = 0;
|
|
|
|
while (cnt++ < audio->str_cfg.buffer_count)
|
|
q6asm_read(audio->ac); /* Push buffer to DSP */
|
|
pr_debug("register the read buffer\n");
|
|
}
|
|
break;
|
|
}
|
|
case AUDIO_PAUSE: {
|
|
pr_debug("%s:session id %d: AUDIO_PAUSE\n", __func__,
|
|
audio->ac->session);
|
|
if (audio->enabled)
|
|
audio_in_pause(audio);
|
|
break;
|
|
}
|
|
case AUDIO_GET_SESSION_ID: {
|
|
if (copy_to_user((void *) arg, &audio->ac->session,
|
|
sizeof(u16))) {
|
|
pr_err("%s: copy_to_user for AUDIO_GET_SESSION_ID failed\n",
|
|
__func__);
|
|
rc = -EFAULT;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
|
|
rc = -EINVAL;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
long audio_in_ioctl(struct file *file,
|
|
unsigned int cmd, unsigned long arg)
|
|
{
|
|
struct q6audio_in *audio = file->private_data;
|
|
int rc = 0;
|
|
|
|
if (cmd == AUDIO_GET_STATS) {
|
|
struct msm_audio_stats stats;
|
|
|
|
memset(&stats, 0, sizeof(stats));
|
|
stats.byte_count = atomic_read(&audio->in_bytes);
|
|
stats.sample_count = atomic_read(&audio->in_samples);
|
|
if (copy_to_user((void *) arg, &stats, sizeof(stats)))
|
|
return -EFAULT;
|
|
return rc;
|
|
}
|
|
|
|
mutex_lock(&audio->lock);
|
|
switch (cmd) {
|
|
case AUDIO_FLUSH:
|
|
case AUDIO_PAUSE:
|
|
case AUDIO_GET_SESSION_ID:
|
|
rc = audio_in_ioctl_shared(file, cmd, arg);
|
|
break;
|
|
case AUDIO_GET_STREAM_CONFIG: {
|
|
struct msm_audio_stream_config cfg;
|
|
|
|
memset(&cfg, 0, sizeof(cfg));
|
|
cfg.buffer_size = audio->str_cfg.buffer_size;
|
|
cfg.buffer_count = audio->str_cfg.buffer_count;
|
|
if (copy_to_user((void *)arg, &cfg, sizeof(cfg)))
|
|
rc = -EFAULT;
|
|
pr_debug("%s:session id %d: AUDIO_GET_STREAM_CONFIG %d %d\n",
|
|
__func__, audio->ac->session, cfg.buffer_size,
|
|
cfg.buffer_count);
|
|
break;
|
|
}
|
|
case AUDIO_SET_STREAM_CONFIG: {
|
|
struct msm_audio_stream_config cfg;
|
|
|
|
if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) {
|
|
pr_err("%s: copy_from_user for AUDIO_SET_STREAM_CONFIG failed\n"
|
|
, __func__);
|
|
rc = -EFAULT;
|
|
break;
|
|
}
|
|
/* Minimum single frame size,
|
|
* but with in maximum frames number
|
|
*/
|
|
if ((cfg.buffer_size < (audio->min_frame_size +
|
|
sizeof(struct meta_out_dsp))) ||
|
|
(cfg.buffer_count < FRAME_NUM)) {
|
|
rc = -EINVAL;
|
|
break;
|
|
}
|
|
if (cfg.buffer_size > MAX_BUFFER_SIZE) {
|
|
rc = -EINVAL;
|
|
break;
|
|
}
|
|
audio->str_cfg.buffer_size = cfg.buffer_size;
|
|
audio->str_cfg.buffer_count = cfg.buffer_count;
|
|
if (audio->opened) {
|
|
rc = q6asm_audio_client_buf_alloc(OUT, audio->ac,
|
|
ALIGN_BUF_SIZE(audio->str_cfg.buffer_size),
|
|
audio->str_cfg.buffer_count);
|
|
if (rc < 0) {
|
|
pr_err("%s: session id %d: Buffer Alloc failed rc=%d\n",
|
|
__func__, audio->ac->session, rc);
|
|
rc = -ENOMEM;
|
|
break;
|
|
}
|
|
}
|
|
audio->buf_alloc |= BUF_ALLOC_OUT;
|
|
rc = 0;
|
|
pr_debug("%s:session id %d: AUDIO_SET_STREAM_CONFIG %d %d\n",
|
|
__func__, audio->ac->session,
|
|
audio->str_cfg.buffer_size,
|
|
audio->str_cfg.buffer_count);
|
|
break;
|
|
}
|
|
case AUDIO_SET_BUF_CFG: {
|
|
struct msm_audio_buf_cfg cfg;
|
|
|
|
if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) {
|
|
rc = -EFAULT;
|
|
break;
|
|
}
|
|
if ((audio->feedback == NON_TUNNEL_MODE) &&
|
|
!cfg.meta_info_enable) {
|
|
rc = -EFAULT;
|
|
break;
|
|
}
|
|
|
|
/* Restrict the num of frames per buf to coincide with
|
|
* default buf size
|
|
*/
|
|
if (cfg.frames_per_buf > audio->max_frames_per_buf) {
|
|
rc = -EFAULT;
|
|
break;
|
|
}
|
|
audio->buf_cfg.meta_info_enable = cfg.meta_info_enable;
|
|
audio->buf_cfg.frames_per_buf = cfg.frames_per_buf;
|
|
pr_debug("%s:session id %d: Set-buf-cfg: meta[%d] framesperbuf[%d]\n",
|
|
__func__,
|
|
audio->ac->session, cfg.meta_info_enable,
|
|
cfg.frames_per_buf);
|
|
break;
|
|
}
|
|
case AUDIO_GET_BUF_CFG: {
|
|
pr_debug("%s:session id %d: Get-buf-cfg: meta[%d] framesperbuf[%d]\n",
|
|
__func__,
|
|
audio->ac->session, audio->buf_cfg.meta_info_enable,
|
|
audio->buf_cfg.frames_per_buf);
|
|
|
|
if (copy_to_user((void *)arg, &audio->buf_cfg,
|
|
sizeof(struct msm_audio_buf_cfg)))
|
|
rc = -EFAULT;
|
|
break;
|
|
}
|
|
case AUDIO_GET_CONFIG: {
|
|
if (copy_to_user((void *)arg, &audio->pcm_cfg,
|
|
sizeof(struct msm_audio_config)))
|
|
rc = -EFAULT;
|
|
break;
|
|
|
|
}
|
|
case AUDIO_SET_CONFIG: {
|
|
struct msm_audio_config cfg;
|
|
|
|
if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) {
|
|
pr_err("%s: copy_from_user for AUDIO_SET_CONFIG failed\n",
|
|
__func__);
|
|
rc = -EFAULT;
|
|
break;
|
|
}
|
|
rc = audio_in_set_config(file, &cfg);
|
|
break;
|
|
}
|
|
default:
|
|
/* call codec specific ioctl */
|
|
rc = audio->enc_ioctl(file, cmd, arg);
|
|
}
|
|
mutex_unlock(&audio->lock);
|
|
return rc;
|
|
}
|
|
|
|
#ifdef CONFIG_COMPAT
|
|
struct msm_audio_stats32 {
|
|
u32 byte_count;
|
|
u32 sample_count;
|
|
u32 unused[2];
|
|
};
|
|
|
|
struct msm_audio_stream_config32 {
|
|
u32 buffer_size;
|
|
u32 buffer_count;
|
|
};
|
|
|
|
struct msm_audio_config32 {
|
|
u32 buffer_size;
|
|
u32 buffer_count;
|
|
u32 channel_count;
|
|
u32 sample_rate;
|
|
u32 type;
|
|
u32 meta_field;
|
|
u32 bits;
|
|
u32 unused[3];
|
|
};
|
|
|
|
struct msm_audio_buf_cfg32 {
|
|
u32 meta_info_enable;
|
|
u32 frames_per_buf;
|
|
};
|
|
|
|
enum {
|
|
AUDIO_GET_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, 3,
|
|
struct msm_audio_config32),
|
|
AUDIO_SET_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, 4,
|
|
struct msm_audio_config32),
|
|
AUDIO_GET_STATS_32 = _IOR(AUDIO_IOCTL_MAGIC, 5,
|
|
struct msm_audio_stats32),
|
|
AUDIO_SET_STREAM_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, 80,
|
|
struct msm_audio_stream_config32),
|
|
AUDIO_GET_STREAM_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, 81,
|
|
struct msm_audio_stream_config32),
|
|
AUDIO_SET_BUF_CFG_32 = _IOW(AUDIO_IOCTL_MAGIC, 94,
|
|
struct msm_audio_buf_cfg32),
|
|
AUDIO_GET_BUF_CFG_32 = _IOW(AUDIO_IOCTL_MAGIC, 93,
|
|
struct msm_audio_buf_cfg32),
|
|
};
|
|
|
|
long audio_in_compat_ioctl(struct file *file,
|
|
unsigned int cmd, unsigned long arg)
|
|
{
|
|
struct q6audio_in *audio = file->private_data;
|
|
int rc = 0;
|
|
|
|
if (cmd == AUDIO_GET_STATS_32) {
|
|
struct msm_audio_stats32 stats_32;
|
|
|
|
memset(&stats_32, 0, sizeof(stats_32));
|
|
stats_32.byte_count = atomic_read(&audio->in_bytes);
|
|
stats_32.sample_count = atomic_read(&audio->in_samples);
|
|
if (copy_to_user((void *) arg, &stats_32, sizeof(stats_32))) {
|
|
pr_err("%s: copy_to_user failed for AUDIO_GET_STATS_32\n",
|
|
__func__);
|
|
return -EFAULT;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
mutex_lock(&audio->lock);
|
|
switch (cmd) {
|
|
case AUDIO_FLUSH:
|
|
case AUDIO_PAUSE:
|
|
case AUDIO_GET_SESSION_ID:
|
|
rc = audio_in_ioctl_shared(file, cmd, arg);
|
|
break;
|
|
case AUDIO_GET_STREAM_CONFIG_32: {
|
|
struct msm_audio_stream_config32 cfg_32;
|
|
|
|
memset(&cfg_32, 0, sizeof(cfg_32));
|
|
cfg_32.buffer_size = audio->str_cfg.buffer_size;
|
|
cfg_32.buffer_count = audio->str_cfg.buffer_count;
|
|
if (copy_to_user((void *)arg, &cfg_32, sizeof(cfg_32))) {
|
|
pr_err("%s: Copy to user failed\n", __func__);
|
|
rc = -EFAULT;
|
|
}
|
|
pr_debug("%s:session id %d: AUDIO_GET_STREAM_CONFIG %d %d\n",
|
|
__func__, audio->ac->session,
|
|
cfg_32.buffer_size,
|
|
cfg_32.buffer_count);
|
|
break;
|
|
}
|
|
case AUDIO_SET_STREAM_CONFIG_32: {
|
|
struct msm_audio_stream_config32 cfg_32;
|
|
struct msm_audio_stream_config cfg;
|
|
|
|
if (copy_from_user(&cfg_32, (void *)arg, sizeof(cfg_32))) {
|
|
pr_err("%s: copy_from_user for AUDIO_SET_STREAM_CONFIG_32 failed\n",
|
|
__func__);
|
|
rc = -EFAULT;
|
|
break;
|
|
}
|
|
cfg.buffer_size = cfg_32.buffer_size;
|
|
cfg.buffer_count = cfg_32.buffer_count;
|
|
/* Minimum single frame size,
|
|
* but with in maximum frames number
|
|
*/
|
|
if ((cfg.buffer_size < (audio->min_frame_size +
|
|
sizeof(struct meta_out_dsp))) ||
|
|
(cfg.buffer_count < FRAME_NUM)) {
|
|
rc = -EINVAL;
|
|
break;
|
|
}
|
|
audio->str_cfg.buffer_size = cfg.buffer_size;
|
|
audio->str_cfg.buffer_count = cfg.buffer_count;
|
|
if (audio->opened) {
|
|
rc = q6asm_audio_client_buf_alloc(OUT, audio->ac,
|
|
ALIGN_BUF_SIZE(audio->str_cfg.buffer_size),
|
|
audio->str_cfg.buffer_count);
|
|
if (rc < 0) {
|
|
pr_err("%s: session id %d:\n",
|
|
__func__, audio->ac->session);
|
|
pr_err("Buffer Alloc failed rc=%d\n", rc);
|
|
rc = -ENOMEM;
|
|
break;
|
|
}
|
|
}
|
|
audio->buf_alloc |= BUF_ALLOC_OUT;
|
|
pr_debug("%s:session id %d: AUDIO_SET_STREAM_CONFIG %d %d\n",
|
|
__func__, audio->ac->session,
|
|
audio->str_cfg.buffer_size,
|
|
audio->str_cfg.buffer_count);
|
|
break;
|
|
}
|
|
case AUDIO_SET_BUF_CFG_32: {
|
|
struct msm_audio_buf_cfg32 cfg_32;
|
|
struct msm_audio_buf_cfg cfg;
|
|
|
|
if (copy_from_user(&cfg_32, (void *)arg, sizeof(cfg_32))) {
|
|
pr_err("%s: copy_from_user for AUDIO_SET_BUG_CFG_32 failed",
|
|
__func__);
|
|
rc = -EFAULT;
|
|
break;
|
|
}
|
|
cfg.meta_info_enable = cfg_32.meta_info_enable;
|
|
cfg.frames_per_buf = cfg_32.frames_per_buf;
|
|
|
|
if ((audio->feedback == NON_TUNNEL_MODE) &&
|
|
!cfg.meta_info_enable) {
|
|
rc = -EFAULT;
|
|
break;
|
|
}
|
|
|
|
/* Restrict the num of frames per buf to coincide with
|
|
* default buf size
|
|
*/
|
|
if (cfg.frames_per_buf > audio->max_frames_per_buf) {
|
|
rc = -EFAULT;
|
|
break;
|
|
}
|
|
audio->buf_cfg.meta_info_enable = cfg.meta_info_enable;
|
|
audio->buf_cfg.frames_per_buf = cfg.frames_per_buf;
|
|
pr_debug("%s:session id %d: Set-buf-cfg: meta[%d] framesperbuf[%d]\n",
|
|
__func__, audio->ac->session, cfg.meta_info_enable,
|
|
cfg.frames_per_buf);
|
|
break;
|
|
}
|
|
case AUDIO_GET_BUF_CFG_32: {
|
|
struct msm_audio_buf_cfg32 cfg_32;
|
|
|
|
pr_debug("%s:session id %d: Get-buf-cfg: meta[%d] framesperbuf[%d]\n",
|
|
__func__,
|
|
audio->ac->session, audio->buf_cfg.meta_info_enable,
|
|
audio->buf_cfg.frames_per_buf);
|
|
cfg_32.meta_info_enable = audio->buf_cfg.meta_info_enable;
|
|
cfg_32.frames_per_buf = audio->buf_cfg.frames_per_buf;
|
|
|
|
if (copy_to_user((void *)arg, &cfg_32,
|
|
sizeof(struct msm_audio_buf_cfg32))) {
|
|
pr_err("%s: Copy to user failed\n", __func__);
|
|
rc = -EFAULT;
|
|
}
|
|
break;
|
|
}
|
|
case AUDIO_GET_CONFIG_32: {
|
|
struct msm_audio_config32 cfg_32;
|
|
|
|
memset(&cfg_32, 0, sizeof(cfg_32));
|
|
cfg_32.buffer_size = audio->pcm_cfg.buffer_size;
|
|
cfg_32.buffer_count = audio->pcm_cfg.buffer_count;
|
|
cfg_32.channel_count = audio->pcm_cfg.channel_count;
|
|
cfg_32.sample_rate = audio->pcm_cfg.sample_rate;
|
|
cfg_32.type = audio->pcm_cfg.type;
|
|
cfg_32.meta_field = audio->pcm_cfg.meta_field;
|
|
cfg_32.bits = audio->pcm_cfg.bits;
|
|
|
|
if (copy_to_user((void *)arg, &cfg_32,
|
|
sizeof(struct msm_audio_config32))) {
|
|
pr_err("%s: Copy to user failed\n", __func__);
|
|
rc = -EFAULT;
|
|
}
|
|
break;
|
|
}
|
|
case AUDIO_SET_CONFIG_32: {
|
|
struct msm_audio_config32 cfg_32;
|
|
struct msm_audio_config cfg;
|
|
|
|
if (copy_from_user(&cfg_32, (void *)arg, sizeof(cfg_32))) {
|
|
pr_err("%s: copy_from_user for AUDIO_SET_CONFIG_32 failed\n",
|
|
__func__);
|
|
rc = -EFAULT;
|
|
break;
|
|
}
|
|
cfg.buffer_size = cfg_32.buffer_size;
|
|
cfg.buffer_count = cfg_32.buffer_count;
|
|
cfg.channel_count = cfg_32.channel_count;
|
|
cfg.sample_rate = cfg_32.sample_rate;
|
|
cfg.type = cfg_32.type;
|
|
cfg.meta_field = cfg_32.meta_field;
|
|
cfg.bits = cfg_32.bits;
|
|
rc = audio_in_set_config(file, &cfg);
|
|
break;
|
|
}
|
|
default:
|
|
/* call codec specific ioctl */
|
|
rc = audio->enc_compat_ioctl(file, cmd, arg);
|
|
}
|
|
mutex_unlock(&audio->lock);
|
|
return rc;
|
|
}
|
|
#endif
|
|
|
|
ssize_t audio_in_read(struct file *file,
|
|
char __user *buf,
|
|
size_t count, loff_t *pos)
|
|
{
|
|
struct q6audio_in *audio = file->private_data;
|
|
const char __user *start = buf;
|
|
unsigned char *data;
|
|
uint32_t offset = 0;
|
|
uint32_t size = 0;
|
|
int rc = 0;
|
|
uint32_t idx;
|
|
struct meta_out_dsp meta;
|
|
uint32_t bytes_to_copy = 0;
|
|
uint32_t mfield_size = (audio->buf_cfg.meta_info_enable == 0) ? 0 :
|
|
(sizeof(unsigned char) +
|
|
(sizeof(struct meta_out_dsp)*(audio->buf_cfg.frames_per_buf)));
|
|
|
|
memset(&meta, 0, sizeof(meta));
|
|
pr_debug("%s:session id %d: read - %zd\n", __func__, audio->ac->session,
|
|
count);
|
|
if (audio->reset_event)
|
|
return -ENETRESET;
|
|
|
|
if (!audio->enabled)
|
|
return -EFAULT;
|
|
mutex_lock(&audio->read_lock);
|
|
while (count > 0) {
|
|
rc = wait_event_interruptible(
|
|
audio->read_wait,
|
|
((atomic_read(&audio->out_count) > 0) ||
|
|
(audio->stopped) ||
|
|
audio->rflush || audio->eos_rsp ||
|
|
audio->event_abort));
|
|
|
|
if (audio->event_abort) {
|
|
rc = -EIO;
|
|
break;
|
|
}
|
|
|
|
|
|
if (rc < 0)
|
|
break;
|
|
|
|
if ((audio->stopped && !(atomic_read(&audio->out_count))) ||
|
|
audio->rflush) {
|
|
pr_debug("%s:session id %d: driver in stop state or flush,No more buf to read",
|
|
__func__,
|
|
audio->ac->session);
|
|
rc = 0;/* End of File */
|
|
break;
|
|
}
|
|
if (!(atomic_read(&audio->out_count)) &&
|
|
(audio->eos_rsp == 1) &&
|
|
(count >= (sizeof(unsigned char) +
|
|
sizeof(struct meta_out_dsp)))) {
|
|
unsigned char num_of_frames;
|
|
|
|
pr_info("%s:session id %d: eos %d at output\n",
|
|
__func__, audio->ac->session, audio->eos_rsp);
|
|
if (buf != start)
|
|
break;
|
|
num_of_frames = 0xFF;
|
|
if (copy_to_user(buf, &num_of_frames,
|
|
sizeof(unsigned char))) {
|
|
rc = -EFAULT;
|
|
break;
|
|
}
|
|
buf += sizeof(unsigned char);
|
|
meta.frame_size = 0xFFFF;
|
|
meta.encoded_pcm_samples = 0xFFFF;
|
|
meta.msw_ts = 0x00;
|
|
meta.lsw_ts = 0x00;
|
|
meta.nflags = AUD_EOS_SET;
|
|
audio->eos_rsp = 0;
|
|
if (copy_to_user(buf, &meta, sizeof(meta))) {
|
|
rc = -EFAULT;
|
|
break;
|
|
}
|
|
buf += sizeof(meta);
|
|
break;
|
|
}
|
|
data = (unsigned char *)q6asm_is_cpu_buf_avail(OUT, audio->ac,
|
|
&size, &idx);
|
|
if ((count >= (size + mfield_size)) && data) {
|
|
if (audio->buf_cfg.meta_info_enable) {
|
|
if (copy_to_user(buf,
|
|
&audio->out_frame_info[idx][0],
|
|
sizeof(unsigned char))) {
|
|
rc = -EFAULT;
|
|
break;
|
|
}
|
|
bytes_to_copy =
|
|
(size + audio->out_frame_info[idx][1]);
|
|
if (bytes_to_copy == 0) {
|
|
rc = 0;
|
|
break;
|
|
}
|
|
/* Number of frames information copied */
|
|
buf += sizeof(unsigned char);
|
|
count -= sizeof(unsigned char);
|
|
} else {
|
|
offset = audio->out_frame_info[idx][1];
|
|
bytes_to_copy = size;
|
|
}
|
|
|
|
pr_debug("%s:session id %d: offset=%d nr of frames= %d\n",
|
|
__func__, audio->ac->session,
|
|
audio->out_frame_info[idx][1],
|
|
audio->out_frame_info[idx][0]);
|
|
|
|
if (copy_to_user(buf, &data[offset], bytes_to_copy)) {
|
|
rc = -EFAULT;
|
|
break;
|
|
}
|
|
count -= bytes_to_copy;
|
|
buf += bytes_to_copy;
|
|
} else {
|
|
pr_err("%s:session id %d: short read data[%pK] bytesavail[%d]bytesrequest[%zd]\n",
|
|
__func__,
|
|
audio->ac->session,
|
|
data, size, count);
|
|
}
|
|
atomic_dec(&audio->out_count);
|
|
q6asm_read(audio->ac);
|
|
break;
|
|
}
|
|
mutex_unlock(&audio->read_lock);
|
|
|
|
pr_debug("%s:session id %d: read: %zd bytes\n", __func__,
|
|
audio->ac->session, (buf-start));
|
|
if (!rc) {
|
|
if (buf > start)
|
|
return buf - start;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static int extract_meta_info(char *buf, unsigned long *msw_ts,
|
|
unsigned long *lsw_ts, unsigned int *flags)
|
|
{
|
|
struct meta_in *meta = (struct meta_in *)buf;
|
|
*msw_ts = meta->ntimestamp.highpart;
|
|
*lsw_ts = meta->ntimestamp.lowpart;
|
|
*flags = meta->nflags;
|
|
return 0;
|
|
}
|
|
|
|
ssize_t audio_in_write(struct file *file,
|
|
const char __user *buf,
|
|
size_t count, loff_t *pos)
|
|
{
|
|
struct q6audio_in *audio = file->private_data;
|
|
const char __user *start = buf;
|
|
size_t xfer = 0;
|
|
char *cpy_ptr;
|
|
int rc = 0;
|
|
unsigned char *data;
|
|
uint32_t size = 0;
|
|
uint32_t idx = 0;
|
|
uint32_t nflags = 0;
|
|
unsigned long msw_ts = 0;
|
|
unsigned long lsw_ts = 0;
|
|
uint32_t mfield_size = (audio->buf_cfg.meta_info_enable == 0) ? 0 :
|
|
sizeof(struct meta_in);
|
|
|
|
pr_debug("%s:session id %d: to write[%zd]\n", __func__,
|
|
audio->ac->session, count);
|
|
if (audio->reset_event)
|
|
return -ENETRESET;
|
|
|
|
if (!audio->enabled)
|
|
return -EFAULT;
|
|
mutex_lock(&audio->write_lock);
|
|
|
|
while (count > 0) {
|
|
rc = wait_event_interruptible(audio->write_wait,
|
|
((atomic_read(&audio->in_count) > 0) ||
|
|
(audio->stopped) ||
|
|
(audio->wflush) || (audio->event_abort)));
|
|
|
|
if (audio->event_abort) {
|
|
rc = -EIO;
|
|
break;
|
|
}
|
|
|
|
if (rc < 0)
|
|
break;
|
|
if (audio->stopped || audio->wflush) {
|
|
pr_debug("%s: session id %d: stop or flush\n", __func__,
|
|
audio->ac->session);
|
|
rc = -EBUSY;
|
|
break;
|
|
}
|
|
/* if no PCM data, might have only eos buffer
|
|
* such case do not hold cpu buffer
|
|
*/
|
|
if ((buf == start) && (count == mfield_size)) {
|
|
char eos_buf[sizeof(struct meta_in)];
|
|
/* Processing beginning of user buffer */
|
|
if (copy_from_user(eos_buf, buf, mfield_size)) {
|
|
rc = -EFAULT;
|
|
break;
|
|
}
|
|
/* Check if EOS flag is set and buffer has
|
|
* contains just meta field
|
|
*/
|
|
extract_meta_info(eos_buf, &msw_ts, &lsw_ts,
|
|
&nflags);
|
|
buf += mfield_size;
|
|
/* send the EOS and return */
|
|
pr_debug("%s:session id %d: send EOS 0x%8x\n",
|
|
__func__,
|
|
audio->ac->session, nflags);
|
|
break;
|
|
}
|
|
data = (unsigned char *)q6asm_is_cpu_buf_avail(IN, audio->ac,
|
|
&size, &idx);
|
|
if (!data) {
|
|
pr_debug("%s:session id %d: No buf available\n",
|
|
__func__, audio->ac->session);
|
|
continue;
|
|
}
|
|
cpy_ptr = data;
|
|
if (audio->buf_cfg.meta_info_enable) {
|
|
if (buf == start) {
|
|
/* Processing beginning of user buffer */
|
|
if (copy_from_user(cpy_ptr, buf, mfield_size)) {
|
|
rc = -EFAULT;
|
|
break;
|
|
}
|
|
/* Check if EOS flag is set and buffer has
|
|
* contains just meta field
|
|
*/
|
|
extract_meta_info(cpy_ptr, &msw_ts, &lsw_ts,
|
|
&nflags);
|
|
buf += mfield_size;
|
|
count -= mfield_size;
|
|
} else {
|
|
pr_debug("%s:session id %d: continuous buffer\n",
|
|
__func__, audio->ac->session);
|
|
}
|
|
}
|
|
|
|
xfer = (count > size) ? size : count;
|
|
if (copy_from_user(cpy_ptr, buf, xfer)) {
|
|
rc = -EFAULT;
|
|
break;
|
|
}
|
|
rc = q6asm_write(audio->ac, xfer, msw_ts, lsw_ts, 0x00);
|
|
if (rc < 0) {
|
|
rc = -EFAULT;
|
|
break;
|
|
}
|
|
atomic_dec(&audio->in_count);
|
|
count -= xfer;
|
|
buf += xfer;
|
|
}
|
|
mutex_unlock(&audio->write_lock);
|
|
pr_debug("%s:session id %d: eos_condition 0x%x buf[0x%pK] start[0x%pK]\n",
|
|
__func__, audio->ac->session,
|
|
nflags, buf, start);
|
|
if (nflags & AUD_EOS_SET) {
|
|
rc = q6asm_cmd(audio->ac, CMD_EOS);
|
|
pr_info("%s:session id %d: eos %d at input\n", __func__,
|
|
audio->ac->session, audio->eos_rsp);
|
|
}
|
|
pr_debug("%s:session id %d: Written %zd Avail Buf[%d]", __func__,
|
|
audio->ac->session, (buf - start - mfield_size),
|
|
atomic_read(&audio->in_count));
|
|
if (!rc) {
|
|
if (buf > start)
|
|
return buf - start;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
int audio_in_release(struct inode *inode, struct file *file)
|
|
{
|
|
unsigned long flags = 0;
|
|
struct q6audio_in *audio = file->private_data;
|
|
|
|
pr_info("%s: session id %d\n", __func__, audio->ac->session);
|
|
mutex_lock(&audio->lock);
|
|
audio_in_disable(audio);
|
|
q6asm_audio_client_free(audio->ac);
|
|
mutex_unlock(&audio->lock);
|
|
spin_lock_irqsave(&enc_dec_lock, flags);
|
|
kfree(audio->enc_cfg);
|
|
kfree(audio->codec_cfg);
|
|
kfree(audio);
|
|
file->private_data = NULL;
|
|
spin_unlock_irqrestore(&enc_dec_lock, flags);
|
|
return 0;
|
|
}
|