mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-05 07:26:48 +07:00
Staging: HTC Dream: add qdsp support
QDSP code is neccessarry for communication with some hardware components on HTC Dream, including camera hardware. It also drives DSP coproccessor. Signed-off-by: Pavel Machek <pavel@ucw.cz> Cc: Brian Swetland <swetland@google.com> Cc: Iliyan Malchev <ibm@android.com> Cc: San Mehat <san@android.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
7bf06dace0
commit
caff4caead
17
drivers/staging/dream/qdsp5/Makefile
Normal file
17
drivers/staging/dream/qdsp5/Makefile
Normal file
@ -0,0 +1,17 @@
|
||||
obj-y += adsp.o
|
||||
ifeq ($(CONFIG_MSM_AMSS_VERSION_6350),y)
|
||||
obj-y += adsp_info.o
|
||||
obj-y += audio_evrc.o audio_qcelp.o audio_amrnb.o audio_aac.o
|
||||
else
|
||||
obj-y += adsp_6225.o
|
||||
endif
|
||||
|
||||
obj-y += adsp_driver.o
|
||||
obj-y += adsp_video_verify_cmd.o
|
||||
obj-y += adsp_videoenc_verify_cmd.o
|
||||
obj-y += adsp_jpeg_verify_cmd.o adsp_jpeg_patch_event.o
|
||||
obj-y += adsp_vfe_verify_cmd.o adsp_vfe_patch_event.o
|
||||
obj-y += adsp_lpm_verify_cmd.o
|
||||
obj-y += audio_out.o audio_in.o audio_mp3.o audmgr.o audpp.o
|
||||
obj-y += snd.o
|
||||
|
1163
drivers/staging/dream/qdsp5/adsp.c
Normal file
1163
drivers/staging/dream/qdsp5/adsp.c
Normal file
File diff suppressed because it is too large
Load Diff
369
drivers/staging/dream/qdsp5/adsp.h
Normal file
369
drivers/staging/dream/qdsp5/adsp.h
Normal file
@ -0,0 +1,369 @@
|
||||
/* arch/arm/mach-msm/qdsp5/adsp.h
|
||||
*
|
||||
* Copyright (c) 2008 QUALCOMM Incorporated
|
||||
* Copyright (C) 2008 Google, Inc.
|
||||
* Author: Iliyan Malchev <ibm@android.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _ARCH_ARM_MACH_MSM_ADSP_H
|
||||
#define _ARCH_ARM_MACH_MSM_ADSP_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/msm_adsp.h>
|
||||
#include <mach/msm_rpcrouter.h>
|
||||
#include <mach/msm_adsp.h>
|
||||
|
||||
int adsp_pmem_fixup(struct msm_adsp_module *module, void **addr,
|
||||
unsigned long len);
|
||||
int adsp_pmem_fixup_kvaddr(struct msm_adsp_module *module, void **addr,
|
||||
unsigned long *kvaddr, unsigned long len);
|
||||
int adsp_pmem_paddr_fixup(struct msm_adsp_module *module, void **addr);
|
||||
|
||||
int adsp_vfe_verify_cmd(struct msm_adsp_module *module,
|
||||
unsigned int queue_id, void *cmd_data,
|
||||
size_t cmd_size);
|
||||
int adsp_jpeg_verify_cmd(struct msm_adsp_module *module,
|
||||
unsigned int queue_id, void *cmd_data,
|
||||
size_t cmd_size);
|
||||
int adsp_lpm_verify_cmd(struct msm_adsp_module *module,
|
||||
unsigned int queue_id, void *cmd_data,
|
||||
size_t cmd_size);
|
||||
int adsp_video_verify_cmd(struct msm_adsp_module *module,
|
||||
unsigned int queue_id, void *cmd_data,
|
||||
size_t cmd_size);
|
||||
int adsp_videoenc_verify_cmd(struct msm_adsp_module *module,
|
||||
unsigned int queue_id, void *cmd_data,
|
||||
size_t cmd_size);
|
||||
|
||||
|
||||
struct adsp_event;
|
||||
|
||||
int adsp_vfe_patch_event(struct msm_adsp_module *module,
|
||||
struct adsp_event *event);
|
||||
|
||||
int adsp_jpeg_patch_event(struct msm_adsp_module *module,
|
||||
struct adsp_event *event);
|
||||
|
||||
|
||||
struct adsp_module_info {
|
||||
const char *name;
|
||||
const char *pdev_name;
|
||||
uint32_t id;
|
||||
const char *clk_name;
|
||||
unsigned long clk_rate;
|
||||
int (*verify_cmd) (struct msm_adsp_module*, unsigned int, void *,
|
||||
size_t);
|
||||
int (*patch_event) (struct msm_adsp_module*, struct adsp_event *);
|
||||
};
|
||||
|
||||
#define ADSP_EVENT_MAX_SIZE 496
|
||||
#define EVENT_LEN 12
|
||||
#define EVENT_MSG_ID ((uint16_t)~0)
|
||||
|
||||
struct adsp_event {
|
||||
struct list_head list;
|
||||
uint32_t size; /* always in bytes */
|
||||
uint16_t msg_id;
|
||||
uint16_t type; /* 0 for msgs (from aDSP), -1 for events (from ARM9) */
|
||||
int is16; /* always 0 (msg is 32-bit) when the event type is 1(ARM9) */
|
||||
union {
|
||||
uint16_t msg16[ADSP_EVENT_MAX_SIZE / 2];
|
||||
uint32_t msg32[ADSP_EVENT_MAX_SIZE / 4];
|
||||
} data;
|
||||
};
|
||||
|
||||
struct adsp_info {
|
||||
uint32_t send_irq;
|
||||
uint32_t read_ctrl;
|
||||
uint32_t write_ctrl;
|
||||
|
||||
uint32_t max_msg16_size;
|
||||
uint32_t max_msg32_size;
|
||||
|
||||
uint32_t max_task_id;
|
||||
uint32_t max_module_id;
|
||||
uint32_t max_queue_id;
|
||||
uint32_t max_image_id;
|
||||
|
||||
/* for each image id, a map of queue id to offset */
|
||||
uint32_t **queue_offset;
|
||||
|
||||
/* for each image id, a map of task id to module id */
|
||||
uint32_t **task_to_module;
|
||||
|
||||
/* for each module id, map of module id to module */
|
||||
struct msm_adsp_module **id_to_module;
|
||||
|
||||
uint32_t module_count;
|
||||
struct adsp_module_info *module;
|
||||
|
||||
/* stats */
|
||||
uint32_t events_received;
|
||||
uint32_t event_backlog_max;
|
||||
|
||||
#if CONFIG_MSM_AMSS_VERSION >= 6350
|
||||
/* rpc_client for init_info */
|
||||
struct msm_rpc_endpoint *init_info_rpc_client;
|
||||
struct adsp_rtos_mp_mtoa_init_info_type *init_info_ptr;
|
||||
wait_queue_head_t init_info_wait;
|
||||
unsigned init_info_state;
|
||||
#endif
|
||||
};
|
||||
|
||||
#define RPC_ADSP_RTOS_ATOM_PROG 0x3000000a
|
||||
#define RPC_ADSP_RTOS_MTOA_PROG 0x3000000b
|
||||
#define RPC_ADSP_RTOS_ATOM_NULL_PROC 0
|
||||
#define RPC_ADSP_RTOS_MTOA_NULL_PROC 0
|
||||
#define RPC_ADSP_RTOS_APP_TO_MODEM_PROC 2
|
||||
#define RPC_ADSP_RTOS_MODEM_TO_APP_PROC 2
|
||||
|
||||
#if CONFIG_MSM_AMSS_VERSION >= 6350
|
||||
#define RPC_ADSP_RTOS_ATOM_VERS MSM_RPC_VERS(1,0)
|
||||
#define RPC_ADSP_RTOS_MTOA_VERS MSM_RPC_VERS(2,1) /* must be actual vers */
|
||||
#define MSM_ADSP_DRIVER_NAME "rs3000000a:00010000"
|
||||
#elif (CONFIG_MSM_AMSS_VERSION == 6220) || (CONFIG_MSM_AMSS_VERSION == 6225)
|
||||
#define RPC_ADSP_RTOS_ATOM_VERS MSM_RPC_VERS(0x71d1094b, 0)
|
||||
#define RPC_ADSP_RTOS_MTOA_VERS MSM_RPC_VERS(0xee3a9966, 0)
|
||||
#define MSM_ADSP_DRIVER_NAME "rs3000000a:71d1094b"
|
||||
#elif CONFIG_MSM_AMSS_VERSION == 6210
|
||||
#define RPC_ADSP_RTOS_ATOM_VERS MSM_RPC_VERS(0x20f17fd3, 0)
|
||||
#define RPC_ADSP_RTOS_MTOA_VERS MSM_RPC_VERS(0x75babbd6, 0)
|
||||
#define MSM_ADSP_DRIVER_NAME "rs3000000a:20f17fd3"
|
||||
#else
|
||||
#error "Unknown AMSS version"
|
||||
#endif
|
||||
|
||||
enum rpc_adsp_rtos_proc_type {
|
||||
RPC_ADSP_RTOS_PROC_NONE = 0,
|
||||
RPC_ADSP_RTOS_PROC_MODEM = 1,
|
||||
RPC_ADSP_RTOS_PROC_APPS = 2,
|
||||
};
|
||||
|
||||
enum {
|
||||
RPC_ADSP_RTOS_CMD_REGISTER_APP,
|
||||
RPC_ADSP_RTOS_CMD_ENABLE,
|
||||
RPC_ADSP_RTOS_CMD_DISABLE,
|
||||
RPC_ADSP_RTOS_CMD_KERNEL_COMMAND,
|
||||
RPC_ADSP_RTOS_CMD_16_COMMAND,
|
||||
RPC_ADSP_RTOS_CMD_32_COMMAND,
|
||||
RPC_ADSP_RTOS_CMD_DISABLE_EVENT_RSP,
|
||||
RPC_ADSP_RTOS_CMD_REMOTE_EVENT,
|
||||
RPC_ADSP_RTOS_CMD_SET_STATE,
|
||||
#if CONFIG_MSM_AMSS_VERSION >= 6350
|
||||
RPC_ADSP_RTOS_CMD_REMOTE_INIT_INFO_EVENT,
|
||||
RPC_ADSP_RTOS_CMD_GET_INIT_INFO,
|
||||
#endif
|
||||
};
|
||||
|
||||
enum rpc_adsp_rtos_mod_status_type {
|
||||
RPC_ADSP_RTOS_MOD_READY,
|
||||
RPC_ADSP_RTOS_MOD_DISABLE,
|
||||
RPC_ADSP_RTOS_SERVICE_RESET,
|
||||
RPC_ADSP_RTOS_CMD_FAIL,
|
||||
RPC_ADSP_RTOS_CMD_SUCCESS,
|
||||
#if CONFIG_MSM_AMSS_VERSION >= 6350
|
||||
RPC_ADSP_RTOS_INIT_INFO,
|
||||
RPC_ADSP_RTOS_DISABLE_FAIL,
|
||||
#endif
|
||||
};
|
||||
|
||||
struct rpc_adsp_rtos_app_to_modem_args_t {
|
||||
struct rpc_request_hdr hdr;
|
||||
uint32_t gotit; /* if 1, the next elements are present */
|
||||
uint32_t cmd; /* e.g., RPC_ADSP_RTOS_CMD_REGISTER_APP */
|
||||
uint32_t proc_id; /* e.g., RPC_ADSP_RTOS_PROC_APPS */
|
||||
uint32_t module; /* e.g., QDSP_MODULE_AUDPPTASK */
|
||||
};
|
||||
|
||||
#if CONFIG_MSM_AMSS_VERSION >= 6350
|
||||
enum qdsp_image_type {
|
||||
QDSP_IMAGE_COMBO,
|
||||
QDSP_IMAGE_GAUDIO,
|
||||
QDSP_IMAGE_QTV_LP,
|
||||
QDSP_IMAGE_MAX,
|
||||
/* DO NOT USE: Force this enum to be a 32bit type to improve speed */
|
||||
QDSP_IMAGE_32BIT_DUMMY = 0x10000
|
||||
};
|
||||
|
||||
struct adsp_rtos_mp_mtoa_header_type {
|
||||
enum rpc_adsp_rtos_mod_status_type event;
|
||||
enum rpc_adsp_rtos_proc_type proc_id;
|
||||
};
|
||||
|
||||
/* ADSP RTOS MP Communications - Modem to APP's Event Info*/
|
||||
struct adsp_rtos_mp_mtoa_type {
|
||||
uint32_t module;
|
||||
uint32_t image;
|
||||
uint32_t apps_okts;
|
||||
};
|
||||
|
||||
/* ADSP RTOS MP Communications - Modem to APP's Init Info */
|
||||
#define IMG_MAX 8
|
||||
#define ENTRIES_MAX 64
|
||||
|
||||
struct queue_to_offset_type {
|
||||
uint32_t queue;
|
||||
uint32_t offset;
|
||||
};
|
||||
|
||||
struct adsp_rtos_mp_mtoa_init_info_type {
|
||||
uint32_t image_count;
|
||||
uint32_t num_queue_offsets;
|
||||
struct queue_to_offset_type queue_offsets_tbl[IMG_MAX][ENTRIES_MAX];
|
||||
uint32_t num_task_module_entries;
|
||||
uint32_t task_to_module_tbl[IMG_MAX][ENTRIES_MAX];
|
||||
|
||||
uint32_t module_table_size;
|
||||
uint32_t module_entries[ENTRIES_MAX];
|
||||
/*
|
||||
* queue_offsets[] is to store only queue_offsets
|
||||
*/
|
||||
uint32_t queue_offsets[IMG_MAX][ENTRIES_MAX];
|
||||
};
|
||||
|
||||
struct adsp_rtos_mp_mtoa_s_type {
|
||||
struct adsp_rtos_mp_mtoa_header_type mp_mtoa_header;
|
||||
|
||||
uint32_t desc_field;
|
||||
union {
|
||||
struct adsp_rtos_mp_mtoa_init_info_type mp_mtoa_init_packet;
|
||||
struct adsp_rtos_mp_mtoa_type mp_mtoa_packet;
|
||||
} adsp_rtos_mp_mtoa_data;
|
||||
};
|
||||
|
||||
struct rpc_adsp_rtos_modem_to_app_args_t {
|
||||
struct rpc_request_hdr hdr;
|
||||
uint32_t gotit; /* if 1, the next elements are present */
|
||||
struct adsp_rtos_mp_mtoa_s_type mtoa_pkt;
|
||||
};
|
||||
#else
|
||||
struct rpc_adsp_rtos_modem_to_app_args_t {
|
||||
struct rpc_request_hdr hdr;
|
||||
uint32_t gotit; /* if 1, the next elements are present */
|
||||
uint32_t event; /* e.g., RPC_ADSP_RTOS_CMD_REGISTER_APP */
|
||||
uint32_t proc_id; /* e.g., RPC_ADSP_RTOS_PROC_APPS */
|
||||
uint32_t module; /* e.g., QDSP_MODULE_AUDPPTASK */
|
||||
uint32_t image; /* RPC_QDSP_IMAGE_GAUDIO */
|
||||
};
|
||||
#endif /* CONFIG_MSM_AMSS_VERSION >= 6350 */
|
||||
|
||||
#define ADSP_STATE_DISABLED 0
|
||||
#define ADSP_STATE_ENABLING 1
|
||||
#define ADSP_STATE_ENABLED 2
|
||||
#define ADSP_STATE_DISABLING 3
|
||||
#if CONFIG_MSM_AMSS_VERSION >= 6350
|
||||
#define ADSP_STATE_INIT_INFO 4
|
||||
#endif
|
||||
|
||||
struct msm_adsp_module {
|
||||
struct mutex lock;
|
||||
const char *name;
|
||||
unsigned id;
|
||||
struct adsp_info *info;
|
||||
|
||||
struct msm_rpc_endpoint *rpc_client;
|
||||
struct msm_adsp_ops *ops;
|
||||
void *driver_data;
|
||||
|
||||
/* statistics */
|
||||
unsigned num_commands;
|
||||
unsigned num_events;
|
||||
|
||||
wait_queue_head_t state_wait;
|
||||
unsigned state;
|
||||
|
||||
struct platform_device pdev;
|
||||
struct clk *clk;
|
||||
int open_count;
|
||||
|
||||
struct mutex pmem_regions_lock;
|
||||
struct hlist_head pmem_regions;
|
||||
int (*verify_cmd) (struct msm_adsp_module*, unsigned int, void *,
|
||||
size_t);
|
||||
int (*patch_event) (struct msm_adsp_module*, struct adsp_event *);
|
||||
};
|
||||
|
||||
extern void msm_adsp_publish_cdevs(struct msm_adsp_module *, unsigned);
|
||||
extern int adsp_init_info(struct adsp_info *info);
|
||||
|
||||
/* Value to indicate that a queue is not defined for a particular image */
|
||||
#if CONFIG_MSM_AMSS_VERSION >= 6350
|
||||
#define QDSP_RTOS_NO_QUEUE 0xfffffffe
|
||||
#else
|
||||
#define QDSP_RTOS_NO_QUEUE 0xffffffff
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Constants used to communicate with the ADSP RTOS
|
||||
*/
|
||||
#define ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_M 0x80000000U
|
||||
#define ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_NAVAIL_V 0x80000000U
|
||||
#define ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_AVAIL_V 0x00000000U
|
||||
|
||||
#define ADSP_RTOS_WRITE_CTRL_WORD_CMD_M 0x70000000U
|
||||
#define ADSP_RTOS_WRITE_CTRL_WORD_CMD_WRITE_REQ_V 0x00000000U
|
||||
#define ADSP_RTOS_WRITE_CTRL_WORD_CMD_WRITE_DONE_V 0x10000000U
|
||||
#define ADSP_RTOS_WRITE_CTRL_WORD_CMD_NO_CMD_V 0x70000000U
|
||||
|
||||
#define ADSP_RTOS_WRITE_CTRL_WORD_STATUS_M 0x0E000000U
|
||||
#define ADSP_RTOS_WRITE_CTRL_WORD_NO_ERR_V 0x00000000U
|
||||
#define ADSP_RTOS_WRITE_CTRL_WORD_NO_FREE_BUF_V 0x02000000U
|
||||
|
||||
#define ADSP_RTOS_WRITE_CTRL_WORD_KERNEL_FLG_M 0x01000000U
|
||||
#define ADSP_RTOS_WRITE_CTRL_WORD_HTOD_MSG_WRITE_V 0x00000000U
|
||||
#define ADSP_RTOS_WRITE_CTRL_WORD_HTOD_CMD_V 0x01000000U
|
||||
|
||||
#define ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M 0x00FFFFFFU
|
||||
#define ADSP_RTOS_WRITE_CTRL_WORD_HTOD_CMD_ID_M 0x00FFFFFFU
|
||||
|
||||
/* Combination of MUTEX and CMD bits to check if the DSP is busy */
|
||||
#define ADSP_RTOS_WRITE_CTRL_WORD_READY_M 0xF0000000U
|
||||
#define ADSP_RTOS_WRITE_CTRL_WORD_READY_V 0x70000000U
|
||||
|
||||
/* RTOS to Host processor command mask values */
|
||||
#define ADSP_RTOS_READ_CTRL_WORD_FLAG_M 0x80000000U
|
||||
#define ADSP_RTOS_READ_CTRL_WORD_FLAG_UP_WAIT_V 0x00000000U
|
||||
#define ADSP_RTOS_READ_CTRL_WORD_FLAG_UP_CONT_V 0x80000000U
|
||||
|
||||
#define ADSP_RTOS_READ_CTRL_WORD_CMD_M 0x60000000U
|
||||
#define ADSP_RTOS_READ_CTRL_WORD_READ_DONE_V 0x00000000U
|
||||
#define ADSP_RTOS_READ_CTRL_WORD_READ_REQ_V 0x20000000U
|
||||
#define ADSP_RTOS_READ_CTRL_WORD_NO_CMD_V 0x60000000U
|
||||
|
||||
/* Combination of FLAG and COMMAND bits to check if MSG ready */
|
||||
#define ADSP_RTOS_READ_CTRL_WORD_READY_M 0xE0000000U
|
||||
#define ADSP_RTOS_READ_CTRL_WORD_READY_V 0xA0000000U
|
||||
#define ADSP_RTOS_READ_CTRL_WORD_CONT_V 0xC0000000U
|
||||
#define ADSP_RTOS_READ_CTRL_WORD_DONE_V 0xE0000000U
|
||||
|
||||
#define ADSP_RTOS_READ_CTRL_WORD_STATUS_M 0x18000000U
|
||||
#define ADSP_RTOS_READ_CTRL_WORD_NO_ERR_V 0x00000000U
|
||||
|
||||
#define ADSP_RTOS_READ_CTRL_WORD_IN_PROG_M 0x04000000U
|
||||
#define ADSP_RTOS_READ_CTRL_WORD_NO_READ_IN_PROG_V 0x00000000U
|
||||
#define ADSP_RTOS_READ_CTRL_WORD_READ_IN_PROG_V 0x04000000U
|
||||
|
||||
#define ADSP_RTOS_READ_CTRL_WORD_CMD_TYPE_M 0x03000000U
|
||||
#define ADSP_RTOS_READ_CTRL_WORD_CMD_TASK_TO_H_V 0x00000000U
|
||||
#define ADSP_RTOS_READ_CTRL_WORD_CMD_KRNL_TO_H_V 0x01000000U
|
||||
#define ADSP_RTOS_READ_CTRL_WORD_CMD_H_TO_KRNL_CFM_V 0x02000000U
|
||||
|
||||
#define ADSP_RTOS_READ_CTRL_WORD_DSP_ADDR_M 0x00FFFFFFU
|
||||
|
||||
#define ADSP_RTOS_READ_CTRL_WORD_MSG_ID_M 0x000000FFU
|
||||
#define ADSP_RTOS_READ_CTRL_WORD_TASK_ID_M 0x0000FF00U
|
||||
|
||||
/* Base address of DSP and DSP hardware registers */
|
||||
#define QDSP_RAMC_OFFSET 0x400000
|
||||
|
||||
#endif /* _ARCH_ARM_MACH_MSM_ADSP_H */
|
283
drivers/staging/dream/qdsp5/adsp_6210.c
Normal file
283
drivers/staging/dream/qdsp5/adsp_6210.c
Normal file
@ -0,0 +1,283 @@
|
||||
/* arch/arm/mach-msm/qdsp5/adsp_6210.h
|
||||
*
|
||||
* Copyright (c) 2008 QUALCOMM Incorporated.
|
||||
*
|
||||
* 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 "adsp.h"
|
||||
|
||||
/* Firmware modules */
|
||||
typedef enum {
|
||||
QDSP_MODULE_KERNEL,
|
||||
QDSP_MODULE_AFETASK,
|
||||
QDSP_MODULE_AUDPLAY0TASK,
|
||||
QDSP_MODULE_AUDPLAY1TASK,
|
||||
QDSP_MODULE_AUDPPTASK,
|
||||
QDSP_MODULE_VIDEOTASK,
|
||||
QDSP_MODULE_VIDEO_AAC_VOC,
|
||||
QDSP_MODULE_PCM_DEC,
|
||||
QDSP_MODULE_AUDIO_DEC_MP3,
|
||||
QDSP_MODULE_AUDIO_DEC_AAC,
|
||||
QDSP_MODULE_AUDIO_DEC_WMA,
|
||||
QDSP_MODULE_HOSTPCM,
|
||||
QDSP_MODULE_DTMF,
|
||||
QDSP_MODULE_AUDRECTASK,
|
||||
QDSP_MODULE_AUDPREPROCTASK,
|
||||
QDSP_MODULE_SBC_ENC,
|
||||
QDSP_MODULE_VOC,
|
||||
QDSP_MODULE_VOC_PCM,
|
||||
QDSP_MODULE_VOCENCTASK,
|
||||
QDSP_MODULE_VOCDECTASK,
|
||||
QDSP_MODULE_VOICEPROCTASK,
|
||||
QDSP_MODULE_VIDEOENCTASK,
|
||||
QDSP_MODULE_VFETASK,
|
||||
QDSP_MODULE_WAV_ENC,
|
||||
QDSP_MODULE_AACLC_ENC,
|
||||
QDSP_MODULE_VIDEO_AMR,
|
||||
QDSP_MODULE_VOC_AMR,
|
||||
QDSP_MODULE_VOC_EVRC,
|
||||
QDSP_MODULE_VOC_13K,
|
||||
QDSP_MODULE_VOC_FGV,
|
||||
QDSP_MODULE_DIAGTASK,
|
||||
QDSP_MODULE_JPEGTASK,
|
||||
QDSP_MODULE_LPMTASK,
|
||||
QDSP_MODULE_QCAMTASK,
|
||||
QDSP_MODULE_MODMATHTASK,
|
||||
QDSP_MODULE_AUDPLAY2TASK,
|
||||
QDSP_MODULE_AUDPLAY3TASK,
|
||||
QDSP_MODULE_AUDPLAY4TASK,
|
||||
QDSP_MODULE_GRAPHICSTASK,
|
||||
QDSP_MODULE_MIDI,
|
||||
QDSP_MODULE_GAUDIO,
|
||||
QDSP_MODULE_VDEC_LP_MODE,
|
||||
QDSP_MODULE_MAX,
|
||||
} qdsp_module_type;
|
||||
|
||||
#define QDSP_RTOS_MAX_TASK_ID 19U
|
||||
|
||||
/* Table of modules indexed by task ID for the GAUDIO image */
|
||||
static qdsp_module_type qdsp_gaudio_task_to_module_table[] = {
|
||||
QDSP_MODULE_KERNEL,
|
||||
QDSP_MODULE_AFETASK,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_AUDPPTASK,
|
||||
QDSP_MODULE_AUDPLAY0TASK,
|
||||
QDSP_MODULE_AUDPLAY1TASK,
|
||||
QDSP_MODULE_AUDPLAY2TASK,
|
||||
QDSP_MODULE_AUDPLAY3TASK,
|
||||
QDSP_MODULE_AUDPLAY4TASK,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_AUDRECTASK,
|
||||
QDSP_MODULE_AUDPREPROCTASK,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_GRAPHICSTASK,
|
||||
QDSP_MODULE_MAX
|
||||
};
|
||||
|
||||
/* Queue offset table indexed by queue ID for the GAUDIO image */
|
||||
static uint32_t qdsp_gaudio_queue_offset_table[] = {
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_lpmCommandQueue */
|
||||
0x3be, /* QDSP_mpuAfeQueue */
|
||||
0x3ee, /* QDSP_mpuGraphicsCmdQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_mpuModmathCmdQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVDecCmdQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVDecPktQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVEncCmdQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecCmdQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecPktQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_txMpuEncQueue */
|
||||
0x3c2, /* QDSP_uPAudPPCmd1Queue */
|
||||
0x3c6, /* QDSP_uPAudPPCmd2Queue */
|
||||
0x3ca, /* QDSP_uPAudPPCmd3Queue */
|
||||
0x3da, /* QDSP_uPAudPlay0BitStreamCtrlQueue */
|
||||
0x3de, /* QDSP_uPAudPlay1BitStreamCtrlQueue */
|
||||
0x3e2, /* QDSP_uPAudPlay2BitStreamCtrlQueue */
|
||||
0x3e6, /* QDSP_uPAudPlay3BitStreamCtrlQueue */
|
||||
0x3ea, /* QDSP_uPAudPlay4BitStreamCtrlQueue */
|
||||
0x3ce, /* QDSP_uPAudPreProcCmdQueue */
|
||||
0x3d6, /* QDSP_uPAudRecBitStreamQueue */
|
||||
0x3d2, /* QDSP_uPAudRecCmdQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegActionCmdQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegCfgCmdQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPVocProcQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandScaleQueue */
|
||||
QDSP_RTOS_NO_QUEUE /* QDSP_vfeCommandTableQueue */
|
||||
};
|
||||
|
||||
/* Table of modules indexed by task ID for the COMBO image */
|
||||
static qdsp_module_type qdsp_combo_task_to_module_table[] = {
|
||||
QDSP_MODULE_KERNEL,
|
||||
QDSP_MODULE_AFETASK,
|
||||
QDSP_MODULE_VOCDECTASK,
|
||||
QDSP_MODULE_VOCENCTASK,
|
||||
QDSP_MODULE_VIDEOTASK,
|
||||
QDSP_MODULE_VIDEOENCTASK,
|
||||
QDSP_MODULE_VOICEPROCTASK,
|
||||
QDSP_MODULE_VFETASK,
|
||||
QDSP_MODULE_JPEGTASK,
|
||||
QDSP_MODULE_AUDPPTASK,
|
||||
QDSP_MODULE_AUDPLAY0TASK,
|
||||
QDSP_MODULE_AUDPLAY1TASK,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_LPMTASK,
|
||||
QDSP_MODULE_AUDRECTASK,
|
||||
QDSP_MODULE_AUDPREPROCTASK,
|
||||
QDSP_MODULE_MODMATHTASK,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX
|
||||
};
|
||||
|
||||
/* Queue offset table indexed by queue ID for the COMBO image */
|
||||
static uint32_t qdsp_combo_queue_offset_table[] = {
|
||||
0x585, /* QDSP_lpmCommandQueue */
|
||||
0x52d, /* QDSP_mpuAfeQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_mpuGraphicsCmdQueue */
|
||||
0x541, /* QDSP_mpuModmathCmdQueue */
|
||||
0x555, /* QDSP_mpuVDecCmdQueue */
|
||||
0x559, /* QDSP_mpuVDecPktQueue */
|
||||
0x551, /* QDSP_mpuVEncCmdQueue */
|
||||
0x535, /* QDSP_rxMpuDecCmdQueue */
|
||||
0x539, /* QDSP_rxMpuDecPktQueue */
|
||||
0x53d, /* QDSP_txMpuEncQueue */
|
||||
0x55d, /* QDSP_uPAudPPCmd1Queue */
|
||||
0x561, /* QDSP_uPAudPPCmd2Queue */
|
||||
0x565, /* QDSP_uPAudPPCmd3Queue */
|
||||
0x575, /* QDSP_uPAudPlay0BitStreamCtrlQueue */
|
||||
0x579, /* QDSP_uPAudPlay1BitStreamCtrlQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay2BitStreamCtrlQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay3BitStreamCtrlQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay4BitStreamCtrlQueue */
|
||||
0x569, /* QDSP_uPAudPreProcCmdQueue */
|
||||
0x571, /* QDSP_uPAudRecBitStreamQueue */
|
||||
0x56d, /* QDSP_uPAudRecCmdQueue */
|
||||
0x581, /* QDSP_uPJpegActionCmdQueue */
|
||||
0x57d, /* QDSP_uPJpegCfgCmdQueue */
|
||||
0x531, /* QDSP_uPVocProcQueue */
|
||||
0x545, /* QDSP_vfeCommandQueue */
|
||||
0x54d, /* QDSP_vfeCommandScaleQueue */
|
||||
0x549 /* QDSP_vfeCommandTableQueue */
|
||||
};
|
||||
|
||||
/* Table of modules indexed by task ID for the QTV_LP image */
|
||||
static qdsp_module_type qdsp_qtv_lp_task_to_module_table[] = {
|
||||
QDSP_MODULE_KERNEL,
|
||||
QDSP_MODULE_AFETASK,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_VIDEOTASK,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_AUDPPTASK,
|
||||
QDSP_MODULE_AUDPLAY0TASK,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_AUDRECTASK,
|
||||
QDSP_MODULE_AUDPREPROCTASK,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX
|
||||
};
|
||||
|
||||
/* Queue offset table indexed by queue ID for the QTV_LP image */
|
||||
static uint32_t qdsp_qtv_lp_queue_offset_table[] = {
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_lpmCommandQueue */
|
||||
0x40c, /* QDSP_mpuAfeQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_mpuGraphicsCmdQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_mpuModmathCmdQueue */
|
||||
0x410, /* QDSP_mpuVDecCmdQueue */
|
||||
0x414, /* QDSP_mpuVDecPktQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVEncCmdQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecCmdQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecPktQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_txMpuEncQueue */
|
||||
0x41c, /* QDSP_uPAudPPCmd1Queue */
|
||||
0x420, /* QDSP_uPAudPPCmd2Queue */
|
||||
0x424, /* QDSP_uPAudPPCmd3Queue */
|
||||
0x430, /* QDSP_uPAudPlay0BitStreamCtrlQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay1BitStreamCtrlQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay2BitStreamCtrlQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay3BitStreamCtrlQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay4BitStreamCtrlQueue */
|
||||
0x418, /* QDSP_uPAudPreProcCmdQueue */
|
||||
0x42c, /* QDSP_uPAudRecBitStreamQueue */
|
||||
0x428, /* QDSP_uPAudRecCmdQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegActionCmdQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegCfgCmdQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPVocProcQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandScaleQueue */
|
||||
QDSP_RTOS_NO_QUEUE /* QDSP_vfeCommandTableQueue */
|
||||
};
|
||||
|
||||
/* Tables to convert tasks to modules */
|
||||
static uint32_t *qdsp_task_to_module[] = {
|
||||
qdsp_combo_task_to_module_table,
|
||||
qdsp_gaudio_task_to_module_table,
|
||||
qdsp_qtv_lp_task_to_module_table,
|
||||
};
|
||||
|
||||
/* Tables to retrieve queue offsets */
|
||||
static uint32_t *qdsp_queue_offset_table[] = {
|
||||
qdsp_combo_queue_offset_table,
|
||||
qdsp_gaudio_queue_offset_table,
|
||||
qdsp_qtv_lp_queue_offset_table,
|
||||
};
|
||||
|
||||
#define QDSP_MODULE(n) \
|
||||
{ .name = #n, .pdev_name = "adsp_" #n, .id = QDSP_MODULE_##n }
|
||||
|
||||
static struct adsp_module_info module_info[] = {
|
||||
QDSP_MODULE(AUDPPTASK),
|
||||
QDSP_MODULE(AUDRECTASK),
|
||||
QDSP_MODULE(AUDPREPROCTASK),
|
||||
QDSP_MODULE(VFETASK),
|
||||
QDSP_MODULE(QCAMTASK),
|
||||
QDSP_MODULE(LPMTASK),
|
||||
QDSP_MODULE(JPEGTASK),
|
||||
QDSP_MODULE(VIDEOTASK),
|
||||
QDSP_MODULE(VDEC_LP_MODE),
|
||||
};
|
||||
|
||||
int adsp_init_info(struct adsp_info *info)
|
||||
{
|
||||
info->send_irq = 0x00c00200;
|
||||
info->read_ctrl = 0x00400038;
|
||||
info->write_ctrl = 0x00400034;
|
||||
|
||||
info->max_msg16_size = 193;
|
||||
info->max_msg32_size = 8;
|
||||
|
||||
info->max_task_id = 16;
|
||||
info->max_module_id = QDSP_MODULE_MAX - 1;
|
||||
info->max_queue_id = QDSP_QUEUE_MAX;
|
||||
info->max_image_id = 2;
|
||||
info->queue_offset = qdsp_queue_offset_table;
|
||||
info->task_to_module = qdsp_task_to_module;
|
||||
|
||||
info->module_count = ARRAY_SIZE(module_info);
|
||||
info->module = module_info;
|
||||
return 0;
|
||||
}
|
284
drivers/staging/dream/qdsp5/adsp_6220.c
Normal file
284
drivers/staging/dream/qdsp5/adsp_6220.c
Normal file
@ -0,0 +1,284 @@
|
||||
/* arch/arm/mach-msm/qdsp5/adsp_6220.h
|
||||
*
|
||||
* Copyright (c) 2008 QUALCOMM Incorporated.
|
||||
*
|
||||
* 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 "adsp.h"
|
||||
|
||||
/* Firmware modules */
|
||||
typedef enum {
|
||||
QDSP_MODULE_KERNEL,
|
||||
QDSP_MODULE_AFETASK,
|
||||
QDSP_MODULE_AUDPLAY0TASK,
|
||||
QDSP_MODULE_AUDPLAY1TASK,
|
||||
QDSP_MODULE_AUDPPTASK,
|
||||
QDSP_MODULE_VIDEOTASK,
|
||||
QDSP_MODULE_VIDEO_AAC_VOC,
|
||||
QDSP_MODULE_PCM_DEC,
|
||||
QDSP_MODULE_AUDIO_DEC_MP3,
|
||||
QDSP_MODULE_AUDIO_DEC_AAC,
|
||||
QDSP_MODULE_AUDIO_DEC_WMA,
|
||||
QDSP_MODULE_HOSTPCM,
|
||||
QDSP_MODULE_DTMF,
|
||||
QDSP_MODULE_AUDRECTASK,
|
||||
QDSP_MODULE_AUDPREPROCTASK,
|
||||
QDSP_MODULE_SBC_ENC,
|
||||
QDSP_MODULE_VOC,
|
||||
QDSP_MODULE_VOC_PCM,
|
||||
QDSP_MODULE_VOCENCTASK,
|
||||
QDSP_MODULE_VOCDECTASK,
|
||||
QDSP_MODULE_VOICEPROCTASK,
|
||||
QDSP_MODULE_VIDEOENCTASK,
|
||||
QDSP_MODULE_VFETASK,
|
||||
QDSP_MODULE_WAV_ENC,
|
||||
QDSP_MODULE_AACLC_ENC,
|
||||
QDSP_MODULE_VIDEO_AMR,
|
||||
QDSP_MODULE_VOC_AMR,
|
||||
QDSP_MODULE_VOC_EVRC,
|
||||
QDSP_MODULE_VOC_13K,
|
||||
QDSP_MODULE_VOC_FGV,
|
||||
QDSP_MODULE_DIAGTASK,
|
||||
QDSP_MODULE_JPEGTASK,
|
||||
QDSP_MODULE_LPMTASK,
|
||||
QDSP_MODULE_QCAMTASK,
|
||||
QDSP_MODULE_MODMATHTASK,
|
||||
QDSP_MODULE_AUDPLAY2TASK,
|
||||
QDSP_MODULE_AUDPLAY3TASK,
|
||||
QDSP_MODULE_AUDPLAY4TASK,
|
||||
QDSP_MODULE_GRAPHICSTASK,
|
||||
QDSP_MODULE_MIDI,
|
||||
QDSP_MODULE_GAUDIO,
|
||||
QDSP_MODULE_VDEC_LP_MODE,
|
||||
QDSP_MODULE_MAX,
|
||||
} qdsp_module_type;
|
||||
|
||||
#define QDSP_RTOS_MAX_TASK_ID 19U
|
||||
|
||||
/* Table of modules indexed by task ID for the GAUDIO image */
|
||||
static qdsp_module_type qdsp_gaudio_task_to_module_table[] = {
|
||||
QDSP_MODULE_KERNEL,
|
||||
QDSP_MODULE_AFETASK,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_AUDPPTASK,
|
||||
QDSP_MODULE_AUDPLAY0TASK,
|
||||
QDSP_MODULE_AUDPLAY1TASK,
|
||||
QDSP_MODULE_AUDPLAY2TASK,
|
||||
QDSP_MODULE_AUDPLAY3TASK,
|
||||
QDSP_MODULE_AUDPLAY4TASK,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_AUDRECTASK,
|
||||
QDSP_MODULE_AUDPREPROCTASK,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_GRAPHICSTASK,
|
||||
QDSP_MODULE_MAX
|
||||
};
|
||||
|
||||
/* Queue offset table indexed by queue ID for the GAUDIO image */
|
||||
static uint32_t qdsp_gaudio_queue_offset_table[] = {
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_lpmCommandQueue */
|
||||
0x3f0, /* QDSP_mpuAfeQueue */
|
||||
0x420, /* QDSP_mpuGraphicsCmdQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_mpuModmathCmdQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVDecCmdQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVDecPktQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVEncCmdQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecCmdQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecPktQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_txMpuEncQueue */
|
||||
0x3f4, /* QDSP_uPAudPPCmd1Queue */
|
||||
0x3f8, /* QDSP_uPAudPPCmd2Queue */
|
||||
0x3fc, /* QDSP_uPAudPPCmd3Queue */
|
||||
0x40c, /* QDSP_uPAudPlay0BitStreamCtrlQueue */
|
||||
0x410, /* QDSP_uPAudPlay1BitStreamCtrlQueue */
|
||||
0x414, /* QDSP_uPAudPlay2BitStreamCtrlQueue */
|
||||
0x418, /* QDSP_uPAudPlay3BitStreamCtrlQueue */
|
||||
0x41c, /* QDSP_uPAudPlay4BitStreamCtrlQueue */
|
||||
0x400, /* QDSP_uPAudPreProcCmdQueue */
|
||||
0x408, /* QDSP_uPAudRecBitStreamQueue */
|
||||
0x404, /* QDSP_uPAudRecCmdQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegActionCmdQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegCfgCmdQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPVocProcQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandScaleQueue */
|
||||
QDSP_RTOS_NO_QUEUE /* QDSP_vfeCommandTableQueue */
|
||||
};
|
||||
|
||||
/* Table of modules indexed by task ID for the COMBO image */
|
||||
static qdsp_module_type qdsp_combo_task_to_module_table[] = {
|
||||
QDSP_MODULE_KERNEL,
|
||||
QDSP_MODULE_AFETASK,
|
||||
QDSP_MODULE_VOCDECTASK,
|
||||
QDSP_MODULE_VOCENCTASK,
|
||||
QDSP_MODULE_VIDEOTASK,
|
||||
QDSP_MODULE_VIDEOENCTASK,
|
||||
QDSP_MODULE_VOICEPROCTASK,
|
||||
QDSP_MODULE_VFETASK,
|
||||
QDSP_MODULE_JPEGTASK,
|
||||
QDSP_MODULE_AUDPPTASK,
|
||||
QDSP_MODULE_AUDPLAY0TASK,
|
||||
QDSP_MODULE_AUDPLAY1TASK,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_LPMTASK,
|
||||
QDSP_MODULE_AUDRECTASK,
|
||||
QDSP_MODULE_AUDPREPROCTASK,
|
||||
QDSP_MODULE_MODMATHTASK,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX
|
||||
};
|
||||
|
||||
/* Queue offset table indexed by queue ID for the COMBO image */
|
||||
static uint32_t qdsp_combo_queue_offset_table[] = {
|
||||
0x6f2, /* QDSP_lpmCommandQueue */
|
||||
0x69e, /* QDSP_mpuAfeQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_mpuGraphicsCmdQueue */
|
||||
0x6b2, /* QDSP_mpuModmathCmdQueue */
|
||||
0x6c6, /* QDSP_mpuVDecCmdQueue */
|
||||
0x6ca, /* QDSP_mpuVDecPktQueue */
|
||||
0x6c2, /* QDSP_mpuVEncCmdQueue */
|
||||
0x6a6, /* QDSP_rxMpuDecCmdQueue */
|
||||
0x6aa, /* QDSP_rxMpuDecPktQueue */
|
||||
0x6ae, /* QDSP_txMpuEncQueue */
|
||||
0x6ce, /* QDSP_uPAudPPCmd1Queue */
|
||||
0x6d2, /* QDSP_uPAudPPCmd2Queue */
|
||||
0x6d6, /* QDSP_uPAudPPCmd3Queue */
|
||||
0x6e6, /* QDSP_uPAudPlay0BitStreamCtrlQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay1BitStreamCtrlQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay2BitStreamCtrlQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay3BitStreamCtrlQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay4BitStreamCtrlQueue */
|
||||
0x6da, /* QDSP_uPAudPreProcCmdQueue */
|
||||
0x6e2, /* QDSP_uPAudRecBitStreamQueue */
|
||||
0x6de, /* QDSP_uPAudRecCmdQueue */
|
||||
0x6ee, /* QDSP_uPJpegActionCmdQueue */
|
||||
0x6ea, /* QDSP_uPJpegCfgCmdQueue */
|
||||
0x6a2, /* QDSP_uPVocProcQueue */
|
||||
0x6b6, /* QDSP_vfeCommandQueue */
|
||||
0x6be, /* QDSP_vfeCommandScaleQueue */
|
||||
0x6ba /* QDSP_vfeCommandTableQueue */
|
||||
};
|
||||
|
||||
/* Table of modules indexed by task ID for the QTV_LP image */
|
||||
static qdsp_module_type qdsp_qtv_lp_task_to_module_table[] = {
|
||||
QDSP_MODULE_KERNEL,
|
||||
QDSP_MODULE_AFETASK,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_VIDEOTASK,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_AUDPPTASK,
|
||||
QDSP_MODULE_AUDPLAY0TASK,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_AUDRECTASK,
|
||||
QDSP_MODULE_AUDPREPROCTASK,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX
|
||||
};
|
||||
|
||||
/* Queue offset table indexed by queue ID for the QTV_LP image */
|
||||
static uint32_t qdsp_qtv_lp_queue_offset_table[] = {
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_lpmCommandQueue */
|
||||
0x430, /* QDSP_mpuAfeQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_mpuGraphicsCmdQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_mpuModmathCmdQueue */
|
||||
0x434, /* QDSP_mpuVDecCmdQueue */
|
||||
0x438, /* QDSP_mpuVDecPktQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVEncCmdQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecCmdQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecPktQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_txMpuEncQueue */
|
||||
0x440, /* QDSP_uPAudPPCmd1Queue */
|
||||
0x444, /* QDSP_uPAudPPCmd2Queue */
|
||||
0x448, /* QDSP_uPAudPPCmd3Queue */
|
||||
0x454, /* QDSP_uPAudPlay0BitStreamCtrlQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay1BitStreamCtrlQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay2BitStreamCtrlQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay3BitStreamCtrlQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay4BitStreamCtrlQueue */
|
||||
0x43c, /* QDSP_uPAudPreProcCmdQueue */
|
||||
0x450, /* QDSP_uPAudRecBitStreamQueue */
|
||||
0x44c, /* QDSP_uPAudRecCmdQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegActionCmdQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegCfgCmdQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPVocProcQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandScaleQueue */
|
||||
QDSP_RTOS_NO_QUEUE /* QDSP_vfeCommandTableQueue */
|
||||
};
|
||||
|
||||
/* Tables to convert tasks to modules */
|
||||
static qdsp_module_type *qdsp_task_to_module[] = {
|
||||
qdsp_combo_task_to_module_table,
|
||||
qdsp_gaudio_task_to_module_table,
|
||||
qdsp_qtv_lp_task_to_module_table,
|
||||
};
|
||||
|
||||
/* Tables to retrieve queue offsets */
|
||||
static uint32_t *qdsp_queue_offset_table[] = {
|
||||
qdsp_combo_queue_offset_table,
|
||||
qdsp_gaudio_queue_offset_table,
|
||||
qdsp_qtv_lp_queue_offset_table,
|
||||
};
|
||||
|
||||
#define QDSP_MODULE(n) \
|
||||
{ .name = #n, .pdev_name = "adsp_" #n, .id = QDSP_MODULE_##n }
|
||||
|
||||
static struct adsp_module_info module_info[] = {
|
||||
QDSP_MODULE(AUDPLAY0TASK),
|
||||
QDSP_MODULE(AUDPPTASK),
|
||||
QDSP_MODULE(AUDPREPROCTASK),
|
||||
QDSP_MODULE(AUDRECTASK),
|
||||
QDSP_MODULE(VFETASK),
|
||||
QDSP_MODULE(QCAMTASK),
|
||||
QDSP_MODULE(LPMTASK),
|
||||
QDSP_MODULE(JPEGTASK),
|
||||
QDSP_MODULE(VIDEOTASK),
|
||||
QDSP_MODULE(VDEC_LP_MODE),
|
||||
};
|
||||
|
||||
int adsp_init_info(struct adsp_info *info)
|
||||
{
|
||||
info->send_irq = 0x00c00200;
|
||||
info->read_ctrl = 0x00400038;
|
||||
info->write_ctrl = 0x00400034;
|
||||
|
||||
info->max_msg16_size = 193;
|
||||
info->max_msg32_size = 8;
|
||||
|
||||
info->max_task_id = 16;
|
||||
info->max_module_id = QDSP_MODULE_MAX - 1;
|
||||
info->max_queue_id = QDSP_QUEUE_MAX;
|
||||
info->max_image_id = 2;
|
||||
info->queue_offset = qdsp_queue_offset_table;
|
||||
info->task_to_module = qdsp_task_to_module;
|
||||
|
||||
info->module_count = ARRAY_SIZE(module_info);
|
||||
info->module = module_info;
|
||||
return 0;
|
||||
}
|
328
drivers/staging/dream/qdsp5/adsp_6225.c
Normal file
328
drivers/staging/dream/qdsp5/adsp_6225.c
Normal file
@ -0,0 +1,328 @@
|
||||
/* arch/arm/mach-msm/qdsp5/adsp_6225.h
|
||||
*
|
||||
* Copyright (c) 2008 QUALCOMM Incorporated.
|
||||
*
|
||||
* 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 "adsp.h"
|
||||
|
||||
/* Firmware modules */
|
||||
typedef enum {
|
||||
QDSP_MODULE_KERNEL,
|
||||
QDSP_MODULE_AFETASK,
|
||||
QDSP_MODULE_AUDPLAY0TASK,
|
||||
QDSP_MODULE_AUDPLAY1TASK,
|
||||
QDSP_MODULE_AUDPPTASK,
|
||||
QDSP_MODULE_VIDEOTASK,
|
||||
QDSP_MODULE_VIDEO_AAC_VOC,
|
||||
QDSP_MODULE_PCM_DEC,
|
||||
QDSP_MODULE_AUDIO_DEC_MP3,
|
||||
QDSP_MODULE_AUDIO_DEC_AAC,
|
||||
QDSP_MODULE_AUDIO_DEC_WMA,
|
||||
QDSP_MODULE_HOSTPCM,
|
||||
QDSP_MODULE_DTMF,
|
||||
QDSP_MODULE_AUDRECTASK,
|
||||
QDSP_MODULE_AUDPREPROCTASK,
|
||||
QDSP_MODULE_SBC_ENC,
|
||||
QDSP_MODULE_VOC_UMTS,
|
||||
QDSP_MODULE_VOC_CDMA,
|
||||
QDSP_MODULE_VOC_PCM,
|
||||
QDSP_MODULE_VOCENCTASK,
|
||||
QDSP_MODULE_VOCDECTASK,
|
||||
QDSP_MODULE_VOICEPROCTASK,
|
||||
QDSP_MODULE_VIDEOENCTASK,
|
||||
QDSP_MODULE_VFETASK,
|
||||
QDSP_MODULE_WAV_ENC,
|
||||
QDSP_MODULE_AACLC_ENC,
|
||||
QDSP_MODULE_VIDEO_AMR,
|
||||
QDSP_MODULE_VOC_AMR,
|
||||
QDSP_MODULE_VOC_EVRC,
|
||||
QDSP_MODULE_VOC_13K,
|
||||
QDSP_MODULE_VOC_FGV,
|
||||
QDSP_MODULE_DIAGTASK,
|
||||
QDSP_MODULE_JPEGTASK,
|
||||
QDSP_MODULE_LPMTASK,
|
||||
QDSP_MODULE_QCAMTASK,
|
||||
QDSP_MODULE_MODMATHTASK,
|
||||
QDSP_MODULE_AUDPLAY2TASK,
|
||||
QDSP_MODULE_AUDPLAY3TASK,
|
||||
QDSP_MODULE_AUDPLAY4TASK,
|
||||
QDSP_MODULE_GRAPHICSTASK,
|
||||
QDSP_MODULE_MIDI,
|
||||
QDSP_MODULE_GAUDIO,
|
||||
QDSP_MODULE_VDEC_LP_MODE,
|
||||
QDSP_MODULE_MAX,
|
||||
} qdsp_module_type;
|
||||
|
||||
#define QDSP_RTOS_MAX_TASK_ID 30U
|
||||
|
||||
/* Table of modules indexed by task ID for the GAUDIO image */
|
||||
static qdsp_module_type qdsp_gaudio_task_to_module_table[] = {
|
||||
QDSP_MODULE_KERNEL,
|
||||
QDSP_MODULE_AFETASK,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_AUDPPTASK,
|
||||
QDSP_MODULE_AUDPLAY0TASK,
|
||||
QDSP_MODULE_AUDPLAY1TASK,
|
||||
QDSP_MODULE_AUDPLAY2TASK,
|
||||
QDSP_MODULE_AUDPLAY3TASK,
|
||||
QDSP_MODULE_AUDPLAY4TASK,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_AUDRECTASK,
|
||||
QDSP_MODULE_AUDPREPROCTASK,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_GRAPHICSTASK,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
};
|
||||
|
||||
/* Queue offset table indexed by queue ID for the GAUDIO image */
|
||||
static uint32_t qdsp_gaudio_queue_offset_table[] = {
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_lpmCommandQueue */
|
||||
0x3f0, /* QDSP_mpuAfeQueue */
|
||||
0x420, /* QDSP_mpuGraphicsCmdQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_mpuModmathCmdQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVDecCmdQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVDecPktQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVEncCmdQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecCmdQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecPktQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_txMpuEncQueue */
|
||||
0x3f4, /* QDSP_uPAudPPCmd1Queue */
|
||||
0x3f8, /* QDSP_uPAudPPCmd2Queue */
|
||||
0x3fc, /* QDSP_uPAudPPCmd3Queue */
|
||||
0x40c, /* QDSP_uPAudPlay0BitStreamCtrlQueue */
|
||||
0x410, /* QDSP_uPAudPlay1BitStreamCtrlQueue */
|
||||
0x414, /* QDSP_uPAudPlay2BitStreamCtrlQueue */
|
||||
0x418, /* QDSP_uPAudPlay3BitStreamCtrlQueue */
|
||||
0x41c, /* QDSP_uPAudPlay4BitStreamCtrlQueue */
|
||||
0x400, /* QDSP_uPAudPreProcCmdQueue */
|
||||
0x408, /* QDSP_uPAudRecBitStreamQueue */
|
||||
0x404, /* QDSP_uPAudRecCmdQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegActionCmdQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegCfgCmdQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPVocProcQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandScaleQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandTableQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPDiagQueue */
|
||||
};
|
||||
|
||||
/* Table of modules indexed by task ID for the COMBO image */
|
||||
static qdsp_module_type qdsp_combo_task_to_module_table[] = {
|
||||
QDSP_MODULE_KERNEL,
|
||||
QDSP_MODULE_AFETASK,
|
||||
QDSP_MODULE_VOCDECTASK,
|
||||
QDSP_MODULE_VOCENCTASK,
|
||||
QDSP_MODULE_VIDEOTASK,
|
||||
QDSP_MODULE_VIDEOENCTASK,
|
||||
QDSP_MODULE_VOICEPROCTASK,
|
||||
QDSP_MODULE_VFETASK,
|
||||
QDSP_MODULE_JPEGTASK,
|
||||
QDSP_MODULE_AUDPPTASK,
|
||||
QDSP_MODULE_AUDPLAY0TASK,
|
||||
QDSP_MODULE_AUDPLAY1TASK,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_LPMTASK,
|
||||
QDSP_MODULE_AUDRECTASK,
|
||||
QDSP_MODULE_AUDPREPROCTASK,
|
||||
QDSP_MODULE_MODMATHTASK,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_DIAGTASK,
|
||||
QDSP_MODULE_MAX,
|
||||
};
|
||||
|
||||
/* Queue offset table indexed by queue ID for the COMBO image */
|
||||
static uint32_t qdsp_combo_queue_offset_table[] = {
|
||||
0x714, /* QDSP_lpmCommandQueue */
|
||||
0x6bc, /* QDSP_mpuAfeQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_mpuGraphicsCmdQueue */
|
||||
0x6d0, /* QDSP_mpuModmathCmdQueue */
|
||||
0x6e8, /* QDSP_mpuVDecCmdQueue */
|
||||
0x6ec, /* QDSP_mpuVDecPktQueue */
|
||||
0x6e4, /* QDSP_mpuVEncCmdQueue */
|
||||
0x6c4, /* QDSP_rxMpuDecCmdQueue */
|
||||
0x6c8, /* QDSP_rxMpuDecPktQueue */
|
||||
0x6cc, /* QDSP_txMpuEncQueue */
|
||||
0x6f0, /* QDSP_uPAudPPCmd1Queue */
|
||||
0x6f4, /* QDSP_uPAudPPCmd2Queue */
|
||||
0x6f8, /* QDSP_uPAudPPCmd3Queue */
|
||||
0x708, /* QDSP_uPAudPlay0BitStreamCtrlQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay1BitStreamCtrlQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay2BitStreamCtrlQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay3BitStreamCtrlQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay4BitStreamCtrlQueue */
|
||||
0x6fc, /* QDSP_uPAudPreProcCmdQueue */
|
||||
0x704, /* QDSP_uPAudRecBitStreamQueue */
|
||||
0x700, /* QDSP_uPAudRecCmdQueue */
|
||||
0x710, /* QDSP_uPJpegActionCmdQueue */
|
||||
0x70c, /* QDSP_uPJpegCfgCmdQueue */
|
||||
0x6c0, /* QDSP_uPVocProcQueue */
|
||||
0x6d8, /* QDSP_vfeCommandQueue */
|
||||
0x6e0, /* QDSP_vfeCommandScaleQueue */
|
||||
0x6dc, /* QDSP_vfeCommandTableQueue */
|
||||
0x6d4, /* QDSP_uPDiagQueue */
|
||||
};
|
||||
|
||||
/* Table of modules indexed by task ID for the QTV_LP image */
|
||||
static qdsp_module_type qdsp_qtv_lp_task_to_module_table[] = {
|
||||
QDSP_MODULE_KERNEL,
|
||||
QDSP_MODULE_AFETASK,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_VIDEOTASK,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_AUDPPTASK,
|
||||
QDSP_MODULE_AUDPLAY0TASK,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_AUDRECTASK,
|
||||
QDSP_MODULE_AUDPREPROCTASK,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
QDSP_MODULE_MAX,
|
||||
};
|
||||
|
||||
/* Queue offset table indexed by queue ID for the QTV_LP image */
|
||||
static uint32_t qdsp_qtv_lp_queue_offset_table[] = {
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_lpmCommandQueue */
|
||||
0x3fe, /* QDSP_mpuAfeQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_mpuGraphicsCmdQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_mpuModmathCmdQueue */
|
||||
0x402, /* QDSP_mpuVDecCmdQueue */
|
||||
0x406, /* QDSP_mpuVDecPktQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVEncCmdQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecCmdQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecPktQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_txMpuEncQueue */
|
||||
0x40e, /* QDSP_uPAudPPCmd1Queue */
|
||||
0x412, /* QDSP_uPAudPPCmd2Queue */
|
||||
0x416, /* QDSP_uPAudPPCmd3Queue */
|
||||
0x422, /* QDSP_uPAudPlay0BitStreamCtrlQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay1BitStreamCtrlQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay2BitStreamCtrlQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay3BitStreamCtrlQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay4BitStreamCtrlQueue */
|
||||
0x40a, /* QDSP_uPAudPreProcCmdQueue */
|
||||
0x41e, /* QDSP_uPAudRecBitStreamQueue */
|
||||
0x41a, /* QDSP_uPAudRecCmdQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegActionCmdQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegCfgCmdQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPVocProcQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandScaleQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandTableQueue */
|
||||
QDSP_RTOS_NO_QUEUE, /* QDSP_uPDiagQueue */
|
||||
};
|
||||
|
||||
/* Tables to convert tasks to modules */
|
||||
static qdsp_module_type *qdsp_task_to_module[] = {
|
||||
qdsp_combo_task_to_module_table,
|
||||
qdsp_gaudio_task_to_module_table,
|
||||
qdsp_qtv_lp_task_to_module_table,
|
||||
};
|
||||
|
||||
/* Tables to retrieve queue offsets */
|
||||
static uint32_t *qdsp_queue_offset_table[] = {
|
||||
qdsp_combo_queue_offset_table,
|
||||
qdsp_gaudio_queue_offset_table,
|
||||
qdsp_qtv_lp_queue_offset_table,
|
||||
};
|
||||
|
||||
#define QDSP_MODULE(n, clkname, clkrate, verify_cmd_func, patch_event_func) \
|
||||
{ .name = #n, .pdev_name = "adsp_" #n, .id = QDSP_MODULE_##n, \
|
||||
.clk_name = clkname, .clk_rate = clkrate, \
|
||||
.verify_cmd = verify_cmd_func, .patch_event = patch_event_func }
|
||||
|
||||
static struct adsp_module_info module_info[] = {
|
||||
QDSP_MODULE(AUDPLAY0TASK, NULL, 0, NULL, NULL),
|
||||
QDSP_MODULE(AUDPPTASK, NULL, 0, NULL, NULL),
|
||||
QDSP_MODULE(AUDRECTASK, NULL, 0, NULL, NULL),
|
||||
QDSP_MODULE(AUDPREPROCTASK, NULL, 0, NULL, NULL),
|
||||
QDSP_MODULE(VFETASK, "vfe_clk", 0, adsp_vfe_verify_cmd,
|
||||
adsp_vfe_patch_event),
|
||||
QDSP_MODULE(QCAMTASK, NULL, 0, NULL, NULL),
|
||||
QDSP_MODULE(LPMTASK, NULL, 0, adsp_lpm_verify_cmd, NULL),
|
||||
QDSP_MODULE(JPEGTASK, "vdc_clk", 0, adsp_jpeg_verify_cmd,
|
||||
adsp_jpeg_patch_event),
|
||||
QDSP_MODULE(VIDEOTASK, "vdc_clk", 96000000,
|
||||
adsp_video_verify_cmd, NULL),
|
||||
QDSP_MODULE(VDEC_LP_MODE, NULL, 0, NULL, NULL),
|
||||
QDSP_MODULE(VIDEOENCTASK, "vdc_clk", 96000000,
|
||||
adsp_videoenc_verify_cmd, NULL),
|
||||
};
|
||||
|
||||
int adsp_init_info(struct adsp_info *info)
|
||||
{
|
||||
info->send_irq = 0x00c00200;
|
||||
info->read_ctrl = 0x00400038;
|
||||
info->write_ctrl = 0x00400034;
|
||||
|
||||
info->max_msg16_size = 193;
|
||||
info->max_msg32_size = 8;
|
||||
|
||||
info->max_task_id = 16;
|
||||
info->max_module_id = QDSP_MODULE_MAX - 1;
|
||||
info->max_queue_id = QDSP_QUEUE_MAX;
|
||||
info->max_image_id = 2;
|
||||
info->queue_offset = qdsp_queue_offset_table;
|
||||
info->task_to_module = qdsp_task_to_module;
|
||||
|
||||
info->module_count = ARRAY_SIZE(module_info);
|
||||
info->module = module_info;
|
||||
return 0;
|
||||
}
|
641
drivers/staging/dream/qdsp5/adsp_driver.c
Normal file
641
drivers/staging/dream/qdsp5/adsp_driver.c
Normal file
@ -0,0 +1,641 @@
|
||||
/* arch/arm/mach-msm/qdsp5/adsp_driver.c
|
||||
*
|
||||
* Copyright (C) 2008 Google, Inc.
|
||||
* Author: Iliyan Malchev <ibm@android.com>
|
||||
*
|
||||
* 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/cdev.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include "adsp.h"
|
||||
|
||||
#include <linux/msm_adsp.h>
|
||||
#include <linux/android_pmem.h>
|
||||
|
||||
struct adsp_pmem_region {
|
||||
struct hlist_node list;
|
||||
void *vaddr;
|
||||
unsigned long paddr;
|
||||
unsigned long kvaddr;
|
||||
unsigned long len;
|
||||
struct file *file;
|
||||
};
|
||||
|
||||
struct adsp_device {
|
||||
struct msm_adsp_module *module;
|
||||
|
||||
spinlock_t event_queue_lock;
|
||||
wait_queue_head_t event_wait;
|
||||
struct list_head event_queue;
|
||||
int abort;
|
||||
|
||||
const char *name;
|
||||
struct device *device;
|
||||
struct cdev cdev;
|
||||
};
|
||||
|
||||
static struct adsp_device *inode_to_device(struct inode *inode);
|
||||
|
||||
#define __CONTAINS(r, v, l) ({ \
|
||||
typeof(r) __r = r; \
|
||||
typeof(v) __v = v; \
|
||||
typeof(v) __e = __v + l; \
|
||||
int res = __v >= __r->vaddr && \
|
||||
__e <= __r->vaddr + __r->len; \
|
||||
res; \
|
||||
})
|
||||
|
||||
#define CONTAINS(r1, r2) ({ \
|
||||
typeof(r2) __r2 = r2; \
|
||||
__CONTAINS(r1, __r2->vaddr, __r2->len); \
|
||||
})
|
||||
|
||||
#define IN_RANGE(r, v) ({ \
|
||||
typeof(r) __r = r; \
|
||||
typeof(v) __vv = v; \
|
||||
int res = ((__vv >= __r->vaddr) && \
|
||||
(__vv < (__r->vaddr + __r->len))); \
|
||||
res; \
|
||||
})
|
||||
|
||||
#define OVERLAPS(r1, r2) ({ \
|
||||
typeof(r1) __r1 = r1; \
|
||||
typeof(r2) __r2 = r2; \
|
||||
typeof(__r2->vaddr) __v = __r2->vaddr; \
|
||||
typeof(__v) __e = __v + __r2->len - 1; \
|
||||
int res = (IN_RANGE(__r1, __v) || IN_RANGE(__r1, __e)); \
|
||||
res; \
|
||||
})
|
||||
|
||||
static int adsp_pmem_check(struct msm_adsp_module *module,
|
||||
void *vaddr, unsigned long len)
|
||||
{
|
||||
struct adsp_pmem_region *region_elt;
|
||||
struct hlist_node *node;
|
||||
struct adsp_pmem_region t = { .vaddr = vaddr, .len = len };
|
||||
|
||||
hlist_for_each_entry(region_elt, node, &module->pmem_regions, list) {
|
||||
if (CONTAINS(region_elt, &t) || CONTAINS(&t, region_elt) ||
|
||||
OVERLAPS(region_elt, &t)) {
|
||||
printk(KERN_ERR "adsp: module %s:"
|
||||
" region (vaddr %p len %ld)"
|
||||
" clashes with registered region"
|
||||
" (vaddr %p paddr %p len %ld)\n",
|
||||
module->name,
|
||||
vaddr, len,
|
||||
region_elt->vaddr,
|
||||
(void *)region_elt->paddr,
|
||||
region_elt->len);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adsp_pmem_add(struct msm_adsp_module *module,
|
||||
struct adsp_pmem_info *info)
|
||||
{
|
||||
unsigned long paddr, kvaddr, len;
|
||||
struct file *file;
|
||||
struct adsp_pmem_region *region;
|
||||
int rc = -EINVAL;
|
||||
|
||||
mutex_lock(&module->pmem_regions_lock);
|
||||
region = kmalloc(sizeof(*region), GFP_KERNEL);
|
||||
if (!region) {
|
||||
rc = -ENOMEM;
|
||||
goto end;
|
||||
}
|
||||
INIT_HLIST_NODE(®ion->list);
|
||||
if (get_pmem_file(info->fd, &paddr, &kvaddr, &len, &file)) {
|
||||
kfree(region);
|
||||
goto end;
|
||||
}
|
||||
|
||||
rc = adsp_pmem_check(module, info->vaddr, len);
|
||||
if (rc < 0) {
|
||||
put_pmem_file(file);
|
||||
kfree(region);
|
||||
goto end;
|
||||
}
|
||||
|
||||
region->vaddr = info->vaddr;
|
||||
region->paddr = paddr;
|
||||
region->kvaddr = kvaddr;
|
||||
region->len = len;
|
||||
region->file = file;
|
||||
|
||||
hlist_add_head(®ion->list, &module->pmem_regions);
|
||||
end:
|
||||
mutex_unlock(&module->pmem_regions_lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int adsp_pmem_lookup_vaddr(struct msm_adsp_module *module, void **addr,
|
||||
unsigned long len, struct adsp_pmem_region **region)
|
||||
{
|
||||
struct hlist_node *node;
|
||||
void *vaddr = *addr;
|
||||
struct adsp_pmem_region *region_elt;
|
||||
|
||||
int match_count = 0;
|
||||
|
||||
*region = NULL;
|
||||
|
||||
/* returns physical address or zero */
|
||||
hlist_for_each_entry(region_elt, node, &module->pmem_regions, list) {
|
||||
if (vaddr >= region_elt->vaddr &&
|
||||
vaddr < region_elt->vaddr + region_elt->len &&
|
||||
vaddr + len <= region_elt->vaddr + region_elt->len) {
|
||||
/* offset since we could pass vaddr inside a registerd
|
||||
* pmem buffer
|
||||
*/
|
||||
|
||||
match_count++;
|
||||
if (!*region)
|
||||
*region = region_elt;
|
||||
}
|
||||
}
|
||||
|
||||
if (match_count > 1) {
|
||||
printk(KERN_ERR "adsp: module %s: "
|
||||
"multiple hits for vaddr %p, len %ld\n",
|
||||
module->name, vaddr, len);
|
||||
hlist_for_each_entry(region_elt, node,
|
||||
&module->pmem_regions, list) {
|
||||
if (vaddr >= region_elt->vaddr &&
|
||||
vaddr < region_elt->vaddr + region_elt->len &&
|
||||
vaddr + len <= region_elt->vaddr + region_elt->len)
|
||||
printk(KERN_ERR "\t%p, %ld --> %p\n",
|
||||
region_elt->vaddr,
|
||||
region_elt->len,
|
||||
(void *)region_elt->paddr);
|
||||
}
|
||||
}
|
||||
|
||||
return *region ? 0 : -1;
|
||||
}
|
||||
|
||||
int adsp_pmem_fixup_kvaddr(struct msm_adsp_module *module, void **addr,
|
||||
unsigned long *kvaddr, unsigned long len)
|
||||
{
|
||||
struct adsp_pmem_region *region;
|
||||
void *vaddr = *addr;
|
||||
unsigned long *paddr = (unsigned long *)addr;
|
||||
int ret;
|
||||
|
||||
ret = adsp_pmem_lookup_vaddr(module, addr, len, ®ion);
|
||||
if (ret) {
|
||||
printk(KERN_ERR "adsp: not patching %s (paddr & kvaddr),"
|
||||
" lookup (%p, %ld) failed\n",
|
||||
module->name, vaddr, len);
|
||||
return ret;
|
||||
}
|
||||
*paddr = region->paddr + (vaddr - region->vaddr);
|
||||
*kvaddr = region->kvaddr + (vaddr - region->vaddr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int adsp_pmem_fixup(struct msm_adsp_module *module, void **addr,
|
||||
unsigned long len)
|
||||
{
|
||||
struct adsp_pmem_region *region;
|
||||
void *vaddr = *addr;
|
||||
unsigned long *paddr = (unsigned long *)addr;
|
||||
int ret;
|
||||
|
||||
ret = adsp_pmem_lookup_vaddr(module, addr, len, ®ion);
|
||||
if (ret) {
|
||||
printk(KERN_ERR "adsp: not patching %s, lookup (%p, %ld) failed\n",
|
||||
module->name, vaddr, len);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*paddr = region->paddr + (vaddr - region->vaddr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adsp_verify_cmd(struct msm_adsp_module *module,
|
||||
unsigned int queue_id, void *cmd_data,
|
||||
size_t cmd_size)
|
||||
{
|
||||
/* call the per module verifier */
|
||||
if (module->verify_cmd)
|
||||
return module->verify_cmd(module, queue_id, cmd_data,
|
||||
cmd_size);
|
||||
else
|
||||
printk(KERN_INFO "adsp: no packet verifying function "
|
||||
"for task %s\n", module->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long adsp_write_cmd(struct adsp_device *adev, void __user *arg)
|
||||
{
|
||||
struct adsp_command_t cmd;
|
||||
unsigned char buf[256];
|
||||
void *cmd_data;
|
||||
long rc;
|
||||
|
||||
if (copy_from_user(&cmd, (void __user *)arg, sizeof(cmd)))
|
||||
return -EFAULT;
|
||||
|
||||
if (cmd.len > 256) {
|
||||
cmd_data = kmalloc(cmd.len, GFP_USER);
|
||||
if (!cmd_data)
|
||||
return -ENOMEM;
|
||||
} else {
|
||||
cmd_data = buf;
|
||||
}
|
||||
|
||||
if (copy_from_user(cmd_data, (void __user *)(cmd.data), cmd.len)) {
|
||||
rc = -EFAULT;
|
||||
goto end;
|
||||
}
|
||||
|
||||
mutex_lock(&adev->module->pmem_regions_lock);
|
||||
if (adsp_verify_cmd(adev->module, cmd.queue, cmd_data, cmd.len)) {
|
||||
printk(KERN_ERR "module %s: verify failed.\n",
|
||||
adev->module->name);
|
||||
rc = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
rc = msm_adsp_write(adev->module, cmd.queue, cmd_data, cmd.len);
|
||||
end:
|
||||
mutex_unlock(&adev->module->pmem_regions_lock);
|
||||
|
||||
if (cmd.len > 256)
|
||||
kfree(cmd_data);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int adsp_events_pending(struct adsp_device *adev)
|
||||
{
|
||||
unsigned long flags;
|
||||
int yes;
|
||||
spin_lock_irqsave(&adev->event_queue_lock, flags);
|
||||
yes = !list_empty(&adev->event_queue);
|
||||
spin_unlock_irqrestore(&adev->event_queue_lock, flags);
|
||||
return yes || adev->abort;
|
||||
}
|
||||
|
||||
static int adsp_pmem_lookup_paddr(struct msm_adsp_module *module, void **addr,
|
||||
struct adsp_pmem_region **region)
|
||||
{
|
||||
struct hlist_node *node;
|
||||
unsigned long paddr = (unsigned long)(*addr);
|
||||
struct adsp_pmem_region *region_elt;
|
||||
|
||||
hlist_for_each_entry(region_elt, node, &module->pmem_regions, list) {
|
||||
if (paddr >= region_elt->paddr &&
|
||||
paddr < region_elt->paddr + region_elt->len) {
|
||||
*region = region_elt;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int adsp_pmem_paddr_fixup(struct msm_adsp_module *module, void **addr)
|
||||
{
|
||||
struct adsp_pmem_region *region;
|
||||
unsigned long paddr = (unsigned long)(*addr);
|
||||
unsigned long *vaddr = (unsigned long *)addr;
|
||||
int ret;
|
||||
|
||||
ret = adsp_pmem_lookup_paddr(module, addr, ®ion);
|
||||
if (ret) {
|
||||
printk(KERN_ERR "adsp: not patching %s, paddr %p lookup failed\n",
|
||||
module->name, vaddr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*vaddr = (unsigned long)region->vaddr + (paddr - region->paddr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adsp_patch_event(struct msm_adsp_module *module,
|
||||
struct adsp_event *event)
|
||||
{
|
||||
/* call the per-module msg verifier */
|
||||
if (module->patch_event)
|
||||
return module->patch_event(module, event);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long adsp_get_event(struct adsp_device *adev, void __user *arg)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct adsp_event *data = NULL;
|
||||
struct adsp_event_t evt;
|
||||
int timeout;
|
||||
long rc = 0;
|
||||
|
||||
if (copy_from_user(&evt, arg, sizeof(struct adsp_event_t)))
|
||||
return -EFAULT;
|
||||
|
||||
timeout = (int)evt.timeout_ms;
|
||||
|
||||
if (timeout > 0) {
|
||||
rc = wait_event_interruptible_timeout(
|
||||
adev->event_wait, adsp_events_pending(adev),
|
||||
msecs_to_jiffies(timeout));
|
||||
if (rc == 0)
|
||||
return -ETIMEDOUT;
|
||||
} else {
|
||||
rc = wait_event_interruptible(
|
||||
adev->event_wait, adsp_events_pending(adev));
|
||||
}
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
if (adev->abort)
|
||||
return -ENODEV;
|
||||
|
||||
spin_lock_irqsave(&adev->event_queue_lock, flags);
|
||||
if (!list_empty(&adev->event_queue)) {
|
||||
data = list_first_entry(&adev->event_queue,
|
||||
struct adsp_event, list);
|
||||
list_del(&data->list);
|
||||
}
|
||||
spin_unlock_irqrestore(&adev->event_queue_lock, flags);
|
||||
|
||||
if (!data)
|
||||
return -EAGAIN;
|
||||
|
||||
/* DSP messages are type 0; they may contain physical addresses */
|
||||
if (data->type == 0)
|
||||
adsp_patch_event(adev->module, data);
|
||||
|
||||
/* map adsp_event --> adsp_event_t */
|
||||
if (evt.len < data->size) {
|
||||
rc = -ETOOSMALL;
|
||||
goto end;
|
||||
}
|
||||
if (data->msg_id != EVENT_MSG_ID) {
|
||||
if (copy_to_user((void *)(evt.data), data->data.msg16,
|
||||
data->size)) {
|
||||
rc = -EFAULT;
|
||||
goto end;
|
||||
}
|
||||
} else {
|
||||
if (copy_to_user((void *)(evt.data), data->data.msg32,
|
||||
data->size)) {
|
||||
rc = -EFAULT;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
evt.type = data->type; /* 0 --> from aDSP, 1 --> from ARM9 */
|
||||
evt.msg_id = data->msg_id;
|
||||
evt.flags = data->is16;
|
||||
evt.len = data->size;
|
||||
if (copy_to_user(arg, &evt, sizeof(evt)))
|
||||
rc = -EFAULT;
|
||||
end:
|
||||
kfree(data);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static long adsp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct adsp_device *adev = filp->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case ADSP_IOCTL_ENABLE:
|
||||
return msm_adsp_enable(adev->module);
|
||||
|
||||
case ADSP_IOCTL_DISABLE:
|
||||
return msm_adsp_disable(adev->module);
|
||||
|
||||
case ADSP_IOCTL_DISABLE_EVENT_RSP:
|
||||
return 0;
|
||||
|
||||
case ADSP_IOCTL_DISABLE_ACK:
|
||||
pr_err("adsp: ADSP_IOCTL_DISABLE_ACK is not implemented.\n");
|
||||
break;
|
||||
|
||||
case ADSP_IOCTL_WRITE_COMMAND:
|
||||
return adsp_write_cmd(adev, (void __user *) arg);
|
||||
|
||||
case ADSP_IOCTL_GET_EVENT:
|
||||
return adsp_get_event(adev, (void __user *) arg);
|
||||
|
||||
case ADSP_IOCTL_SET_CLKRATE: {
|
||||
#if CONFIG_MSM_AMSS_VERSION==6350
|
||||
unsigned long clk_rate;
|
||||
if (copy_from_user(&clk_rate, (void *) arg, sizeof(clk_rate)))
|
||||
return -EFAULT;
|
||||
return adsp_set_clkrate(adev->module, clk_rate);
|
||||
#endif
|
||||
}
|
||||
|
||||
case ADSP_IOCTL_REGISTER_PMEM: {
|
||||
struct adsp_pmem_info info;
|
||||
if (copy_from_user(&info, (void *) arg, sizeof(info)))
|
||||
return -EFAULT;
|
||||
return adsp_pmem_add(adev->module, &info);
|
||||
}
|
||||
|
||||
case ADSP_IOCTL_ABORT_EVENT_READ:
|
||||
adev->abort = 1;
|
||||
wake_up(&adev->event_wait);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int adsp_release(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct adsp_device *adev = filp->private_data;
|
||||
struct msm_adsp_module *module = adev->module;
|
||||
struct hlist_node *node, *tmp;
|
||||
struct adsp_pmem_region *region;
|
||||
|
||||
pr_info("adsp_release() '%s'\n", adev->name);
|
||||
|
||||
/* clear module before putting it to avoid race with open() */
|
||||
adev->module = NULL;
|
||||
|
||||
mutex_lock(&module->pmem_regions_lock);
|
||||
hlist_for_each_safe(node, tmp, &module->pmem_regions) {
|
||||
region = hlist_entry(node, struct adsp_pmem_region, list);
|
||||
hlist_del(node);
|
||||
put_pmem_file(region->file);
|
||||
kfree(region);
|
||||
}
|
||||
mutex_unlock(&module->pmem_regions_lock);
|
||||
BUG_ON(!hlist_empty(&module->pmem_regions));
|
||||
|
||||
msm_adsp_put(module);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void adsp_event(void *driver_data, unsigned id, size_t len,
|
||||
void (*getevent)(void *ptr, size_t len))
|
||||
{
|
||||
struct adsp_device *adev = driver_data;
|
||||
struct adsp_event *event;
|
||||
unsigned long flags;
|
||||
|
||||
if (len > ADSP_EVENT_MAX_SIZE) {
|
||||
pr_err("adsp_event: event too large (%d bytes)\n", len);
|
||||
return;
|
||||
}
|
||||
|
||||
event = kmalloc(sizeof(*event), GFP_ATOMIC);
|
||||
if (!event) {
|
||||
pr_err("adsp_event: cannot allocate buffer\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (id != EVENT_MSG_ID) {
|
||||
event->type = 0;
|
||||
event->is16 = 0;
|
||||
event->msg_id = id;
|
||||
event->size = len;
|
||||
|
||||
getevent(event->data.msg16, len);
|
||||
} else {
|
||||
event->type = 1;
|
||||
event->is16 = 1;
|
||||
event->msg_id = id;
|
||||
event->size = len;
|
||||
getevent(event->data.msg32, len);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&adev->event_queue_lock, flags);
|
||||
list_add_tail(&event->list, &adev->event_queue);
|
||||
spin_unlock_irqrestore(&adev->event_queue_lock, flags);
|
||||
wake_up(&adev->event_wait);
|
||||
}
|
||||
|
||||
static struct msm_adsp_ops adsp_ops = {
|
||||
.event = adsp_event,
|
||||
};
|
||||
|
||||
static int adsp_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct adsp_device *adev;
|
||||
int rc;
|
||||
|
||||
rc = nonseekable_open(inode, filp);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
adev = inode_to_device(inode);
|
||||
if (!adev)
|
||||
return -ENODEV;
|
||||
|
||||
pr_info("adsp_open() name = '%s'\n", adev->name);
|
||||
|
||||
rc = msm_adsp_get(adev->name, &adev->module, &adsp_ops, adev);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
pr_info("adsp_open() module '%s' adev %p\n", adev->name, adev);
|
||||
filp->private_data = adev;
|
||||
adev->abort = 0;
|
||||
INIT_HLIST_HEAD(&adev->module->pmem_regions);
|
||||
mutex_init(&adev->module->pmem_regions_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned adsp_device_count;
|
||||
static struct adsp_device *adsp_devices;
|
||||
|
||||
static struct adsp_device *inode_to_device(struct inode *inode)
|
||||
{
|
||||
unsigned n = MINOR(inode->i_rdev);
|
||||
if (n < adsp_device_count) {
|
||||
if (adsp_devices[n].device)
|
||||
return adsp_devices + n;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static dev_t adsp_devno;
|
||||
static struct class *adsp_class;
|
||||
|
||||
static struct file_operations adsp_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = adsp_open,
|
||||
.unlocked_ioctl = adsp_ioctl,
|
||||
.release = adsp_release,
|
||||
};
|
||||
|
||||
static void adsp_create(struct adsp_device *adev, const char *name,
|
||||
struct device *parent, dev_t devt)
|
||||
{
|
||||
struct device *dev;
|
||||
int rc;
|
||||
|
||||
dev = device_create(adsp_class, parent, devt, "%s", name);
|
||||
if (IS_ERR(dev))
|
||||
return;
|
||||
|
||||
init_waitqueue_head(&adev->event_wait);
|
||||
INIT_LIST_HEAD(&adev->event_queue);
|
||||
spin_lock_init(&adev->event_queue_lock);
|
||||
|
||||
cdev_init(&adev->cdev, &adsp_fops);
|
||||
adev->cdev.owner = THIS_MODULE;
|
||||
|
||||
rc = cdev_add(&adev->cdev, devt, 1);
|
||||
if (rc < 0) {
|
||||
device_destroy(adsp_class, devt);
|
||||
} else {
|
||||
adev->device = dev;
|
||||
adev->name = name;
|
||||
}
|
||||
}
|
||||
|
||||
void msm_adsp_publish_cdevs(struct msm_adsp_module *modules, unsigned n)
|
||||
{
|
||||
int rc;
|
||||
|
||||
adsp_devices = kzalloc(sizeof(struct adsp_device) * n, GFP_KERNEL);
|
||||
if (!adsp_devices)
|
||||
return;
|
||||
|
||||
adsp_class = class_create(THIS_MODULE, "adsp");
|
||||
if (IS_ERR(adsp_class))
|
||||
goto fail_create_class;
|
||||
|
||||
rc = alloc_chrdev_region(&adsp_devno, 0, n, "adsp");
|
||||
if (rc < 0)
|
||||
goto fail_alloc_region;
|
||||
|
||||
adsp_device_count = n;
|
||||
for (n = 0; n < adsp_device_count; n++) {
|
||||
adsp_create(adsp_devices + n,
|
||||
modules[n].name, &modules[n].pdev.dev,
|
||||
MKDEV(MAJOR(adsp_devno), n));
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
fail_alloc_region:
|
||||
class_unregister(adsp_class);
|
||||
fail_create_class:
|
||||
kfree(adsp_devices);
|
||||
}
|
121
drivers/staging/dream/qdsp5/adsp_info.c
Normal file
121
drivers/staging/dream/qdsp5/adsp_info.c
Normal file
@ -0,0 +1,121 @@
|
||||
/* arch/arm/mach-msm/adsp_info.c
|
||||
*
|
||||
* Copyright (c) 2008 QUALCOMM Incorporated.
|
||||
* Copyright (c) 2008 QUALCOMM USA, INC.
|
||||
*
|
||||
* 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 "adsp.h"
|
||||
|
||||
/* Firmware modules */
|
||||
#define QDSP_MODULE_KERNEL 0x0106dd4e
|
||||
#define QDSP_MODULE_AFETASK 0x0106dd6f
|
||||
#define QDSP_MODULE_AUDPLAY0TASK 0x0106dd70
|
||||
#define QDSP_MODULE_AUDPLAY1TASK 0x0106dd71
|
||||
#define QDSP_MODULE_AUDPPTASK 0x0106dd72
|
||||
#define QDSP_MODULE_VIDEOTASK 0x0106dd73
|
||||
#define QDSP_MODULE_VIDEO_AAC_VOC 0x0106dd74
|
||||
#define QDSP_MODULE_PCM_DEC 0x0106dd75
|
||||
#define QDSP_MODULE_AUDIO_DEC_MP3 0x0106dd76
|
||||
#define QDSP_MODULE_AUDIO_DEC_AAC 0x0106dd77
|
||||
#define QDSP_MODULE_AUDIO_DEC_WMA 0x0106dd78
|
||||
#define QDSP_MODULE_HOSTPCM 0x0106dd79
|
||||
#define QDSP_MODULE_DTMF 0x0106dd7a
|
||||
#define QDSP_MODULE_AUDRECTASK 0x0106dd7b
|
||||
#define QDSP_MODULE_AUDPREPROCTASK 0x0106dd7c
|
||||
#define QDSP_MODULE_SBC_ENC 0x0106dd7d
|
||||
#define QDSP_MODULE_VOC_UMTS 0x0106dd9a
|
||||
#define QDSP_MODULE_VOC_CDMA 0x0106dd98
|
||||
#define QDSP_MODULE_VOC_PCM 0x0106dd7f
|
||||
#define QDSP_MODULE_VOCENCTASK 0x0106dd80
|
||||
#define QDSP_MODULE_VOCDECTASK 0x0106dd81
|
||||
#define QDSP_MODULE_VOICEPROCTASK 0x0106dd82
|
||||
#define QDSP_MODULE_VIDEOENCTASK 0x0106dd83
|
||||
#define QDSP_MODULE_VFETASK 0x0106dd84
|
||||
#define QDSP_MODULE_WAV_ENC 0x0106dd85
|
||||
#define QDSP_MODULE_AACLC_ENC 0x0106dd86
|
||||
#define QDSP_MODULE_VIDEO_AMR 0x0106dd87
|
||||
#define QDSP_MODULE_VOC_AMR 0x0106dd88
|
||||
#define QDSP_MODULE_VOC_EVRC 0x0106dd89
|
||||
#define QDSP_MODULE_VOC_13K 0x0106dd8a
|
||||
#define QDSP_MODULE_VOC_FGV 0x0106dd8b
|
||||
#define QDSP_MODULE_DIAGTASK 0x0106dd8c
|
||||
#define QDSP_MODULE_JPEGTASK 0x0106dd8d
|
||||
#define QDSP_MODULE_LPMTASK 0x0106dd8e
|
||||
#define QDSP_MODULE_QCAMTASK 0x0106dd8f
|
||||
#define QDSP_MODULE_MODMATHTASK 0x0106dd90
|
||||
#define QDSP_MODULE_AUDPLAY2TASK 0x0106dd91
|
||||
#define QDSP_MODULE_AUDPLAY3TASK 0x0106dd92
|
||||
#define QDSP_MODULE_AUDPLAY4TASK 0x0106dd93
|
||||
#define QDSP_MODULE_GRAPHICSTASK 0x0106dd94
|
||||
#define QDSP_MODULE_MIDI 0x0106dd95
|
||||
#define QDSP_MODULE_GAUDIO 0x0106dd96
|
||||
#define QDSP_MODULE_VDEC_LP_MODE 0x0106dd97
|
||||
#define QDSP_MODULE_MAX 0x7fffffff
|
||||
|
||||
/* DO NOT USE: Force this enum to be a 32bit type to improve speed */
|
||||
#define QDSP_MODULE_32BIT_DUMMY 0x10000
|
||||
|
||||
static uint32_t *qdsp_task_to_module[IMG_MAX];
|
||||
static uint32_t *qdsp_queue_offset_table[IMG_MAX];
|
||||
|
||||
#define QDSP_MODULE(n, clkname, clkrate, verify_cmd_func, patch_event_func) \
|
||||
{ .name = #n, .pdev_name = "adsp_" #n, .id = QDSP_MODULE_##n, \
|
||||
.clk_name = clkname, .clk_rate = clkrate, \
|
||||
.verify_cmd = verify_cmd_func, .patch_event = patch_event_func }
|
||||
|
||||
static struct adsp_module_info module_info[] = {
|
||||
QDSP_MODULE(AUDPLAY0TASK, NULL, 0, NULL, NULL),
|
||||
QDSP_MODULE(AUDPPTASK, NULL, 0, NULL, NULL),
|
||||
QDSP_MODULE(AUDRECTASK, NULL, 0, NULL, NULL),
|
||||
QDSP_MODULE(AUDPREPROCTASK, NULL, 0, NULL, NULL),
|
||||
QDSP_MODULE(VFETASK, "vfe_clk", 0, adsp_vfe_verify_cmd,
|
||||
adsp_vfe_patch_event),
|
||||
QDSP_MODULE(QCAMTASK, NULL, 0, NULL, NULL),
|
||||
QDSP_MODULE(LPMTASK, NULL, 0, adsp_lpm_verify_cmd, NULL),
|
||||
QDSP_MODULE(JPEGTASK, "vdc_clk", 96000000, adsp_jpeg_verify_cmd,
|
||||
adsp_jpeg_patch_event),
|
||||
QDSP_MODULE(VIDEOTASK, "vdc_clk", 96000000,
|
||||
adsp_video_verify_cmd, NULL),
|
||||
QDSP_MODULE(VDEC_LP_MODE, NULL, 0, NULL, NULL),
|
||||
QDSP_MODULE(VIDEOENCTASK, "vdc_clk", 96000000,
|
||||
adsp_videoenc_verify_cmd, NULL),
|
||||
};
|
||||
|
||||
int adsp_init_info(struct adsp_info *info)
|
||||
{
|
||||
uint32_t img_num;
|
||||
|
||||
info->send_irq = 0x00c00200;
|
||||
info->read_ctrl = 0x00400038;
|
||||
info->write_ctrl = 0x00400034;
|
||||
|
||||
info->max_msg16_size = 193;
|
||||
info->max_msg32_size = 8;
|
||||
for (img_num = 0; img_num < IMG_MAX; img_num++)
|
||||
qdsp_queue_offset_table[img_num] =
|
||||
&info->init_info_ptr->queue_offsets[img_num][0];
|
||||
|
||||
for (img_num = 0; img_num < IMG_MAX; img_num++)
|
||||
qdsp_task_to_module[img_num] =
|
||||
&info->init_info_ptr->task_to_module_tbl[img_num][0];
|
||||
info->max_task_id = 30;
|
||||
info->max_module_id = QDSP_MODULE_MAX - 1;
|
||||
info->max_queue_id = QDSP_MAX_NUM_QUEUES;
|
||||
info->max_image_id = 2;
|
||||
info->queue_offset = qdsp_queue_offset_table;
|
||||
info->task_to_module = qdsp_task_to_module;
|
||||
|
||||
info->module_count = ARRAY_SIZE(module_info);
|
||||
info->module = module_info;
|
||||
return 0;
|
||||
}
|
31
drivers/staging/dream/qdsp5/adsp_jpeg_patch_event.c
Normal file
31
drivers/staging/dream/qdsp5/adsp_jpeg_patch_event.c
Normal file
@ -0,0 +1,31 @@
|
||||
/* arch/arm/mach-msm/qdsp5/adsp_jpeg_patch_event.c
|
||||
*
|
||||
* Verification code for aDSP JPEG events.
|
||||
*
|
||||
* Copyright (c) 2008 QUALCOMM Incorporated
|
||||
* Copyright (C) 2008 Google, Inc.
|
||||
*
|
||||
* 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 <mach/qdsp5/qdsp5jpegmsg.h>
|
||||
#include "adsp.h"
|
||||
|
||||
int adsp_jpeg_patch_event(struct msm_adsp_module *module,
|
||||
struct adsp_event *event)
|
||||
{
|
||||
if (event->msg_id == JPEG_MSG_ENC_OP_PRODUCED) {
|
||||
jpeg_msg_enc_op_produced *op = (jpeg_msg_enc_op_produced *)event->data.msg16;
|
||||
return adsp_pmem_paddr_fixup(module, (void **)&op->op_buf_addr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
182
drivers/staging/dream/qdsp5/adsp_jpeg_verify_cmd.c
Normal file
182
drivers/staging/dream/qdsp5/adsp_jpeg_verify_cmd.c
Normal file
@ -0,0 +1,182 @@
|
||||
/* arch/arm/mach-msm/qdsp5/adsp_jpeg_verify_cmd.c
|
||||
*
|
||||
* Verification code for aDSP JPEG packets from userspace.
|
||||
*
|
||||
* Copyright (c) 2008 QUALCOMM Incorporated
|
||||
* Copyright (C) 2008 Google, Inc.
|
||||
*
|
||||
* 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 <mach/qdsp5/qdsp5jpegcmdi.h>
|
||||
#include "adsp.h"
|
||||
|
||||
static uint32_t dec_fmt;
|
||||
|
||||
static inline void get_sizes(jpeg_cmd_enc_cfg *cmd, uint32_t *luma_size,
|
||||
uint32_t *chroma_size)
|
||||
{
|
||||
uint32_t fmt, luma_width, luma_height;
|
||||
|
||||
fmt = cmd->process_cfg & JPEG_CMD_ENC_PROCESS_CFG_IP_DATA_FORMAT_M;
|
||||
luma_width = (cmd->ip_size_cfg & JPEG_CMD_IP_SIZE_CFG_LUMA_WIDTH_M)
|
||||
>> 16;
|
||||
luma_height = cmd->frag_cfg & JPEG_CMD_FRAG_SIZE_LUMA_HEIGHT_M;
|
||||
*luma_size = luma_width * luma_height;
|
||||
if (fmt == JPEG_CMD_ENC_PROCESS_CFG_IP_DATA_FORMAT_H2V2)
|
||||
*chroma_size = *luma_size/2;
|
||||
else
|
||||
*chroma_size = *luma_size;
|
||||
}
|
||||
|
||||
static inline int verify_jpeg_cmd_enc_cfg(struct msm_adsp_module *module,
|
||||
void *cmd_data, size_t cmd_size)
|
||||
{
|
||||
jpeg_cmd_enc_cfg *cmd = (jpeg_cmd_enc_cfg *)cmd_data;
|
||||
uint32_t luma_size, chroma_size;
|
||||
int i, num_frags;
|
||||
|
||||
if (cmd_size != sizeof(jpeg_cmd_enc_cfg)) {
|
||||
printk(KERN_ERR "adsp: module %s: JPEG ENC CFG invalid cmd_size %d\n",
|
||||
module->name, cmd_size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
get_sizes(cmd, &luma_size, &chroma_size);
|
||||
num_frags = (cmd->process_cfg >> 10) & 0xf;
|
||||
num_frags = ((num_frags == 1) ? num_frags : num_frags * 2);
|
||||
for (i = 0; i < num_frags; i += 2) {
|
||||
if (adsp_pmem_fixup(module, (void **)(&cmd->frag_cfg_part[i]), luma_size) ||
|
||||
adsp_pmem_fixup(module, (void **)(&cmd->frag_cfg_part[i+1]), chroma_size))
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (adsp_pmem_fixup(module, (void **)&cmd->op_buf_0_cfg_part1,
|
||||
cmd->op_buf_0_cfg_part2) ||
|
||||
adsp_pmem_fixup(module, (void **)&cmd->op_buf_1_cfg_part1,
|
||||
cmd->op_buf_1_cfg_part2))
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int verify_jpeg_cmd_dec_cfg(struct msm_adsp_module *module,
|
||||
void *cmd_data, size_t cmd_size)
|
||||
{
|
||||
jpeg_cmd_dec_cfg *cmd = (jpeg_cmd_dec_cfg *)cmd_data;
|
||||
uint32_t div;
|
||||
|
||||
if (cmd_size != sizeof(jpeg_cmd_dec_cfg)) {
|
||||
printk(KERN_ERR "adsp: module %s: JPEG DEC CFG invalid cmd_size %d\n",
|
||||
module->name, cmd_size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (adsp_pmem_fixup(module, (void **)&cmd->ip_stream_buf_cfg_part1,
|
||||
cmd->ip_stream_buf_cfg_part2) ||
|
||||
adsp_pmem_fixup(module, (void **)&cmd->op_stream_buf_0_cfg_part1,
|
||||
cmd->op_stream_buf_0_cfg_part2) ||
|
||||
adsp_pmem_fixup(module, (void **)&cmd->op_stream_buf_1_cfg_part1,
|
||||
cmd->op_stream_buf_1_cfg_part2))
|
||||
return -1;
|
||||
dec_fmt = cmd->op_data_format &
|
||||
JPEG_CMD_DEC_OP_DATA_FORMAT_M;
|
||||
div = (dec_fmt == JPEG_CMD_DEC_OP_DATA_FORMAT_H2V2) ? 2 : 1;
|
||||
if (adsp_pmem_fixup(module, (void **)&cmd->op_stream_buf_0_cfg_part3,
|
||||
cmd->op_stream_buf_0_cfg_part2 / div) ||
|
||||
adsp_pmem_fixup(module, (void **)&cmd->op_stream_buf_1_cfg_part3,
|
||||
cmd->op_stream_buf_1_cfg_part2 / div))
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int verify_jpeg_cfg_cmd(struct msm_adsp_module *module,
|
||||
void *cmd_data, size_t cmd_size)
|
||||
{
|
||||
uint32_t cmd_id = ((uint32_t *)cmd_data)[0];
|
||||
switch(cmd_id) {
|
||||
case JPEG_CMD_ENC_CFG:
|
||||
return verify_jpeg_cmd_enc_cfg(module, cmd_data, cmd_size);
|
||||
case JPEG_CMD_DEC_CFG:
|
||||
return verify_jpeg_cmd_dec_cfg(module, cmd_data, cmd_size);
|
||||
default:
|
||||
if (cmd_id > 1) {
|
||||
printk(KERN_ERR "adsp: module %s: invalid JPEG CFG cmd_id %d\n", module->name, cmd_id);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int verify_jpeg_action_cmd(struct msm_adsp_module *module,
|
||||
void *cmd_data, size_t cmd_size)
|
||||
{
|
||||
uint32_t cmd_id = ((uint32_t *)cmd_data)[0];
|
||||
switch (cmd_id) {
|
||||
case JPEG_CMD_ENC_OP_CONSUMED:
|
||||
{
|
||||
jpeg_cmd_enc_op_consumed *cmd =
|
||||
(jpeg_cmd_enc_op_consumed *)cmd_data;
|
||||
|
||||
if (cmd_size != sizeof(jpeg_cmd_enc_op_consumed)) {
|
||||
printk(KERN_ERR "adsp: module %s: JPEG_CMD_ENC_OP_CONSUMED invalid size %d\n",
|
||||
module->name, cmd_size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (adsp_pmem_fixup(module, (void **)&cmd->op_buf_addr,
|
||||
cmd->op_buf_size))
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case JPEG_CMD_DEC_OP_CONSUMED:
|
||||
{
|
||||
uint32_t div;
|
||||
jpeg_cmd_dec_op_consumed *cmd =
|
||||
(jpeg_cmd_dec_op_consumed *)cmd_data;
|
||||
|
||||
if (cmd_size != sizeof(jpeg_cmd_enc_op_consumed)) {
|
||||
printk(KERN_ERR "adsp: module %s: JPEG_CMD_DEC_OP_CONSUMED invalid size %d\n",
|
||||
module->name, cmd_size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
div = (dec_fmt == JPEG_CMD_DEC_OP_DATA_FORMAT_H2V2) ? 2 : 1;
|
||||
if (adsp_pmem_fixup(module, (void **)&cmd->luma_op_buf_addr,
|
||||
cmd->luma_op_buf_size) ||
|
||||
adsp_pmem_fixup(module, (void **)&cmd->chroma_op_buf_addr,
|
||||
cmd->luma_op_buf_size / div))
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (cmd_id > 7) {
|
||||
printk(KERN_ERR "adsp: module %s: invalid cmd_id %d\n",
|
||||
module->name, cmd_id);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int adsp_jpeg_verify_cmd(struct msm_adsp_module *module,
|
||||
unsigned int queue_id, void *cmd_data,
|
||||
size_t cmd_size)
|
||||
{
|
||||
switch(queue_id) {
|
||||
case QDSP_uPJpegCfgCmdQueue:
|
||||
return verify_jpeg_cfg_cmd(module, cmd_data, cmd_size);
|
||||
case QDSP_uPJpegActionCmdQueue:
|
||||
return verify_jpeg_action_cmd(module, cmd_data, cmd_size);
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
65
drivers/staging/dream/qdsp5/adsp_lpm_verify_cmd.c
Normal file
65
drivers/staging/dream/qdsp5/adsp_lpm_verify_cmd.c
Normal file
@ -0,0 +1,65 @@
|
||||
/* arch/arm/mach-msm/qdsp5/adsp_lpm_verify_cmd.c
|
||||
*
|
||||
* Verificion code for aDSP LPM packets from userspace.
|
||||
*
|
||||
* Copyright (c) 2008 QUALCOMM Incorporated
|
||||
* Copyright (C) 2008 Google, Inc.
|
||||
*
|
||||
* 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 <mach/qdsp5/qdsp5lpmcmdi.h>
|
||||
#include "adsp.h"
|
||||
|
||||
int adsp_lpm_verify_cmd(struct msm_adsp_module *module,
|
||||
unsigned int queue_id, void *cmd_data,
|
||||
size_t cmd_size)
|
||||
{
|
||||
uint32_t cmd_id, col_height, input_row_incr, output_row_incr,
|
||||
input_size, output_size;
|
||||
uint32_t size_mask = 0x0fff;
|
||||
lpm_cmd_start *cmd;
|
||||
|
||||
if (queue_id != QDSP_lpmCommandQueue) {
|
||||
printk(KERN_ERR "adsp: module %s: wrong queue id %d\n",
|
||||
module->name, queue_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
cmd = (lpm_cmd_start *)cmd_data;
|
||||
cmd_id = cmd->cmd_id;
|
||||
|
||||
if (cmd_id == LPM_CMD_START) {
|
||||
if (cmd_size != sizeof(lpm_cmd_start)) {
|
||||
printk(KERN_ERR "adsp: module %s: wrong size %d, expect %d\n",
|
||||
module->name, cmd_size, sizeof(lpm_cmd_start));
|
||||
return -1;
|
||||
}
|
||||
col_height = cmd->ip_data_cfg_part1 & size_mask;
|
||||
input_row_incr = cmd->ip_data_cfg_part2 & size_mask;
|
||||
output_row_incr = cmd->op_data_cfg_part1 & size_mask;
|
||||
input_size = col_height * input_row_incr;
|
||||
output_size = col_height * output_row_incr;
|
||||
if ((cmd->ip_data_cfg_part4 && adsp_pmem_fixup(module,
|
||||
(void **)(&cmd->ip_data_cfg_part4),
|
||||
input_size)) ||
|
||||
(cmd->op_data_cfg_part3 && adsp_pmem_fixup(module,
|
||||
(void **)(&cmd->op_data_cfg_part3),
|
||||
output_size)))
|
||||
return -1;
|
||||
} else if (cmd_id > 1) {
|
||||
printk(KERN_ERR "adsp: module %s: invalid cmd_id %d\n",
|
||||
module->name, cmd_id);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
54
drivers/staging/dream/qdsp5/adsp_vfe_patch_event.c
Normal file
54
drivers/staging/dream/qdsp5/adsp_vfe_patch_event.c
Normal file
@ -0,0 +1,54 @@
|
||||
/* arch/arm/mach-msm/qdsp5/adsp_vfe_patch_event.c
|
||||
*
|
||||
* Verification code for aDSP VFE packets from userspace.
|
||||
*
|
||||
* Copyright (c) 2008 QUALCOMM Incorporated
|
||||
* Copyright (C) 2008 Google, Inc.
|
||||
*
|
||||
* 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 <mach/qdsp5/qdsp5vfemsg.h>
|
||||
#include "adsp.h"
|
||||
|
||||
static int patch_op_event(struct msm_adsp_module *module,
|
||||
struct adsp_event *event)
|
||||
{
|
||||
vfe_msg_op1 *op = (vfe_msg_op1 *)event->data.msg16;
|
||||
if (adsp_pmem_paddr_fixup(module, (void **)&op->op1_buf_y_addr) ||
|
||||
adsp_pmem_paddr_fixup(module, (void **)&op->op1_buf_cbcr_addr))
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int patch_af_wb_event(struct msm_adsp_module *module,
|
||||
struct adsp_event *event)
|
||||
{
|
||||
vfe_msg_stats_wb_exp *af = (vfe_msg_stats_wb_exp *)event->data.msg16;
|
||||
return adsp_pmem_paddr_fixup(module, (void **)&af->wb_exp_stats_op_buf);
|
||||
}
|
||||
|
||||
int adsp_vfe_patch_event(struct msm_adsp_module *module,
|
||||
struct adsp_event *event)
|
||||
{
|
||||
switch(event->msg_id) {
|
||||
case VFE_MSG_OP1:
|
||||
case VFE_MSG_OP2:
|
||||
return patch_op_event(module, event);
|
||||
case VFE_MSG_STATS_AF:
|
||||
case VFE_MSG_STATS_WB_EXP:
|
||||
return patch_af_wb_event(module, event);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
239
drivers/staging/dream/qdsp5/adsp_vfe_verify_cmd.c
Normal file
239
drivers/staging/dream/qdsp5/adsp_vfe_verify_cmd.c
Normal file
@ -0,0 +1,239 @@
|
||||
/* arch/arm/mach-msm/qdsp5/adsp_vfe_verify_cmd.c
|
||||
*
|
||||
* Verification code for aDSP VFE packets from userspace.
|
||||
*
|
||||
* Copyright (c) 2008 QUALCOMM Incorporated
|
||||
* Copyright (C) 2008 Google, Inc.
|
||||
*
|
||||
* 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 <mach/qdsp5/qdsp5vfecmdi.h>
|
||||
#include "adsp.h"
|
||||
|
||||
static uint32_t size1_y, size2_y, size1_cbcr, size2_cbcr;
|
||||
static uint32_t af_size = 4228;
|
||||
static uint32_t awb_size = 8196;
|
||||
|
||||
static inline int verify_cmd_op_ack(struct msm_adsp_module *module,
|
||||
void *cmd_data, size_t cmd_size)
|
||||
{
|
||||
vfe_cmd_op1_ack *cmd = (vfe_cmd_op1_ack *)cmd_data;
|
||||
void **addr_y = (void **)&cmd->op1_buf_y_addr;
|
||||
void **addr_cbcr = (void **)(&cmd->op1_buf_cbcr_addr);
|
||||
|
||||
if (cmd_size != sizeof(vfe_cmd_op1_ack))
|
||||
return -1;
|
||||
if ((*addr_y && adsp_pmem_fixup(module, addr_y, size1_y)) ||
|
||||
(*addr_cbcr && adsp_pmem_fixup(module, addr_cbcr, size1_cbcr)))
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int verify_cmd_stats_autofocus_cfg(struct msm_adsp_module *module,
|
||||
void *cmd_data, size_t cmd_size)
|
||||
{
|
||||
int i;
|
||||
vfe_cmd_stats_autofocus_cfg *cmd =
|
||||
(vfe_cmd_stats_autofocus_cfg *)cmd_data;
|
||||
|
||||
if (cmd_size != sizeof(vfe_cmd_stats_autofocus_cfg))
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
void **addr = (void **)(&cmd->af_stats_op_buf[i]);
|
||||
if (*addr && adsp_pmem_fixup(module, addr, af_size))
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int verify_cmd_stats_wb_exp_cfg(struct msm_adsp_module *module,
|
||||
void *cmd_data, size_t cmd_size)
|
||||
{
|
||||
vfe_cmd_stats_wb_exp_cfg *cmd =
|
||||
(vfe_cmd_stats_wb_exp_cfg *)cmd_data;
|
||||
int i;
|
||||
|
||||
if (cmd_size != sizeof(vfe_cmd_stats_wb_exp_cfg))
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
void **addr = (void **)(&cmd->wb_exp_stats_op_buf[i]);
|
||||
if (*addr && adsp_pmem_fixup(module, addr, awb_size))
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int verify_cmd_stats_af_ack(struct msm_adsp_module *module,
|
||||
void *cmd_data, size_t cmd_size)
|
||||
{
|
||||
vfe_cmd_stats_af_ack *cmd = (vfe_cmd_stats_af_ack *)cmd_data;
|
||||
void **addr = (void **)&cmd->af_stats_op_buf;
|
||||
|
||||
if (cmd_size != sizeof(vfe_cmd_stats_af_ack))
|
||||
return -1;
|
||||
|
||||
if (*addr && adsp_pmem_fixup(module, addr, af_size))
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int verify_cmd_stats_wb_exp_ack(struct msm_adsp_module *module,
|
||||
void *cmd_data, size_t cmd_size)
|
||||
{
|
||||
vfe_cmd_stats_wb_exp_ack *cmd =
|
||||
(vfe_cmd_stats_wb_exp_ack *)cmd_data;
|
||||
void **addr = (void **)&cmd->wb_exp_stats_op_buf;
|
||||
|
||||
if (cmd_size != sizeof(vfe_cmd_stats_wb_exp_ack))
|
||||
return -1;
|
||||
|
||||
if (*addr && adsp_pmem_fixup(module, addr, awb_size))
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int verify_vfe_command(struct msm_adsp_module *module,
|
||||
void *cmd_data, size_t cmd_size)
|
||||
{
|
||||
uint32_t cmd_id = ((uint32_t *)cmd_data)[0];
|
||||
switch (cmd_id) {
|
||||
case VFE_CMD_OP1_ACK:
|
||||
return verify_cmd_op_ack(module, cmd_data, cmd_size);
|
||||
case VFE_CMD_OP2_ACK:
|
||||
return verify_cmd_op_ack(module, cmd_data, cmd_size);
|
||||
case VFE_CMD_STATS_AUTOFOCUS_CFG:
|
||||
return verify_cmd_stats_autofocus_cfg(module, cmd_data,
|
||||
cmd_size);
|
||||
case VFE_CMD_STATS_WB_EXP_CFG:
|
||||
return verify_cmd_stats_wb_exp_cfg(module, cmd_data, cmd_size);
|
||||
case VFE_CMD_STATS_AF_ACK:
|
||||
return verify_cmd_stats_af_ack(module, cmd_data, cmd_size);
|
||||
case VFE_CMD_STATS_WB_EXP_ACK:
|
||||
return verify_cmd_stats_wb_exp_ack(module, cmd_data, cmd_size);
|
||||
default:
|
||||
if (cmd_id > 29) {
|
||||
printk(KERN_ERR "adsp: module %s: invalid VFE command id %d\n", module->name, cmd_id);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int verify_vfe_command_scale(struct msm_adsp_module *module,
|
||||
void *cmd_data, size_t cmd_size)
|
||||
{
|
||||
uint32_t cmd_id = ((uint32_t *)cmd_data)[0];
|
||||
// FIXME: check the size
|
||||
if (cmd_id > 1) {
|
||||
printk(KERN_ERR "adsp: module %s: invalid VFE SCALE command id %d\n", module->name, cmd_id);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static uint32_t get_size(uint32_t hw)
|
||||
{
|
||||
uint32_t height, width;
|
||||
uint32_t height_mask = 0x3ffc;
|
||||
uint32_t width_mask = 0x3ffc000;
|
||||
|
||||
height = (hw & height_mask) >> 2;
|
||||
width = (hw & width_mask) >> 14 ;
|
||||
return height * width;
|
||||
}
|
||||
|
||||
static int verify_vfe_command_table(struct msm_adsp_module *module,
|
||||
void *cmd_data, size_t cmd_size)
|
||||
{
|
||||
uint32_t cmd_id = ((uint32_t *)cmd_data)[0];
|
||||
int i;
|
||||
|
||||
switch (cmd_id) {
|
||||
case VFE_CMD_AXI_IP_CFG:
|
||||
{
|
||||
vfe_cmd_axi_ip_cfg *cmd = (vfe_cmd_axi_ip_cfg *)cmd_data;
|
||||
uint32_t size;
|
||||
if (cmd_size != sizeof(vfe_cmd_axi_ip_cfg)) {
|
||||
printk(KERN_ERR "adsp: module %s: invalid VFE TABLE (VFE_CMD_AXI_IP_CFG) command size %d\n",
|
||||
module->name, cmd_size);
|
||||
return -1;
|
||||
}
|
||||
size = get_size(cmd->ip_cfg_part2);
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
void **addr = (void **)
|
||||
&cmd->ip_buf_addr[i];
|
||||
if (*addr && adsp_pmem_fixup(module, addr, size))
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
case VFE_CMD_AXI_OP_CFG:
|
||||
{
|
||||
vfe_cmd_axi_op_cfg *cmd = (vfe_cmd_axi_op_cfg *)cmd_data;
|
||||
void **addr1_y, **addr2_y, **addr1_cbcr, **addr2_cbcr;
|
||||
|
||||
if (cmd_size != sizeof(vfe_cmd_axi_op_cfg)) {
|
||||
printk(KERN_ERR "adsp: module %s: invalid VFE TABLE (VFE_CMD_AXI_OP_CFG) command size %d\n",
|
||||
module->name, cmd_size);
|
||||
return -1;
|
||||
}
|
||||
size1_y = get_size(cmd->op1_y_cfg_part2);
|
||||
size1_cbcr = get_size(cmd->op1_cbcr_cfg_part2);
|
||||
size2_y = get_size(cmd->op2_y_cfg_part2);
|
||||
size2_cbcr = get_size(cmd->op2_cbcr_cfg_part2);
|
||||
for (i = 0; i < 8; i++) {
|
||||
addr1_y = (void **)(&cmd->op1_buf1_addr[2*i]);
|
||||
addr1_cbcr = (void **)(&cmd->op1_buf1_addr[2*i+1]);
|
||||
addr2_y = (void **)(&cmd->op2_buf1_addr[2*i]);
|
||||
addr2_cbcr = (void **)(&cmd->op2_buf1_addr[2*i+1]);
|
||||
/*
|
||||
printk("module %s: [%d] %p %p %p %p\n",
|
||||
module->name, i,
|
||||
*addr1_y, *addr1_cbcr, *addr2_y, *addr2_cbcr);
|
||||
*/
|
||||
if ((*addr1_y && adsp_pmem_fixup(module, addr1_y, size1_y)) ||
|
||||
(*addr1_cbcr && adsp_pmem_fixup(module, addr1_cbcr, size1_cbcr)) ||
|
||||
(*addr2_y && adsp_pmem_fixup(module, addr2_y, size2_y)) ||
|
||||
(*addr2_cbcr && adsp_pmem_fixup(module, addr2_cbcr, size2_cbcr)))
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
default:
|
||||
if (cmd_id > 4) {
|
||||
printk(KERN_ERR "adsp: module %s: invalid VFE TABLE command id %d\n",
|
||||
module->name, cmd_id);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int adsp_vfe_verify_cmd(struct msm_adsp_module *module,
|
||||
unsigned int queue_id, void *cmd_data,
|
||||
size_t cmd_size)
|
||||
{
|
||||
switch (queue_id) {
|
||||
case QDSP_vfeCommandQueue:
|
||||
return verify_vfe_command(module, cmd_data, cmd_size);
|
||||
case QDSP_vfeCommandScaleQueue:
|
||||
return verify_vfe_command_scale(module, cmd_data, cmd_size);
|
||||
case QDSP_vfeCommandTableQueue:
|
||||
return verify_vfe_command_table(module, cmd_data, cmd_size);
|
||||
default:
|
||||
printk(KERN_ERR "adsp: module %s: unknown queue id %d\n",
|
||||
module->name, queue_id);
|
||||
return -1;
|
||||
}
|
||||
}
|
163
drivers/staging/dream/qdsp5/adsp_video_verify_cmd.c
Normal file
163
drivers/staging/dream/qdsp5/adsp_video_verify_cmd.c
Normal file
@ -0,0 +1,163 @@
|
||||
/* arch/arm/mach-msm/qdsp5/adsp_video_verify_cmd.c
|
||||
*
|
||||
* Verificion code for aDSP VDEC packets from userspace.
|
||||
*
|
||||
* Copyright (c) 2008 QUALCOMM Incorporated
|
||||
* Copyright (C) 2008 Google, Inc.
|
||||
*
|
||||
* 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/io.h>
|
||||
|
||||
#define ADSP_DEBUG_MSGS 0
|
||||
#if ADSP_DEBUG_MSGS
|
||||
#define DLOG(fmt,args...) \
|
||||
do { printk(KERN_INFO "[%s:%s:%d] "fmt, __FILE__, __func__, __LINE__, \
|
||||
##args); } \
|
||||
while (0)
|
||||
#else
|
||||
#define DLOG(x...) do {} while (0)
|
||||
#endif
|
||||
|
||||
|
||||
#include <mach/qdsp5/qdsp5vdeccmdi.h>
|
||||
#include "adsp.h"
|
||||
|
||||
static inline void *high_low_short_to_ptr(unsigned short high,
|
||||
unsigned short low)
|
||||
{
|
||||
return (void *)((((unsigned long)high) << 16) | ((unsigned long)low));
|
||||
}
|
||||
|
||||
static inline void ptr_to_high_low_short(void *ptr, unsigned short *high,
|
||||
unsigned short *low)
|
||||
{
|
||||
*high = (unsigned short)((((unsigned long)ptr) >> 16) & 0xffff);
|
||||
*low = (unsigned short)((unsigned long)ptr & 0xffff);
|
||||
}
|
||||
|
||||
static int pmem_fixup_high_low(unsigned short *high,
|
||||
unsigned short *low,
|
||||
unsigned short size_high,
|
||||
unsigned short size_low,
|
||||
struct msm_adsp_module *module,
|
||||
unsigned long *addr, unsigned long *size)
|
||||
{
|
||||
void *phys_addr;
|
||||
unsigned long phys_size;
|
||||
unsigned long kvaddr;
|
||||
|
||||
phys_addr = high_low_short_to_ptr(*high, *low);
|
||||
phys_size = (unsigned long)high_low_short_to_ptr(size_high, size_low);
|
||||
DLOG("virt %x %x\n", phys_addr, phys_size);
|
||||
if (adsp_pmem_fixup_kvaddr(module, &phys_addr, &kvaddr, phys_size)) {
|
||||
DLOG("ah%x al%x sh%x sl%x addr %x size %x\n",
|
||||
*high, *low, size_high, size_low, phys_addr, phys_size);
|
||||
return -1;
|
||||
}
|
||||
ptr_to_high_low_short(phys_addr, high, low);
|
||||
DLOG("phys %x %x\n", phys_addr, phys_size);
|
||||
if (addr)
|
||||
*addr = kvaddr;
|
||||
if (size)
|
||||
*size = phys_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int verify_vdec_pkt_cmd(struct msm_adsp_module *module,
|
||||
void *cmd_data, size_t cmd_size)
|
||||
{
|
||||
unsigned short cmd_id = ((unsigned short *)cmd_data)[0];
|
||||
viddec_cmd_subframe_pkt *pkt;
|
||||
unsigned long subframe_pkt_addr;
|
||||
unsigned long subframe_pkt_size;
|
||||
viddec_cmd_frame_header_packet *frame_header_pkt;
|
||||
int i, num_addr, skip;
|
||||
unsigned short *frame_buffer_high, *frame_buffer_low;
|
||||
unsigned long frame_buffer_size;
|
||||
unsigned short frame_buffer_size_high, frame_buffer_size_low;
|
||||
|
||||
DLOG("cmd_size %d cmd_id %d cmd_data %x\n", cmd_size, cmd_id, cmd_data);
|
||||
if (cmd_id != VIDDEC_CMD_SUBFRAME_PKT) {
|
||||
printk(KERN_INFO "adsp_video: unknown video packet %u\n",
|
||||
cmd_id);
|
||||
return 0;
|
||||
}
|
||||
if (cmd_size < sizeof(viddec_cmd_subframe_pkt))
|
||||
return -1;
|
||||
|
||||
pkt = (viddec_cmd_subframe_pkt *)cmd_data;
|
||||
|
||||
if (pmem_fixup_high_low(&(pkt->subframe_packet_high),
|
||||
&(pkt->subframe_packet_low),
|
||||
pkt->subframe_packet_size_high,
|
||||
pkt->subframe_packet_size_low,
|
||||
module,
|
||||
&subframe_pkt_addr,
|
||||
&subframe_pkt_size))
|
||||
return -1;
|
||||
|
||||
/* deref those ptrs and check if they are a frame header packet */
|
||||
frame_header_pkt = (viddec_cmd_frame_header_packet *)subframe_pkt_addr;
|
||||
|
||||
switch (frame_header_pkt->packet_id) {
|
||||
case 0xB201: /* h.264 */
|
||||
num_addr = skip = 8;
|
||||
break;
|
||||
case 0x4D01: /* mpeg-4 and h.263 */
|
||||
num_addr = 3;
|
||||
skip = 0;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
frame_buffer_high = &frame_header_pkt->frame_buffer_0_high;
|
||||
frame_buffer_low = &frame_header_pkt->frame_buffer_0_low;
|
||||
frame_buffer_size = (frame_header_pkt->x_dimension *
|
||||
frame_header_pkt->y_dimension * 3) / 2;
|
||||
ptr_to_high_low_short((void *)frame_buffer_size,
|
||||
&frame_buffer_size_high,
|
||||
&frame_buffer_size_low);
|
||||
for (i = 0; i < num_addr; i++) {
|
||||
if (pmem_fixup_high_low(frame_buffer_high, frame_buffer_low,
|
||||
frame_buffer_size_high,
|
||||
frame_buffer_size_low,
|
||||
module,
|
||||
NULL, NULL))
|
||||
return -1;
|
||||
frame_buffer_high += 2;
|
||||
frame_buffer_low += 2;
|
||||
}
|
||||
/* Patch the output buffer. */
|
||||
frame_buffer_high += 2*skip;
|
||||
frame_buffer_low += 2*skip;
|
||||
if (pmem_fixup_high_low(frame_buffer_high, frame_buffer_low,
|
||||
frame_buffer_size_high,
|
||||
frame_buffer_size_low, module, NULL, NULL))
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int adsp_video_verify_cmd(struct msm_adsp_module *module,
|
||||
unsigned int queue_id, void *cmd_data,
|
||||
size_t cmd_size)
|
||||
{
|
||||
switch (queue_id) {
|
||||
case QDSP_mpuVDecPktQueue:
|
||||
DLOG("\n");
|
||||
return verify_vdec_pkt_cmd(module, cmd_data, cmd_size);
|
||||
default:
|
||||
printk(KERN_INFO "unknown video queue %u\n", queue_id);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
235
drivers/staging/dream/qdsp5/adsp_videoenc_verify_cmd.c
Normal file
235
drivers/staging/dream/qdsp5/adsp_videoenc_verify_cmd.c
Normal file
@ -0,0 +1,235 @@
|
||||
/* arch/arm/mach-msm/qdsp5/adsp_video_verify_cmd.c
|
||||
*
|
||||
* Verificion code for aDSP VENC packets from userspace.
|
||||
*
|
||||
* Copyright (c) 2008 QUALCOMM Incorporated
|
||||
* Copyright (C) 2008 Google, Inc.
|
||||
*
|
||||
* 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/io.h>
|
||||
|
||||
#define ADSP_DEBUG_MSGS 0
|
||||
#if ADSP_DEBUG_MSGS
|
||||
#define DLOG(fmt,args...) \
|
||||
do { printk(KERN_INFO "[%s:%s:%d] "fmt, __FILE__, __func__, __LINE__, \
|
||||
##args); } \
|
||||
while (0)
|
||||
#else
|
||||
#define DLOG(x...) do {} while (0)
|
||||
#endif
|
||||
|
||||
#include <mach/qdsp5/qdsp5venccmdi.h>
|
||||
#include "adsp.h"
|
||||
|
||||
|
||||
static unsigned short x_dimension, y_dimension;
|
||||
|
||||
static inline void *high_low_short_to_ptr(unsigned short high,
|
||||
unsigned short low)
|
||||
{
|
||||
return (void *)((((unsigned long)high) << 16) | ((unsigned long)low));
|
||||
}
|
||||
|
||||
static inline void ptr_to_high_low_short(void *ptr, unsigned short *high,
|
||||
unsigned short *low)
|
||||
{
|
||||
*high = (unsigned short)((((unsigned long)ptr) >> 16) & 0xffff);
|
||||
*low = (unsigned short)((unsigned long)ptr & 0xffff);
|
||||
}
|
||||
|
||||
static int pmem_fixup_high_low(unsigned short *high,
|
||||
unsigned short *low,
|
||||
unsigned short size_high,
|
||||
unsigned short size_low,
|
||||
struct msm_adsp_module *module,
|
||||
unsigned long *addr, unsigned long *size)
|
||||
{
|
||||
void *phys_addr;
|
||||
unsigned long phys_size;
|
||||
unsigned long kvaddr;
|
||||
|
||||
phys_addr = high_low_short_to_ptr(*high, *low);
|
||||
phys_size = (unsigned long)high_low_short_to_ptr(size_high, size_low);
|
||||
DLOG("virt %x %x\n", phys_addr, phys_size);
|
||||
if (adsp_pmem_fixup_kvaddr(module, &phys_addr, &kvaddr, phys_size)) {
|
||||
DLOG("ah%x al%x sh%x sl%x addr %x size %x\n",
|
||||
*high, *low, size_high, size_low, phys_addr, phys_size);
|
||||
return -1;
|
||||
}
|
||||
ptr_to_high_low_short(phys_addr, high, low);
|
||||
DLOG("phys %x %x\n", phys_addr, phys_size);
|
||||
if (addr)
|
||||
*addr = kvaddr;
|
||||
if (size)
|
||||
*size = phys_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int verify_venc_cmd(struct msm_adsp_module *module,
|
||||
void *cmd_data, size_t cmd_size)
|
||||
{
|
||||
unsigned short cmd_id = ((unsigned short *)cmd_data)[0];
|
||||
unsigned long frame_buf_size, luma_buf_size, chroma_buf_size;
|
||||
unsigned short frame_buf_size_high, frame_buf_size_low;
|
||||
unsigned short luma_buf_size_high, luma_buf_size_low;
|
||||
unsigned short chroma_buf_size_high, chroma_buf_size_low;
|
||||
videnc_cmd_cfg *config_cmd;
|
||||
videnc_cmd_frame_start *frame_cmd;
|
||||
videnc_cmd_dis *dis_cmd;
|
||||
|
||||
DLOG("cmd_size %d cmd_id %d cmd_data %x\n", cmd_size, cmd_id, cmd_data);
|
||||
switch (cmd_id) {
|
||||
case VIDENC_CMD_ACTIVE:
|
||||
if (cmd_size < sizeof(videnc_cmd_active))
|
||||
return -1;
|
||||
break;
|
||||
case VIDENC_CMD_IDLE:
|
||||
if (cmd_size < sizeof(videnc_cmd_idle))
|
||||
return -1;
|
||||
x_dimension = y_dimension = 0;
|
||||
break;
|
||||
case VIDENC_CMD_STATUS_QUERY:
|
||||
if (cmd_size < sizeof(videnc_cmd_status_query))
|
||||
return -1;
|
||||
break;
|
||||
case VIDENC_CMD_RC_CFG:
|
||||
if (cmd_size < sizeof(videnc_cmd_rc_cfg))
|
||||
return -1;
|
||||
break;
|
||||
case VIDENC_CMD_INTRA_REFRESH:
|
||||
if (cmd_size < sizeof(videnc_cmd_intra_refresh))
|
||||
return -1;
|
||||
break;
|
||||
case VIDENC_CMD_DIGITAL_ZOOM:
|
||||
if (cmd_size < sizeof(videnc_cmd_digital_zoom))
|
||||
return -1;
|
||||
break;
|
||||
case VIDENC_CMD_DIS_CFG:
|
||||
if (cmd_size < sizeof(videnc_cmd_dis_cfg))
|
||||
return -1;
|
||||
break;
|
||||
case VIDENC_CMD_CFG:
|
||||
if (cmd_size < sizeof(videnc_cmd_cfg))
|
||||
return -1;
|
||||
config_cmd = (videnc_cmd_cfg *)cmd_data;
|
||||
x_dimension = ((config_cmd->venc_frame_dim) & 0xFF00)>>8;
|
||||
x_dimension = x_dimension*16;
|
||||
y_dimension = (config_cmd->venc_frame_dim) & 0xFF;
|
||||
y_dimension = y_dimension * 16;
|
||||
break;
|
||||
case VIDENC_CMD_FRAME_START:
|
||||
if (cmd_size < sizeof(videnc_cmd_frame_start))
|
||||
return -1;
|
||||
frame_cmd = (videnc_cmd_frame_start *)cmd_data;
|
||||
luma_buf_size = x_dimension * y_dimension;
|
||||
chroma_buf_size = luma_buf_size>>1;
|
||||
frame_buf_size = luma_buf_size + chroma_buf_size;
|
||||
ptr_to_high_low_short((void *)luma_buf_size,
|
||||
&luma_buf_size_high,
|
||||
&luma_buf_size_low);
|
||||
ptr_to_high_low_short((void *)chroma_buf_size,
|
||||
&chroma_buf_size_high,
|
||||
&chroma_buf_size_low);
|
||||
ptr_to_high_low_short((void *)frame_buf_size,
|
||||
&frame_buf_size_high,
|
||||
&frame_buf_size_low);
|
||||
/* Address of raw Y data. */
|
||||
if (pmem_fixup_high_low(&frame_cmd->input_luma_addr_high,
|
||||
&frame_cmd->input_luma_addr_low,
|
||||
luma_buf_size_high,
|
||||
luma_buf_size_low,
|
||||
module,
|
||||
NULL, NULL))
|
||||
return -1;
|
||||
/* Address of raw CbCr data */
|
||||
if (pmem_fixup_high_low(&frame_cmd->input_chroma_addr_high,
|
||||
&frame_cmd->input_chroma_addr_low,
|
||||
chroma_buf_size_high,
|
||||
chroma_buf_size_low,
|
||||
module,
|
||||
NULL, NULL))
|
||||
return -1;
|
||||
/* Reference VOP */
|
||||
if (pmem_fixup_high_low(&frame_cmd->ref_vop_buf_ptr_high,
|
||||
&frame_cmd->ref_vop_buf_ptr_low,
|
||||
frame_buf_size_high,
|
||||
frame_buf_size_low,
|
||||
module,
|
||||
NULL, NULL))
|
||||
return -1;
|
||||
/* Encoded Packet Address */
|
||||
if (pmem_fixup_high_low(&frame_cmd->enc_pkt_buf_ptr_high,
|
||||
&frame_cmd->enc_pkt_buf_ptr_low,
|
||||
frame_cmd->enc_pkt_buf_size_high,
|
||||
frame_cmd->enc_pkt_buf_size_low,
|
||||
module,
|
||||
NULL, NULL))
|
||||
return -1;
|
||||
/* Unfiltered VOP Buffer Address */
|
||||
if (pmem_fixup_high_low(
|
||||
&frame_cmd->unfilt_recon_vop_buf_ptr_high,
|
||||
&frame_cmd->unfilt_recon_vop_buf_ptr_low,
|
||||
frame_buf_size_high,
|
||||
frame_buf_size_low,
|
||||
module,
|
||||
NULL, NULL))
|
||||
return -1;
|
||||
/* Filtered VOP Buffer Address */
|
||||
if (pmem_fixup_high_low(&frame_cmd->filt_recon_vop_buf_ptr_high,
|
||||
&frame_cmd->filt_recon_vop_buf_ptr_low,
|
||||
frame_buf_size_high,
|
||||
frame_buf_size_low,
|
||||
module,
|
||||
NULL, NULL))
|
||||
return -1;
|
||||
break;
|
||||
case VIDENC_CMD_DIS:
|
||||
if (cmd_size < sizeof(videnc_cmd_dis))
|
||||
return -1;
|
||||
dis_cmd = (videnc_cmd_dis *)cmd_data;
|
||||
luma_buf_size = x_dimension * y_dimension;
|
||||
ptr_to_high_low_short((void *)luma_buf_size,
|
||||
&luma_buf_size_high,
|
||||
&luma_buf_size_low);
|
||||
/* Prev VFE Luma Output Address */
|
||||
if (pmem_fixup_high_low(&dis_cmd->vfe_out_prev_luma_addr_high,
|
||||
&dis_cmd->vfe_out_prev_luma_addr_low,
|
||||
luma_buf_size_high,
|
||||
luma_buf_size_low,
|
||||
module,
|
||||
NULL, NULL))
|
||||
return -1;
|
||||
break;
|
||||
default:
|
||||
printk(KERN_INFO "adsp_video:unknown encoder video command %u\n",
|
||||
cmd_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int adsp_videoenc_verify_cmd(struct msm_adsp_module *module,
|
||||
unsigned int queue_id, void *cmd_data,
|
||||
size_t cmd_size)
|
||||
{
|
||||
switch (queue_id) {
|
||||
case QDSP_mpuVEncCmdQueue:
|
||||
DLOG("\n");
|
||||
return verify_venc_cmd(module, cmd_data, cmd_size);
|
||||
default:
|
||||
printk(KERN_INFO "unknown video queue %u\n", queue_id);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
1052
drivers/staging/dream/qdsp5/audio_aac.c
Normal file
1052
drivers/staging/dream/qdsp5/audio_aac.c
Normal file
File diff suppressed because it is too large
Load Diff
873
drivers/staging/dream/qdsp5/audio_amrnb.c
Normal file
873
drivers/staging/dream/qdsp5/audio_amrnb.c
Normal file
@ -0,0 +1,873 @@
|
||||
/* linux/arch/arm/mach-msm/qdsp5/audio_amrnb.c
|
||||
*
|
||||
* amrnb audio decoder device
|
||||
*
|
||||
* Copyright (c) 2008 QUALCOMM USA, INC.
|
||||
*
|
||||
* Based on the mp3 native driver in arch/arm/mach-msm/qdsp5/audio_mp3.c
|
||||
*
|
||||
* Copyright (C) 2008 Google, Inc.
|
||||
* Copyright (C) 2008 HTC Corporation
|
||||
*
|
||||
* All source code in this file is licensed under the following license except
|
||||
* where indicated.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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, you can find it at http://www.fsf.org
|
||||
*/
|
||||
|
||||
#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/delay.h>
|
||||
|
||||
#include <asm/atomic.h>
|
||||
#include <asm/ioctls.h>
|
||||
#include <mach/msm_adsp.h>
|
||||
#include <linux/msm_audio.h>
|
||||
#include "audmgr.h"
|
||||
|
||||
#include <mach/qdsp5/qdsp5audppcmdi.h>
|
||||
#include <mach/qdsp5/qdsp5audppmsg.h>
|
||||
#include <mach/qdsp5/qdsp5audplaycmdi.h>
|
||||
#include <mach/qdsp5/qdsp5audplaymsg.h>
|
||||
|
||||
/* for queue ids - should be relative to module number*/
|
||||
#include "adsp.h"
|
||||
|
||||
#define DEBUG
|
||||
#ifdef DEBUG
|
||||
#define dprintk(format, arg...) \
|
||||
printk(KERN_DEBUG format, ## arg)
|
||||
#else
|
||||
#define dprintk(format, arg...) do {} while (0)
|
||||
#endif
|
||||
|
||||
#define BUFSZ 1024 /* Hold minimum 700ms voice data */
|
||||
#define DMASZ (BUFSZ * 2)
|
||||
|
||||
#define AUDPLAY_INVALID_READ_PTR_OFFSET 0xFFFF
|
||||
#define AUDDEC_DEC_AMRNB 10
|
||||
|
||||
#define PCM_BUFSZ_MIN 1600 /* 100ms worth of data */
|
||||
#define AMRNB_DECODED_FRSZ 320 /* AMR-NB 20ms 8KHz mono PCM size */
|
||||
#define PCM_BUF_MAX_COUNT 5 /* DSP only accepts 5 buffers at most
|
||||
but support 2 buffers currently */
|
||||
#define ROUTING_MODE_FTRT 1
|
||||
#define ROUTING_MODE_RT 2
|
||||
/* Decoder status received from AUDPPTASK */
|
||||
#define AUDPP_DEC_STATUS_SLEEP 0
|
||||
#define AUDPP_DEC_STATUS_INIT 1
|
||||
#define AUDPP_DEC_STATUS_CFG 2
|
||||
#define AUDPP_DEC_STATUS_PLAY 3
|
||||
|
||||
struct buffer {
|
||||
void *data;
|
||||
unsigned size;
|
||||
unsigned used; /* Input usage actual DSP produced PCM size */
|
||||
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 write_wait;
|
||||
|
||||
/* Host PCM section */
|
||||
struct buffer in[PCM_BUF_MAX_COUNT];
|
||||
struct mutex read_lock;
|
||||
wait_queue_head_t read_wait; /* Wait queue for read */
|
||||
char *read_data; /* pointer to reader buffer */
|
||||
dma_addr_t read_phys; /* physical address of reader buffer */
|
||||
uint8_t read_next; /* index to input buffers to be read next */
|
||||
uint8_t fill_next; /* index to buffer that DSP should be filling */
|
||||
uint8_t pcm_buf_count; /* number of pcm buffer allocated */
|
||||
/* ---- End of Host PCM section */
|
||||
|
||||
struct msm_adsp_module *audplay;
|
||||
|
||||
struct audmgr audmgr;
|
||||
|
||||
/* data allocated for various buffers */
|
||||
char *data;
|
||||
dma_addr_t phys;
|
||||
|
||||
uint8_t opened:1;
|
||||
uint8_t enabled:1;
|
||||
uint8_t running:1;
|
||||
uint8_t stopped:1; /* set when stopped, cleared on flush */
|
||||
uint8_t pcm_feedback:1;
|
||||
uint8_t buf_refresh:1;
|
||||
|
||||
unsigned volume;
|
||||
|
||||
uint16_t dec_id;
|
||||
uint32_t read_ptr_offset;
|
||||
};
|
||||
|
||||
struct audpp_cmd_cfg_adec_params_amrnb {
|
||||
audpp_cmd_cfg_adec_params_common common;
|
||||
unsigned short stereo_cfg;
|
||||
} __attribute__((packed)) ;
|
||||
|
||||
static int auddec_dsp_config(struct audio *audio, int enable);
|
||||
static void audpp_cmd_cfg_adec_params(struct audio *audio);
|
||||
static void audpp_cmd_cfg_routing_mode(struct audio *audio);
|
||||
static void audamrnb_send_data(struct audio *audio, unsigned needed);
|
||||
static void audamrnb_config_hostpcm(struct audio *audio);
|
||||
static void audamrnb_buffer_refresh(struct audio *audio);
|
||||
static void audamrnb_dsp_event(void *private, unsigned id, uint16_t *msg);
|
||||
|
||||
/* must be called with audio->lock held */
|
||||
static int audamrnb_enable(struct audio *audio)
|
||||
{
|
||||
struct audmgr_config cfg;
|
||||
int rc;
|
||||
|
||||
dprintk("audamrnb_enable()\n");
|
||||
|
||||
if (audio->enabled)
|
||||
return 0;
|
||||
|
||||
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_PLAYBACK;
|
||||
cfg.codec = RPC_AUD_DEF_CODEC_AMR_NB;
|
||||
cfg.snd_method = RPC_SND_METHOD_MIDI;
|
||||
|
||||
rc = audmgr_enable(&audio->audmgr, &cfg);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
if (msm_adsp_enable(audio->audplay)) {
|
||||
pr_err("audio: msm_adsp_enable(audplay) failed\n");
|
||||
audmgr_disable(&audio->audmgr);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (audpp_enable(audio->dec_id, audamrnb_dsp_event, audio)) {
|
||||
pr_err("audio: audpp_enable() failed\n");
|
||||
msm_adsp_disable(audio->audplay);
|
||||
audmgr_disable(&audio->audmgr);
|
||||
return -ENODEV;
|
||||
}
|
||||
audio->enabled = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* must be called with audio->lock held */
|
||||
static int audamrnb_disable(struct audio *audio)
|
||||
{
|
||||
dprintk("audamrnb_disable()\n");
|
||||
if (audio->enabled) {
|
||||
audio->enabled = 0;
|
||||
auddec_dsp_config(audio, 0);
|
||||
wake_up(&audio->write_wait);
|
||||
wake_up(&audio->read_wait);
|
||||
msm_adsp_disable(audio->audplay);
|
||||
audpp_disable(audio->dec_id, audio);
|
||||
audmgr_disable(&audio->audmgr);
|
||||
audio->out_needed = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ------------------- dsp --------------------- */
|
||||
static void audamrnb_update_pcm_buf_entry(struct audio *audio,
|
||||
uint32_t *payload)
|
||||
{
|
||||
uint8_t index;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&audio->dsp_lock, flags);
|
||||
for (index = 0; index < payload[1]; index++) {
|
||||
if (audio->in[audio->fill_next].addr ==
|
||||
payload[2 + index * 2]) {
|
||||
dprintk("audamrnb_update_pcm_buf_entry: in[%d] ready\n",
|
||||
audio->fill_next);
|
||||
audio->in[audio->fill_next].used =
|
||||
payload[3 + index * 2];
|
||||
if ((++audio->fill_next) == audio->pcm_buf_count)
|
||||
audio->fill_next = 0;
|
||||
|
||||
} else {
|
||||
pr_err
|
||||
("audamrnb_update_pcm_buf_entry: expected=%x ret=%x\n"
|
||||
, audio->in[audio->fill_next].addr,
|
||||
payload[1 + index * 2]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (audio->in[audio->fill_next].used == 0) {
|
||||
audamrnb_buffer_refresh(audio);
|
||||
} else {
|
||||
dprintk("audamrnb_update_pcm_buf_entry: read cannot keep up\n");
|
||||
audio->buf_refresh = 1;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&audio->dsp_lock, flags);
|
||||
wake_up(&audio->read_wait);
|
||||
}
|
||||
|
||||
static void audplay_dsp_event(void *data, unsigned id, size_t len,
|
||||
void (*getevent) (void *ptr, size_t len))
|
||||
{
|
||||
struct audio *audio = data;
|
||||
uint32_t msg[28];
|
||||
getevent(msg, sizeof(msg));
|
||||
|
||||
dprintk("audplay_dsp_event: msg_id=%x\n", id);
|
||||
|
||||
switch (id) {
|
||||
case AUDPLAY_MSG_DEC_NEEDS_DATA:
|
||||
audamrnb_send_data(audio, 1);
|
||||
break;
|
||||
|
||||
case AUDPLAY_MSG_BUFFER_UPDATE:
|
||||
audamrnb_update_pcm_buf_entry(audio, msg);
|
||||
break;
|
||||
|
||||
default:
|
||||
pr_err("unexpected message from decoder \n");
|
||||
}
|
||||
}
|
||||
|
||||
static void audamrnb_dsp_event(void *private, unsigned id, uint16_t *msg)
|
||||
{
|
||||
struct audio *audio = private;
|
||||
|
||||
switch (id) {
|
||||
case AUDPP_MSG_STATUS_MSG:{
|
||||
unsigned status = msg[1];
|
||||
|
||||
switch (status) {
|
||||
case AUDPP_DEC_STATUS_SLEEP:
|
||||
dprintk("decoder status: sleep \n");
|
||||
break;
|
||||
|
||||
case AUDPP_DEC_STATUS_INIT:
|
||||
dprintk("decoder status: init \n");
|
||||
audpp_cmd_cfg_routing_mode(audio);
|
||||
break;
|
||||
|
||||
case AUDPP_DEC_STATUS_CFG:
|
||||
dprintk("decoder status: cfg \n");
|
||||
break;
|
||||
case AUDPP_DEC_STATUS_PLAY:
|
||||
dprintk("decoder status: play \n");
|
||||
if (audio->pcm_feedback) {
|
||||
audamrnb_config_hostpcm(audio);
|
||||
audamrnb_buffer_refresh(audio);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pr_err("unknown decoder status \n");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AUDPP_MSG_CFG_MSG:
|
||||
if (msg[0] == AUDPP_MSG_ENA_ENA) {
|
||||
dprintk("audamrnb_dsp_event: CFG_MSG ENABLE\n");
|
||||
auddec_dsp_config(audio, 1);
|
||||
audio->out_needed = 0;
|
||||
audio->running = 1;
|
||||
audpp_set_volume_and_pan(audio->dec_id, audio->volume,
|
||||
0);
|
||||
audpp_avsync(audio->dec_id, 22050);
|
||||
} else if (msg[0] == AUDPP_MSG_ENA_DIS) {
|
||||
dprintk("audamrnb_dsp_event: CFG_MSG DISABLE\n");
|
||||
audpp_avsync(audio->dec_id, 0);
|
||||
audio->running = 0;
|
||||
} else {
|
||||
pr_err("audamrnb_dsp_event: CFG_MSG %d?\n", msg[0]);
|
||||
}
|
||||
break;
|
||||
case AUDPP_MSG_ROUTING_ACK:
|
||||
dprintk("audamrnb_dsp_event: ROUTING_ACK mode=%d\n", msg[1]);
|
||||
audpp_cmd_cfg_adec_params(audio);
|
||||
break;
|
||||
|
||||
default:
|
||||
pr_err("audamrnb_dsp_event: UNKNOWN (%d)\n", id);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct msm_adsp_ops audplay_adsp_ops_amrnb = {
|
||||
.event = audplay_dsp_event,
|
||||
};
|
||||
|
||||
#define audplay_send_queue0(audio, cmd, len) \
|
||||
msm_adsp_write(audio->audplay, QDSP_uPAudPlay0BitStreamCtrlQueue, \
|
||||
cmd, len)
|
||||
|
||||
static int auddec_dsp_config(struct audio *audio, int enable)
|
||||
{
|
||||
audpp_cmd_cfg_dec_type cmd;
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE;
|
||||
if (enable)
|
||||
cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
|
||||
AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_AMRNB;
|
||||
else
|
||||
cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | AUDPP_CMD_DIS_DEC_V;
|
||||
|
||||
return audpp_send_queue1(&cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
static void audpp_cmd_cfg_adec_params(struct audio *audio)
|
||||
{
|
||||
struct audpp_cmd_cfg_adec_params_amrnb cmd;
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS;
|
||||
cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_V13K_LEN;
|
||||
cmd.common.dec_id = audio->dec_id;
|
||||
cmd.common.input_sampling_frequency = 8000;
|
||||
cmd.stereo_cfg = AUDPP_CMD_PCM_INTF_MONO_V;
|
||||
|
||||
audpp_send_queue2(&cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
static void audpp_cmd_cfg_routing_mode(struct audio *audio)
|
||||
{
|
||||
struct audpp_cmd_routing_mode cmd;
|
||||
dprintk("audpp_cmd_cfg_routing_mode()\n");
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.cmd_id = AUDPP_CMD_ROUTING_MODE;
|
||||
cmd.object_number = audio->dec_id;
|
||||
if (audio->pcm_feedback)
|
||||
cmd.routing_mode = ROUTING_MODE_FTRT;
|
||||
else
|
||||
cmd.routing_mode = ROUTING_MODE_RT;
|
||||
|
||||
audpp_send_queue1(&cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
static int audplay_dsp_send_data_avail(struct audio *audio,
|
||||
unsigned idx, unsigned len)
|
||||
{
|
||||
audplay_cmd_bitstream_data_avail cmd;
|
||||
|
||||
cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL;
|
||||
cmd.decoder_id = audio->dec_id;
|
||||
cmd.buf_ptr = audio->out[idx].addr;
|
||||
cmd.buf_size = len / 2;
|
||||
cmd.partition_number = 0;
|
||||
return audplay_send_queue0(audio, &cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
static void audamrnb_buffer_refresh(struct audio *audio)
|
||||
{
|
||||
struct audplay_cmd_buffer_refresh refresh_cmd;
|
||||
|
||||
refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH;
|
||||
refresh_cmd.num_buffers = 1;
|
||||
refresh_cmd.buf0_address = audio->in[audio->fill_next].addr;
|
||||
refresh_cmd.buf0_length = audio->in[audio->fill_next].size -
|
||||
(audio->in[audio->fill_next].size % AMRNB_DECODED_FRSZ);
|
||||
refresh_cmd.buf_read_count = 0;
|
||||
dprintk("audplay_buffer_fresh: buf0_addr=%x buf0_len=%d\n",
|
||||
refresh_cmd.buf0_address, refresh_cmd.buf0_length);
|
||||
(void)audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd));
|
||||
}
|
||||
|
||||
static void audamrnb_config_hostpcm(struct audio *audio)
|
||||
{
|
||||
struct audplay_cmd_hpcm_buf_cfg cfg_cmd;
|
||||
|
||||
dprintk("audamrnb_config_hostpcm()\n");
|
||||
cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG;
|
||||
cfg_cmd.max_buffers = audio->pcm_buf_count;
|
||||
cfg_cmd.byte_swap = 0;
|
||||
cfg_cmd.hostpcm_config = (0x8000) | (0x4000);
|
||||
cfg_cmd.feedback_frequency = 1;
|
||||
cfg_cmd.partition_number = 0;
|
||||
(void)audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd));
|
||||
|
||||
}
|
||||
|
||||
static void audamrnb_send_data(struct audio *audio, unsigned needed)
|
||||
{
|
||||
struct buffer *frame;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&audio->dsp_lock, flags);
|
||||
if (!audio->running)
|
||||
goto done;
|
||||
|
||||
if (needed) {
|
||||
/* We were called from the callback because the DSP
|
||||
* requested more data. Note that the DSP does want
|
||||
* more data, and if a buffer was in-flight, mark it
|
||||
* as available (since the DSP must now be done with
|
||||
* it).
|
||||
*/
|
||||
audio->out_needed = 1;
|
||||
frame = audio->out + audio->out_tail;
|
||||
if (frame->used == 0xffffffff) {
|
||||
frame->used = 0;
|
||||
audio->out_tail ^= 1;
|
||||
wake_up(&audio->write_wait);
|
||||
}
|
||||
}
|
||||
|
||||
if (audio->out_needed) {
|
||||
/* If the DSP currently wants data and we have a
|
||||
* buffer available, we will send it and reset
|
||||
* the needed flag. We'll mark the buffer as in-flight
|
||||
* so that it won't be recycled until the next buffer
|
||||
* is requested
|
||||
*/
|
||||
|
||||
frame = audio->out + audio->out_tail;
|
||||
if (frame->used) {
|
||||
BUG_ON(frame->used == 0xffffffff);
|
||||
/* printk("frame %d busy\n", audio->out_tail); */
|
||||
audplay_dsp_send_data_avail(audio, audio->out_tail,
|
||||
frame->used);
|
||||
frame->used = 0xffffffff;
|
||||
audio->out_needed = 0;
|
||||
}
|
||||
}
|
||||
done:
|
||||
spin_unlock_irqrestore(&audio->dsp_lock, flags);
|
||||
}
|
||||
|
||||
/* ------------------- device --------------------- */
|
||||
|
||||
static void audamrnb_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;
|
||||
atomic_set(&audio->out_bytes, 0);
|
||||
}
|
||||
|
||||
static void audamrnb_flush_pcm_buf(struct audio *audio)
|
||||
{
|
||||
uint8_t index;
|
||||
|
||||
for (index = 0; index < PCM_BUF_MAX_COUNT; index++)
|
||||
audio->in[index].used = 0;
|
||||
|
||||
audio->read_next = 0;
|
||||
audio->fill_next = 0;
|
||||
}
|
||||
|
||||
static long audamrnb_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct audio *audio = file->private_data;
|
||||
int rc = 0;
|
||||
|
||||
dprintk("audamrnb_ioctl() cmd = %d\n", cmd);
|
||||
|
||||
if (cmd == AUDIO_GET_STATS) {
|
||||
struct msm_audio_stats stats;
|
||||
stats.byte_count = audpp_avsync_byte_count(audio->dec_id);
|
||||
stats.sample_count = audpp_avsync_sample_count(audio->dec_id);
|
||||
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(audio->dec_id, arg, 0);
|
||||
spin_unlock_irqrestore(&audio->dsp_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
mutex_lock(&audio->lock);
|
||||
switch (cmd) {
|
||||
case AUDIO_START:
|
||||
rc = audamrnb_enable(audio);
|
||||
break;
|
||||
case AUDIO_STOP:
|
||||
rc = audamrnb_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->write_wait);
|
||||
mutex_lock(&audio->write_lock);
|
||||
audamrnb_flush(audio);
|
||||
mutex_unlock(&audio->write_lock);
|
||||
wake_up(&audio->read_wait);
|
||||
mutex_lock(&audio->read_lock);
|
||||
audamrnb_flush_pcm_buf(audio);
|
||||
mutex_unlock(&audio->read_lock);
|
||||
break;
|
||||
}
|
||||
|
||||
case AUDIO_SET_CONFIG:{
|
||||
dprintk("AUDIO_SET_CONFIG not applicable \n");
|
||||
break;
|
||||
}
|
||||
case AUDIO_GET_CONFIG:{
|
||||
struct msm_audio_config config;
|
||||
config.buffer_size = BUFSZ;
|
||||
config.buffer_count = 2;
|
||||
config.sample_rate = 8000;
|
||||
config.channel_count = 1;
|
||||
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;
|
||||
}
|
||||
case AUDIO_GET_PCM_CONFIG:{
|
||||
struct msm_audio_pcm_config config;
|
||||
config.pcm_feedback = 0;
|
||||
config.buffer_count = PCM_BUF_MAX_COUNT;
|
||||
config.buffer_size = PCM_BUFSZ_MIN;
|
||||
if (copy_to_user((void *)arg, &config,
|
||||
sizeof(config)))
|
||||
rc = -EFAULT;
|
||||
else
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
case AUDIO_SET_PCM_CONFIG:{
|
||||
struct msm_audio_pcm_config config;
|
||||
if (copy_from_user
|
||||
(&config, (void *)arg, sizeof(config))) {
|
||||
rc = -EFAULT;
|
||||
break;
|
||||
}
|
||||
if ((config.buffer_count > PCM_BUF_MAX_COUNT) ||
|
||||
(config.buffer_count == 1))
|
||||
config.buffer_count = PCM_BUF_MAX_COUNT;
|
||||
|
||||
if (config.buffer_size < PCM_BUFSZ_MIN)
|
||||
config.buffer_size = PCM_BUFSZ_MIN;
|
||||
|
||||
/* Check if pcm feedback is required */
|
||||
if ((config.pcm_feedback) && (!audio->read_data)) {
|
||||
dprintk("audamrnb_ioctl: allocate PCM buf %d\n",
|
||||
config.buffer_count *
|
||||
config.buffer_size);
|
||||
audio->read_data =
|
||||
dma_alloc_coherent(NULL,
|
||||
config.buffer_size *
|
||||
config.buffer_count,
|
||||
&audio->read_phys,
|
||||
GFP_KERNEL);
|
||||
if (!audio->read_data) {
|
||||
pr_err("audamrnb_ioctl: no mem for pcm buf\n");
|
||||
rc = -1;
|
||||
} else {
|
||||
uint8_t index;
|
||||
uint32_t offset = 0;
|
||||
audio->pcm_feedback = 1;
|
||||
audio->buf_refresh = 0;
|
||||
audio->pcm_buf_count =
|
||||
config.buffer_count;
|
||||
audio->read_next = 0;
|
||||
audio->fill_next = 0;
|
||||
|
||||
for (index = 0;
|
||||
index < config.buffer_count; index++) {
|
||||
audio->in[index].data =
|
||||
audio->read_data + offset;
|
||||
audio->in[index].addr =
|
||||
audio->read_phys + offset;
|
||||
audio->in[index].size =
|
||||
config.buffer_size;
|
||||
audio->in[index].used = 0;
|
||||
offset += config.buffer_size;
|
||||
}
|
||||
rc = 0;
|
||||
}
|
||||
} else {
|
||||
rc = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
rc = -EINVAL;
|
||||
}
|
||||
mutex_unlock(&audio->lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static ssize_t audamrnb_read(struct file *file, char __user *buf, size_t count,
|
||||
loff_t *pos)
|
||||
{
|
||||
struct audio *audio = file->private_data;
|
||||
const char __user *start = buf;
|
||||
int rc = 0;
|
||||
|
||||
if (!audio->pcm_feedback)
|
||||
return 0; /* PCM feedback is not enabled. Nothing to read */
|
||||
|
||||
mutex_lock(&audio->read_lock);
|
||||
dprintk("audamrnb_read() %d \n", count);
|
||||
while (count > 0) {
|
||||
rc = wait_event_interruptible(audio->read_wait,
|
||||
(audio->in[audio->read_next].
|
||||
used > 0) || (audio->stopped));
|
||||
|
||||
if (rc < 0)
|
||||
break;
|
||||
|
||||
if (audio->stopped) {
|
||||
rc = -EBUSY;
|
||||
break;
|
||||
}
|
||||
|
||||
if (count < audio->in[audio->read_next].used) {
|
||||
/* Read must happen in frame boundary. Since driver does
|
||||
* not know frame size, read count must be greater or
|
||||
* equal to size of PCM samples
|
||||
*/
|
||||
dprintk("audamrnb_read:read stop - partial frame\n");
|
||||
break;
|
||||
} else {
|
||||
dprintk("audamrnb_read: read from in[%d]\n",
|
||||
audio->read_next);
|
||||
if (copy_to_user
|
||||
(buf, audio->in[audio->read_next].data,
|
||||
audio->in[audio->read_next].used)) {
|
||||
pr_err("audamrnb_read: invalid addr %x \n",
|
||||
(unsigned int)buf);
|
||||
rc = -EFAULT;
|
||||
break;
|
||||
}
|
||||
count -= audio->in[audio->read_next].used;
|
||||
buf += audio->in[audio->read_next].used;
|
||||
audio->in[audio->read_next].used = 0;
|
||||
if ((++audio->read_next) == audio->pcm_buf_count)
|
||||
audio->read_next = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (audio->buf_refresh) {
|
||||
audio->buf_refresh = 0;
|
||||
dprintk("audamrnb_read: kick start pcm feedback again\n");
|
||||
audamrnb_buffer_refresh(audio);
|
||||
}
|
||||
|
||||
mutex_unlock(&audio->read_lock);
|
||||
|
||||
if (buf > start)
|
||||
rc = buf - start;
|
||||
|
||||
dprintk("audamrnb_read: read %d bytes\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static ssize_t audamrnb_write(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *pos)
|
||||
{
|
||||
struct audio *audio = file->private_data;
|
||||
const char __user *start = buf;
|
||||
struct buffer *frame;
|
||||
size_t xfer;
|
||||
int rc = 0;
|
||||
|
||||
if (count & 1)
|
||||
return -EINVAL;
|
||||
dprintk("audamrnb_write() \n");
|
||||
mutex_lock(&audio->write_lock);
|
||||
while (count > 0) {
|
||||
frame = audio->out + audio->out_head;
|
||||
rc = wait_event_interruptible(audio->write_wait,
|
||||
(frame->used == 0)
|
||||
|| (audio->stopped));
|
||||
dprintk("audamrnb_write() buffer available\n");
|
||||
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;
|
||||
|
||||
audamrnb_send_data(audio, 0);
|
||||
|
||||
}
|
||||
mutex_unlock(&audio->write_lock);
|
||||
if (buf > start)
|
||||
return buf - start;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int audamrnb_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct audio *audio = file->private_data;
|
||||
|
||||
dprintk("audamrnb_release()\n");
|
||||
|
||||
mutex_lock(&audio->lock);
|
||||
audamrnb_disable(audio);
|
||||
audamrnb_flush(audio);
|
||||
audamrnb_flush_pcm_buf(audio);
|
||||
msm_adsp_put(audio->audplay);
|
||||
audio->audplay = NULL;
|
||||
audio->opened = 0;
|
||||
dma_free_coherent(NULL, DMASZ, audio->data, audio->phys);
|
||||
audio->data = NULL;
|
||||
if (audio->read_data != NULL) {
|
||||
dma_free_coherent(NULL,
|
||||
audio->in[0].size * audio->pcm_buf_count,
|
||||
audio->read_data, audio->read_phys);
|
||||
audio->read_data = NULL;
|
||||
}
|
||||
audio->pcm_feedback = 0;
|
||||
mutex_unlock(&audio->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct audio the_amrnb_audio;
|
||||
|
||||
static int audamrnb_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct audio *audio = &the_amrnb_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;
|
||||
|
||||
rc = msm_adsp_get("AUDPLAY0TASK", &audio->audplay,
|
||||
&audplay_adsp_ops_amrnb, audio);
|
||||
if (rc) {
|
||||
pr_err("audio: failed to get audplay0 dsp module\n");
|
||||
audmgr_disable(&audio->audmgr);
|
||||
dma_free_coherent(NULL, DMASZ, audio->data, audio->phys);
|
||||
audio->data = NULL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
audio->dec_id = 0;
|
||||
|
||||
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; /* Q13 1.0 */
|
||||
|
||||
audamrnb_flush(audio);
|
||||
|
||||
file->private_data = audio;
|
||||
audio->opened = 1;
|
||||
rc = 0;
|
||||
done:
|
||||
mutex_unlock(&audio->lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static struct file_operations audio_amrnb_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = audamrnb_open,
|
||||
.release = audamrnb_release,
|
||||
.read = audamrnb_read,
|
||||
.write = audamrnb_write,
|
||||
.unlocked_ioctl = audamrnb_ioctl,
|
||||
};
|
||||
|
||||
struct miscdevice audio_amrnb_misc = {
|
||||
.minor = MISC_DYNAMIC_MINOR,
|
||||
.name = "msm_amrnb",
|
||||
.fops = &audio_amrnb_fops,
|
||||
};
|
||||
|
||||
static int __init audamrnb_init(void)
|
||||
{
|
||||
mutex_init(&the_amrnb_audio.lock);
|
||||
mutex_init(&the_amrnb_audio.write_lock);
|
||||
mutex_init(&the_amrnb_audio.read_lock);
|
||||
spin_lock_init(&the_amrnb_audio.dsp_lock);
|
||||
init_waitqueue_head(&the_amrnb_audio.write_wait);
|
||||
init_waitqueue_head(&the_amrnb_audio.read_wait);
|
||||
the_amrnb_audio.read_data = NULL;
|
||||
return misc_register(&audio_amrnb_misc);
|
||||
}
|
||||
|
||||
static void __exit audamrnb_exit(void)
|
||||
{
|
||||
misc_deregister(&audio_amrnb_misc);
|
||||
}
|
||||
|
||||
module_init(audamrnb_init);
|
||||
module_exit(audamrnb_exit);
|
||||
|
||||
MODULE_DESCRIPTION("MSM AMR-NB driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("QUALCOMM Inc");
|
845
drivers/staging/dream/qdsp5/audio_evrc.c
Normal file
845
drivers/staging/dream/qdsp5/audio_evrc.c
Normal file
@ -0,0 +1,845 @@
|
||||
/* arch/arm/mach-msm/audio_evrc.c
|
||||
*
|
||||
* Copyright (c) 2008 QUALCOMM USA, INC.
|
||||
*
|
||||
* This code also borrows from audio_aac.c, which is
|
||||
* 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.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you can find it at http://www.fsf.org.
|
||||
*/
|
||||
|
||||
#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/delay.h>
|
||||
|
||||
#include <asm/atomic.h>
|
||||
#include <asm/ioctls.h>
|
||||
#include <mach/msm_adsp.h>
|
||||
#include <linux/msm_audio.h>
|
||||
#include "audmgr.h"
|
||||
|
||||
#include <mach/qdsp5/qdsp5audppcmdi.h>
|
||||
#include <mach/qdsp5/qdsp5audppmsg.h>
|
||||
#include <mach/qdsp5/qdsp5audplaycmdi.h>
|
||||
#include <mach/qdsp5/qdsp5audplaymsg.h>
|
||||
|
||||
#include "adsp.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
#define dprintk(format, arg...) \
|
||||
printk(KERN_DEBUG format, ## arg)
|
||||
#else
|
||||
#define dprintk(format, arg...) do {} while (0)
|
||||
#endif
|
||||
|
||||
/* Hold 30 packets of 24 bytes each*/
|
||||
#define BUFSZ 720
|
||||
#define DMASZ (BUFSZ * 2)
|
||||
|
||||
#define AUDDEC_DEC_EVRC 12
|
||||
|
||||
#define PCM_BUFSZ_MIN 1600 /* 100ms worth of data */
|
||||
#define PCM_BUF_MAX_COUNT 5
|
||||
/* DSP only accepts 5 buffers at most
|
||||
* but support 2 buffers currently
|
||||
*/
|
||||
#define EVRC_DECODED_FRSZ 320 /* EVRC 20ms 8KHz mono PCM size */
|
||||
|
||||
#define ROUTING_MODE_FTRT 1
|
||||
#define ROUTING_MODE_RT 2
|
||||
/* Decoder status received from AUDPPTASK */
|
||||
#define AUDPP_DEC_STATUS_SLEEP 0
|
||||
#define AUDPP_DEC_STATUS_INIT 1
|
||||
#define AUDPP_DEC_STATUS_CFG 2
|
||||
#define AUDPP_DEC_STATUS_PLAY 3
|
||||
|
||||
struct buffer {
|
||||
void *data;
|
||||
unsigned size;
|
||||
unsigned used; /* Input usage actual DSP produced PCM size */
|
||||
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 write_wait;
|
||||
|
||||
/* Host PCM section */
|
||||
struct buffer in[PCM_BUF_MAX_COUNT];
|
||||
struct mutex read_lock;
|
||||
wait_queue_head_t read_wait; /* Wait queue for read */
|
||||
char *read_data; /* pointer to reader buffer */
|
||||
dma_addr_t read_phys; /* physical address of reader buffer */
|
||||
uint8_t read_next; /* index to input buffers to be read next */
|
||||
uint8_t fill_next; /* index to buffer that DSP should be filling */
|
||||
uint8_t pcm_buf_count; /* number of pcm buffer allocated */
|
||||
/* ---- End of Host PCM section */
|
||||
|
||||
struct msm_adsp_module *audplay;
|
||||
struct audmgr audmgr;
|
||||
|
||||
/* data allocated for various buffers */
|
||||
char *data;
|
||||
dma_addr_t phys;
|
||||
|
||||
uint8_t opened:1;
|
||||
uint8_t enabled:1;
|
||||
uint8_t running:1;
|
||||
uint8_t stopped:1; /* set when stopped, cleared on flush */
|
||||
uint8_t pcm_feedback:1;
|
||||
uint8_t buf_refresh:1;
|
||||
|
||||
unsigned volume;
|
||||
uint16_t dec_id;
|
||||
uint32_t read_ptr_offset;
|
||||
};
|
||||
static struct audio the_evrc_audio;
|
||||
|
||||
static int auddec_dsp_config(struct audio *audio, int enable);
|
||||
static void audpp_cmd_cfg_adec_params(struct audio *audio);
|
||||
static void audpp_cmd_cfg_routing_mode(struct audio *audio);
|
||||
static void audevrc_send_data(struct audio *audio, unsigned needed);
|
||||
static void audevrc_dsp_event(void *private, unsigned id, uint16_t *msg);
|
||||
static void audevrc_config_hostpcm(struct audio *audio);
|
||||
static void audevrc_buffer_refresh(struct audio *audio);
|
||||
|
||||
/* must be called with audio->lock held */
|
||||
static int audevrc_enable(struct audio *audio)
|
||||
{
|
||||
struct audmgr_config cfg;
|
||||
int rc;
|
||||
|
||||
if (audio->enabled)
|
||||
return 0;
|
||||
|
||||
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_PLAYBACK;
|
||||
cfg.codec = RPC_AUD_DEF_CODEC_EVRC;
|
||||
cfg.snd_method = RPC_SND_METHOD_MIDI;
|
||||
|
||||
rc = audmgr_enable(&audio->audmgr, &cfg);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
if (msm_adsp_enable(audio->audplay)) {
|
||||
pr_err("audio: msm_adsp_enable(audplay) failed\n");
|
||||
audmgr_disable(&audio->audmgr);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (audpp_enable(audio->dec_id, audevrc_dsp_event, audio)) {
|
||||
pr_err("audio: audpp_enable() failed\n");
|
||||
msm_adsp_disable(audio->audplay);
|
||||
audmgr_disable(&audio->audmgr);
|
||||
return -ENODEV;
|
||||
}
|
||||
audio->enabled = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* must be called with audio->lock held */
|
||||
static int audevrc_disable(struct audio *audio)
|
||||
{
|
||||
if (audio->enabled) {
|
||||
audio->enabled = 0;
|
||||
auddec_dsp_config(audio, 0);
|
||||
wake_up(&audio->write_wait);
|
||||
wake_up(&audio->read_wait);
|
||||
msm_adsp_disable(audio->audplay);
|
||||
audpp_disable(audio->dec_id, audio);
|
||||
audmgr_disable(&audio->audmgr);
|
||||
audio->out_needed = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ------------------- dsp --------------------- */
|
||||
|
||||
static void audevrc_update_pcm_buf_entry(struct audio *audio,
|
||||
uint32_t *payload)
|
||||
{
|
||||
uint8_t index;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&audio->dsp_lock, flags);
|
||||
for (index = 0; index < payload[1]; index++) {
|
||||
if (audio->in[audio->fill_next].addr
|
||||
== payload[2 + index * 2]) {
|
||||
dprintk("audevrc_update_pcm_buf_entry: in[%d] ready\n",
|
||||
audio->fill_next);
|
||||
audio->in[audio->fill_next].used =
|
||||
payload[3 + index * 2];
|
||||
if ((++audio->fill_next) == audio->pcm_buf_count)
|
||||
audio->fill_next = 0;
|
||||
|
||||
} else {
|
||||
pr_err
|
||||
("audevrc_update_pcm_buf_entry: expected=%x ret=%x\n",
|
||||
audio->in[audio->fill_next].addr,
|
||||
payload[1 + index * 2]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (audio->in[audio->fill_next].used == 0) {
|
||||
audevrc_buffer_refresh(audio);
|
||||
} else {
|
||||
dprintk("audevrc_update_pcm_buf_entry: read cannot keep up\n");
|
||||
audio->buf_refresh = 1;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&audio->dsp_lock, flags);
|
||||
wake_up(&audio->read_wait);
|
||||
}
|
||||
|
||||
static void audplay_dsp_event(void *data, unsigned id, size_t len,
|
||||
void (*getevent) (void *ptr, size_t len))
|
||||
{
|
||||
struct audio *audio = data;
|
||||
uint32_t msg[28];
|
||||
getevent(msg, sizeof(msg));
|
||||
|
||||
dprintk("audplay_dsp_event: msg_id=%x\n", id);
|
||||
switch (id) {
|
||||
case AUDPLAY_MSG_DEC_NEEDS_DATA:
|
||||
audevrc_send_data(audio, 1);
|
||||
break;
|
||||
case AUDPLAY_MSG_BUFFER_UPDATE:
|
||||
dprintk("audevrc_update_pcm_buf_entry:======> \n");
|
||||
audevrc_update_pcm_buf_entry(audio, msg);
|
||||
break;
|
||||
default:
|
||||
pr_err("unexpected message from decoder \n");
|
||||
}
|
||||
}
|
||||
|
||||
static void audevrc_dsp_event(void *private, unsigned id, uint16_t *msg)
|
||||
{
|
||||
struct audio *audio = private;
|
||||
|
||||
switch (id) {
|
||||
case AUDPP_MSG_STATUS_MSG:{
|
||||
unsigned status = msg[1];
|
||||
|
||||
switch (status) {
|
||||
case AUDPP_DEC_STATUS_SLEEP:
|
||||
dprintk("decoder status: sleep \n");
|
||||
break;
|
||||
|
||||
case AUDPP_DEC_STATUS_INIT:
|
||||
dprintk("decoder status: init \n");
|
||||
audpp_cmd_cfg_routing_mode(audio);
|
||||
break;
|
||||
|
||||
case AUDPP_DEC_STATUS_CFG:
|
||||
dprintk("decoder status: cfg \n");
|
||||
break;
|
||||
case AUDPP_DEC_STATUS_PLAY:
|
||||
dprintk("decoder status: play \n");
|
||||
if (audio->pcm_feedback) {
|
||||
audevrc_config_hostpcm(audio);
|
||||
audevrc_buffer_refresh(audio);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pr_err("unknown decoder status \n");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AUDPP_MSG_CFG_MSG:
|
||||
if (msg[0] == AUDPP_MSG_ENA_ENA) {
|
||||
dprintk("audevrc_dsp_event: CFG_MSG ENABLE\n");
|
||||
auddec_dsp_config(audio, 1);
|
||||
audio->out_needed = 0;
|
||||
audio->running = 1;
|
||||
audpp_set_volume_and_pan(audio->dec_id, audio->volume,
|
||||
0);
|
||||
audpp_avsync(audio->dec_id, 22050);
|
||||
} else if (msg[0] == AUDPP_MSG_ENA_DIS) {
|
||||
dprintk("audevrc_dsp_event: CFG_MSG DISABLE\n");
|
||||
audpp_avsync(audio->dec_id, 0);
|
||||
audio->running = 0;
|
||||
} else {
|
||||
pr_err("audevrc_dsp_event: CFG_MSG %d?\n", msg[0]);
|
||||
}
|
||||
break;
|
||||
case AUDPP_MSG_ROUTING_ACK:
|
||||
dprintk("audevrc_dsp_event: ROUTING_ACK\n");
|
||||
audpp_cmd_cfg_adec_params(audio);
|
||||
break;
|
||||
|
||||
default:
|
||||
pr_err("audevrc_dsp_event: UNKNOWN (%d)\n", id);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct msm_adsp_ops audplay_adsp_ops_evrc = {
|
||||
.event = audplay_dsp_event,
|
||||
};
|
||||
|
||||
#define audplay_send_queue0(audio, cmd, len) \
|
||||
msm_adsp_write(audio->audplay, QDSP_uPAudPlay0BitStreamCtrlQueue, \
|
||||
cmd, len)
|
||||
|
||||
static int auddec_dsp_config(struct audio *audio, int enable)
|
||||
{
|
||||
audpp_cmd_cfg_dec_type cmd;
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE;
|
||||
if (enable)
|
||||
cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
|
||||
AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_EVRC;
|
||||
else
|
||||
cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | AUDPP_CMD_DIS_DEC_V;
|
||||
|
||||
return audpp_send_queue1(&cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
static void audpp_cmd_cfg_adec_params(struct audio *audio)
|
||||
{
|
||||
struct audpp_cmd_cfg_adec_params_evrc cmd;
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS;
|
||||
cmd.common.length = sizeof(cmd);
|
||||
cmd.common.dec_id = audio->dec_id;
|
||||
cmd.common.input_sampling_frequency = 8000;
|
||||
cmd.stereo_cfg = AUDPP_CMD_PCM_INTF_MONO_V;
|
||||
|
||||
audpp_send_queue2(&cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
static void audpp_cmd_cfg_routing_mode(struct audio *audio)
|
||||
{
|
||||
struct audpp_cmd_routing_mode cmd;
|
||||
dprintk("audpp_cmd_cfg_routing_mode()\n");
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.cmd_id = AUDPP_CMD_ROUTING_MODE;
|
||||
cmd.object_number = audio->dec_id;
|
||||
if (audio->pcm_feedback)
|
||||
cmd.routing_mode = ROUTING_MODE_FTRT;
|
||||
else
|
||||
cmd.routing_mode = ROUTING_MODE_RT;
|
||||
|
||||
audpp_send_queue1(&cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
static int audplay_dsp_send_data_avail(struct audio *audio,
|
||||
unsigned idx, unsigned len)
|
||||
{
|
||||
audplay_cmd_bitstream_data_avail cmd;
|
||||
|
||||
cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL;
|
||||
cmd.decoder_id = audio->dec_id;
|
||||
cmd.buf_ptr = audio->out[idx].addr;
|
||||
cmd.buf_size = len / 2;
|
||||
cmd.partition_number = 0;
|
||||
return audplay_send_queue0(audio, &cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
static void audevrc_buffer_refresh(struct audio *audio)
|
||||
{
|
||||
struct audplay_cmd_buffer_refresh refresh_cmd;
|
||||
|
||||
refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH;
|
||||
refresh_cmd.num_buffers = 1;
|
||||
refresh_cmd.buf0_address = audio->in[audio->fill_next].addr;
|
||||
refresh_cmd.buf0_length = audio->in[audio->fill_next].size;
|
||||
|
||||
refresh_cmd.buf_read_count = 0;
|
||||
dprintk("audplay_buffer_fresh: buf0_addr=%x buf0_len=%d\n",
|
||||
refresh_cmd.buf0_address, refresh_cmd.buf0_length);
|
||||
audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd));
|
||||
}
|
||||
|
||||
static void audevrc_config_hostpcm(struct audio *audio)
|
||||
{
|
||||
struct audplay_cmd_hpcm_buf_cfg cfg_cmd;
|
||||
|
||||
dprintk("audevrc_config_hostpcm()\n");
|
||||
cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG;
|
||||
cfg_cmd.max_buffers = 1;
|
||||
cfg_cmd.byte_swap = 0;
|
||||
cfg_cmd.hostpcm_config = (0x8000) | (0x4000);
|
||||
cfg_cmd.feedback_frequency = 1;
|
||||
cfg_cmd.partition_number = 0;
|
||||
audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd));
|
||||
|
||||
}
|
||||
|
||||
static void audevrc_send_data(struct audio *audio, unsigned needed)
|
||||
{
|
||||
struct buffer *frame;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&audio->dsp_lock, flags);
|
||||
if (!audio->running)
|
||||
goto done;
|
||||
|
||||
if (needed) {
|
||||
/* We were called from the callback because the DSP
|
||||
* requested more data. Note that the DSP does want
|
||||
* more data, and if a buffer was in-flight, mark it
|
||||
* as available (since the DSP must now be done with
|
||||
* it).
|
||||
*/
|
||||
audio->out_needed = 1;
|
||||
frame = audio->out + audio->out_tail;
|
||||
if (frame->used == 0xffffffff) {
|
||||
dprintk("frame %d free\n", audio->out_tail);
|
||||
frame->used = 0;
|
||||
audio->out_tail ^= 1;
|
||||
wake_up(&audio->write_wait);
|
||||
}
|
||||
}
|
||||
|
||||
if (audio->out_needed) {
|
||||
/* If the DSP currently wants data and we have a
|
||||
* buffer available, we will send it and reset
|
||||
* the needed flag. We'll mark the buffer as in-flight
|
||||
* so that it won't be recycled until the next buffer
|
||||
* is requested
|
||||
*/
|
||||
|
||||
frame = audio->out + audio->out_tail;
|
||||
if (frame->used) {
|
||||
BUG_ON(frame->used == 0xffffffff);
|
||||
dprintk("frame %d busy\n", audio->out_tail);
|
||||
audplay_dsp_send_data_avail(audio, audio->out_tail,
|
||||
frame->used);
|
||||
frame->used = 0xffffffff;
|
||||
audio->out_needed = 0;
|
||||
}
|
||||
}
|
||||
done:
|
||||
spin_unlock_irqrestore(&audio->dsp_lock, flags);
|
||||
}
|
||||
|
||||
/* ------------------- device --------------------- */
|
||||
|
||||
static void audevrc_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;
|
||||
atomic_set(&audio->out_bytes, 0);
|
||||
}
|
||||
|
||||
static void audevrc_flush_pcm_buf(struct audio *audio)
|
||||
{
|
||||
uint8_t index;
|
||||
|
||||
for (index = 0; index < PCM_BUF_MAX_COUNT; index++)
|
||||
audio->in[index].used = 0;
|
||||
|
||||
audio->read_next = 0;
|
||||
audio->fill_next = 0;
|
||||
}
|
||||
|
||||
static long audevrc_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct audio *audio = file->private_data;
|
||||
int rc = 0;
|
||||
|
||||
dprintk("audevrc_ioctl() cmd = %d\n", cmd);
|
||||
|
||||
if (cmd == AUDIO_GET_STATS) {
|
||||
struct msm_audio_stats stats;
|
||||
stats.byte_count = audpp_avsync_byte_count(audio->dec_id);
|
||||
stats.sample_count = audpp_avsync_sample_count(audio->dec_id);
|
||||
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(audio->dec_id, arg, 0);
|
||||
spin_unlock_irqrestore(&audio->dsp_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
mutex_lock(&audio->lock);
|
||||
switch (cmd) {
|
||||
case AUDIO_START:
|
||||
rc = audevrc_enable(audio);
|
||||
break;
|
||||
case AUDIO_STOP:
|
||||
rc = audevrc_disable(audio);
|
||||
audio->stopped = 1;
|
||||
break;
|
||||
case AUDIO_SET_CONFIG:{
|
||||
dprintk("AUDIO_SET_CONFIG not applicable \n");
|
||||
break;
|
||||
}
|
||||
case AUDIO_GET_CONFIG:{
|
||||
struct msm_audio_config config;
|
||||
config.buffer_size = BUFSZ;
|
||||
config.buffer_count = 2;
|
||||
config.sample_rate = 8000;
|
||||
config.channel_count = 1;
|
||||
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;
|
||||
}
|
||||
case AUDIO_GET_PCM_CONFIG:{
|
||||
struct msm_audio_pcm_config config;
|
||||
config.pcm_feedback = 0;
|
||||
config.buffer_count = PCM_BUF_MAX_COUNT;
|
||||
config.buffer_size = PCM_BUFSZ_MIN;
|
||||
if (copy_to_user((void *)arg, &config, sizeof(config)))
|
||||
rc = -EFAULT;
|
||||
else
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
case AUDIO_SET_PCM_CONFIG:{
|
||||
struct msm_audio_pcm_config config;
|
||||
if (copy_from_user
|
||||
(&config, (void *)arg, sizeof(config))) {
|
||||
rc = -EFAULT;
|
||||
break;
|
||||
}
|
||||
if ((config.buffer_count > PCM_BUF_MAX_COUNT) ||
|
||||
(config.buffer_count == 1))
|
||||
config.buffer_count = PCM_BUF_MAX_COUNT;
|
||||
|
||||
if (config.buffer_size < PCM_BUFSZ_MIN)
|
||||
config.buffer_size = PCM_BUFSZ_MIN;
|
||||
|
||||
/* Check if pcm feedback is required */
|
||||
if ((config.pcm_feedback) && (!audio->read_data)) {
|
||||
dprintk("audevrc_ioctl: allocate PCM buf %d\n",
|
||||
config.buffer_count *
|
||||
config.buffer_size);
|
||||
audio->read_data =
|
||||
dma_alloc_coherent(NULL,
|
||||
config.buffer_size *
|
||||
config.buffer_count,
|
||||
&audio->read_phys,
|
||||
GFP_KERNEL);
|
||||
if (!audio->read_data) {
|
||||
pr_err
|
||||
("audevrc_ioctl: no mem for pcm buf\n");
|
||||
rc = -1;
|
||||
} else {
|
||||
uint8_t index;
|
||||
uint32_t offset = 0;
|
||||
audio->pcm_feedback = 1;
|
||||
audio->buf_refresh = 0;
|
||||
audio->pcm_buf_count =
|
||||
config.buffer_count;
|
||||
audio->read_next = 0;
|
||||
audio->fill_next = 0;
|
||||
|
||||
for (index = 0;
|
||||
index < config.buffer_count;
|
||||
index++) {
|
||||
audio->in[index].data =
|
||||
audio->read_data + offset;
|
||||
audio->in[index].addr =
|
||||
audio->read_phys + offset;
|
||||
audio->in[index].size =
|
||||
config.buffer_size;
|
||||
audio->in[index].used = 0;
|
||||
offset += config.buffer_size;
|
||||
}
|
||||
rc = 0;
|
||||
}
|
||||
} else {
|
||||
rc = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AUDIO_PAUSE:
|
||||
dprintk("%s: AUDIO_PAUSE %ld\n", __func__, arg);
|
||||
rc = audpp_pause(audio->dec_id, (int) arg);
|
||||
break;
|
||||
default:
|
||||
rc = -EINVAL;
|
||||
}
|
||||
mutex_unlock(&audio->lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static ssize_t audevrc_read(struct file *file, char __user *buf, size_t count,
|
||||
loff_t *pos)
|
||||
{
|
||||
struct audio *audio = file->private_data;
|
||||
const char __user *start = buf;
|
||||
int rc = 0;
|
||||
if (!audio->pcm_feedback) {
|
||||
return 0;
|
||||
/* PCM feedback is not enabled. Nothing to read */
|
||||
}
|
||||
mutex_lock(&audio->read_lock);
|
||||
dprintk("audevrc_read() \n");
|
||||
while (count > 0) {
|
||||
rc = wait_event_interruptible(audio->read_wait,
|
||||
(audio->in[audio->read_next].
|
||||
used > 0) || (audio->stopped));
|
||||
dprintk("audevrc_read() wait terminated \n");
|
||||
if (rc < 0)
|
||||
break;
|
||||
if (audio->stopped) {
|
||||
rc = -EBUSY;
|
||||
break;
|
||||
}
|
||||
if (count < audio->in[audio->read_next].used) {
|
||||
/* Read must happen in frame boundary. Since driver does
|
||||
* not know frame size, read count must be greater or
|
||||
* equal to size of PCM samples
|
||||
*/
|
||||
dprintk("audevrc_read:read stop - partial frame\n");
|
||||
break;
|
||||
} else {
|
||||
dprintk("audevrc_read: read from in[%d]\n",
|
||||
audio->read_next);
|
||||
if (copy_to_user
|
||||
(buf, audio->in[audio->read_next].data,
|
||||
audio->in[audio->read_next].used)) {
|
||||
pr_err("audevrc_read: invalid addr %x \n",
|
||||
(unsigned int)buf);
|
||||
rc = -EFAULT;
|
||||
break;
|
||||
}
|
||||
count -= audio->in[audio->read_next].used;
|
||||
buf += audio->in[audio->read_next].used;
|
||||
audio->in[audio->read_next].used = 0;
|
||||
if ((++audio->read_next) == audio->pcm_buf_count)
|
||||
audio->read_next = 0;
|
||||
if (audio->in[audio->read_next].used == 0)
|
||||
break; /* No data ready at this moment
|
||||
* Exit while loop to prevent
|
||||
* output thread sleep too long
|
||||
*/
|
||||
|
||||
}
|
||||
}
|
||||
if (audio->buf_refresh) {
|
||||
audio->buf_refresh = 0;
|
||||
dprintk("audevrc_read: kick start pcm feedback again\n");
|
||||
audevrc_buffer_refresh(audio);
|
||||
}
|
||||
mutex_unlock(&audio->read_lock);
|
||||
if (buf > start)
|
||||
rc = buf - start;
|
||||
dprintk("audevrc_read: read %d bytes\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static ssize_t audevrc_write(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *pos)
|
||||
{
|
||||
struct audio *audio = file->private_data;
|
||||
const char __user *start = buf;
|
||||
struct buffer *frame;
|
||||
size_t xfer;
|
||||
int rc = 0;
|
||||
|
||||
if (count & 1)
|
||||
return -EINVAL;
|
||||
mutex_lock(&audio->write_lock);
|
||||
dprintk("audevrc_write() \n");
|
||||
while (count > 0) {
|
||||
frame = audio->out + audio->out_head;
|
||||
rc = wait_event_interruptible(audio->write_wait,
|
||||
(frame->used == 0)
|
||||
|| (audio->stopped));
|
||||
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;
|
||||
|
||||
audevrc_send_data(audio, 0);
|
||||
|
||||
}
|
||||
mutex_unlock(&audio->write_lock);
|
||||
if (buf > start)
|
||||
return buf - start;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int audevrc_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct audio *audio = file->private_data;
|
||||
|
||||
dprintk("audevrc_release()\n");
|
||||
|
||||
mutex_lock(&audio->lock);
|
||||
audevrc_disable(audio);
|
||||
audevrc_flush(audio);
|
||||
audevrc_flush_pcm_buf(audio);
|
||||
msm_adsp_put(audio->audplay);
|
||||
audio->audplay = NULL;
|
||||
audio->opened = 0;
|
||||
dma_free_coherent(NULL, DMASZ, audio->data, audio->phys);
|
||||
audio->data = NULL;
|
||||
if (audio->read_data != NULL) {
|
||||
dma_free_coherent(NULL,
|
||||
audio->in[0].size * audio->pcm_buf_count,
|
||||
audio->read_data, audio->read_phys);
|
||||
audio->read_data = NULL;
|
||||
}
|
||||
audio->pcm_feedback = 0;
|
||||
mutex_unlock(&audio->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct audio the_evrc_audio;
|
||||
|
||||
static int audevrc_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct audio *audio = &the_evrc_audio;
|
||||
int rc;
|
||||
|
||||
if (audio->opened) {
|
||||
pr_err("audio: busy\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* Acquire Lock */
|
||||
mutex_lock(&audio->lock);
|
||||
|
||||
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 dma_fail;
|
||||
}
|
||||
}
|
||||
|
||||
rc = audmgr_open(&audio->audmgr);
|
||||
if (rc)
|
||||
goto audmgr_fail;
|
||||
|
||||
rc = msm_adsp_get("AUDPLAY0TASK", &audio->audplay,
|
||||
&audplay_adsp_ops_evrc, audio);
|
||||
if (rc) {
|
||||
pr_err("audio: failed to get audplay0 dsp module\n");
|
||||
goto adsp_fail;
|
||||
}
|
||||
|
||||
audio->dec_id = 0;
|
||||
|
||||
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 = 0x3FFF;
|
||||
|
||||
audevrc_flush(audio);
|
||||
|
||||
audio->opened = 1;
|
||||
file->private_data = audio;
|
||||
|
||||
mutex_unlock(&audio->lock);
|
||||
return rc;
|
||||
|
||||
adsp_fail:
|
||||
audmgr_close(&audio->audmgr);
|
||||
audmgr_fail:
|
||||
dma_free_coherent(NULL, DMASZ, audio->data, audio->phys);
|
||||
dma_fail:
|
||||
mutex_unlock(&audio->lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static struct file_operations audio_evrc_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = audevrc_open,
|
||||
.release = audevrc_release,
|
||||
.read = audevrc_read,
|
||||
.write = audevrc_write,
|
||||
.unlocked_ioctl = audevrc_ioctl,
|
||||
};
|
||||
|
||||
struct miscdevice audio_evrc_misc = {
|
||||
.minor = MISC_DYNAMIC_MINOR,
|
||||
.name = "msm_evrc",
|
||||
.fops = &audio_evrc_fops,
|
||||
};
|
||||
|
||||
static int __init audevrc_init(void)
|
||||
{
|
||||
mutex_init(&the_evrc_audio.lock);
|
||||
mutex_init(&the_evrc_audio.write_lock);
|
||||
mutex_init(&the_evrc_audio.read_lock);
|
||||
spin_lock_init(&the_evrc_audio.dsp_lock);
|
||||
init_waitqueue_head(&the_evrc_audio.write_wait);
|
||||
init_waitqueue_head(&the_evrc_audio.read_wait);
|
||||
the_evrc_audio.read_data = NULL;
|
||||
return misc_register(&audio_evrc_misc);
|
||||
}
|
||||
|
||||
static void __exit audevrc_exit(void)
|
||||
{
|
||||
misc_deregister(&audio_evrc_misc);
|
||||
}
|
||||
|
||||
module_init(audevrc_init);
|
||||
module_exit(audevrc_exit);
|
||||
|
||||
MODULE_DESCRIPTION("MSM EVRC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("QUALCOMM Inc");
|
967
drivers/staging/dream/qdsp5/audio_in.c
Normal file
967
drivers/staging/dream/qdsp5/audio_in.c
Normal file
@ -0,0 +1,967 @@
|
||||
/* arch/arm/mach-msm/qdsp5/audio_in.c
|
||||
*
|
||||
* pcm audio input 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/delay.h>
|
||||
|
||||
#include <linux/msm_audio.h>
|
||||
|
||||
#include <asm/atomic.h>
|
||||
#include <asm/ioctls.h>
|
||||
#include <mach/msm_adsp.h>
|
||||
#include <mach/msm_rpcrouter.h>
|
||||
|
||||
#include "audmgr.h"
|
||||
|
||||
#include <mach/qdsp5/qdsp5audpreproccmdi.h>
|
||||
#include <mach/qdsp5/qdsp5audpreprocmsg.h>
|
||||
#include <mach/qdsp5/qdsp5audreccmdi.h>
|
||||
#include <mach/qdsp5/qdsp5audrecmsg.h>
|
||||
|
||||
/* for queue ids - should be relative to module number*/
|
||||
#include "adsp.h"
|
||||
|
||||
/* FRAME_NUM must be a power of two */
|
||||
#define FRAME_NUM (8)
|
||||
#define FRAME_SIZE (2052 * 2)
|
||||
#define MONO_DATA_SIZE (2048)
|
||||
#define STEREO_DATA_SIZE (MONO_DATA_SIZE * 2)
|
||||
#define DMASZ (FRAME_SIZE * FRAME_NUM)
|
||||
|
||||
#define AGC_PARAM_SIZE (20)
|
||||
#define NS_PARAM_SIZE (6)
|
||||
#define IIR_PARAM_SIZE (48)
|
||||
#define DEBUG (0)
|
||||
|
||||
#define AGC_ENABLE 0x0001
|
||||
#define NS_ENABLE 0x0002
|
||||
#define IIR_ENABLE 0x0004
|
||||
|
||||
struct tx_agc_config {
|
||||
uint16_t agc_params[AGC_PARAM_SIZE];
|
||||
};
|
||||
|
||||
struct ns_config {
|
||||
uint16_t ns_params[NS_PARAM_SIZE];
|
||||
};
|
||||
|
||||
struct tx_iir_filter {
|
||||
uint16_t num_bands;
|
||||
uint16_t iir_params[IIR_PARAM_SIZE];
|
||||
};
|
||||
|
||||
struct audpre_cmd_iir_config_type {
|
||||
uint16_t cmd_id;
|
||||
uint16_t active_flag;
|
||||
uint16_t num_bands;
|
||||
uint16_t iir_params[IIR_PARAM_SIZE];
|
||||
};
|
||||
|
||||
struct buffer {
|
||||
void *data;
|
||||
uint32_t size;
|
||||
uint32_t read;
|
||||
uint32_t addr;
|
||||
};
|
||||
|
||||
struct audio_in {
|
||||
struct buffer in[FRAME_NUM];
|
||||
|
||||
spinlock_t dsp_lock;
|
||||
|
||||
atomic_t in_bytes;
|
||||
|
||||
struct mutex lock;
|
||||
struct mutex read_lock;
|
||||
wait_queue_head_t wait;
|
||||
|
||||
struct msm_adsp_module *audpre;
|
||||
struct msm_adsp_module *audrec;
|
||||
|
||||
/* configuration to use on next enable */
|
||||
uint32_t samp_rate;
|
||||
uint32_t channel_mode;
|
||||
uint32_t buffer_size; /* 2048 for mono, 4096 for stereo */
|
||||
uint32_t type; /* 0 for PCM ,1 for AAC */
|
||||
uint32_t dsp_cnt;
|
||||
uint32_t in_head; /* next buffer dsp will write */
|
||||
uint32_t in_tail; /* next buffer read() will read */
|
||||
uint32_t in_count; /* number of buffers available to read() */
|
||||
|
||||
unsigned short samp_rate_index;
|
||||
|
||||
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 */
|
||||
|
||||
/* audpre settings */
|
||||
int agc_enable;
|
||||
struct tx_agc_config agc;
|
||||
|
||||
int ns_enable;
|
||||
struct ns_config ns;
|
||||
|
||||
int iir_enable;
|
||||
struct tx_iir_filter iir;
|
||||
};
|
||||
|
||||
static int audio_in_dsp_enable(struct audio_in *audio, int enable);
|
||||
static int audio_in_encoder_config(struct audio_in *audio);
|
||||
static int audio_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt);
|
||||
static void audio_flush(struct audio_in *audio);
|
||||
static int audio_dsp_set_agc(struct audio_in *audio);
|
||||
static int audio_dsp_set_ns(struct audio_in *audio);
|
||||
static int audio_dsp_set_tx_iir(struct audio_in *audio);
|
||||
|
||||
static unsigned convert_dsp_samp_index(unsigned index)
|
||||
{
|
||||
switch (index) {
|
||||
case 48000: return AUDREC_CMD_SAMP_RATE_INDX_48000;
|
||||
case 44100: return AUDREC_CMD_SAMP_RATE_INDX_44100;
|
||||
case 32000: return AUDREC_CMD_SAMP_RATE_INDX_32000;
|
||||
case 24000: return AUDREC_CMD_SAMP_RATE_INDX_24000;
|
||||
case 22050: return AUDREC_CMD_SAMP_RATE_INDX_22050;
|
||||
case 16000: return AUDREC_CMD_SAMP_RATE_INDX_16000;
|
||||
case 12000: return AUDREC_CMD_SAMP_RATE_INDX_12000;
|
||||
case 11025: return AUDREC_CMD_SAMP_RATE_INDX_11025;
|
||||
case 8000: return AUDREC_CMD_SAMP_RATE_INDX_8000;
|
||||
default: return AUDREC_CMD_SAMP_RATE_INDX_11025;
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned convert_samp_rate(unsigned hz)
|
||||
{
|
||||
switch (hz) {
|
||||
case 48000: return RPC_AUD_DEF_SAMPLE_RATE_48000;
|
||||
case 44100: return RPC_AUD_DEF_SAMPLE_RATE_44100;
|
||||
case 32000: return RPC_AUD_DEF_SAMPLE_RATE_32000;
|
||||
case 24000: return RPC_AUD_DEF_SAMPLE_RATE_24000;
|
||||
case 22050: return RPC_AUD_DEF_SAMPLE_RATE_22050;
|
||||
case 16000: return RPC_AUD_DEF_SAMPLE_RATE_16000;
|
||||
case 12000: return RPC_AUD_DEF_SAMPLE_RATE_12000;
|
||||
case 11025: return RPC_AUD_DEF_SAMPLE_RATE_11025;
|
||||
case 8000: return RPC_AUD_DEF_SAMPLE_RATE_8000;
|
||||
default: return RPC_AUD_DEF_SAMPLE_RATE_11025;
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned convert_samp_index(unsigned index)
|
||||
{
|
||||
switch (index) {
|
||||
case RPC_AUD_DEF_SAMPLE_RATE_48000: return 48000;
|
||||
case RPC_AUD_DEF_SAMPLE_RATE_44100: return 44100;
|
||||
case RPC_AUD_DEF_SAMPLE_RATE_32000: return 32000;
|
||||
case RPC_AUD_DEF_SAMPLE_RATE_24000: return 24000;
|
||||
case RPC_AUD_DEF_SAMPLE_RATE_22050: return 22050;
|
||||
case RPC_AUD_DEF_SAMPLE_RATE_16000: return 16000;
|
||||
case RPC_AUD_DEF_SAMPLE_RATE_12000: return 12000;
|
||||
case RPC_AUD_DEF_SAMPLE_RATE_11025: return 11025;
|
||||
case RPC_AUD_DEF_SAMPLE_RATE_8000: return 8000;
|
||||
default: return 11025;
|
||||
}
|
||||
}
|
||||
|
||||
/* must be called with audio->lock held */
|
||||
static int audio_in_enable(struct audio_in *audio)
|
||||
{
|
||||
struct audmgr_config cfg;
|
||||
int rc;
|
||||
|
||||
if (audio->enabled)
|
||||
return 0;
|
||||
|
||||
cfg.tx_rate = audio->samp_rate;
|
||||
cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE;
|
||||
cfg.def_method = RPC_AUD_DEF_METHOD_RECORD;
|
||||
if (audio->type == AUDREC_CMD_TYPE_0_INDEX_WAV)
|
||||
cfg.codec = RPC_AUD_DEF_CODEC_PCM;
|
||||
else
|
||||
cfg.codec = RPC_AUD_DEF_CODEC_AAC;
|
||||
cfg.snd_method = RPC_SND_METHOD_MIDI;
|
||||
|
||||
rc = audmgr_enable(&audio->audmgr, &cfg);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
if (msm_adsp_enable(audio->audpre)) {
|
||||
pr_err("audrec: msm_adsp_enable(audpre) failed\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
if (msm_adsp_enable(audio->audrec)) {
|
||||
pr_err("audrec: msm_adsp_enable(audrec) failed\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
audio->enabled = 1;
|
||||
audio_in_dsp_enable(audio, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* must be called with audio->lock held */
|
||||
static int audio_in_disable(struct audio_in *audio)
|
||||
{
|
||||
if (audio->enabled) {
|
||||
audio->enabled = 0;
|
||||
|
||||
audio_in_dsp_enable(audio, 0);
|
||||
|
||||
wake_up(&audio->wait);
|
||||
|
||||
msm_adsp_disable(audio->audrec);
|
||||
msm_adsp_disable(audio->audpre);
|
||||
audmgr_disable(&audio->audmgr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ------------------- dsp --------------------- */
|
||||
static void audpre_dsp_event(void *data, unsigned id, size_t len,
|
||||
void (*getevent)(void *ptr, size_t len))
|
||||
{
|
||||
uint16_t msg[2];
|
||||
getevent(msg, sizeof(msg));
|
||||
|
||||
switch (id) {
|
||||
case AUDPREPROC_MSG_CMD_CFG_DONE_MSG:
|
||||
pr_info("audpre: type %d, status_flag %d\n", msg[0], msg[1]);
|
||||
break;
|
||||
case AUDPREPROC_MSG_ERROR_MSG_ID:
|
||||
pr_info("audpre: err_index %d\n", msg[0]);
|
||||
break;
|
||||
default:
|
||||
pr_err("audpre: unknown event %d\n", id);
|
||||
}
|
||||
}
|
||||
|
||||
struct audio_frame {
|
||||
uint16_t count_low;
|
||||
uint16_t count_high;
|
||||
uint16_t bytes;
|
||||
uint16_t unknown;
|
||||
unsigned char samples[];
|
||||
} __attribute__((packed));
|
||||
|
||||
static void audio_in_get_dsp_frames(struct audio_in *audio)
|
||||
{
|
||||
struct audio_frame *frame;
|
||||
uint32_t index;
|
||||
unsigned long flags;
|
||||
|
||||
index = audio->in_head;
|
||||
|
||||
/* XXX check for bogus frame size? */
|
||||
|
||||
frame = (void *) (((char *)audio->in[index].data) - sizeof(*frame));
|
||||
|
||||
spin_lock_irqsave(&audio->dsp_lock, flags);
|
||||
audio->in[index].size = frame->bytes;
|
||||
|
||||
audio->in_head = (audio->in_head + 1) & (FRAME_NUM - 1);
|
||||
|
||||
/* If overflow, move the tail index foward. */
|
||||
if (audio->in_head == audio->in_tail)
|
||||
audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1);
|
||||
else
|
||||
audio->in_count++;
|
||||
|
||||
audio_dsp_read_buffer(audio, audio->dsp_cnt++);
|
||||
spin_unlock_irqrestore(&audio->dsp_lock, flags);
|
||||
|
||||
wake_up(&audio->wait);
|
||||
}
|
||||
|
||||
static void audrec_dsp_event(void *data, unsigned id, size_t len,
|
||||
void (*getevent)(void *ptr, size_t len))
|
||||
{
|
||||
struct audio_in *audio = data;
|
||||
uint16_t msg[3];
|
||||
getevent(msg, sizeof(msg));
|
||||
|
||||
switch (id) {
|
||||
case AUDREC_MSG_CMD_CFG_DONE_MSG:
|
||||
if (msg[0] & AUDREC_MSG_CFG_DONE_TYPE_0_UPDATE) {
|
||||
if (msg[0] & AUDREC_MSG_CFG_DONE_TYPE_0_ENA) {
|
||||
pr_info("audpre: CFG ENABLED\n");
|
||||
audio_dsp_set_agc(audio);
|
||||
audio_dsp_set_ns(audio);
|
||||
audio_dsp_set_tx_iir(audio);
|
||||
audio_in_encoder_config(audio);
|
||||
} else {
|
||||
pr_info("audrec: CFG SLEEP\n");
|
||||
audio->running = 0;
|
||||
}
|
||||
} else {
|
||||
pr_info("audrec: CMD_CFG_DONE %x\n", msg[0]);
|
||||
}
|
||||
break;
|
||||
case AUDREC_MSG_CMD_AREC_PARAM_CFG_DONE_MSG: {
|
||||
pr_info("audrec: PARAM CFG DONE\n");
|
||||
audio->running = 1;
|
||||
break;
|
||||
}
|
||||
case AUDREC_MSG_FATAL_ERR_MSG:
|
||||
pr_err("audrec: ERROR %x\n", msg[0]);
|
||||
break;
|
||||
case AUDREC_MSG_PACKET_READY_MSG:
|
||||
/* REC_DBG("type %x, count %d", msg[0], (msg[1] | (msg[2] << 16))); */
|
||||
audio_in_get_dsp_frames(audio);
|
||||
break;
|
||||
default:
|
||||
pr_err("audrec: unknown event %d\n", id);
|
||||
}
|
||||
}
|
||||
|
||||
struct msm_adsp_ops audpre_adsp_ops = {
|
||||
.event = audpre_dsp_event,
|
||||
};
|
||||
|
||||
struct msm_adsp_ops audrec_adsp_ops = {
|
||||
.event = audrec_dsp_event,
|
||||
};
|
||||
|
||||
|
||||
#define audio_send_queue_pre(audio, cmd, len) \
|
||||
msm_adsp_write(audio->audpre, QDSP_uPAudPreProcCmdQueue, cmd, len)
|
||||
#define audio_send_queue_recbs(audio, cmd, len) \
|
||||
msm_adsp_write(audio->audrec, QDSP_uPAudRecBitStreamQueue, cmd, len)
|
||||
#define audio_send_queue_rec(audio, cmd, len) \
|
||||
msm_adsp_write(audio->audrec, \
|
||||
QDSP_uPAudRecCmdQueue, cmd, len)
|
||||
|
||||
static int audio_dsp_set_agc(struct audio_in *audio)
|
||||
{
|
||||
audpreproc_cmd_cfg_agc_params cmd;
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.cmd_id = AUDPREPROC_CMD_CFG_AGC_PARAMS;
|
||||
|
||||
if (audio->agc_enable) {
|
||||
/* cmd.tx_agc_param_mask = 0xFE00 from sample code */
|
||||
cmd.tx_agc_param_mask =
|
||||
(1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_SLOPE) |
|
||||
(1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_TH) |
|
||||
(1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_EXP_SLOPE) |
|
||||
(1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_EXP_TH) |
|
||||
(1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_AIG_FLAG) |
|
||||
(1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_STATIC_GAIN) |
|
||||
(1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_TX_AGC_ENA_FLAG);
|
||||
cmd.tx_agc_enable_flag =
|
||||
AUDPREPROC_CMD_TX_AGC_ENA_FLAG_ENA;
|
||||
memcpy(&cmd.static_gain, &audio->agc.agc_params[0],
|
||||
sizeof(uint16_t) * 6);
|
||||
/* cmd.param_mask = 0xFFF0 from sample code */
|
||||
cmd.param_mask =
|
||||
(1 << AUDPREPROC_CMD_PARAM_MASK_RMS_TAY) |
|
||||
(1 << AUDPREPROC_CMD_PARAM_MASK_RELEASEK) |
|
||||
(1 << AUDPREPROC_CMD_PARAM_MASK_DELAY) |
|
||||
(1 << AUDPREPROC_CMD_PARAM_MASK_ATTACKK) |
|
||||
(1 << AUDPREPROC_CMD_PARAM_MASK_LEAKRATE_SLOW) |
|
||||
(1 << AUDPREPROC_CMD_PARAM_MASK_LEAKRATE_FAST) |
|
||||
(1 << AUDPREPROC_CMD_PARAM_MASK_AIG_RELEASEK) |
|
||||
(1 << AUDPREPROC_CMD_PARAM_MASK_AIG_MIN) |
|
||||
(1 << AUDPREPROC_CMD_PARAM_MASK_AIG_MAX) |
|
||||
(1 << AUDPREPROC_CMD_PARAM_MASK_LEAK_UP) |
|
||||
(1 << AUDPREPROC_CMD_PARAM_MASK_LEAK_DOWN) |
|
||||
(1 << AUDPREPROC_CMD_PARAM_MASK_AIG_ATTACKK);
|
||||
memcpy(&cmd.aig_attackk, &audio->agc.agc_params[6],
|
||||
sizeof(uint16_t) * 14);
|
||||
|
||||
} else {
|
||||
cmd.tx_agc_param_mask =
|
||||
(1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_TX_AGC_ENA_FLAG);
|
||||
cmd.tx_agc_enable_flag =
|
||||
AUDPREPROC_CMD_TX_AGC_ENA_FLAG_DIS;
|
||||
}
|
||||
#if DEBUG
|
||||
pr_info("cmd_id = 0x%04x\n", cmd.cmd_id);
|
||||
pr_info("tx_agc_param_mask = 0x%04x\n", cmd.tx_agc_param_mask);
|
||||
pr_info("tx_agc_enable_flag = 0x%04x\n", cmd.tx_agc_enable_flag);
|
||||
pr_info("static_gain = 0x%04x\n", cmd.static_gain);
|
||||
pr_info("adaptive_gain_flag = 0x%04x\n", cmd.adaptive_gain_flag);
|
||||
pr_info("expander_th = 0x%04x\n", cmd.expander_th);
|
||||
pr_info("expander_slope = 0x%04x\n", cmd.expander_slope);
|
||||
pr_info("compressor_th = 0x%04x\n", cmd.compressor_th);
|
||||
pr_info("compressor_slope = 0x%04x\n", cmd.compressor_slope);
|
||||
pr_info("param_mask = 0x%04x\n", cmd.param_mask);
|
||||
pr_info("aig_attackk = 0x%04x\n", cmd.aig_attackk);
|
||||
pr_info("aig_leak_down = 0x%04x\n", cmd.aig_leak_down);
|
||||
pr_info("aig_leak_up = 0x%04x\n", cmd.aig_leak_up);
|
||||
pr_info("aig_max = 0x%04x\n", cmd.aig_max);
|
||||
pr_info("aig_min = 0x%04x\n", cmd.aig_min);
|
||||
pr_info("aig_releasek = 0x%04x\n", cmd.aig_releasek);
|
||||
pr_info("aig_leakrate_fast = 0x%04x\n", cmd.aig_leakrate_fast);
|
||||
pr_info("aig_leakrate_slow = 0x%04x\n", cmd.aig_leakrate_slow);
|
||||
pr_info("attackk_msw = 0x%04x\n", cmd.attackk_msw);
|
||||
pr_info("attackk_lsw = 0x%04x\n", cmd.attackk_lsw);
|
||||
pr_info("delay = 0x%04x\n", cmd.delay);
|
||||
pr_info("releasek_msw = 0x%04x\n", cmd.releasek_msw);
|
||||
pr_info("releasek_lsw = 0x%04x\n", cmd.releasek_lsw);
|
||||
pr_info("rms_tav = 0x%04x\n", cmd.rms_tav);
|
||||
#endif
|
||||
return audio_send_queue_pre(audio, &cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
static int audio_dsp_set_ns(struct audio_in *audio)
|
||||
{
|
||||
audpreproc_cmd_cfg_ns_params cmd;
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.cmd_id = AUDPREPROC_CMD_CFG_NS_PARAMS;
|
||||
|
||||
if (audio->ns_enable) {
|
||||
/* cmd.ec_mode_new is fixed as 0x0064 when enable from sample code */
|
||||
cmd.ec_mode_new =
|
||||
AUDPREPROC_CMD_EC_MODE_NEW_NS_ENA |
|
||||
AUDPREPROC_CMD_EC_MODE_NEW_HB_ENA |
|
||||
AUDPREPROC_CMD_EC_MODE_NEW_VA_ENA;
|
||||
memcpy(&cmd.dens_gamma_n, &audio->ns.ns_params,
|
||||
sizeof(audio->ns.ns_params));
|
||||
} else {
|
||||
cmd.ec_mode_new =
|
||||
AUDPREPROC_CMD_EC_MODE_NEW_NLMS_DIS |
|
||||
AUDPREPROC_CMD_EC_MODE_NEW_DES_DIS |
|
||||
AUDPREPROC_CMD_EC_MODE_NEW_NS_DIS |
|
||||
AUDPREPROC_CMD_EC_MODE_NEW_CNI_DIS |
|
||||
AUDPREPROC_CMD_EC_MODE_NEW_NLES_DIS |
|
||||
AUDPREPROC_CMD_EC_MODE_NEW_HB_DIS |
|
||||
AUDPREPROC_CMD_EC_MODE_NEW_VA_DIS |
|
||||
AUDPREPROC_CMD_EC_MODE_NEW_PCD_DIS |
|
||||
AUDPREPROC_CMD_EC_MODE_NEW_FEHI_DIS |
|
||||
AUDPREPROC_CMD_EC_MODE_NEW_NEHI_DIS |
|
||||
AUDPREPROC_CMD_EC_MODE_NEW_NLPP_DIS |
|
||||
AUDPREPROC_CMD_EC_MODE_NEW_FNE_DIS |
|
||||
AUDPREPROC_CMD_EC_MODE_NEW_PRENLMS_DIS;
|
||||
}
|
||||
#if DEBUG
|
||||
pr_info("cmd_id = 0x%04x\n", cmd.cmd_id);
|
||||
pr_info("ec_mode_new = 0x%04x\n", cmd.ec_mode_new);
|
||||
pr_info("dens_gamma_n = 0x%04x\n", cmd.dens_gamma_n);
|
||||
pr_info("dens_nfe_block_size = 0x%04x\n", cmd.dens_nfe_block_size);
|
||||
pr_info("dens_limit_ns = 0x%04x\n", cmd.dens_limit_ns);
|
||||
pr_info("dens_limit_ns_d = 0x%04x\n", cmd.dens_limit_ns_d);
|
||||
pr_info("wb_gamma_e = 0x%04x\n", cmd.wb_gamma_e);
|
||||
pr_info("wb_gamma_n = 0x%04x\n", cmd.wb_gamma_n);
|
||||
#endif
|
||||
return audio_send_queue_pre(audio, &cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
static int audio_dsp_set_tx_iir(struct audio_in *audio)
|
||||
{
|
||||
struct audpre_cmd_iir_config_type cmd;
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.cmd_id = AUDPREPROC_CMD_CFG_IIR_TUNING_FILTER_PARAMS;
|
||||
|
||||
if (audio->iir_enable) {
|
||||
cmd.active_flag = AUDPREPROC_CMD_IIR_ACTIVE_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 = AUDPREPROC_CMD_IIR_ACTIVE_FLAG_DIS;
|
||||
}
|
||||
#if DEBUG
|
||||
pr_info("cmd_id = 0x%04x\n", cmd.cmd_id);
|
||||
pr_info("active_flag = 0x%04x\n", cmd.active_flag);
|
||||
#endif
|
||||
return audio_send_queue_pre(audio, &cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
static int audio_in_dsp_enable(struct audio_in *audio, int enable)
|
||||
{
|
||||
audrec_cmd_cfg cmd;
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.cmd_id = AUDREC_CMD_CFG;
|
||||
cmd.type_0 = enable ? AUDREC_CMD_TYPE_0_ENA : AUDREC_CMD_TYPE_0_DIS;
|
||||
cmd.type_0 |= (AUDREC_CMD_TYPE_0_UPDATE | audio->type);
|
||||
cmd.type_1 = 0;
|
||||
|
||||
return audio_send_queue_rec(audio, &cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
static int audio_in_encoder_config(struct audio_in *audio)
|
||||
{
|
||||
audrec_cmd_arec0param_cfg cmd;
|
||||
uint16_t *data = (void *) audio->data;
|
||||
unsigned n;
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.cmd_id = AUDREC_CMD_AREC0PARAM_CFG;
|
||||
cmd.ptr_to_extpkt_buffer_msw = audio->phys >> 16;
|
||||
cmd.ptr_to_extpkt_buffer_lsw = audio->phys;
|
||||
cmd.buf_len = FRAME_NUM; /* Both WAV and AAC use 8 frames */
|
||||
cmd.samp_rate_index = audio->samp_rate_index;
|
||||
cmd.stereo_mode = audio->channel_mode; /* 0 for mono, 1 for stereo */
|
||||
|
||||
/* FIXME have no idea why cmd.rec_quality is fixed
|
||||
* as 0x1C00 from sample code
|
||||
*/
|
||||
cmd.rec_quality = 0x1C00;
|
||||
|
||||
/* prepare buffer pointers:
|
||||
* Mono: 1024 samples + 4 halfword header
|
||||
* Stereo: 2048 samples + 4 halfword header
|
||||
* AAC
|
||||
* Mono/Stere: 768 + 4 halfword header
|
||||
*/
|
||||
for (n = 0; n < FRAME_NUM; n++) {
|
||||
audio->in[n].data = data + 4;
|
||||
if (audio->type == AUDREC_CMD_TYPE_0_INDEX_WAV)
|
||||
data += (4 + (audio->channel_mode ? 2048 : 1024));
|
||||
else if (audio->type == AUDREC_CMD_TYPE_0_INDEX_AAC)
|
||||
data += (4 + 768);
|
||||
}
|
||||
|
||||
return audio_send_queue_rec(audio, &cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
static int audio_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt)
|
||||
{
|
||||
audrec_cmd_packet_ext_ptr cmd;
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.cmd_id = AUDREC_CMD_PACKET_EXT_PTR;
|
||||
/* Both WAV and AAC use AUDREC_CMD_TYPE_0 */
|
||||
cmd.type = AUDREC_CMD_TYPE_0;
|
||||
cmd.curr_rec_count_msw = read_cnt >> 16;
|
||||
cmd.curr_rec_count_lsw = read_cnt;
|
||||
|
||||
return audio_send_queue_recbs(audio, &cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
/* ------------------- device --------------------- */
|
||||
|
||||
static void audio_enable_agc(struct audio_in *audio, int enable)
|
||||
{
|
||||
if (audio->agc_enable != enable) {
|
||||
audio->agc_enable = enable;
|
||||
if (audio->running)
|
||||
audio_dsp_set_agc(audio);
|
||||
}
|
||||
}
|
||||
|
||||
static void audio_enable_ns(struct audio_in *audio, int enable)
|
||||
{
|
||||
if (audio->ns_enable != enable) {
|
||||
audio->ns_enable = enable;
|
||||
if (audio->running)
|
||||
audio_dsp_set_ns(audio);
|
||||
}
|
||||
}
|
||||
|
||||
static void audio_enable_tx_iir(struct audio_in *audio, int enable)
|
||||
{
|
||||
if (audio->iir_enable != enable) {
|
||||
audio->iir_enable = enable;
|
||||
if (audio->running)
|
||||
audio_dsp_set_tx_iir(audio);
|
||||
}
|
||||
}
|
||||
|
||||
static void audio_flush(struct audio_in *audio)
|
||||
{
|
||||
int i;
|
||||
|
||||
audio->dsp_cnt = 0;
|
||||
audio->in_head = 0;
|
||||
audio->in_tail = 0;
|
||||
audio->in_count = 0;
|
||||
for (i = 0; i < FRAME_NUM; i++) {
|
||||
audio->in[i].size = 0;
|
||||
audio->in[i].read = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static long audio_in_ioctl(struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct audio_in *audio = file->private_data;
|
||||
int rc;
|
||||
|
||||
if (cmd == AUDIO_GET_STATS) {
|
||||
struct msm_audio_stats stats;
|
||||
stats.byte_count = atomic_read(&audio->in_bytes);
|
||||
if (copy_to_user((void *) arg, &stats, sizeof(stats)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
mutex_lock(&audio->lock);
|
||||
switch (cmd) {
|
||||
case AUDIO_START:
|
||||
rc = audio_in_enable(audio);
|
||||
break;
|
||||
case AUDIO_STOP:
|
||||
rc = audio_in_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 read_lock.
|
||||
* While audio->stopped read threads will always
|
||||
* exit immediately.
|
||||
*/
|
||||
wake_up(&audio->wait);
|
||||
mutex_lock(&audio->read_lock);
|
||||
audio_flush(audio);
|
||||
mutex_unlock(&audio->read_lock);
|
||||
}
|
||||
case AUDIO_SET_CONFIG: {
|
||||
struct msm_audio_config cfg;
|
||||
if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) {
|
||||
rc = -EFAULT;
|
||||
break;
|
||||
}
|
||||
if (cfg.channel_count == 1) {
|
||||
cfg.channel_count = AUDREC_CMD_STEREO_MODE_MONO;
|
||||
} else if (cfg.channel_count == 2) {
|
||||
cfg.channel_count = AUDREC_CMD_STEREO_MODE_STEREO;
|
||||
} else {
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (cfg.type == 0) {
|
||||
cfg.type = AUDREC_CMD_TYPE_0_INDEX_WAV;
|
||||
} else if (cfg.type == 1) {
|
||||
cfg.type = AUDREC_CMD_TYPE_0_INDEX_AAC;
|
||||
} else {
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
audio->samp_rate = convert_samp_rate(cfg.sample_rate);
|
||||
audio->samp_rate_index =
|
||||
convert_dsp_samp_index(cfg.sample_rate);
|
||||
audio->channel_mode = cfg.channel_count;
|
||||
audio->buffer_size =
|
||||
audio->channel_mode ? STEREO_DATA_SIZE
|
||||
: MONO_DATA_SIZE;
|
||||
audio->type = cfg.type;
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
case AUDIO_GET_CONFIG: {
|
||||
struct msm_audio_config cfg;
|
||||
cfg.buffer_size = audio->buffer_size;
|
||||
cfg.buffer_count = FRAME_NUM;
|
||||
cfg.sample_rate = convert_samp_index(audio->samp_rate);
|
||||
if (audio->channel_mode == AUDREC_CMD_STEREO_MODE_MONO)
|
||||
cfg.channel_count = 1;
|
||||
else
|
||||
cfg.channel_count = 2;
|
||||
if (audio->type == AUDREC_CMD_TYPE_0_INDEX_WAV)
|
||||
cfg.type = 0;
|
||||
else
|
||||
cfg.type = 1;
|
||||
cfg.unused[0] = 0;
|
||||
cfg.unused[1] = 0;
|
||||
cfg.unused[2] = 0;
|
||||
if (copy_to_user((void *) arg, &cfg, sizeof(cfg)))
|
||||
rc = -EFAULT;
|
||||
else
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
rc = -EINVAL;
|
||||
}
|
||||
mutex_unlock(&audio->lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static ssize_t audio_in_read(struct file *file,
|
||||
char __user *buf,
|
||||
size_t count, loff_t *pos)
|
||||
{
|
||||
struct audio_in *audio = file->private_data;
|
||||
unsigned long flags;
|
||||
const char __user *start = buf;
|
||||
void *data;
|
||||
uint32_t index;
|
||||
uint32_t size;
|
||||
int rc = 0;
|
||||
|
||||
mutex_lock(&audio->read_lock);
|
||||
while (count > 0) {
|
||||
rc = wait_event_interruptible(
|
||||
audio->wait, (audio->in_count > 0) || audio->stopped);
|
||||
if (rc < 0)
|
||||
break;
|
||||
|
||||
if (audio->stopped) {
|
||||
rc = -EBUSY;
|
||||
break;
|
||||
}
|
||||
|
||||
index = audio->in_tail;
|
||||
data = (uint8_t *) audio->in[index].data;
|
||||
size = audio->in[index].size;
|
||||
if (count >= size) {
|
||||
if (copy_to_user(buf, data, size)) {
|
||||
rc = -EFAULT;
|
||||
break;
|
||||
}
|
||||
spin_lock_irqsave(&audio->dsp_lock, flags);
|
||||
if (index != audio->in_tail) {
|
||||
/* overrun -- data is invalid and we need to retry */
|
||||
spin_unlock_irqrestore(&audio->dsp_lock, flags);
|
||||
continue;
|
||||
}
|
||||
audio->in[index].size = 0;
|
||||
audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1);
|
||||
audio->in_count--;
|
||||
spin_unlock_irqrestore(&audio->dsp_lock, flags);
|
||||
count -= size;
|
||||
buf += size;
|
||||
if (audio->type == AUDREC_CMD_TYPE_0_INDEX_AAC)
|
||||
break;
|
||||
} else {
|
||||
pr_err("audio_in: short read\n");
|
||||
break;
|
||||
}
|
||||
if (audio->type == AUDREC_CMD_TYPE_0_INDEX_AAC)
|
||||
break; /* AAC only read one frame */
|
||||
}
|
||||
mutex_unlock(&audio->read_lock);
|
||||
|
||||
if (buf > start)
|
||||
return buf - start;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static ssize_t audio_in_write(struct file *file,
|
||||
const char __user *buf,
|
||||
size_t count, loff_t *pos)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int audio_in_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct audio_in *audio = file->private_data;
|
||||
|
||||
mutex_lock(&audio->lock);
|
||||
audio_in_disable(audio);
|
||||
audio_flush(audio);
|
||||
msm_adsp_put(audio->audrec);
|
||||
msm_adsp_put(audio->audpre);
|
||||
audio->audrec = NULL;
|
||||
audio->audpre = NULL;
|
||||
audio->opened = 0;
|
||||
mutex_unlock(&audio->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct audio_in the_audio_in;
|
||||
|
||||
static int audio_in_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct audio_in *audio = &the_audio_in;
|
||||
int rc;
|
||||
|
||||
mutex_lock(&audio->lock);
|
||||
if (audio->opened) {
|
||||
rc = -EBUSY;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Settings will be re-config at AUDIO_SET_CONFIG,
|
||||
* but at least we need to have initial config
|
||||
*/
|
||||
audio->samp_rate = RPC_AUD_DEF_SAMPLE_RATE_11025;
|
||||
audio->samp_rate_index = AUDREC_CMD_SAMP_RATE_INDX_11025;
|
||||
audio->channel_mode = AUDREC_CMD_STEREO_MODE_MONO;
|
||||
audio->buffer_size = MONO_DATA_SIZE;
|
||||
audio->type = AUDREC_CMD_TYPE_0_INDEX_WAV;
|
||||
|
||||
rc = audmgr_open(&audio->audmgr);
|
||||
if (rc)
|
||||
goto done;
|
||||
rc = msm_adsp_get("AUDPREPROCTASK", &audio->audpre,
|
||||
&audpre_adsp_ops, audio);
|
||||
if (rc)
|
||||
goto done;
|
||||
rc = msm_adsp_get("AUDRECTASK", &audio->audrec,
|
||||
&audrec_adsp_ops, audio);
|
||||
if (rc)
|
||||
goto done;
|
||||
|
||||
audio->dsp_cnt = 0;
|
||||
audio->stopped = 0;
|
||||
|
||||
audio_flush(audio);
|
||||
|
||||
file->private_data = audio;
|
||||
audio->opened = 1;
|
||||
rc = 0;
|
||||
done:
|
||||
mutex_unlock(&audio->lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static long audpre_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct audio_in *audio = file->private_data;
|
||||
int rc = 0, enable;
|
||||
uint16_t enable_mask;
|
||||
#if DEBUG
|
||||
int i;
|
||||
#endif
|
||||
|
||||
mutex_lock(&audio->lock);
|
||||
switch (cmd) {
|
||||
case AUDIO_ENABLE_AUDPRE: {
|
||||
if (copy_from_user(&enable_mask, (void *) arg,
|
||||
sizeof(enable_mask)))
|
||||
goto out_fault;
|
||||
|
||||
enable = (enable_mask & AGC_ENABLE) ? 1 : 0;
|
||||
audio_enable_agc(audio, enable);
|
||||
enable = (enable_mask & NS_ENABLE) ? 1 : 0;
|
||||
audio_enable_ns(audio, enable);
|
||||
enable = (enable_mask & IIR_ENABLE) ? 1 : 0;
|
||||
audio_enable_tx_iir(audio, enable);
|
||||
break;
|
||||
}
|
||||
case AUDIO_SET_AGC: {
|
||||
if (copy_from_user(&audio->agc, (void *) arg,
|
||||
sizeof(audio->agc)))
|
||||
goto out_fault;
|
||||
#if DEBUG
|
||||
pr_info("set agc\n");
|
||||
for (i = 0; i < AGC_PARAM_SIZE; i++) \
|
||||
pr_info("agc_params[%d] = 0x%04x\n", i,
|
||||
audio->agc.agc_params[i]);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case AUDIO_SET_NS: {
|
||||
if (copy_from_user(&audio->ns, (void *) arg,
|
||||
sizeof(audio->ns)))
|
||||
goto out_fault;
|
||||
#if DEBUG
|
||||
pr_info("set ns\n");
|
||||
for (i = 0; i < NS_PARAM_SIZE; i++) \
|
||||
pr_info("ns_params[%d] = 0x%04x\n",
|
||||
i, audio->ns.ns_params[i]);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case AUDIO_SET_TX_IIR: {
|
||||
if (copy_from_user(&audio->iir, (void *) arg,
|
||||
sizeof(audio->iir)))
|
||||
goto out_fault;
|
||||
#if DEBUG
|
||||
pr_info("set iir\n");
|
||||
pr_info("iir.num_bands = 0x%04x\n", audio->iir.num_bands);
|
||||
for (i = 0; i < IIR_PARAM_SIZE; i++) \
|
||||
pr_info("iir_params[%d] = 0x%04x\n",
|
||||
i, audio->iir.iir_params[i]);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
default:
|
||||
rc = -EINVAL;
|
||||
}
|
||||
|
||||
goto out;
|
||||
|
||||
out_fault:
|
||||
rc = -EFAULT;
|
||||
out:
|
||||
mutex_unlock(&audio->lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int audpre_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct audio_in *audio = &the_audio_in;
|
||||
file->private_data = audio;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct file_operations audio_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = audio_in_open,
|
||||
.release = audio_in_release,
|
||||
.read = audio_in_read,
|
||||
.write = audio_in_write,
|
||||
.unlocked_ioctl = audio_in_ioctl,
|
||||
};
|
||||
|
||||
static struct file_operations audpre_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = audpre_open,
|
||||
.unlocked_ioctl = audpre_ioctl,
|
||||
};
|
||||
|
||||
struct miscdevice audio_in_misc = {
|
||||
.minor = MISC_DYNAMIC_MINOR,
|
||||
.name = "msm_pcm_in",
|
||||
.fops = &audio_fops,
|
||||
};
|
||||
|
||||
struct miscdevice audpre_misc = {
|
||||
.minor = MISC_DYNAMIC_MINOR,
|
||||
.name = "msm_audpre",
|
||||
.fops = &audpre_fops,
|
||||
};
|
||||
|
||||
static int __init audio_in_init(void)
|
||||
{
|
||||
int rc;
|
||||
the_audio_in.data = dma_alloc_coherent(NULL, DMASZ,
|
||||
&the_audio_in.phys, GFP_KERNEL);
|
||||
if (!the_audio_in.data) {
|
||||
printk(KERN_ERR "%s: Unable to allocate DMA buffer\n",
|
||||
__func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
mutex_init(&the_audio_in.lock);
|
||||
mutex_init(&the_audio_in.read_lock);
|
||||
spin_lock_init(&the_audio_in.dsp_lock);
|
||||
init_waitqueue_head(&the_audio_in.wait);
|
||||
rc = misc_register(&audio_in_misc);
|
||||
if (!rc) {
|
||||
rc = misc_register(&audpre_misc);
|
||||
if (rc < 0)
|
||||
misc_deregister(&audio_in_misc);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
device_initcall(audio_in_init);
|
971
drivers/staging/dream/qdsp5/audio_mp3.c
Normal file
971
drivers/staging/dream/qdsp5/audio_mp3.c
Normal file
@ -0,0 +1,971 @@
|
||||
/* arch/arm/mach-msm/qdsp5/audio_mp3.c
|
||||
*
|
||||
* mp3 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/delay.h>
|
||||
|
||||
#include <asm/atomic.h>
|
||||
#include <asm/ioctls.h>
|
||||
#include <mach/msm_adsp.h>
|
||||
|
||||
#include <linux/msm_audio.h>
|
||||
|
||||
#include "audmgr.h"
|
||||
|
||||
#include <mach/qdsp5/qdsp5audppcmdi.h>
|
||||
#include <mach/qdsp5/qdsp5audppmsg.h>
|
||||
#include <mach/qdsp5/qdsp5audplaycmdi.h>
|
||||
#include <mach/qdsp5/qdsp5audplaymsg.h>
|
||||
|
||||
/* for queue ids - should be relative to module number*/
|
||||
#include "adsp.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
#define dprintk(format, arg...) \
|
||||
printk(KERN_DEBUG format, ## arg)
|
||||
#else
|
||||
#define dprintk(format, arg...) do {} while (0)
|
||||
#endif
|
||||
|
||||
/* Size must be power of 2 */
|
||||
#define BUFSZ_MAX 32768
|
||||
#define BUFSZ_MIN 4096
|
||||
#define DMASZ_MAX (BUFSZ_MAX * 2)
|
||||
#define DMASZ_MIN (BUFSZ_MIN * 2)
|
||||
|
||||
#define AUDPLAY_INVALID_READ_PTR_OFFSET 0xFFFF
|
||||
#define AUDDEC_DEC_MP3 2
|
||||
|
||||
#define PCM_BUFSZ_MIN 4800 /* Hold one stereo MP3 frame */
|
||||
#define PCM_BUF_MAX_COUNT 5 /* DSP only accepts 5 buffers at most
|
||||
but support 2 buffers currently */
|
||||
#define ROUTING_MODE_FTRT 1
|
||||
#define ROUTING_MODE_RT 2
|
||||
/* Decoder status received from AUDPPTASK */
|
||||
#define AUDPP_DEC_STATUS_SLEEP 0
|
||||
#define AUDPP_DEC_STATUS_INIT 1
|
||||
#define AUDPP_DEC_STATUS_CFG 2
|
||||
#define AUDPP_DEC_STATUS_PLAY 3
|
||||
|
||||
struct buffer {
|
||||
void *data;
|
||||
unsigned size;
|
||||
unsigned used; /* Input usage actual DSP produced PCM size */
|
||||
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 */
|
||||
unsigned out_dma_sz;
|
||||
|
||||
atomic_t out_bytes;
|
||||
|
||||
struct mutex lock;
|
||||
struct mutex write_lock;
|
||||
wait_queue_head_t write_wait;
|
||||
|
||||
/* Host PCM section */
|
||||
struct buffer in[PCM_BUF_MAX_COUNT];
|
||||
struct mutex read_lock;
|
||||
wait_queue_head_t read_wait; /* Wait queue for read */
|
||||
char *read_data; /* pointer to reader buffer */
|
||||
dma_addr_t read_phys; /* physical address of reader buffer */
|
||||
uint8_t read_next; /* index to input buffers to be read next */
|
||||
uint8_t fill_next; /* index to buffer that DSP should be filling */
|
||||
uint8_t pcm_buf_count; /* number of pcm buffer allocated */
|
||||
/* ---- End of Host PCM section */
|
||||
|
||||
struct msm_adsp_module *audplay;
|
||||
|
||||
/* configuration to use on next enable */
|
||||
uint32_t out_sample_rate;
|
||||
uint32_t out_channel_mode;
|
||||
|
||||
struct audmgr audmgr;
|
||||
|
||||
/* data allocated for various buffers */
|
||||
char *data;
|
||||
dma_addr_t phys;
|
||||
|
||||
int rflush; /* Read flush */
|
||||
int wflush; /* Write flush */
|
||||
int opened;
|
||||
int enabled;
|
||||
int running;
|
||||
int stopped; /* set when stopped, cleared on flush */
|
||||
int pcm_feedback;
|
||||
int buf_refresh;
|
||||
|
||||
int reserved; /* A byte is being reserved */
|
||||
char rsv_byte; /* Handle odd length user data */
|
||||
|
||||
unsigned volume;
|
||||
|
||||
uint16_t dec_id;
|
||||
uint32_t read_ptr_offset;
|
||||
};
|
||||
|
||||
static int auddec_dsp_config(struct audio *audio, int enable);
|
||||
static void audpp_cmd_cfg_adec_params(struct audio *audio);
|
||||
static void audpp_cmd_cfg_routing_mode(struct audio *audio);
|
||||
static void audplay_send_data(struct audio *audio, unsigned needed);
|
||||
static void audplay_config_hostpcm(struct audio *audio);
|
||||
static void audplay_buffer_refresh(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;
|
||||
|
||||
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_PLAYBACK;
|
||||
cfg.codec = RPC_AUD_DEF_CODEC_MP3;
|
||||
cfg.snd_method = RPC_SND_METHOD_MIDI;
|
||||
|
||||
rc = audmgr_enable(&audio->audmgr, &cfg);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
if (msm_adsp_enable(audio->audplay)) {
|
||||
pr_err("audio: msm_adsp_enable(audplay) failed\n");
|
||||
audmgr_disable(&audio->audmgr);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) {
|
||||
pr_err("audio: audpp_enable() failed\n");
|
||||
msm_adsp_disable(audio->audplay);
|
||||
audmgr_disable(&audio->audmgr);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
audio->enabled = 1;
|
||||
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;
|
||||
auddec_dsp_config(audio, 0);
|
||||
wake_up(&audio->write_wait);
|
||||
wake_up(&audio->read_wait);
|
||||
msm_adsp_disable(audio->audplay);
|
||||
audpp_disable(audio->dec_id, audio);
|
||||
audmgr_disable(&audio->audmgr);
|
||||
audio->out_needed = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ------------------- dsp --------------------- */
|
||||
static void audio_update_pcm_buf_entry(struct audio *audio, uint32_t *payload)
|
||||
{
|
||||
uint8_t index;
|
||||
unsigned long flags;
|
||||
|
||||
if (audio->rflush) {
|
||||
audio->buf_refresh = 1;
|
||||
return;
|
||||
}
|
||||
spin_lock_irqsave(&audio->dsp_lock, flags);
|
||||
for (index = 0; index < payload[1]; index++) {
|
||||
if (audio->in[audio->fill_next].addr ==
|
||||
payload[2 + index * 2]) {
|
||||
pr_info("audio_update_pcm_buf_entry: in[%d] ready\n",
|
||||
audio->fill_next);
|
||||
audio->in[audio->fill_next].used =
|
||||
payload[3 + index * 2];
|
||||
if ((++audio->fill_next) == audio->pcm_buf_count)
|
||||
audio->fill_next = 0;
|
||||
|
||||
} else {
|
||||
pr_err
|
||||
("audio_update_pcm_buf_entry: expected=%x ret=%x\n"
|
||||
, audio->in[audio->fill_next].addr,
|
||||
payload[1 + index * 2]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (audio->in[audio->fill_next].used == 0) {
|
||||
audplay_buffer_refresh(audio);
|
||||
} else {
|
||||
pr_info("audio_update_pcm_buf_entry: read cannot keep up\n");
|
||||
audio->buf_refresh = 1;
|
||||
}
|
||||
wake_up(&audio->read_wait);
|
||||
spin_unlock_irqrestore(&audio->dsp_lock, flags);
|
||||
|
||||
}
|
||||
|
||||
static void audplay_dsp_event(void *data, unsigned id, size_t len,
|
||||
void (*getevent) (void *ptr, size_t len))
|
||||
{
|
||||
struct audio *audio = data;
|
||||
uint32_t msg[28];
|
||||
getevent(msg, sizeof(msg));
|
||||
|
||||
dprintk("audplay_dsp_event: msg_id=%x\n", id);
|
||||
|
||||
switch (id) {
|
||||
case AUDPLAY_MSG_DEC_NEEDS_DATA:
|
||||
audplay_send_data(audio, 1);
|
||||
break;
|
||||
|
||||
case AUDPLAY_MSG_BUFFER_UPDATE:
|
||||
audio_update_pcm_buf_entry(audio, msg);
|
||||
break;
|
||||
|
||||
default:
|
||||
pr_err("unexpected message from decoder \n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void audio_dsp_event(void *private, unsigned id, uint16_t *msg)
|
||||
{
|
||||
struct audio *audio = private;
|
||||
|
||||
switch (id) {
|
||||
case AUDPP_MSG_STATUS_MSG:{
|
||||
unsigned status = msg[1];
|
||||
|
||||
switch (status) {
|
||||
case AUDPP_DEC_STATUS_SLEEP:
|
||||
pr_info("decoder status: sleep \n");
|
||||
break;
|
||||
|
||||
case AUDPP_DEC_STATUS_INIT:
|
||||
pr_info("decoder status: init \n");
|
||||
audpp_cmd_cfg_routing_mode(audio);
|
||||
break;
|
||||
|
||||
case AUDPP_DEC_STATUS_CFG:
|
||||
pr_info("decoder status: cfg \n");
|
||||
break;
|
||||
case AUDPP_DEC_STATUS_PLAY:
|
||||
pr_info("decoder status: play \n");
|
||||
if (audio->pcm_feedback) {
|
||||
audplay_config_hostpcm(audio);
|
||||
audplay_buffer_refresh(audio);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pr_err("unknown decoder status \n");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AUDPP_MSG_CFG_MSG:
|
||||
if (msg[0] == AUDPP_MSG_ENA_ENA) {
|
||||
pr_info("audio_dsp_event: CFG_MSG ENABLE\n");
|
||||
auddec_dsp_config(audio, 1);
|
||||
audio->out_needed = 0;
|
||||
audio->running = 1;
|
||||
audpp_set_volume_and_pan(audio->dec_id, audio->volume,
|
||||
0);
|
||||
audpp_avsync(audio->dec_id, 22050);
|
||||
} else if (msg[0] == AUDPP_MSG_ENA_DIS) {
|
||||
pr_info("audio_dsp_event: CFG_MSG DISABLE\n");
|
||||
audpp_avsync(audio->dec_id, 0);
|
||||
audio->running = 0;
|
||||
} else {
|
||||
pr_err("audio_dsp_event: CFG_MSG %d?\n", msg[0]);
|
||||
}
|
||||
break;
|
||||
case AUDPP_MSG_ROUTING_ACK:
|
||||
pr_info("audio_dsp_event: ROUTING_ACK mode=%d\n", msg[1]);
|
||||
audpp_cmd_cfg_adec_params(audio);
|
||||
break;
|
||||
|
||||
case AUDPP_MSG_FLUSH_ACK:
|
||||
dprintk("%s: FLUSH_ACK\n", __func__);
|
||||
audio->wflush = 0;
|
||||
audio->rflush = 0;
|
||||
if (audio->pcm_feedback)
|
||||
audplay_buffer_refresh(audio);
|
||||
break;
|
||||
|
||||
default:
|
||||
pr_err("audio_dsp_event: UNKNOWN (%d)\n", id);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
struct msm_adsp_ops audplay_adsp_ops = {
|
||||
.event = audplay_dsp_event,
|
||||
};
|
||||
|
||||
|
||||
#define audplay_send_queue0(audio, cmd, len) \
|
||||
msm_adsp_write(audio->audplay, QDSP_uPAudPlay0BitStreamCtrlQueue, \
|
||||
cmd, len)
|
||||
|
||||
static int auddec_dsp_config(struct audio *audio, int enable)
|
||||
{
|
||||
audpp_cmd_cfg_dec_type cmd;
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE;
|
||||
if (enable)
|
||||
cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
|
||||
AUDPP_CMD_ENA_DEC_V |
|
||||
AUDDEC_DEC_MP3;
|
||||
else
|
||||
cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
|
||||
AUDPP_CMD_DIS_DEC_V;
|
||||
|
||||
return audpp_send_queue1(&cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
static void audpp_cmd_cfg_adec_params(struct audio *audio)
|
||||
{
|
||||
audpp_cmd_cfg_adec_params_mp3 cmd;
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS;
|
||||
cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_MP3_LEN;
|
||||
cmd.common.dec_id = audio->dec_id;
|
||||
cmd.common.input_sampling_frequency = audio->out_sample_rate;
|
||||
|
||||
audpp_send_queue2(&cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
static void audpp_cmd_cfg_routing_mode(struct audio *audio)
|
||||
{
|
||||
struct audpp_cmd_routing_mode cmd;
|
||||
pr_info("audpp_cmd_cfg_routing_mode()\n");
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.cmd_id = AUDPP_CMD_ROUTING_MODE;
|
||||
cmd.object_number = audio->dec_id;
|
||||
if (audio->pcm_feedback)
|
||||
cmd.routing_mode = ROUTING_MODE_FTRT;
|
||||
else
|
||||
cmd.routing_mode = ROUTING_MODE_RT;
|
||||
|
||||
audpp_send_queue1(&cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
static int audplay_dsp_send_data_avail(struct audio *audio,
|
||||
unsigned idx, unsigned len)
|
||||
{
|
||||
audplay_cmd_bitstream_data_avail cmd;
|
||||
|
||||
cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL;
|
||||
cmd.decoder_id = audio->dec_id;
|
||||
cmd.buf_ptr = audio->out[idx].addr;
|
||||
cmd.buf_size = len/2;
|
||||
cmd.partition_number = 0;
|
||||
return audplay_send_queue0(audio, &cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
static void audplay_buffer_refresh(struct audio *audio)
|
||||
{
|
||||
struct audplay_cmd_buffer_refresh refresh_cmd;
|
||||
|
||||
refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH;
|
||||
refresh_cmd.num_buffers = 1;
|
||||
refresh_cmd.buf0_address = audio->in[audio->fill_next].addr;
|
||||
refresh_cmd.buf0_length = audio->in[audio->fill_next].size -
|
||||
(audio->in[audio->fill_next].size % 576); /* Mp3 frame size */
|
||||
refresh_cmd.buf_read_count = 0;
|
||||
pr_info("audplay_buffer_fresh: buf0_addr=%x buf0_len=%d\n",
|
||||
refresh_cmd.buf0_address, refresh_cmd.buf0_length);
|
||||
(void)audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd));
|
||||
}
|
||||
|
||||
static void audplay_config_hostpcm(struct audio *audio)
|
||||
{
|
||||
struct audplay_cmd_hpcm_buf_cfg cfg_cmd;
|
||||
|
||||
pr_info("audplay_config_hostpcm()\n");
|
||||
cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG;
|
||||
cfg_cmd.max_buffers = 1;
|
||||
cfg_cmd.byte_swap = 0;
|
||||
cfg_cmd.hostpcm_config = (0x8000) | (0x4000);
|
||||
cfg_cmd.feedback_frequency = 1;
|
||||
cfg_cmd.partition_number = 0;
|
||||
(void)audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd));
|
||||
|
||||
}
|
||||
|
||||
static void audplay_send_data(struct audio *audio, unsigned needed)
|
||||
{
|
||||
struct buffer *frame;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&audio->dsp_lock, flags);
|
||||
if (!audio->running)
|
||||
goto done;
|
||||
|
||||
if (audio->wflush) {
|
||||
audio->out_needed = 1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (needed && !audio->wflush) {
|
||||
/* We were called from the callback because the DSP
|
||||
* requested more data. Note that the DSP does want
|
||||
* more data, and if a buffer was in-flight, mark it
|
||||
* as available (since the DSP must now be done with
|
||||
* it).
|
||||
*/
|
||||
audio->out_needed = 1;
|
||||
frame = audio->out + audio->out_tail;
|
||||
if (frame->used == 0xffffffff) {
|
||||
dprintk("frame %d free\n", audio->out_tail);
|
||||
frame->used = 0;
|
||||
audio->out_tail ^= 1;
|
||||
wake_up(&audio->write_wait);
|
||||
}
|
||||
}
|
||||
|
||||
if (audio->out_needed) {
|
||||
/* If the DSP currently wants data and we have a
|
||||
* buffer available, we will send it and reset
|
||||
* the needed flag. We'll mark the buffer as in-flight
|
||||
* so that it won't be recycled until the next buffer
|
||||
* is requested
|
||||
*/
|
||||
|
||||
frame = audio->out + audio->out_tail;
|
||||
if (frame->used) {
|
||||
BUG_ON(frame->used == 0xffffffff);
|
||||
dprintk("frame %d busy\n", audio->out_tail);
|
||||
audplay_dsp_send_data_avail(audio, audio->out_tail,
|
||||
frame->used);
|
||||
frame->used = 0xffffffff;
|
||||
audio->out_needed = 0;
|
||||
}
|
||||
}
|
||||
done:
|
||||
spin_unlock_irqrestore(&audio->dsp_lock, flags);
|
||||
}
|
||||
|
||||
/* ------------------- device --------------------- */
|
||||
|
||||
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->reserved = 0;
|
||||
atomic_set(&audio->out_bytes, 0);
|
||||
}
|
||||
|
||||
static void audio_flush_pcm_buf(struct audio *audio)
|
||||
{
|
||||
uint8_t index;
|
||||
|
||||
for (index = 0; index < PCM_BUF_MAX_COUNT; index++)
|
||||
audio->in[index].used = 0;
|
||||
|
||||
audio->read_next = 0;
|
||||
audio->fill_next = 0;
|
||||
}
|
||||
|
||||
static void audio_ioport_reset(struct audio *audio)
|
||||
{
|
||||
/* Make sure read/write thread are free from
|
||||
* sleep and knowing that system is not able
|
||||
* to process io request at the moment
|
||||
*/
|
||||
wake_up(&audio->write_wait);
|
||||
mutex_lock(&audio->write_lock);
|
||||
audio_flush(audio);
|
||||
mutex_unlock(&audio->write_lock);
|
||||
wake_up(&audio->read_wait);
|
||||
mutex_lock(&audio->read_lock);
|
||||
audio_flush_pcm_buf(audio);
|
||||
mutex_unlock(&audio->read_lock);
|
||||
}
|
||||
|
||||
static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct audio *audio = file->private_data;
|
||||
int rc = 0;
|
||||
|
||||
pr_info("audio_ioctl() cmd = %d\n", cmd);
|
||||
|
||||
if (cmd == AUDIO_GET_STATS) {
|
||||
struct msm_audio_stats stats;
|
||||
stats.byte_count = audpp_avsync_byte_count(audio->dec_id);
|
||||
stats.sample_count = audpp_avsync_sample_count(audio->dec_id);
|
||||
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(audio->dec_id, arg, 0);
|
||||
spin_unlock_irqrestore(&audio->dsp_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
mutex_lock(&audio->lock);
|
||||
switch (cmd) {
|
||||
case AUDIO_START:
|
||||
rc = audio_enable(audio);
|
||||
break;
|
||||
case AUDIO_STOP:
|
||||
rc = audio_disable(audio);
|
||||
audio->stopped = 1;
|
||||
audio_ioport_reset(audio);
|
||||
audio->stopped = 0;
|
||||
break;
|
||||
case AUDIO_FLUSH:
|
||||
dprintk("%s: AUDIO_FLUSH\n", __func__);
|
||||
audio->rflush = 1;
|
||||
audio->wflush = 1;
|
||||
audio_ioport_reset(audio);
|
||||
audio->rflush = 0;
|
||||
audio->wflush = 0;
|
||||
|
||||
if (audio->buf_refresh) {
|
||||
audio->buf_refresh = 0;
|
||||
audplay_buffer_refresh(audio);
|
||||
}
|
||||
break;
|
||||
|
||||
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 = (audio->out_dma_sz >> 1);
|
||||
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;
|
||||
}
|
||||
case AUDIO_GET_PCM_CONFIG:{
|
||||
struct msm_audio_pcm_config config;
|
||||
config.pcm_feedback = 0;
|
||||
config.buffer_count = PCM_BUF_MAX_COUNT;
|
||||
config.buffer_size = PCM_BUFSZ_MIN;
|
||||
if (copy_to_user((void *)arg, &config,
|
||||
sizeof(config)))
|
||||
rc = -EFAULT;
|
||||
else
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
case AUDIO_SET_PCM_CONFIG:{
|
||||
struct msm_audio_pcm_config config;
|
||||
if (copy_from_user
|
||||
(&config, (void *)arg, sizeof(config))) {
|
||||
rc = -EFAULT;
|
||||
break;
|
||||
}
|
||||
if ((config.buffer_count > PCM_BUF_MAX_COUNT) ||
|
||||
(config.buffer_count == 1))
|
||||
config.buffer_count = PCM_BUF_MAX_COUNT;
|
||||
|
||||
if (config.buffer_size < PCM_BUFSZ_MIN)
|
||||
config.buffer_size = PCM_BUFSZ_MIN;
|
||||
|
||||
/* Check if pcm feedback is required */
|
||||
if ((config.pcm_feedback) && (!audio->read_data)) {
|
||||
pr_info("ioctl: allocate PCM buffer %d\n",
|
||||
config.buffer_count *
|
||||
config.buffer_size);
|
||||
audio->read_data =
|
||||
dma_alloc_coherent(NULL,
|
||||
config.buffer_size *
|
||||
config.buffer_count,
|
||||
&audio->read_phys,
|
||||
GFP_KERNEL);
|
||||
if (!audio->read_data) {
|
||||
pr_err("audio_mp3: malloc pcm \
|
||||
buf failed\n");
|
||||
rc = -1;
|
||||
} else {
|
||||
uint8_t index;
|
||||
uint32_t offset = 0;
|
||||
audio->pcm_feedback = 1;
|
||||
audio->buf_refresh = 0;
|
||||
audio->pcm_buf_count =
|
||||
config.buffer_count;
|
||||
audio->read_next = 0;
|
||||
audio->fill_next = 0;
|
||||
|
||||
for (index = 0;
|
||||
index < config.buffer_count;
|
||||
index++) {
|
||||
audio->in[index].data =
|
||||
audio->read_data + offset;
|
||||
audio->in[index].addr =
|
||||
audio->read_phys + offset;
|
||||
audio->in[index].size =
|
||||
config.buffer_size;
|
||||
audio->in[index].used = 0;
|
||||
offset += config.buffer_size;
|
||||
}
|
||||
rc = 0;
|
||||
}
|
||||
} else {
|
||||
rc = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AUDIO_PAUSE:
|
||||
dprintk("%s: AUDIO_PAUSE %ld\n", __func__, arg);
|
||||
rc = audpp_pause(audio->dec_id, (int) arg);
|
||||
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)
|
||||
{
|
||||
struct audio *audio = file->private_data;
|
||||
const char __user *start = buf;
|
||||
int rc = 0;
|
||||
|
||||
if (!audio->pcm_feedback)
|
||||
return 0; /* PCM feedback disabled. Nothing to read */
|
||||
|
||||
mutex_lock(&audio->read_lock);
|
||||
pr_info("audio_read() %d \n", count);
|
||||
while (count > 0) {
|
||||
rc = wait_event_interruptible(audio->read_wait,
|
||||
(audio->in[audio->read_next].
|
||||
used > 0) || (audio->stopped)
|
||||
|| (audio->rflush));
|
||||
|
||||
if (rc < 0)
|
||||
break;
|
||||
|
||||
if (audio->stopped || audio->rflush) {
|
||||
rc = -EBUSY;
|
||||
break;
|
||||
}
|
||||
|
||||
if (count < audio->in[audio->read_next].used) {
|
||||
/* Read must happen in frame boundary. Since
|
||||
* driver does not know frame size, read count
|
||||
* must be greater or equal
|
||||
* to size of PCM samples
|
||||
*/
|
||||
pr_info("audio_read: no partial frame done reading\n");
|
||||
break;
|
||||
} else {
|
||||
pr_info("audio_read: read from in[%d]\n",
|
||||
audio->read_next);
|
||||
if (copy_to_user
|
||||
(buf, audio->in[audio->read_next].data,
|
||||
audio->in[audio->read_next].used)) {
|
||||
pr_err("audio_read: invalid addr %x \n",
|
||||
(unsigned int)buf);
|
||||
rc = -EFAULT;
|
||||
break;
|
||||
}
|
||||
count -= audio->in[audio->read_next].used;
|
||||
buf += audio->in[audio->read_next].used;
|
||||
audio->in[audio->read_next].used = 0;
|
||||
if ((++audio->read_next) == audio->pcm_buf_count)
|
||||
audio->read_next = 0;
|
||||
if (audio->in[audio->read_next].used == 0)
|
||||
break; /* No data ready at this moment
|
||||
* Exit while loop to prevent
|
||||
* output thread sleep too long
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
/* don't feed output buffer to HW decoder during flushing
|
||||
* buffer refresh command will be sent once flush completes
|
||||
* send buf refresh command here can confuse HW decoder
|
||||
*/
|
||||
if (audio->buf_refresh && !audio->rflush) {
|
||||
audio->buf_refresh = 0;
|
||||
pr_info("audio_read: kick start pcm feedback again\n");
|
||||
audplay_buffer_refresh(audio);
|
||||
}
|
||||
|
||||
mutex_unlock(&audio->read_lock);
|
||||
|
||||
if (buf > start)
|
||||
rc = buf - start;
|
||||
|
||||
pr_info("audio_read: read %d bytes\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static ssize_t audio_write(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *pos)
|
||||
{
|
||||
struct audio *audio = file->private_data;
|
||||
const char __user *start = buf;
|
||||
struct buffer *frame;
|
||||
size_t xfer;
|
||||
char *cpy_ptr;
|
||||
int rc = 0;
|
||||
unsigned dsize;
|
||||
|
||||
mutex_lock(&audio->write_lock);
|
||||
while (count > 0) {
|
||||
frame = audio->out + audio->out_head;
|
||||
cpy_ptr = frame->data;
|
||||
dsize = 0;
|
||||
rc = wait_event_interruptible(audio->write_wait,
|
||||
(frame->used == 0)
|
||||
|| (audio->stopped)
|
||||
|| (audio->wflush));
|
||||
if (rc < 0)
|
||||
break;
|
||||
if (audio->stopped || audio->wflush) {
|
||||
rc = -EBUSY;
|
||||
break;
|
||||
}
|
||||
|
||||
if (audio->reserved) {
|
||||
dprintk("%s: append reserved byte %x\n",
|
||||
__func__, audio->rsv_byte);
|
||||
*cpy_ptr = audio->rsv_byte;
|
||||
xfer = (count > (frame->size - 1)) ?
|
||||
frame->size - 1 : count;
|
||||
cpy_ptr++;
|
||||
dsize = 1;
|
||||
audio->reserved = 0;
|
||||
} else
|
||||
xfer = (count > frame->size) ? frame->size : count;
|
||||
|
||||
if (copy_from_user(cpy_ptr, buf, xfer)) {
|
||||
rc = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
dsize += xfer;
|
||||
if (dsize & 1) {
|
||||
audio->rsv_byte = ((char *) frame->data)[dsize - 1];
|
||||
dprintk("%s: odd length buf reserve last byte %x\n",
|
||||
__func__, audio->rsv_byte);
|
||||
audio->reserved = 1;
|
||||
dsize--;
|
||||
}
|
||||
count -= xfer;
|
||||
buf += xfer;
|
||||
|
||||
if (dsize > 0) {
|
||||
audio->out_head ^= 1;
|
||||
frame->used = dsize;
|
||||
audplay_send_data(audio, 0);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&audio->write_lock);
|
||||
if (buf > start)
|
||||
return buf - start;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int audio_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct audio *audio = file->private_data;
|
||||
|
||||
dprintk("audio_release()\n");
|
||||
|
||||
mutex_lock(&audio->lock);
|
||||
audio_disable(audio);
|
||||
audio_flush(audio);
|
||||
audio_flush_pcm_buf(audio);
|
||||
msm_adsp_put(audio->audplay);
|
||||
audio->audplay = NULL;
|
||||
audio->opened = 0;
|
||||
audio->reserved = 0;
|
||||
dma_free_coherent(NULL, audio->out_dma_sz, audio->data, audio->phys);
|
||||
audio->data = NULL;
|
||||
if (audio->read_data != NULL) {
|
||||
dma_free_coherent(NULL,
|
||||
audio->in[0].size * audio->pcm_buf_count,
|
||||
audio->read_data, audio->read_phys);
|
||||
audio->read_data = NULL;
|
||||
}
|
||||
audio->pcm_feedback = 0;
|
||||
mutex_unlock(&audio->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct audio the_mp3_audio;
|
||||
|
||||
static int audio_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct audio *audio = &the_mp3_audio;
|
||||
int rc;
|
||||
unsigned pmem_sz;
|
||||
|
||||
mutex_lock(&audio->lock);
|
||||
|
||||
if (audio->opened) {
|
||||
pr_err("audio: busy\n");
|
||||
rc = -EBUSY;
|
||||
goto done;
|
||||
}
|
||||
|
||||
pmem_sz = DMASZ_MAX;
|
||||
|
||||
while (pmem_sz >= DMASZ_MIN) {
|
||||
audio->data = dma_alloc_coherent(NULL, pmem_sz,
|
||||
&audio->phys, GFP_KERNEL);
|
||||
if (audio->data)
|
||||
break;
|
||||
else if (pmem_sz == DMASZ_MIN) {
|
||||
pr_err("audio: could not allocate DMA buffers\n");
|
||||
rc = -ENOMEM;
|
||||
goto done;
|
||||
} else
|
||||
pmem_sz >>= 1;
|
||||
}
|
||||
|
||||
dprintk("%s: allocated %d bytes DMA buffer\n", __func__, pmem_sz);
|
||||
|
||||
rc = audmgr_open(&audio->audmgr);
|
||||
if (rc) {
|
||||
dma_free_coherent(NULL, pmem_sz,
|
||||
audio->data, audio->phys);
|
||||
goto done;
|
||||
}
|
||||
|
||||
rc = msm_adsp_get("AUDPLAY0TASK", &audio->audplay, &audplay_adsp_ops,
|
||||
audio);
|
||||
if (rc) {
|
||||
pr_err("audio: failed to get audplay0 dsp module\n");
|
||||
dma_free_coherent(NULL, pmem_sz,
|
||||
audio->data, audio->phys);
|
||||
audmgr_close(&audio->audmgr);
|
||||
goto done;
|
||||
}
|
||||
|
||||
audio->out_dma_sz = pmem_sz;
|
||||
pmem_sz >>= 1; /* Shift by 1 to get size of ping pong buffer */
|
||||
|
||||
audio->out_sample_rate = 44100;
|
||||
audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V;
|
||||
audio->dec_id = 0;
|
||||
|
||||
audio->out[0].data = audio->data + 0;
|
||||
audio->out[0].addr = audio->phys + 0;
|
||||
audio->out[0].size = pmem_sz;
|
||||
|
||||
audio->out[1].data = audio->data + pmem_sz;
|
||||
audio->out[1].addr = audio->phys + pmem_sz;
|
||||
audio->out[1].size = pmem_sz;
|
||||
|
||||
audio->volume = 0x2000; /* equal to Q13 number 1.0 Unit Gain */
|
||||
|
||||
audio_flush(audio);
|
||||
|
||||
file->private_data = audio;
|
||||
audio->opened = 1;
|
||||
rc = 0;
|
||||
done:
|
||||
mutex_unlock(&audio->lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static struct file_operations audio_mp3_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = audio_open,
|
||||
.release = audio_release,
|
||||
.read = audio_read,
|
||||
.write = audio_write,
|
||||
.unlocked_ioctl = audio_ioctl,
|
||||
};
|
||||
|
||||
struct miscdevice audio_mp3_misc = {
|
||||
.minor = MISC_DYNAMIC_MINOR,
|
||||
.name = "msm_mp3",
|
||||
.fops = &audio_mp3_fops,
|
||||
};
|
||||
|
||||
static int __init audio_init(void)
|
||||
{
|
||||
mutex_init(&the_mp3_audio.lock);
|
||||
mutex_init(&the_mp3_audio.write_lock);
|
||||
mutex_init(&the_mp3_audio.read_lock);
|
||||
spin_lock_init(&the_mp3_audio.dsp_lock);
|
||||
init_waitqueue_head(&the_mp3_audio.write_wait);
|
||||
init_waitqueue_head(&the_mp3_audio.read_wait);
|
||||
the_mp3_audio.read_data = NULL;
|
||||
return misc_register(&audio_mp3_misc);
|
||||
}
|
||||
|
||||
device_initcall(audio_init);
|
851
drivers/staging/dream/qdsp5/audio_out.c
Normal file
851
drivers/staging/dream/qdsp5/audio_out.c
Normal file
@ -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;
|
||||
}
|
||||
|
||||
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);
|
856
drivers/staging/dream/qdsp5/audio_qcelp.c
Normal file
856
drivers/staging/dream/qdsp5/audio_qcelp.c
Normal file
@ -0,0 +1,856 @@
|
||||
/* arch/arm/mach-msm/qdsp5/audio_qcelp.c
|
||||
*
|
||||
* qcelp 13k audio decoder device
|
||||
*
|
||||
* Copyright (c) 2008 QUALCOMM USA, INC.
|
||||
*
|
||||
* This code is based in part on audio_mp3.c, which is
|
||||
* 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.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you can find it at http://www.fsf.org.
|
||||
*
|
||||
*/
|
||||
|
||||
#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 <asm/ioctls.h>
|
||||
#include <mach/msm_adsp.h>
|
||||
#include <linux/msm_audio.h>
|
||||
#include <mach/qdsp5/qdsp5audppcmdi.h>
|
||||
#include <mach/qdsp5/qdsp5audppmsg.h>
|
||||
#include <mach/qdsp5/qdsp5audplaycmdi.h>
|
||||
#include <mach/qdsp5/qdsp5audplaymsg.h>
|
||||
|
||||
#include "audmgr.h"
|
||||
/* for queue ids - should be relative to module number*/
|
||||
#include "adsp.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
#define dprintk(format, arg...) \
|
||||
printk(KERN_DEBUG format, ## arg)
|
||||
#else
|
||||
#define dprintk(format, arg...) do {} while (0)
|
||||
#endif
|
||||
|
||||
#define BUFSZ 1080 /* QCELP 13K Hold 600ms packet data = 36 * 30 */
|
||||
#define BUF_COUNT 2
|
||||
#define DMASZ (BUFSZ * BUF_COUNT)
|
||||
|
||||
#define PCM_BUFSZ_MIN 1600 /* 100ms worth of data */
|
||||
#define PCM_BUF_MAX_COUNT 5
|
||||
|
||||
#define AUDDEC_DEC_QCELP 9
|
||||
|
||||
#define ROUTING_MODE_FTRT 1
|
||||
#define ROUTING_MODE_RT 2
|
||||
/* Decoder status received from AUDPPTASK */
|
||||
#define AUDPP_DEC_STATUS_SLEEP 0
|
||||
#define AUDPP_DEC_STATUS_INIT 1
|
||||
#define AUDPP_DEC_STATUS_CFG 2
|
||||
#define AUDPP_DEC_STATUS_PLAY 3
|
||||
|
||||
struct buffer {
|
||||
void *data;
|
||||
unsigned size;
|
||||
unsigned used; /* Input usage actual DSP produced PCM size */
|
||||
unsigned addr;
|
||||
};
|
||||
|
||||
struct audio {
|
||||
struct buffer out[BUF_COUNT];
|
||||
|
||||
spinlock_t dsp_lock;
|
||||
|
||||
uint8_t out_head;
|
||||
uint8_t out_tail;
|
||||
uint8_t out_needed; /* number of buffers the dsp is waiting for */
|
||||
|
||||
struct mutex lock;
|
||||
struct mutex write_lock;
|
||||
wait_queue_head_t write_wait;
|
||||
|
||||
/* Host PCM section - START */
|
||||
struct buffer in[PCM_BUF_MAX_COUNT];
|
||||
struct mutex read_lock;
|
||||
wait_queue_head_t read_wait; /* Wait queue for read */
|
||||
char *read_data; /* pointer to reader buffer */
|
||||
dma_addr_t read_phys; /* physical address of reader buffer */
|
||||
uint8_t read_next; /* index to input buffers to be read next */
|
||||
uint8_t fill_next; /* index to buffer that DSP should be filling */
|
||||
uint8_t pcm_buf_count; /* number of pcm buffer allocated */
|
||||
/* Host PCM section - END */
|
||||
|
||||
struct msm_adsp_module *audplay;
|
||||
|
||||
struct audmgr audmgr;
|
||||
|
||||
/* data allocated for various buffers */
|
||||
char *data;
|
||||
dma_addr_t phys;
|
||||
|
||||
uint8_t opened:1;
|
||||
uint8_t enabled:1;
|
||||
uint8_t running:1;
|
||||
uint8_t stopped:1; /* set when stopped, cleared on flush */
|
||||
uint8_t pcm_feedback:1; /* set when non-tunnel mode */
|
||||
uint8_t buf_refresh:1;
|
||||
|
||||
unsigned volume;
|
||||
|
||||
uint16_t dec_id;
|
||||
};
|
||||
|
||||
static struct audio the_qcelp_audio;
|
||||
|
||||
static int auddec_dsp_config(struct audio *audio, int enable);
|
||||
static void audpp_cmd_cfg_adec_params(struct audio *audio);
|
||||
static void audpp_cmd_cfg_routing_mode(struct audio *audio);
|
||||
static void audqcelp_send_data(struct audio *audio, unsigned needed);
|
||||
static void audqcelp_config_hostpcm(struct audio *audio);
|
||||
static void audqcelp_buffer_refresh(struct audio *audio);
|
||||
static void audqcelp_dsp_event(void *private, unsigned id, uint16_t *msg);
|
||||
|
||||
/* must be called with audio->lock held */
|
||||
static int audqcelp_enable(struct audio *audio)
|
||||
{
|
||||
struct audmgr_config cfg;
|
||||
int rc;
|
||||
|
||||
dprintk("audqcelp_enable()\n");
|
||||
|
||||
if (audio->enabled)
|
||||
return 0;
|
||||
|
||||
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_PLAYBACK;
|
||||
cfg.codec = RPC_AUD_DEF_CODEC_13K;
|
||||
cfg.snd_method = RPC_SND_METHOD_MIDI;
|
||||
|
||||
rc = audmgr_enable(&audio->audmgr, &cfg);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
if (msm_adsp_enable(audio->audplay)) {
|
||||
pr_err("audio: msm_adsp_enable(audplay) failed\n");
|
||||
audmgr_disable(&audio->audmgr);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (audpp_enable(audio->dec_id, audqcelp_dsp_event, audio)) {
|
||||
pr_err("audio: audpp_enable() failed\n");
|
||||
msm_adsp_disable(audio->audplay);
|
||||
audmgr_disable(&audio->audmgr);
|
||||
return -ENODEV;
|
||||
}
|
||||
audio->enabled = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* must be called with audio->lock held */
|
||||
static int audqcelp_disable(struct audio *audio)
|
||||
{
|
||||
dprintk("audqcelp_disable()\n");
|
||||
if (audio->enabled) {
|
||||
audio->enabled = 0;
|
||||
auddec_dsp_config(audio, 0);
|
||||
wake_up(&audio->write_wait);
|
||||
wake_up(&audio->read_wait);
|
||||
msm_adsp_disable(audio->audplay);
|
||||
audpp_disable(audio->dec_id, audio);
|
||||
audmgr_disable(&audio->audmgr);
|
||||
audio->out_needed = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ------------------- dsp --------------------- */
|
||||
static void audqcelp_update_pcm_buf_entry(struct audio *audio,
|
||||
uint32_t *payload)
|
||||
{
|
||||
uint8_t index;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&audio->dsp_lock, flags);
|
||||
for (index = 0; index < payload[1]; index++) {
|
||||
if (audio->in[audio->fill_next].addr ==
|
||||
payload[2 + index * 2]) {
|
||||
dprintk("audqcelp_update_pcm_buf_entry: in[%d] ready\n",
|
||||
audio->fill_next);
|
||||
audio->in[audio->fill_next].used =
|
||||
payload[3 + index * 2];
|
||||
if ((++audio->fill_next) == audio->pcm_buf_count)
|
||||
audio->fill_next = 0;
|
||||
} else {
|
||||
pr_err(
|
||||
"audqcelp_update_pcm_buf_entry: expected=%x ret=%x\n",
|
||||
audio->in[audio->fill_next].addr,
|
||||
payload[1 + index * 2]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (audio->in[audio->fill_next].used == 0) {
|
||||
audqcelp_buffer_refresh(audio);
|
||||
} else {
|
||||
dprintk("audqcelp_update_pcm_buf_entry: read cannot keep up\n");
|
||||
audio->buf_refresh = 1;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&audio->dsp_lock, flags);
|
||||
wake_up(&audio->read_wait);
|
||||
}
|
||||
|
||||
static void audplay_dsp_event(void *data, unsigned id, size_t len,
|
||||
void (*getevent) (void *ptr, size_t len))
|
||||
{
|
||||
struct audio *audio = data;
|
||||
uint32_t msg[28];
|
||||
getevent(msg, sizeof(msg));
|
||||
|
||||
dprintk("audplay_dsp_event: msg_id=%x\n", id);
|
||||
|
||||
switch (id) {
|
||||
case AUDPLAY_MSG_DEC_NEEDS_DATA:
|
||||
audqcelp_send_data(audio, 1);
|
||||
break;
|
||||
|
||||
case AUDPLAY_MSG_BUFFER_UPDATE:
|
||||
audqcelp_update_pcm_buf_entry(audio, msg);
|
||||
break;
|
||||
|
||||
default:
|
||||
pr_err("unexpected message from decoder \n");
|
||||
}
|
||||
}
|
||||
|
||||
static void audqcelp_dsp_event(void *private, unsigned id, uint16_t *msg)
|
||||
{
|
||||
struct audio *audio = private;
|
||||
|
||||
switch (id) {
|
||||
case AUDPP_MSG_STATUS_MSG:{
|
||||
unsigned status = msg[1];
|
||||
|
||||
switch (status) {
|
||||
case AUDPP_DEC_STATUS_SLEEP:
|
||||
dprintk("decoder status: sleep \n");
|
||||
break;
|
||||
|
||||
case AUDPP_DEC_STATUS_INIT:
|
||||
dprintk("decoder status: init \n");
|
||||
audpp_cmd_cfg_routing_mode(audio);
|
||||
break;
|
||||
|
||||
case AUDPP_DEC_STATUS_CFG:
|
||||
dprintk("decoder status: cfg \n");
|
||||
break;
|
||||
case AUDPP_DEC_STATUS_PLAY:
|
||||
dprintk("decoder status: play \n");
|
||||
if (audio->pcm_feedback) {
|
||||
audqcelp_config_hostpcm(audio);
|
||||
audqcelp_buffer_refresh(audio);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pr_err("unknown decoder status \n");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AUDPP_MSG_CFG_MSG:
|
||||
if (msg[0] == AUDPP_MSG_ENA_ENA) {
|
||||
dprintk("audqcelp_dsp_event: CFG_MSG ENABLE\n");
|
||||
auddec_dsp_config(audio, 1);
|
||||
audio->out_needed = 0;
|
||||
audio->running = 1;
|
||||
audpp_set_volume_and_pan(audio->dec_id, audio->volume,
|
||||
0);
|
||||
audpp_avsync(audio->dec_id, 22050);
|
||||
} else if (msg[0] == AUDPP_MSG_ENA_DIS) {
|
||||
dprintk("audqcelp_dsp_event: CFG_MSG DISABLE\n");
|
||||
audpp_avsync(audio->dec_id, 0);
|
||||
audio->running = 0;
|
||||
} else {
|
||||
pr_err("audqcelp_dsp_event: CFG_MSG %d?\n", msg[0]);
|
||||
}
|
||||
break;
|
||||
case AUDPP_MSG_ROUTING_ACK:
|
||||
dprintk("audqcelp_dsp_event: ROUTING_ACK mode=%d\n", msg[1]);
|
||||
audpp_cmd_cfg_adec_params(audio);
|
||||
break;
|
||||
default:
|
||||
pr_err("audqcelp_dsp_event: UNKNOWN (%d)\n", id);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct msm_adsp_ops audplay_adsp_ops_qcelp = {
|
||||
.event = audplay_dsp_event,
|
||||
};
|
||||
|
||||
#define audplay_send_queue0(audio, cmd, len) \
|
||||
msm_adsp_write(audio->audplay, QDSP_uPAudPlay0BitStreamCtrlQueue, \
|
||||
cmd, len)
|
||||
|
||||
static int auddec_dsp_config(struct audio *audio, int enable)
|
||||
{
|
||||
audpp_cmd_cfg_dec_type cmd;
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE;
|
||||
if (enable)
|
||||
cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
|
||||
AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_QCELP;
|
||||
else
|
||||
cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | AUDPP_CMD_DIS_DEC_V;
|
||||
|
||||
return audpp_send_queue1(&cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
static void audpp_cmd_cfg_adec_params(struct audio *audio)
|
||||
{
|
||||
struct audpp_cmd_cfg_adec_params_v13k cmd;
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS;
|
||||
cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_V13K_LEN;
|
||||
cmd.common.dec_id = audio->dec_id;
|
||||
cmd.common.input_sampling_frequency = 8000;
|
||||
cmd.stereo_cfg = AUDPP_CMD_PCM_INTF_MONO_V;
|
||||
|
||||
audpp_send_queue2(&cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
static void audpp_cmd_cfg_routing_mode(struct audio *audio)
|
||||
{
|
||||
struct audpp_cmd_routing_mode cmd;
|
||||
dprintk("audpp_cmd_cfg_routing_mode()\n");
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.cmd_id = AUDPP_CMD_ROUTING_MODE;
|
||||
cmd.object_number = audio->dec_id;
|
||||
if (audio->pcm_feedback)
|
||||
cmd.routing_mode = ROUTING_MODE_FTRT;
|
||||
else
|
||||
cmd.routing_mode = ROUTING_MODE_RT;
|
||||
audpp_send_queue1(&cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
static int audplay_dsp_send_data_avail(struct audio *audio,
|
||||
unsigned idx, unsigned len)
|
||||
{
|
||||
audplay_cmd_bitstream_data_avail cmd;
|
||||
|
||||
cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL;
|
||||
cmd.decoder_id = audio->dec_id;
|
||||
cmd.buf_ptr = audio->out[idx].addr;
|
||||
cmd.buf_size = len / 2;
|
||||
cmd.partition_number = 0;
|
||||
return audplay_send_queue0(audio, &cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
static void audqcelp_buffer_refresh(struct audio *audio)
|
||||
{
|
||||
struct audplay_cmd_buffer_refresh refresh_cmd;
|
||||
|
||||
refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH;
|
||||
refresh_cmd.num_buffers = 1;
|
||||
refresh_cmd.buf0_address = audio->in[audio->fill_next].addr;
|
||||
refresh_cmd.buf0_length = audio->in[audio->fill_next].size;
|
||||
refresh_cmd.buf_read_count = 0;
|
||||
dprintk("audplay_buffer_fresh: buf0_addr=%x buf0_len=%d\n",
|
||||
refresh_cmd.buf0_address, refresh_cmd.buf0_length);
|
||||
|
||||
(void)audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd));
|
||||
}
|
||||
|
||||
static void audqcelp_config_hostpcm(struct audio *audio)
|
||||
{
|
||||
struct audplay_cmd_hpcm_buf_cfg cfg_cmd;
|
||||
|
||||
dprintk("audqcelp_config_hostpcm()\n");
|
||||
cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG;
|
||||
cfg_cmd.max_buffers = audio->pcm_buf_count;
|
||||
cfg_cmd.byte_swap = 0;
|
||||
cfg_cmd.hostpcm_config = (0x8000) | (0x4000);
|
||||
cfg_cmd.feedback_frequency = 1;
|
||||
cfg_cmd.partition_number = 0;
|
||||
|
||||
(void)audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd));
|
||||
}
|
||||
|
||||
static void audqcelp_send_data(struct audio *audio, unsigned needed)
|
||||
{
|
||||
struct buffer *frame;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&audio->dsp_lock, flags);
|
||||
if (!audio->running)
|
||||
goto done;
|
||||
|
||||
if (needed) {
|
||||
/* We were called from the callback because the DSP
|
||||
* requested more data. Note that the DSP does want
|
||||
* more data, and if a buffer was in-flight, mark it
|
||||
* as available (since the DSP must now be done with
|
||||
* it).
|
||||
*/
|
||||
audio->out_needed = 1;
|
||||
frame = audio->out + audio->out_tail;
|
||||
if (frame->used == 0xffffffff) {
|
||||
dprintk("frame %d free\n", audio->out_tail);
|
||||
frame->used = 0;
|
||||
audio->out_tail ^= 1;
|
||||
wake_up(&audio->write_wait);
|
||||
}
|
||||
}
|
||||
|
||||
if (audio->out_needed) {
|
||||
/* If the DSP currently wants data and we have a
|
||||
* buffer available, we will send it and reset
|
||||
* the needed flag. We'll mark the buffer as in-flight
|
||||
* so that it won't be recycled until the next buffer
|
||||
* is requested
|
||||
*/
|
||||
|
||||
frame = audio->out + audio->out_tail;
|
||||
if (frame->used) {
|
||||
BUG_ON(frame->used == 0xffffffff);
|
||||
dprintk("frame %d busy\n", audio->out_tail);
|
||||
audplay_dsp_send_data_avail(audio, audio->out_tail,
|
||||
frame->used);
|
||||
frame->used = 0xffffffff;
|
||||
audio->out_needed = 0;
|
||||
}
|
||||
}
|
||||
done:
|
||||
spin_unlock_irqrestore(&audio->dsp_lock, flags);
|
||||
}
|
||||
|
||||
/* ------------------- device --------------------- */
|
||||
|
||||
static void audqcelp_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 void audqcelp_flush_pcm_buf(struct audio *audio)
|
||||
{
|
||||
uint8_t index;
|
||||
|
||||
for (index = 0; index < PCM_BUF_MAX_COUNT; index++)
|
||||
audio->in[index].used = 0;
|
||||
|
||||
audio->read_next = 0;
|
||||
audio->fill_next = 0;
|
||||
}
|
||||
|
||||
static long audqcelp_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct audio *audio = file->private_data;
|
||||
int rc = 0;
|
||||
|
||||
dprintk("audqcelp_ioctl() cmd = %d\n", cmd);
|
||||
|
||||
if (cmd == AUDIO_GET_STATS) {
|
||||
struct msm_audio_stats stats;
|
||||
stats.byte_count = audpp_avsync_byte_count(audio->dec_id);
|
||||
stats.sample_count = audpp_avsync_sample_count(audio->dec_id);
|
||||
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(audio->dec_id, arg, 0);
|
||||
spin_unlock_irqrestore(&audio->dsp_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
mutex_lock(&audio->lock);
|
||||
switch (cmd) {
|
||||
case AUDIO_START:
|
||||
rc = audqcelp_enable(audio);
|
||||
break;
|
||||
case AUDIO_STOP:
|
||||
rc = audqcelp_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->write_wait);
|
||||
mutex_lock(&audio->write_lock);
|
||||
audqcelp_flush(audio);
|
||||
mutex_unlock(&audio->write_lock);
|
||||
wake_up(&audio->read_wait);
|
||||
mutex_lock(&audio->read_lock);
|
||||
audqcelp_flush_pcm_buf(audio);
|
||||
mutex_unlock(&audio->read_lock);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AUDIO_SET_CONFIG:
|
||||
dprintk("AUDIO_SET_CONFIG not applicable \n");
|
||||
break;
|
||||
case AUDIO_GET_CONFIG:{
|
||||
struct msm_audio_config config;
|
||||
config.buffer_size = BUFSZ;
|
||||
config.buffer_count = BUF_COUNT;
|
||||
config.sample_rate = 8000;
|
||||
config.channel_count = 1;
|
||||
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;
|
||||
}
|
||||
case AUDIO_GET_PCM_CONFIG:{
|
||||
struct msm_audio_pcm_config config;
|
||||
|
||||
config.pcm_feedback = 0;
|
||||
config.buffer_count = PCM_BUF_MAX_COUNT;
|
||||
config.buffer_size = PCM_BUFSZ_MIN;
|
||||
if (copy_to_user((void *)arg, &config,
|
||||
sizeof(config)))
|
||||
rc = -EFAULT;
|
||||
else
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
case AUDIO_SET_PCM_CONFIG:{
|
||||
struct msm_audio_pcm_config config;
|
||||
|
||||
if (copy_from_user(&config, (void *)arg,
|
||||
sizeof(config))) {
|
||||
rc = -EFAULT;
|
||||
break;
|
||||
}
|
||||
if ((config.buffer_count > PCM_BUF_MAX_COUNT) ||
|
||||
(config.buffer_count == 1))
|
||||
config.buffer_count = PCM_BUF_MAX_COUNT;
|
||||
|
||||
if (config.buffer_size < PCM_BUFSZ_MIN)
|
||||
config.buffer_size = PCM_BUFSZ_MIN;
|
||||
|
||||
/* Check if pcm feedback is required */
|
||||
if ((config.pcm_feedback) && (!audio->read_data)) {
|
||||
dprintk(
|
||||
"audqcelp_ioctl: allocate PCM buf %d\n",
|
||||
config.buffer_count * config.buffer_size);
|
||||
audio->read_data = dma_alloc_coherent(NULL,
|
||||
config.buffer_size * config.buffer_count,
|
||||
&audio->read_phys, GFP_KERNEL);
|
||||
if (!audio->read_data) {
|
||||
pr_err(
|
||||
"audqcelp_ioctl: no mem for pcm buf\n"
|
||||
);
|
||||
rc = -ENOMEM;
|
||||
} else {
|
||||
uint8_t index;
|
||||
uint32_t offset = 0;
|
||||
|
||||
audio->pcm_feedback = 1;
|
||||
audio->buf_refresh = 0;
|
||||
audio->pcm_buf_count =
|
||||
config.buffer_count;
|
||||
audio->read_next = 0;
|
||||
audio->fill_next = 0;
|
||||
|
||||
for (index = 0;
|
||||
index < config.buffer_count; index++) {
|
||||
audio->in[index].data =
|
||||
audio->read_data + offset;
|
||||
audio->in[index].addr =
|
||||
audio->read_phys + offset;
|
||||
audio->in[index].size =
|
||||
config.buffer_size;
|
||||
audio->in[index].used = 0;
|
||||
offset += config.buffer_size;
|
||||
}
|
||||
rc = 0;
|
||||
}
|
||||
} else {
|
||||
rc = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AUDIO_PAUSE:
|
||||
dprintk("%s: AUDIO_PAUSE %ld\n", __func__, arg);
|
||||
rc = audpp_pause(audio->dec_id, (int) arg);
|
||||
break;
|
||||
default:
|
||||
rc = -EINVAL;
|
||||
}
|
||||
mutex_unlock(&audio->lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static ssize_t audqcelp_read(struct file *file, char __user *buf, size_t count,
|
||||
loff_t *pos)
|
||||
{
|
||||
struct audio *audio = file->private_data;
|
||||
const char __user *start = buf;
|
||||
int rc = 0;
|
||||
|
||||
if (!audio->pcm_feedback)
|
||||
return 0; /* PCM feedback is not enabled. Nothing to read */
|
||||
|
||||
mutex_lock(&audio->read_lock);
|
||||
dprintk("audqcelp_read() %d \n", count);
|
||||
while (count > 0) {
|
||||
rc = wait_event_interruptible(audio->read_wait,
|
||||
(audio->in[audio->read_next].used > 0) ||
|
||||
(audio->stopped));
|
||||
if (rc < 0)
|
||||
break;
|
||||
|
||||
if (audio->stopped) {
|
||||
rc = -EBUSY;
|
||||
break;
|
||||
}
|
||||
|
||||
if (count < audio->in[audio->read_next].used) {
|
||||
/* Read must happen in frame boundary. Since driver does
|
||||
not know frame size, read count must be greater or equal
|
||||
to size of PCM samples */
|
||||
dprintk("audqcelp_read:read stop - partial frame\n");
|
||||
break;
|
||||
} else {
|
||||
dprintk("audqcelp_read: read from in[%d]\n",
|
||||
audio->read_next);
|
||||
if (copy_to_user(buf,
|
||||
audio->in[audio->read_next].data,
|
||||
audio->in[audio->read_next].used)) {
|
||||
pr_err("audqcelp_read: invalid addr %x \n",
|
||||
(unsigned int)buf);
|
||||
rc = -EFAULT;
|
||||
break;
|
||||
}
|
||||
count -= audio->in[audio->read_next].used;
|
||||
buf += audio->in[audio->read_next].used;
|
||||
audio->in[audio->read_next].used = 0;
|
||||
if ((++audio->read_next) == audio->pcm_buf_count)
|
||||
audio->read_next = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (audio->buf_refresh) {
|
||||
audio->buf_refresh = 0;
|
||||
dprintk("audqcelp_read: kick start pcm feedback again\n");
|
||||
audqcelp_buffer_refresh(audio);
|
||||
}
|
||||
|
||||
mutex_unlock(&audio->read_lock);
|
||||
|
||||
if (buf > start)
|
||||
rc = buf - start;
|
||||
|
||||
dprintk("audqcelp_read: read %d bytes\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static ssize_t audqcelp_write(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *pos)
|
||||
{
|
||||
struct audio *audio = file->private_data;
|
||||
const char __user *start = buf;
|
||||
struct buffer *frame;
|
||||
size_t xfer;
|
||||
int rc = 0;
|
||||
|
||||
if (count & 1)
|
||||
return -EINVAL;
|
||||
dprintk("audqcelp_write() \n");
|
||||
mutex_lock(&audio->write_lock);
|
||||
while (count > 0) {
|
||||
frame = audio->out + audio->out_head;
|
||||
rc = wait_event_interruptible(audio->write_wait,
|
||||
(frame->used == 0)
|
||||
|| (audio->stopped));
|
||||
dprintk("audqcelp_write() buffer available\n");
|
||||
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;
|
||||
|
||||
audqcelp_send_data(audio, 0);
|
||||
|
||||
}
|
||||
mutex_unlock(&audio->write_lock);
|
||||
if (buf > start)
|
||||
return buf - start;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int audqcelp_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct audio *audio = file->private_data;
|
||||
|
||||
dprintk("audqcelp_release()\n");
|
||||
|
||||
mutex_lock(&audio->lock);
|
||||
audqcelp_disable(audio);
|
||||
audqcelp_flush(audio);
|
||||
audqcelp_flush_pcm_buf(audio);
|
||||
msm_adsp_put(audio->audplay);
|
||||
audio->audplay = NULL;
|
||||
audio->opened = 0;
|
||||
if (audio->data)
|
||||
dma_free_coherent(NULL, DMASZ, audio->data, audio->phys);
|
||||
audio->data = NULL;
|
||||
if (audio->read_data) {
|
||||
dma_free_coherent(NULL,
|
||||
audio->in[0].size * audio->pcm_buf_count,
|
||||
audio->read_data, audio->read_phys);
|
||||
audio->read_data = NULL;
|
||||
}
|
||||
audio->pcm_feedback = 0;
|
||||
mutex_unlock(&audio->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int audqcelp_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct audio *audio = &the_qcelp_audio;
|
||||
int rc;
|
||||
|
||||
mutex_lock(&audio->lock);
|
||||
|
||||
if (audio->opened) {
|
||||
pr_err("audio: busy\n");
|
||||
rc = -EBUSY;
|
||||
goto done;
|
||||
}
|
||||
|
||||
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 err;
|
||||
|
||||
rc = msm_adsp_get("AUDPLAY0TASK", &audio->audplay,
|
||||
&audplay_adsp_ops_qcelp, audio);
|
||||
if (rc) {
|
||||
pr_err("audio: failed to get audplay0 dsp module\n");
|
||||
audmgr_close(&audio->audmgr);
|
||||
goto err;
|
||||
}
|
||||
|
||||
audio->dec_id = 0;
|
||||
|
||||
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; /* Q13 1.0 */
|
||||
|
||||
audqcelp_flush(audio);
|
||||
|
||||
file->private_data = audio;
|
||||
audio->opened = 1;
|
||||
rc = 0;
|
||||
done:
|
||||
mutex_unlock(&audio->lock);
|
||||
return rc;
|
||||
err:
|
||||
dma_free_coherent(NULL, DMASZ, audio->data, audio->phys);
|
||||
mutex_unlock(&audio->lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static struct file_operations audio_qcelp_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = audqcelp_open,
|
||||
.release = audqcelp_release,
|
||||
.read = audqcelp_read,
|
||||
.write = audqcelp_write,
|
||||
.unlocked_ioctl = audqcelp_ioctl,
|
||||
};
|
||||
|
||||
struct miscdevice audio_qcelp_misc = {
|
||||
.minor = MISC_DYNAMIC_MINOR,
|
||||
.name = "msm_qcelp",
|
||||
.fops = &audio_qcelp_fops,
|
||||
};
|
||||
|
||||
static int __init audqcelp_init(void)
|
||||
{
|
||||
mutex_init(&the_qcelp_audio.lock);
|
||||
mutex_init(&the_qcelp_audio.write_lock);
|
||||
mutex_init(&the_qcelp_audio.read_lock);
|
||||
spin_lock_init(&the_qcelp_audio.dsp_lock);
|
||||
init_waitqueue_head(&the_qcelp_audio.write_wait);
|
||||
init_waitqueue_head(&the_qcelp_audio.read_wait);
|
||||
the_qcelp_audio.read_data = NULL;
|
||||
return misc_register(&audio_qcelp_misc);
|
||||
}
|
||||
|
||||
static void __exit audqcelp_exit(void)
|
||||
{
|
||||
misc_deregister(&audio_qcelp_misc);
|
||||
}
|
||||
|
||||
module_init(audqcelp_init);
|
||||
module_exit(audqcelp_exit);
|
||||
|
||||
MODULE_DESCRIPTION("MSM QCELP 13K driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("QUALCOMM");
|
313
drivers/staging/dream/qdsp5/audmgr.c
Normal file
313
drivers/staging/dream/qdsp5/audmgr.c
Normal file
@ -0,0 +1,313 @@
|
||||
/* arch/arm/mach-msm/qdsp5/audmgr.c
|
||||
*
|
||||
* interface to "audmgr" service on the baseband cpu
|
||||
*
|
||||
* Copyright (C) 2008 Google, Inc.
|
||||
*
|
||||
* 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/uaccess.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
#include <asm/atomic.h>
|
||||
#include <mach/msm_rpcrouter.h>
|
||||
|
||||
#include "audmgr.h"
|
||||
|
||||
#define STATE_CLOSED 0
|
||||
#define STATE_DISABLED 1
|
||||
#define STATE_ENABLING 2
|
||||
#define STATE_ENABLED 3
|
||||
#define STATE_DISABLING 4
|
||||
#define STATE_ERROR 5
|
||||
|
||||
static void rpc_ack(struct msm_rpc_endpoint *ept, uint32_t xid)
|
||||
{
|
||||
uint32_t rep[6];
|
||||
|
||||
rep[0] = cpu_to_be32(xid);
|
||||
rep[1] = cpu_to_be32(1);
|
||||
rep[2] = cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED);
|
||||
rep[3] = cpu_to_be32(RPC_ACCEPTSTAT_SUCCESS);
|
||||
rep[4] = 0;
|
||||
rep[5] = 0;
|
||||
|
||||
msm_rpc_write(ept, rep, sizeof(rep));
|
||||
}
|
||||
|
||||
static void process_audmgr_callback(struct audmgr *am,
|
||||
struct rpc_audmgr_cb_func_ptr *args,
|
||||
int len)
|
||||
{
|
||||
if (len < (sizeof(uint32_t) * 3))
|
||||
return;
|
||||
if (be32_to_cpu(args->set_to_one) != 1)
|
||||
return;
|
||||
|
||||
switch (be32_to_cpu(args->status)) {
|
||||
case RPC_AUDMGR_STATUS_READY:
|
||||
if (len < sizeof(uint32_t) * 4)
|
||||
break;
|
||||
am->handle = be32_to_cpu(args->u.handle);
|
||||
pr_info("audmgr: rpc READY handle=0x%08x\n", am->handle);
|
||||
break;
|
||||
case RPC_AUDMGR_STATUS_CODEC_CONFIG: {
|
||||
uint32_t volume;
|
||||
if (len < sizeof(uint32_t) * 4)
|
||||
break;
|
||||
volume = be32_to_cpu(args->u.volume);
|
||||
pr_info("audmgr: rpc CODEC_CONFIG volume=0x%08x\n", volume);
|
||||
am->state = STATE_ENABLED;
|
||||
wake_up(&am->wait);
|
||||
break;
|
||||
}
|
||||
case RPC_AUDMGR_STATUS_PENDING:
|
||||
pr_err("audmgr: PENDING?\n");
|
||||
break;
|
||||
case RPC_AUDMGR_STATUS_SUSPEND:
|
||||
pr_err("audmgr: SUSPEND?\n");
|
||||
break;
|
||||
case RPC_AUDMGR_STATUS_FAILURE:
|
||||
pr_err("audmgr: FAILURE\n");
|
||||
break;
|
||||
case RPC_AUDMGR_STATUS_VOLUME_CHANGE:
|
||||
pr_err("audmgr: VOLUME_CHANGE?\n");
|
||||
break;
|
||||
case RPC_AUDMGR_STATUS_DISABLED:
|
||||
pr_err("audmgr: DISABLED\n");
|
||||
am->state = STATE_DISABLED;
|
||||
wake_up(&am->wait);
|
||||
break;
|
||||
case RPC_AUDMGR_STATUS_ERROR:
|
||||
pr_err("audmgr: ERROR?\n");
|
||||
am->state = STATE_ERROR;
|
||||
wake_up(&am->wait);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void process_rpc_request(uint32_t proc, uint32_t xid,
|
||||
void *data, int len, void *private)
|
||||
{
|
||||
struct audmgr *am = private;
|
||||
uint32_t *x = data;
|
||||
|
||||
if (0) {
|
||||
int n = len / 4;
|
||||
pr_info("rpc_call proc %d:", proc);
|
||||
while (n--)
|
||||
printk(" %08x", be32_to_cpu(*x++));
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
if (proc == AUDMGR_CB_FUNC_PTR)
|
||||
process_audmgr_callback(am, data, len);
|
||||
else
|
||||
pr_err("audmgr: unknown rpc proc %d\n", proc);
|
||||
rpc_ack(am->ept, xid);
|
||||
}
|
||||
|
||||
#define RPC_TYPE_REQUEST 0
|
||||
#define RPC_TYPE_REPLY 1
|
||||
|
||||
#define RPC_VERSION 2
|
||||
|
||||
#define RPC_COMMON_HDR_SZ (sizeof(uint32_t) * 2)
|
||||
#define RPC_REQUEST_HDR_SZ (sizeof(struct rpc_request_hdr))
|
||||
#define RPC_REPLY_HDR_SZ (sizeof(uint32_t) * 3)
|
||||
#define RPC_REPLY_SZ (sizeof(uint32_t) * 6)
|
||||
|
||||
static int audmgr_rpc_thread(void *data)
|
||||
{
|
||||
struct audmgr *am = data;
|
||||
struct rpc_request_hdr *hdr = NULL;
|
||||
uint32_t type;
|
||||
int len;
|
||||
|
||||
pr_info("audmgr_rpc_thread() start\n");
|
||||
|
||||
while (!kthread_should_stop()) {
|
||||
if (hdr) {
|
||||
kfree(hdr);
|
||||
hdr = NULL;
|
||||
}
|
||||
len = msm_rpc_read(am->ept, (void **) &hdr, -1, -1);
|
||||
if (len < 0) {
|
||||
pr_err("audmgr: rpc read failed (%d)\n", len);
|
||||
break;
|
||||
}
|
||||
if (len < RPC_COMMON_HDR_SZ)
|
||||
continue;
|
||||
|
||||
type = be32_to_cpu(hdr->type);
|
||||
if (type == RPC_TYPE_REPLY) {
|
||||
struct rpc_reply_hdr *rep = (void *) hdr;
|
||||
uint32_t status;
|
||||
if (len < RPC_REPLY_HDR_SZ)
|
||||
continue;
|
||||
status = be32_to_cpu(rep->reply_stat);
|
||||
if (status == RPCMSG_REPLYSTAT_ACCEPTED) {
|
||||
status = be32_to_cpu(rep->data.acc_hdr.accept_stat);
|
||||
pr_info("audmgr: rpc_reply status %d\n", status);
|
||||
} else {
|
||||
pr_info("audmgr: rpc_reply denied!\n");
|
||||
}
|
||||
/* process reply */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (len < RPC_REQUEST_HDR_SZ)
|
||||
continue;
|
||||
|
||||
process_rpc_request(be32_to_cpu(hdr->procedure),
|
||||
be32_to_cpu(hdr->xid),
|
||||
(void *) (hdr + 1),
|
||||
len - sizeof(*hdr),
|
||||
data);
|
||||
}
|
||||
pr_info("audmgr_rpc_thread() exit\n");
|
||||
if (hdr) {
|
||||
kfree(hdr);
|
||||
hdr = NULL;
|
||||
}
|
||||
am->task = NULL;
|
||||
wake_up(&am->wait);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct audmgr_enable_msg {
|
||||
struct rpc_request_hdr hdr;
|
||||
struct rpc_audmgr_enable_client_args args;
|
||||
};
|
||||
|
||||
struct audmgr_disable_msg {
|
||||
struct rpc_request_hdr hdr;
|
||||
uint32_t handle;
|
||||
};
|
||||
|
||||
int audmgr_open(struct audmgr *am)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (am->state != STATE_CLOSED)
|
||||
return 0;
|
||||
|
||||
am->ept = msm_rpc_connect(AUDMGR_PROG,
|
||||
AUDMGR_VERS,
|
||||
MSM_RPC_UNINTERRUPTIBLE);
|
||||
|
||||
init_waitqueue_head(&am->wait);
|
||||
|
||||
if (IS_ERR(am->ept)) {
|
||||
rc = PTR_ERR(am->ept);
|
||||
am->ept = NULL;
|
||||
pr_err("audmgr: failed to connect to audmgr svc\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
am->task = kthread_run(audmgr_rpc_thread, am, "audmgr_rpc");
|
||||
if (IS_ERR(am->task)) {
|
||||
rc = PTR_ERR(am->task);
|
||||
am->task = NULL;
|
||||
msm_rpc_close(am->ept);
|
||||
am->ept = NULL;
|
||||
return rc;
|
||||
}
|
||||
|
||||
am->state = STATE_DISABLED;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(audmgr_open);
|
||||
|
||||
int audmgr_close(struct audmgr *am)
|
||||
{
|
||||
return -EBUSY;
|
||||
}
|
||||
EXPORT_SYMBOL(audmgr_close);
|
||||
|
||||
int audmgr_enable(struct audmgr *am, struct audmgr_config *cfg)
|
||||
{
|
||||
struct audmgr_enable_msg msg;
|
||||
int rc;
|
||||
|
||||
if (am->state == STATE_ENABLED)
|
||||
return 0;
|
||||
|
||||
if (am->state == STATE_DISABLING)
|
||||
pr_err("audmgr: state is DISABLING in enable?\n");
|
||||
am->state = STATE_ENABLING;
|
||||
|
||||
msg.args.set_to_one = cpu_to_be32(1);
|
||||
msg.args.tx_sample_rate = cpu_to_be32(cfg->tx_rate);
|
||||
msg.args.rx_sample_rate = cpu_to_be32(cfg->rx_rate);
|
||||
msg.args.def_method = cpu_to_be32(cfg->def_method);
|
||||
msg.args.codec_type = cpu_to_be32(cfg->codec);
|
||||
msg.args.snd_method = cpu_to_be32(cfg->snd_method);
|
||||
msg.args.cb_func = cpu_to_be32(0x11111111);
|
||||
msg.args.client_data = cpu_to_be32(0x11223344);
|
||||
|
||||
msm_rpc_setup_req(&msg.hdr, AUDMGR_PROG, msm_rpc_get_vers(am->ept),
|
||||
AUDMGR_ENABLE_CLIENT);
|
||||
|
||||
rc = msm_rpc_write(am->ept, &msg, sizeof(msg));
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
rc = wait_event_timeout(am->wait, am->state != STATE_ENABLING, 15 * HZ);
|
||||
if (rc == 0) {
|
||||
pr_err("audmgr_enable: ARM9 did not reply to RPC am->state = %d\n", am->state);
|
||||
BUG();
|
||||
}
|
||||
if (am->state == STATE_ENABLED)
|
||||
return 0;
|
||||
|
||||
pr_err("audmgr: unexpected state %d while enabling?!\n", am->state);
|
||||
return -ENODEV;
|
||||
}
|
||||
EXPORT_SYMBOL(audmgr_enable);
|
||||
|
||||
int audmgr_disable(struct audmgr *am)
|
||||
{
|
||||
struct audmgr_disable_msg msg;
|
||||
int rc;
|
||||
|
||||
if (am->state == STATE_DISABLED)
|
||||
return 0;
|
||||
|
||||
msm_rpc_setup_req(&msg.hdr, AUDMGR_PROG, msm_rpc_get_vers(am->ept),
|
||||
AUDMGR_DISABLE_CLIENT);
|
||||
msg.handle = cpu_to_be32(am->handle);
|
||||
|
||||
am->state = STATE_DISABLING;
|
||||
|
||||
rc = msm_rpc_write(am->ept, &msg, sizeof(msg));
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
rc = wait_event_timeout(am->wait, am->state != STATE_DISABLING, 15 * HZ);
|
||||
if (rc == 0) {
|
||||
pr_err("audmgr_disable: ARM9 did not reply to RPC am->state = %d\n", am->state);
|
||||
BUG();
|
||||
}
|
||||
|
||||
if (am->state == STATE_DISABLED)
|
||||
return 0;
|
||||
|
||||
pr_err("audmgr: unexpected state %d while disabling?!\n", am->state);
|
||||
return -ENODEV;
|
||||
}
|
||||
EXPORT_SYMBOL(audmgr_disable);
|
215
drivers/staging/dream/qdsp5/audmgr.h
Normal file
215
drivers/staging/dream/qdsp5/audmgr.h
Normal file
@ -0,0 +1,215 @@
|
||||
/* arch/arm/mach-msm/qdsp5/audmgr.h
|
||||
*
|
||||
* Copyright 2008 (c) QUALCOMM Incorporated.
|
||||
* Copyright (C) 2008 Google, Inc.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _ARCH_ARM_MACH_MSM_AUDMGR_H
|
||||
#define _ARCH_ARM_MACH_MSM_AUDMGR_H
|
||||
|
||||
#if CONFIG_MSM_AMSS_VERSION==6350
|
||||
#include "audmgr_new.h"
|
||||
#else
|
||||
|
||||
enum rpc_aud_def_sample_rate_type {
|
||||
RPC_AUD_DEF_SAMPLE_RATE_NONE,
|
||||
RPC_AUD_DEF_SAMPLE_RATE_8000,
|
||||
RPC_AUD_DEF_SAMPLE_RATE_11025,
|
||||
RPC_AUD_DEF_SAMPLE_RATE_12000,
|
||||
RPC_AUD_DEF_SAMPLE_RATE_16000,
|
||||
RPC_AUD_DEF_SAMPLE_RATE_22050,
|
||||
RPC_AUD_DEF_SAMPLE_RATE_24000,
|
||||
RPC_AUD_DEF_SAMPLE_RATE_32000,
|
||||
RPC_AUD_DEF_SAMPLE_RATE_44100,
|
||||
RPC_AUD_DEF_SAMPLE_RATE_48000,
|
||||
RPC_AUD_DEF_SAMPLE_RATE_MAX,
|
||||
};
|
||||
|
||||
enum rpc_aud_def_method_type {
|
||||
RPC_AUD_DEF_METHOD_NONE,
|
||||
RPC_AUD_DEF_METHOD_KEY_BEEP,
|
||||
RPC_AUD_DEF_METHOD_PLAYBACK,
|
||||
RPC_AUD_DEF_METHOD_VOICE,
|
||||
RPC_AUD_DEF_METHOD_RECORD,
|
||||
RPC_AUD_DEF_METHOD_HOST_PCM,
|
||||
RPC_AUD_DEF_METHOD_MIDI_OUT,
|
||||
RPC_AUD_DEF_METHOD_RECORD_SBC,
|
||||
RPC_AUD_DEF_METHOD_DTMF_RINGER,
|
||||
RPC_AUD_DEF_METHOD_MAX,
|
||||
};
|
||||
|
||||
enum rpc_aud_def_codec_type {
|
||||
RPC_AUD_DEF_CODEC_NONE,
|
||||
RPC_AUD_DEF_CODEC_DTMF,
|
||||
RPC_AUD_DEF_CODEC_MIDI,
|
||||
RPC_AUD_DEF_CODEC_MP3,
|
||||
RPC_AUD_DEF_CODEC_PCM,
|
||||
RPC_AUD_DEF_CODEC_AAC,
|
||||
RPC_AUD_DEF_CODEC_WMA,
|
||||
RPC_AUD_DEF_CODEC_RA,
|
||||
RPC_AUD_DEF_CODEC_ADPCM,
|
||||
RPC_AUD_DEF_CODEC_GAUDIO,
|
||||
RPC_AUD_DEF_CODEC_VOC_EVRC,
|
||||
RPC_AUD_DEF_CODEC_VOC_13K,
|
||||
RPC_AUD_DEF_CODEC_VOC_4GV_NB,
|
||||
RPC_AUD_DEF_CODEC_VOC_AMR,
|
||||
RPC_AUD_DEF_CODEC_VOC_EFR,
|
||||
RPC_AUD_DEF_CODEC_VOC_FR,
|
||||
RPC_AUD_DEF_CODEC_VOC_HR,
|
||||
RPC_AUD_DEF_CODEC_VOC,
|
||||
RPC_AUD_DEF_CODEC_SBC,
|
||||
RPC_AUD_DEF_CODEC_VOC_PCM,
|
||||
RPC_AUD_DEF_CODEC_AMR_WB,
|
||||
RPC_AUD_DEF_CODEC_AMR_WB_PLUS,
|
||||
RPC_AUD_DEF_CODEC_MAX,
|
||||
};
|
||||
|
||||
enum rpc_snd_method_type {
|
||||
RPC_SND_METHOD_VOICE = 0,
|
||||
RPC_SND_METHOD_KEY_BEEP,
|
||||
RPC_SND_METHOD_MESSAGE,
|
||||
RPC_SND_METHOD_RING,
|
||||
RPC_SND_METHOD_MIDI,
|
||||
RPC_SND_METHOD_AUX,
|
||||
RPC_SND_METHOD_MAX,
|
||||
};
|
||||
|
||||
enum rpc_voc_codec_type {
|
||||
RPC_VOC_CODEC_DEFAULT,
|
||||
RPC_VOC_CODEC_ON_CHIP_0 = RPC_VOC_CODEC_DEFAULT,
|
||||
RPC_VOC_CODEC_ON_CHIP_1,
|
||||
RPC_VOC_CODEC_STEREO_HEADSET,
|
||||
RPC_VOC_CODEC_ON_CHIP_AUX,
|
||||
RPC_VOC_CODEC_BT_OFF_BOARD,
|
||||
RPC_VOC_CODEC_BT_A2DP,
|
||||
RPC_VOC_CODEC_OFF_BOARD,
|
||||
RPC_VOC_CODEC_SDAC,
|
||||
RPC_VOC_CODEC_RX_EXT_SDAC_TX_INTERNAL,
|
||||
RPC_VOC_CODEC_IN_STEREO_SADC_OUT_MONO_HANDSET,
|
||||
RPC_VOC_CODEC_IN_STEREO_SADC_OUT_STEREO_HEADSET,
|
||||
RPC_VOC_CODEC_TX_INT_SADC_RX_EXT_AUXPCM,
|
||||
RPC_VOC_CODEC_EXT_STEREO_SADC_OUT_MONO_HANDSET,
|
||||
RPC_VOC_CODEC_EXT_STEREO_SADC_OUT_STEREO_HEADSET,
|
||||
RPC_VOC_CODEC_TTY_ON_CHIP_1,
|
||||
RPC_VOC_CODEC_TTY_OFF_BOARD,
|
||||
RPC_VOC_CODEC_TTY_VCO,
|
||||
RPC_VOC_CODEC_TTY_HCO,
|
||||
RPC_VOC_CODEC_ON_CHIP_0_DUAL_MIC,
|
||||
RPC_VOC_CODEC_MAX,
|
||||
RPC_VOC_CODEC_NONE,
|
||||
};
|
||||
|
||||
enum rpc_audmgr_status_type {
|
||||
RPC_AUDMGR_STATUS_READY,
|
||||
RPC_AUDMGR_STATUS_CODEC_CONFIG,
|
||||
RPC_AUDMGR_STATUS_PENDING,
|
||||
RPC_AUDMGR_STATUS_SUSPEND,
|
||||
RPC_AUDMGR_STATUS_FAILURE,
|
||||
RPC_AUDMGR_STATUS_VOLUME_CHANGE,
|
||||
RPC_AUDMGR_STATUS_DISABLED,
|
||||
RPC_AUDMGR_STATUS_ERROR,
|
||||
};
|
||||
|
||||
struct rpc_audmgr_enable_client_args {
|
||||
uint32_t set_to_one;
|
||||
uint32_t tx_sample_rate;
|
||||
uint32_t rx_sample_rate;
|
||||
uint32_t def_method;
|
||||
uint32_t codec_type;
|
||||
uint32_t snd_method;
|
||||
|
||||
uint32_t cb_func;
|
||||
uint32_t client_data;
|
||||
};
|
||||
|
||||
#define AUDMGR_ENABLE_CLIENT 2
|
||||
#define AUDMGR_DISABLE_CLIENT 3
|
||||
#define AUDMGR_SUSPEND_EVENT_RSP 4
|
||||
#define AUDMGR_REGISTER_OPERATION_LISTENER 5
|
||||
#define AUDMGR_UNREGISTER_OPERATION_LISTENER 6
|
||||
#define AUDMGR_REGISTER_CODEC_LISTENER 7
|
||||
#define AUDMGR_GET_RX_SAMPLE_RATE 8
|
||||
#define AUDMGR_GET_TX_SAMPLE_RATE 9
|
||||
#define AUDMGR_SET_DEVICE_MODE 10
|
||||
|
||||
#if CONFIG_MSM_AMSS_VERSION < 6220
|
||||
#define AUDMGR_PROG_VERS "rs30000013:46255756"
|
||||
#define AUDMGR_PROG 0x30000013
|
||||
#define AUDMGR_VERS 0x46255756
|
||||
#else
|
||||
#define AUDMGR_PROG_VERS "rs30000013:e94e8f0c"
|
||||
#define AUDMGR_PROG 0x30000013
|
||||
#define AUDMGR_VERS 0xe94e8f0c
|
||||
#endif
|
||||
|
||||
struct rpc_audmgr_cb_func_ptr {
|
||||
uint32_t cb_id;
|
||||
uint32_t set_to_one;
|
||||
uint32_t status;
|
||||
union {
|
||||
uint32_t handle;
|
||||
uint32_t volume;
|
||||
|
||||
} u;
|
||||
};
|
||||
|
||||
#define AUDMGR_CB_FUNC_PTR 1
|
||||
#define AUDMGR_OPR_LSTNR_CB_FUNC_PTR 2
|
||||
#define AUDMGR_CODEC_LSTR_FUNC_PTR 3
|
||||
|
||||
#if CONFIG_MSM_AMSS_VERSION < 6220
|
||||
#define AUDMGR_CB_PROG 0x31000013
|
||||
#define AUDMGR_CB_VERS 0x5fa922a9
|
||||
#else
|
||||
#define AUDMGR_CB_PROG 0x31000013
|
||||
#define AUDMGR_CB_VERS 0x21570ba7
|
||||
#endif
|
||||
|
||||
struct audmgr {
|
||||
wait_queue_head_t wait;
|
||||
uint32_t handle;
|
||||
struct msm_rpc_endpoint *ept;
|
||||
struct task_struct *task;
|
||||
int state;
|
||||
};
|
||||
|
||||
struct audmgr_config {
|
||||
uint32_t tx_rate;
|
||||
uint32_t rx_rate;
|
||||
uint32_t def_method;
|
||||
uint32_t codec;
|
||||
uint32_t snd_method;
|
||||
};
|
||||
|
||||
int audmgr_open(struct audmgr *am);
|
||||
int audmgr_close(struct audmgr *am);
|
||||
int audmgr_enable(struct audmgr *am, struct audmgr_config *cfg);
|
||||
int audmgr_disable(struct audmgr *am);
|
||||
|
||||
typedef void (*audpp_event_func)(void *private, unsigned id, uint16_t *msg);
|
||||
|
||||
int audpp_enable(int id, audpp_event_func func, void *private);
|
||||
void audpp_disable(int id, void *private);
|
||||
|
||||
int audpp_send_queue1(void *cmd, unsigned len);
|
||||
int audpp_send_queue2(void *cmd, unsigned len);
|
||||
int audpp_send_queue3(void *cmd, unsigned len);
|
||||
|
||||
int audpp_pause(unsigned id, int pause);
|
||||
int audpp_set_volume_and_pan(unsigned id, unsigned volume, int pan);
|
||||
void audpp_avsync(int id, unsigned rate);
|
||||
unsigned audpp_avsync_sample_count(int id);
|
||||
unsigned audpp_avsync_byte_count(int id);
|
||||
|
||||
#endif
|
||||
#endif
|
213
drivers/staging/dream/qdsp5/audmgr_new.h
Normal file
213
drivers/staging/dream/qdsp5/audmgr_new.h
Normal file
@ -0,0 +1,213 @@
|
||||
/* arch/arm/mach-msm/qdsp5/audmgr.h
|
||||
*
|
||||
* Copyright 2008 (c) QUALCOMM Incorporated.
|
||||
* Copyright (C) 2008 Google, Inc.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _ARCH_ARM_MACH_MSM_AUDMGR_NEW_H
|
||||
#define _ARCH_ARM_MACH_MSM_AUDMGR_NEW_H
|
||||
|
||||
enum rpc_aud_def_sample_rate_type {
|
||||
RPC_AUD_DEF_SAMPLE_RATE_NONE,
|
||||
RPC_AUD_DEF_SAMPLE_RATE_8000,
|
||||
RPC_AUD_DEF_SAMPLE_RATE_11025,
|
||||
RPC_AUD_DEF_SAMPLE_RATE_12000,
|
||||
RPC_AUD_DEF_SAMPLE_RATE_16000,
|
||||
RPC_AUD_DEF_SAMPLE_RATE_22050,
|
||||
RPC_AUD_DEF_SAMPLE_RATE_24000,
|
||||
RPC_AUD_DEF_SAMPLE_RATE_32000,
|
||||
RPC_AUD_DEF_SAMPLE_RATE_44100,
|
||||
RPC_AUD_DEF_SAMPLE_RATE_48000,
|
||||
RPC_AUD_DEF_SAMPLE_RATE_MAX,
|
||||
};
|
||||
|
||||
enum rpc_aud_def_method_type {
|
||||
RPC_AUD_DEF_METHOD_NONE,
|
||||
RPC_AUD_DEF_METHOD_KEY_BEEP,
|
||||
RPC_AUD_DEF_METHOD_PLAYBACK,
|
||||
RPC_AUD_DEF_METHOD_VOICE,
|
||||
RPC_AUD_DEF_METHOD_RECORD,
|
||||
RPC_AUD_DEF_METHOD_HOST_PCM,
|
||||
RPC_AUD_DEF_METHOD_MIDI_OUT,
|
||||
RPC_AUD_DEF_METHOD_RECORD_SBC,
|
||||
RPC_AUD_DEF_METHOD_DTMF_RINGER,
|
||||
RPC_AUD_DEF_METHOD_MAX,
|
||||
};
|
||||
|
||||
enum rpc_aud_def_codec_type {
|
||||
RPC_AUD_DEF_CODEC_NONE,
|
||||
RPC_AUD_DEF_CODEC_DTMF,
|
||||
RPC_AUD_DEF_CODEC_MIDI,
|
||||
RPC_AUD_DEF_CODEC_MP3,
|
||||
RPC_AUD_DEF_CODEC_PCM,
|
||||
RPC_AUD_DEF_CODEC_AAC,
|
||||
RPC_AUD_DEF_CODEC_WMA,
|
||||
RPC_AUD_DEF_CODEC_RA,
|
||||
RPC_AUD_DEF_CODEC_ADPCM,
|
||||
RPC_AUD_DEF_CODEC_GAUDIO,
|
||||
RPC_AUD_DEF_CODEC_VOC_EVRC,
|
||||
RPC_AUD_DEF_CODEC_VOC_13K,
|
||||
RPC_AUD_DEF_CODEC_VOC_4GV_NB,
|
||||
RPC_AUD_DEF_CODEC_VOC_AMR,
|
||||
RPC_AUD_DEF_CODEC_VOC_EFR,
|
||||
RPC_AUD_DEF_CODEC_VOC_FR,
|
||||
RPC_AUD_DEF_CODEC_VOC_HR,
|
||||
RPC_AUD_DEF_CODEC_VOC_CDMA,
|
||||
RPC_AUD_DEF_CODEC_VOC_CDMA_WB,
|
||||
RPC_AUD_DEF_CODEC_VOC_UMTS,
|
||||
RPC_AUD_DEF_CODEC_VOC_UMTS_WB,
|
||||
RPC_AUD_DEF_CODEC_SBC,
|
||||
RPC_AUD_DEF_CODEC_VOC_PCM,
|
||||
RPC_AUD_DEF_CODEC_AMR_WB,
|
||||
RPC_AUD_DEF_CODEC_AMR_WB_PLUS,
|
||||
RPC_AUD_DEF_CODEC_AAC_BSAC,
|
||||
RPC_AUD_DEF_CODEC_MAX,
|
||||
RPC_AUD_DEF_CODEC_AMR_NB,
|
||||
RPC_AUD_DEF_CODEC_13K,
|
||||
RPC_AUD_DEF_CODEC_EVRC,
|
||||
RPC_AUD_DEF_CODEC_MAX_002,
|
||||
};
|
||||
|
||||
enum rpc_snd_method_type {
|
||||
RPC_SND_METHOD_VOICE = 0,
|
||||
RPC_SND_METHOD_KEY_BEEP,
|
||||
RPC_SND_METHOD_MESSAGE,
|
||||
RPC_SND_METHOD_RING,
|
||||
RPC_SND_METHOD_MIDI,
|
||||
RPC_SND_METHOD_AUX,
|
||||
RPC_SND_METHOD_MAX,
|
||||
};
|
||||
|
||||
enum rpc_voc_codec_type {
|
||||
RPC_VOC_CODEC_DEFAULT,
|
||||
RPC_VOC_CODEC_ON_CHIP_0 = RPC_VOC_CODEC_DEFAULT,
|
||||
RPC_VOC_CODEC_ON_CHIP_1,
|
||||
RPC_VOC_CODEC_STEREO_HEADSET,
|
||||
RPC_VOC_CODEC_ON_CHIP_AUX,
|
||||
RPC_VOC_CODEC_BT_OFF_BOARD,
|
||||
RPC_VOC_CODEC_BT_A2DP,
|
||||
RPC_VOC_CODEC_OFF_BOARD,
|
||||
RPC_VOC_CODEC_SDAC,
|
||||
RPC_VOC_CODEC_RX_EXT_SDAC_TX_INTERNAL,
|
||||
RPC_VOC_CODEC_IN_STEREO_SADC_OUT_MONO_HANDSET,
|
||||
RPC_VOC_CODEC_IN_STEREO_SADC_OUT_STEREO_HEADSET,
|
||||
RPC_VOC_CODEC_TX_INT_SADC_RX_EXT_AUXPCM,
|
||||
RPC_VOC_CODEC_EXT_STEREO_SADC_OUT_MONO_HANDSET,
|
||||
RPC_VOC_CODEC_EXT_STEREO_SADC_OUT_STEREO_HEADSET,
|
||||
RPC_VOC_CODEC_TTY_ON_CHIP_1,
|
||||
RPC_VOC_CODEC_TTY_OFF_BOARD,
|
||||
RPC_VOC_CODEC_TTY_VCO,
|
||||
RPC_VOC_CODEC_TTY_HCO,
|
||||
RPC_VOC_CODEC_ON_CHIP_0_DUAL_MIC,
|
||||
RPC_VOC_CODEC_MAX,
|
||||
RPC_VOC_CODEC_NONE,
|
||||
};
|
||||
|
||||
enum rpc_audmgr_status_type {
|
||||
RPC_AUDMGR_STATUS_READY,
|
||||
RPC_AUDMGR_STATUS_CODEC_CONFIG,
|
||||
RPC_AUDMGR_STATUS_PENDING,
|
||||
RPC_AUDMGR_STATUS_SUSPEND,
|
||||
RPC_AUDMGR_STATUS_FAILURE,
|
||||
RPC_AUDMGR_STATUS_VOLUME_CHANGE,
|
||||
RPC_AUDMGR_STATUS_DISABLED,
|
||||
RPC_AUDMGR_STATUS_ERROR,
|
||||
};
|
||||
|
||||
struct rpc_audmgr_enable_client_args {
|
||||
uint32_t set_to_one;
|
||||
uint32_t tx_sample_rate;
|
||||
uint32_t rx_sample_rate;
|
||||
uint32_t def_method;
|
||||
uint32_t codec_type;
|
||||
uint32_t snd_method;
|
||||
|
||||
uint32_t cb_func;
|
||||
uint32_t client_data;
|
||||
};
|
||||
|
||||
#define AUDMGR_ENABLE_CLIENT 2
|
||||
#define AUDMGR_DISABLE_CLIENT 3
|
||||
#define AUDMGR_SUSPEND_EVENT_RSP 4
|
||||
#define AUDMGR_REGISTER_OPERATION_LISTENER 5
|
||||
#define AUDMGR_UNREGISTER_OPERATION_LISTENER 6
|
||||
#define AUDMGR_REGISTER_CODEC_LISTENER 7
|
||||
#define AUDMGR_GET_RX_SAMPLE_RATE 8
|
||||
#define AUDMGR_GET_TX_SAMPLE_RATE 9
|
||||
#define AUDMGR_SET_DEVICE_MODE 10
|
||||
|
||||
#define AUDMGR_PROG 0x30000013
|
||||
#define AUDMGR_VERS MSM_RPC_VERS(1,0)
|
||||
|
||||
struct rpc_audmgr_cb_func_ptr {
|
||||
uint32_t cb_id;
|
||||
uint32_t status; /* Audmgr status */
|
||||
uint32_t set_to_one; /* Pointer status (1 = valid, 0 = invalid) */
|
||||
uint32_t disc;
|
||||
/* disc = AUDMGR_STATUS_READY => data=handle
|
||||
disc = AUDMGR_STATUS_CODEC_CONFIG => data = handle
|
||||
disc = AUDMGR_STATUS_DISABLED => data =status_disabled
|
||||
disc = AUDMGR_STATUS_VOLUME_CHANGE => data = volume-change */
|
||||
union {
|
||||
uint32_t handle;
|
||||
uint32_t volume;
|
||||
uint32_t status_disabled;
|
||||
uint32_t volume_change;
|
||||
} u;
|
||||
};
|
||||
|
||||
#define AUDMGR_CB_FUNC_PTR 1
|
||||
#define AUDMGR_OPR_LSTNR_CB_FUNC_PTR 2
|
||||
#define AUDMGR_CODEC_LSTR_FUNC_PTR 3
|
||||
|
||||
#define AUDMGR_CB_PROG 0x31000013
|
||||
#define AUDMGR_CB_VERS 0xf8e3e2d9
|
||||
|
||||
struct audmgr {
|
||||
wait_queue_head_t wait;
|
||||
uint32_t handle;
|
||||
struct msm_rpc_endpoint *ept;
|
||||
struct task_struct *task;
|
||||
int state;
|
||||
};
|
||||
|
||||
struct audmgr_config {
|
||||
uint32_t tx_rate;
|
||||
uint32_t rx_rate;
|
||||
uint32_t def_method;
|
||||
uint32_t codec;
|
||||
uint32_t snd_method;
|
||||
};
|
||||
|
||||
int audmgr_open(struct audmgr *am);
|
||||
int audmgr_close(struct audmgr *am);
|
||||
int audmgr_enable(struct audmgr *am, struct audmgr_config *cfg);
|
||||
int audmgr_disable(struct audmgr *am);
|
||||
|
||||
typedef void (*audpp_event_func)(void *private, unsigned id, uint16_t *msg);
|
||||
|
||||
int audpp_enable(int id, audpp_event_func func, void *private);
|
||||
void audpp_disable(int id, void *private);
|
||||
|
||||
int audpp_send_queue1(void *cmd, unsigned len);
|
||||
int audpp_send_queue2(void *cmd, unsigned len);
|
||||
int audpp_send_queue3(void *cmd, unsigned len);
|
||||
|
||||
int audpp_set_volume_and_pan(unsigned id, unsigned volume, int pan);
|
||||
int audpp_pause(unsigned id, int pause);
|
||||
int audpp_flush(unsigned id);
|
||||
void audpp_avsync(int id, unsigned rate);
|
||||
unsigned audpp_avsync_sample_count(int id);
|
||||
unsigned audpp_avsync_byte_count(int id);
|
||||
|
||||
#endif
|
429
drivers/staging/dream/qdsp5/audpp.c
Normal file
429
drivers/staging/dream/qdsp5/audpp.c
Normal file
@ -0,0 +1,429 @@
|
||||
|
||||
/* arch/arm/mach-msm/qdsp5/audpp.c
|
||||
*
|
||||
* common code to deal with the AUDPP dsp task (audio postproc)
|
||||
*
|
||||
* Copyright (C) 2008 Google, Inc.
|
||||
*
|
||||
* 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/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/delay.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>
|
||||
|
||||
/* for queue ids - should be relative to module number*/
|
||||
#include "adsp.h"
|
||||
|
||||
#include "evlog.h"
|
||||
|
||||
|
||||
enum {
|
||||
EV_NULL,
|
||||
EV_ENABLE,
|
||||
EV_DISABLE,
|
||||
EV_EVENT,
|
||||
EV_DATA,
|
||||
};
|
||||
|
||||
static const char *dsp_log_strings[] = {
|
||||
"NULL",
|
||||
"ENABLE",
|
||||
"DISABLE",
|
||||
"EVENT",
|
||||
"DATA",
|
||||
};
|
||||
|
||||
DECLARE_LOG(dsp_log, 64, dsp_log_strings);
|
||||
|
||||
static int __init _dsp_log_init(void)
|
||||
{
|
||||
return ev_log_init(&dsp_log);
|
||||
}
|
||||
module_init(_dsp_log_init);
|
||||
#define LOG(id,arg) ev_log_write(&dsp_log, id, arg)
|
||||
|
||||
static DEFINE_MUTEX(audpp_lock);
|
||||
|
||||
#define CH_COUNT 5
|
||||
#define AUDPP_CLNT_MAX_COUNT 6
|
||||
#define AUDPP_AVSYNC_INFO_SIZE 7
|
||||
|
||||
struct audpp_state {
|
||||
struct msm_adsp_module *mod;
|
||||
audpp_event_func func[AUDPP_CLNT_MAX_COUNT];
|
||||
void *private[AUDPP_CLNT_MAX_COUNT];
|
||||
struct mutex *lock;
|
||||
unsigned open_count;
|
||||
unsigned enabled;
|
||||
|
||||
/* which channels are actually enabled */
|
||||
unsigned avsync_mask;
|
||||
|
||||
/* flags, 48 bits sample/bytes counter per channel */
|
||||
uint16_t avsync[CH_COUNT * AUDPP_CLNT_MAX_COUNT + 1];
|
||||
};
|
||||
|
||||
struct audpp_state the_audpp_state = {
|
||||
.lock = &audpp_lock,
|
||||
};
|
||||
|
||||
int audpp_send_queue1(void *cmd, unsigned len)
|
||||
{
|
||||
return msm_adsp_write(the_audpp_state.mod,
|
||||
QDSP_uPAudPPCmd1Queue, cmd, len);
|
||||
}
|
||||
EXPORT_SYMBOL(audpp_send_queue1);
|
||||
|
||||
int audpp_send_queue2(void *cmd, unsigned len)
|
||||
{
|
||||
return msm_adsp_write(the_audpp_state.mod,
|
||||
QDSP_uPAudPPCmd2Queue, cmd, len);
|
||||
}
|
||||
EXPORT_SYMBOL(audpp_send_queue2);
|
||||
|
||||
int audpp_send_queue3(void *cmd, unsigned len)
|
||||
{
|
||||
return msm_adsp_write(the_audpp_state.mod,
|
||||
QDSP_uPAudPPCmd3Queue, cmd, len);
|
||||
}
|
||||
EXPORT_SYMBOL(audpp_send_queue3);
|
||||
|
||||
static int audpp_dsp_config(int enable)
|
||||
{
|
||||
audpp_cmd_cfg cmd;
|
||||
|
||||
cmd.cmd_id = AUDPP_CMD_CFG;
|
||||
cmd.cfg = enable ? AUDPP_CMD_CFG_ENABLE : AUDPP_CMD_CFG_SLEEP;
|
||||
|
||||
return audpp_send_queue1(&cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
static void audpp_broadcast(struct audpp_state *audpp, unsigned id,
|
||||
uint16_t *msg)
|
||||
{
|
||||
unsigned n;
|
||||
for (n = 0; n < AUDPP_CLNT_MAX_COUNT; n++) {
|
||||
if (audpp->func[n])
|
||||
audpp->func[n] (audpp->private[n], id, msg);
|
||||
}
|
||||
}
|
||||
|
||||
static void audpp_notify_clnt(struct audpp_state *audpp, unsigned clnt_id,
|
||||
unsigned id, uint16_t *msg)
|
||||
{
|
||||
if (clnt_id < AUDPP_CLNT_MAX_COUNT && audpp->func[clnt_id])
|
||||
audpp->func[clnt_id] (audpp->private[clnt_id], id, msg);
|
||||
}
|
||||
|
||||
static void audpp_dsp_event(void *data, unsigned id, size_t len,
|
||||
void (*getevent)(void *ptr, size_t len))
|
||||
{
|
||||
struct audpp_state *audpp = data;
|
||||
uint16_t msg[8];
|
||||
|
||||
if (id == AUDPP_MSG_AVSYNC_MSG) {
|
||||
getevent(audpp->avsync, sizeof(audpp->avsync));
|
||||
|
||||
/* mask off any channels we're not watching to avoid
|
||||
* cases where we might get one last update after
|
||||
* disabling avsync and end up in an odd state when
|
||||
* we next read...
|
||||
*/
|
||||
audpp->avsync[0] &= audpp->avsync_mask;
|
||||
return;
|
||||
}
|
||||
|
||||
getevent(msg, sizeof(msg));
|
||||
|
||||
LOG(EV_EVENT, (id << 16) | msg[0]);
|
||||
LOG(EV_DATA, (msg[1] << 16) | msg[2]);
|
||||
|
||||
switch (id) {
|
||||
case AUDPP_MSG_STATUS_MSG:{
|
||||
unsigned cid = msg[0];
|
||||
pr_info("audpp: status %d %d %d\n", cid, msg[1],
|
||||
msg[2]);
|
||||
if ((cid < 5) && audpp->func[cid])
|
||||
audpp->func[cid] (audpp->private[cid], id, msg);
|
||||
break;
|
||||
}
|
||||
case AUDPP_MSG_HOST_PCM_INTF_MSG:
|
||||
if (audpp->func[5])
|
||||
audpp->func[5] (audpp->private[5], id, msg);
|
||||
break;
|
||||
case AUDPP_MSG_PCMDMAMISSED:
|
||||
pr_err("audpp: DMA missed obj=%x\n", msg[0]);
|
||||
break;
|
||||
case AUDPP_MSG_CFG_MSG:
|
||||
if (msg[0] == AUDPP_MSG_ENA_ENA) {
|
||||
pr_info("audpp: ENABLE\n");
|
||||
audpp->enabled = 1;
|
||||
audpp_broadcast(audpp, id, msg);
|
||||
} else if (msg[0] == AUDPP_MSG_ENA_DIS) {
|
||||
pr_info("audpp: DISABLE\n");
|
||||
audpp->enabled = 0;
|
||||
audpp_broadcast(audpp, id, msg);
|
||||
} else {
|
||||
pr_err("audpp: invalid config msg %d\n", msg[0]);
|
||||
}
|
||||
break;
|
||||
case AUDPP_MSG_ROUTING_ACK:
|
||||
audpp_broadcast(audpp, id, msg);
|
||||
break;
|
||||
case AUDPP_MSG_FLUSH_ACK:
|
||||
audpp_notify_clnt(audpp, msg[0], id, msg);
|
||||
break;
|
||||
default:
|
||||
pr_info("audpp: unhandled msg id %x\n", id);
|
||||
}
|
||||
}
|
||||
|
||||
static struct msm_adsp_ops adsp_ops = {
|
||||
.event = audpp_dsp_event,
|
||||
};
|
||||
|
||||
static void audpp_fake_event(struct audpp_state *audpp, int id,
|
||||
unsigned event, unsigned arg)
|
||||
{
|
||||
uint16_t msg[1];
|
||||
msg[0] = arg;
|
||||
audpp->func[id] (audpp->private[id], event, msg);
|
||||
}
|
||||
|
||||
int audpp_enable(int id, audpp_event_func func, void *private)
|
||||
{
|
||||
struct audpp_state *audpp = &the_audpp_state;
|
||||
int res = 0;
|
||||
|
||||
if (id < -1 || id > 4)
|
||||
return -EINVAL;
|
||||
|
||||
if (id == -1)
|
||||
id = 5;
|
||||
|
||||
mutex_lock(audpp->lock);
|
||||
if (audpp->func[id]) {
|
||||
res = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
audpp->func[id] = func;
|
||||
audpp->private[id] = private;
|
||||
|
||||
LOG(EV_ENABLE, 1);
|
||||
if (audpp->open_count++ == 0) {
|
||||
pr_info("audpp: enable\n");
|
||||
res = msm_adsp_get("AUDPPTASK", &audpp->mod, &adsp_ops, audpp);
|
||||
if (res < 0) {
|
||||
pr_err("audpp: cannot open AUDPPTASK\n");
|
||||
audpp->open_count = 0;
|
||||
audpp->func[id] = NULL;
|
||||
audpp->private[id] = NULL;
|
||||
goto out;
|
||||
}
|
||||
LOG(EV_ENABLE, 2);
|
||||
msm_adsp_enable(audpp->mod);
|
||||
audpp_dsp_config(1);
|
||||
} else {
|
||||
unsigned long flags;
|
||||
local_irq_save(flags);
|
||||
if (audpp->enabled)
|
||||
audpp_fake_event(audpp, id,
|
||||
AUDPP_MSG_CFG_MSG, AUDPP_MSG_ENA_ENA);
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
res = 0;
|
||||
out:
|
||||
mutex_unlock(audpp->lock);
|
||||
return res;
|
||||
}
|
||||
EXPORT_SYMBOL(audpp_enable);
|
||||
|
||||
void audpp_disable(int id, void *private)
|
||||
{
|
||||
struct audpp_state *audpp = &the_audpp_state;
|
||||
unsigned long flags;
|
||||
|
||||
if (id < -1 || id > 4)
|
||||
return;
|
||||
|
||||
if (id == -1)
|
||||
id = 5;
|
||||
|
||||
mutex_lock(audpp->lock);
|
||||
LOG(EV_DISABLE, 1);
|
||||
if (!audpp->func[id])
|
||||
goto out;
|
||||
if (audpp->private[id] != private)
|
||||
goto out;
|
||||
|
||||
local_irq_save(flags);
|
||||
audpp_fake_event(audpp, id, AUDPP_MSG_CFG_MSG, AUDPP_MSG_ENA_DIS);
|
||||
audpp->func[id] = NULL;
|
||||
audpp->private[id] = NULL;
|
||||
local_irq_restore(flags);
|
||||
|
||||
if (--audpp->open_count == 0) {
|
||||
pr_info("audpp: disable\n");
|
||||
LOG(EV_DISABLE, 2);
|
||||
audpp_dsp_config(0);
|
||||
msm_adsp_disable(audpp->mod);
|
||||
msm_adsp_put(audpp->mod);
|
||||
audpp->mod = NULL;
|
||||
}
|
||||
out:
|
||||
mutex_unlock(audpp->lock);
|
||||
}
|
||||
EXPORT_SYMBOL(audpp_disable);
|
||||
|
||||
#define BAD_ID(id) ((id < 0) || (id >= CH_COUNT))
|
||||
|
||||
void audpp_avsync(int id, unsigned rate)
|
||||
{
|
||||
unsigned long flags;
|
||||
audpp_cmd_avsync cmd;
|
||||
|
||||
if (BAD_ID(id))
|
||||
return;
|
||||
|
||||
local_irq_save(flags);
|
||||
if (rate)
|
||||
the_audpp_state.avsync_mask |= (1 << id);
|
||||
else
|
||||
the_audpp_state.avsync_mask &= (~(1 << id));
|
||||
the_audpp_state.avsync[0] &= the_audpp_state.avsync_mask;
|
||||
local_irq_restore(flags);
|
||||
|
||||
cmd.cmd_id = AUDPP_CMD_AVSYNC;
|
||||
cmd.object_number = id;
|
||||
cmd.interrupt_interval_lsw = rate;
|
||||
cmd.interrupt_interval_msw = rate >> 16;
|
||||
audpp_send_queue1(&cmd, sizeof(cmd));
|
||||
}
|
||||
EXPORT_SYMBOL(audpp_avsync);
|
||||
|
||||
unsigned audpp_avsync_sample_count(int id)
|
||||
{
|
||||
uint16_t *avsync = the_audpp_state.avsync;
|
||||
unsigned val;
|
||||
unsigned long flags;
|
||||
unsigned mask;
|
||||
|
||||
if (BAD_ID(id))
|
||||
return 0;
|
||||
|
||||
mask = 1 << id;
|
||||
id = id * AUDPP_AVSYNC_INFO_SIZE + 2;
|
||||
local_irq_save(flags);
|
||||
if (avsync[0] & mask)
|
||||
val = (avsync[id] << 16) | avsync[id + 1];
|
||||
else
|
||||
val = 0;
|
||||
local_irq_restore(flags);
|
||||
|
||||
return val;
|
||||
}
|
||||
EXPORT_SYMBOL(audpp_avsync_sample_count);
|
||||
|
||||
unsigned audpp_avsync_byte_count(int id)
|
||||
{
|
||||
uint16_t *avsync = the_audpp_state.avsync;
|
||||
unsigned val;
|
||||
unsigned long flags;
|
||||
unsigned mask;
|
||||
|
||||
if (BAD_ID(id))
|
||||
return 0;
|
||||
|
||||
mask = 1 << id;
|
||||
id = id * AUDPP_AVSYNC_INFO_SIZE + 5;
|
||||
local_irq_save(flags);
|
||||
if (avsync[0] & mask)
|
||||
val = (avsync[id] << 16) | avsync[id + 1];
|
||||
else
|
||||
val = 0;
|
||||
local_irq_restore(flags);
|
||||
|
||||
return val;
|
||||
}
|
||||
EXPORT_SYMBOL(audpp_avsync_byte_count);
|
||||
|
||||
#define AUDPP_CMD_CFG_OBJ_UPDATE 0x8000
|
||||
#define AUDPP_CMD_VOLUME_PAN 0
|
||||
|
||||
int audpp_set_volume_and_pan(unsigned id, unsigned volume, int pan)
|
||||
{
|
||||
/* cmd, obj_cfg[7], cmd_type, volume, pan */
|
||||
uint16_t cmd[11];
|
||||
|
||||
if (id > 6)
|
||||
return -EINVAL;
|
||||
|
||||
memset(cmd, 0, sizeof(cmd));
|
||||
cmd[0] = AUDPP_CMD_CFG_OBJECT_PARAMS;
|
||||
cmd[1 + id] = AUDPP_CMD_CFG_OBJ_UPDATE;
|
||||
cmd[8] = AUDPP_CMD_VOLUME_PAN;
|
||||
cmd[9] = volume;
|
||||
cmd[10] = pan;
|
||||
|
||||
return audpp_send_queue3(cmd, sizeof(cmd));
|
||||
}
|
||||
EXPORT_SYMBOL(audpp_set_volume_and_pan);
|
||||
|
||||
int audpp_pause(unsigned id, int pause)
|
||||
{
|
||||
/* pause 1 = pause 0 = resume */
|
||||
u16 pause_cmd[AUDPP_CMD_DEC_CTRL_LEN / sizeof(unsigned short)];
|
||||
|
||||
if (id >= CH_COUNT)
|
||||
return -EINVAL;
|
||||
|
||||
memset(pause_cmd, 0, sizeof(pause_cmd));
|
||||
|
||||
pause_cmd[0] = AUDPP_CMD_DEC_CTRL;
|
||||
if (pause == 1)
|
||||
pause_cmd[1 + id] = AUDPP_CMD_UPDATE_V | AUDPP_CMD_PAUSE_V;
|
||||
else if (pause == 0)
|
||||
pause_cmd[1 + id] = AUDPP_CMD_UPDATE_V | AUDPP_CMD_RESUME_V;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return audpp_send_queue1(pause_cmd, sizeof(pause_cmd));
|
||||
}
|
||||
EXPORT_SYMBOL(audpp_pause);
|
||||
|
||||
int audpp_flush(unsigned id)
|
||||
{
|
||||
u16 flush_cmd[AUDPP_CMD_DEC_CTRL_LEN / sizeof(unsigned short)];
|
||||
|
||||
if (id >= CH_COUNT)
|
||||
return -EINVAL;
|
||||
|
||||
memset(flush_cmd, 0, sizeof(flush_cmd));
|
||||
|
||||
flush_cmd[0] = AUDPP_CMD_DEC_CTRL;
|
||||
flush_cmd[1 + id] = AUDPP_CMD_UPDATE_V | AUDPP_CMD_FLUSH_V;
|
||||
|
||||
return audpp_send_queue1(flush_cmd, sizeof(flush_cmd));
|
||||
}
|
||||
EXPORT_SYMBOL(audpp_flush);
|
133
drivers/staging/dream/qdsp5/evlog.h
Normal file
133
drivers/staging/dream/qdsp5/evlog.h
Normal file
@ -0,0 +1,133 @@
|
||||
/* arch/arm/mach-msm/qdsp5/evlog.h
|
||||
*
|
||||
* simple event log debugging facility
|
||||
*
|
||||
* Copyright (C) 2008 Google, Inc.
|
||||
*
|
||||
* 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/fs.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
#define EV_LOG_ENTRY_NAME(n) n##_entry
|
||||
|
||||
#define DECLARE_LOG(_name, _size, _str) \
|
||||
static struct ev_entry EV_LOG_ENTRY_NAME(_name)[_size]; \
|
||||
static struct ev_log _name = { \
|
||||
.name = #_name, \
|
||||
.strings = _str, \
|
||||
.num_strings = ARRAY_SIZE(_str), \
|
||||
.entry = EV_LOG_ENTRY_NAME(_name), \
|
||||
.max = ARRAY_SIZE(EV_LOG_ENTRY_NAME(_name)), \
|
||||
}
|
||||
|
||||
struct ev_entry {
|
||||
ktime_t when;
|
||||
uint32_t id;
|
||||
uint32_t arg;
|
||||
};
|
||||
|
||||
struct ev_log {
|
||||
struct ev_entry *entry;
|
||||
unsigned max;
|
||||
unsigned next;
|
||||
unsigned fault;
|
||||
const char **strings;
|
||||
unsigned num_strings;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
static char ev_buf[4096];
|
||||
|
||||
static ssize_t ev_log_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct ev_log *log = file->private_data;
|
||||
struct ev_entry *entry;
|
||||
unsigned long flags;
|
||||
int size = 0;
|
||||
unsigned n, id, max;
|
||||
ktime_t now, t;
|
||||
|
||||
max = log->max;
|
||||
now = ktime_get();
|
||||
local_irq_save(flags);
|
||||
n = (log->next - 1) & (max - 1);
|
||||
entry = log->entry;
|
||||
while (n != log->next) {
|
||||
t = ktime_sub(now, entry[n].when);
|
||||
id = entry[n].id;
|
||||
if (id) {
|
||||
const char *str;
|
||||
if (id < log->num_strings)
|
||||
str = log->strings[id];
|
||||
else
|
||||
str = "UNKNOWN";
|
||||
size += scnprintf(ev_buf + size, 4096 - size,
|
||||
"%8d.%03d %08x %s\n",
|
||||
t.tv.sec, t.tv.nsec / 1000000,
|
||||
entry[n].arg, str);
|
||||
}
|
||||
n = (n - 1) & (max - 1);
|
||||
}
|
||||
log->fault = 0;
|
||||
local_irq_restore(flags);
|
||||
return simple_read_from_buffer(buf, count, ppos, ev_buf, size);
|
||||
}
|
||||
|
||||
static void ev_log_write(struct ev_log *log, unsigned id, unsigned arg)
|
||||
{
|
||||
struct ev_entry *entry;
|
||||
unsigned long flags;
|
||||
local_irq_save(flags);
|
||||
|
||||
if (log->fault) {
|
||||
if (log->fault == 1)
|
||||
goto done;
|
||||
log->fault--;
|
||||
}
|
||||
|
||||
entry = log->entry + log->next;
|
||||
entry->when = ktime_get();
|
||||
entry->id = id;
|
||||
entry->arg = arg;
|
||||
log->next = (log->next + 1) & (log->max - 1);
|
||||
done:
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
static void ev_log_freeze(struct ev_log *log, unsigned count)
|
||||
{
|
||||
unsigned long flags;
|
||||
local_irq_save(flags);
|
||||
log->fault = count;
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
static int ev_log_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
file->private_data = inode->i_private;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations ev_log_ops = {
|
||||
.read = ev_log_read,
|
||||
.open = ev_log_open,
|
||||
};
|
||||
|
||||
static int ev_log_init(struct ev_log *log)
|
||||
{
|
||||
debugfs_create_file(log->name, 0444, 0, log, &ev_log_ops);
|
||||
return 0;
|
||||
}
|
||||
|
279
drivers/staging/dream/qdsp5/snd.c
Normal file
279
drivers/staging/dream/qdsp5/snd.c
Normal file
@ -0,0 +1,279 @@
|
||||
/* arch/arm/mach-msm/qdsp5/snd.c
|
||||
*
|
||||
* interface to "snd" service on the baseband cpu
|
||||
*
|
||||
* 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/delay.h>
|
||||
#include <linux/msm_audio.h>
|
||||
|
||||
#include <asm/atomic.h>
|
||||
#include <asm/ioctls.h>
|
||||
#include <mach/board.h>
|
||||
#include <mach/msm_rpcrouter.h>
|
||||
|
||||
struct snd_ctxt {
|
||||
struct mutex lock;
|
||||
int opened;
|
||||
struct msm_rpc_endpoint *ept;
|
||||
struct msm_snd_endpoints *snd_epts;
|
||||
};
|
||||
|
||||
static struct snd_ctxt the_snd;
|
||||
|
||||
#define RPC_SND_PROG 0x30000002
|
||||
#define RPC_SND_CB_PROG 0x31000002
|
||||
#if CONFIG_MSM_AMSS_VERSION == 6210
|
||||
#define RPC_SND_VERS 0x94756085 /* 2490720389 */
|
||||
#elif (CONFIG_MSM_AMSS_VERSION == 6220) || \
|
||||
(CONFIG_MSM_AMSS_VERSION == 6225)
|
||||
#define RPC_SND_VERS 0xaa2b1a44 /* 2854951492 */
|
||||
#elif CONFIG_MSM_AMSS_VERSION == 6350
|
||||
#define RPC_SND_VERS MSM_RPC_VERS(1,0)
|
||||
#endif
|
||||
|
||||
#define SND_SET_DEVICE_PROC 2
|
||||
#define SND_SET_VOLUME_PROC 3
|
||||
|
||||
struct rpc_snd_set_device_args {
|
||||
uint32_t device;
|
||||
uint32_t ear_mute;
|
||||
uint32_t mic_mute;
|
||||
|
||||
uint32_t cb_func;
|
||||
uint32_t client_data;
|
||||
};
|
||||
|
||||
struct rpc_snd_set_volume_args {
|
||||
uint32_t device;
|
||||
uint32_t method;
|
||||
uint32_t volume;
|
||||
|
||||
uint32_t cb_func;
|
||||
uint32_t client_data;
|
||||
};
|
||||
|
||||
struct snd_set_device_msg {
|
||||
struct rpc_request_hdr hdr;
|
||||
struct rpc_snd_set_device_args args;
|
||||
};
|
||||
|
||||
struct snd_set_volume_msg {
|
||||
struct rpc_request_hdr hdr;
|
||||
struct rpc_snd_set_volume_args args;
|
||||
};
|
||||
|
||||
struct snd_endpoint *get_snd_endpoints(int *size);
|
||||
|
||||
static inline int check_mute(int mute)
|
||||
{
|
||||
return (mute == SND_MUTE_MUTED ||
|
||||
mute == SND_MUTE_UNMUTED) ? 0 : -EINVAL;
|
||||
}
|
||||
|
||||
static int get_endpoint(struct snd_ctxt *snd, unsigned long arg)
|
||||
{
|
||||
int rc = 0, index;
|
||||
struct msm_snd_endpoint ept;
|
||||
|
||||
if (copy_from_user(&ept, (void __user *)arg, sizeof(ept))) {
|
||||
pr_err("snd_ioctl get endpoint: invalid read pointer.\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
index = ept.id;
|
||||
if (index < 0 || index >= snd->snd_epts->num) {
|
||||
pr_err("snd_ioctl get endpoint: invalid index!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ept.id = snd->snd_epts->endpoints[index].id;
|
||||
strncpy(ept.name,
|
||||
snd->snd_epts->endpoints[index].name,
|
||||
sizeof(ept.name));
|
||||
|
||||
if (copy_to_user((void __user *)arg, &ept, sizeof(ept))) {
|
||||
pr_err("snd_ioctl get endpoint: invalid write pointer.\n");
|
||||
rc = -EFAULT;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static long snd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct snd_set_device_msg dmsg;
|
||||
struct snd_set_volume_msg vmsg;
|
||||
struct msm_snd_device_config dev;
|
||||
struct msm_snd_volume_config vol;
|
||||
struct snd_ctxt *snd = file->private_data;
|
||||
int rc = 0;
|
||||
|
||||
mutex_lock(&snd->lock);
|
||||
switch (cmd) {
|
||||
case SND_SET_DEVICE:
|
||||
if (copy_from_user(&dev, (void __user *) arg, sizeof(dev))) {
|
||||
pr_err("snd_ioctl set device: invalid pointer.\n");
|
||||
rc = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
dmsg.args.device = cpu_to_be32(dev.device);
|
||||
dmsg.args.ear_mute = cpu_to_be32(dev.ear_mute);
|
||||
dmsg.args.mic_mute = cpu_to_be32(dev.mic_mute);
|
||||
if (check_mute(dev.ear_mute) < 0 ||
|
||||
check_mute(dev.mic_mute) < 0) {
|
||||
pr_err("snd_ioctl set device: invalid mute status.\n");
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
dmsg.args.cb_func = -1;
|
||||
dmsg.args.client_data = 0;
|
||||
|
||||
pr_info("snd_set_device %d %d %d\n", dev.device,
|
||||
dev.ear_mute, dev.mic_mute);
|
||||
|
||||
rc = msm_rpc_call(snd->ept,
|
||||
SND_SET_DEVICE_PROC,
|
||||
&dmsg, sizeof(dmsg), 5 * HZ);
|
||||
break;
|
||||
|
||||
case SND_SET_VOLUME:
|
||||
if (copy_from_user(&vol, (void __user *) arg, sizeof(vol))) {
|
||||
pr_err("snd_ioctl set volume: invalid pointer.\n");
|
||||
rc = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
vmsg.args.device = cpu_to_be32(vol.device);
|
||||
vmsg.args.method = cpu_to_be32(vol.method);
|
||||
if (vol.method != SND_METHOD_VOICE) {
|
||||
pr_err("snd_ioctl set volume: invalid method.\n");
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
vmsg.args.volume = cpu_to_be32(vol.volume);
|
||||
vmsg.args.cb_func = -1;
|
||||
vmsg.args.client_data = 0;
|
||||
|
||||
pr_info("snd_set_volume %d %d %d\n", vol.device,
|
||||
vol.method, vol.volume);
|
||||
|
||||
rc = msm_rpc_call(snd->ept,
|
||||
SND_SET_VOLUME_PROC,
|
||||
&vmsg, sizeof(vmsg), 5 * HZ);
|
||||
break;
|
||||
|
||||
case SND_GET_NUM_ENDPOINTS:
|
||||
if (copy_to_user((void __user *)arg,
|
||||
&snd->snd_epts->num, sizeof(unsigned))) {
|
||||
pr_err("snd_ioctl get endpoint: invalid pointer.\n");
|
||||
rc = -EFAULT;
|
||||
}
|
||||
break;
|
||||
|
||||
case SND_GET_ENDPOINT:
|
||||
rc = get_endpoint(snd, arg);
|
||||
break;
|
||||
|
||||
default:
|
||||
pr_err("snd_ioctl unknown command.\n");
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&snd->lock);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int snd_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct snd_ctxt *snd = file->private_data;
|
||||
|
||||
mutex_lock(&snd->lock);
|
||||
snd->opened = 0;
|
||||
mutex_unlock(&snd->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct snd_ctxt *snd = &the_snd;
|
||||
int rc = 0;
|
||||
|
||||
mutex_lock(&snd->lock);
|
||||
if (snd->opened == 0) {
|
||||
if (snd->ept == NULL) {
|
||||
snd->ept = msm_rpc_connect(RPC_SND_PROG, RPC_SND_VERS,
|
||||
MSM_RPC_UNINTERRUPTIBLE);
|
||||
if (IS_ERR(snd->ept)) {
|
||||
rc = PTR_ERR(snd->ept);
|
||||
snd->ept = NULL;
|
||||
pr_err("snd: failed to connect snd svc\n");
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
file->private_data = snd;
|
||||
snd->opened = 1;
|
||||
} else {
|
||||
pr_err("snd already opened.\n");
|
||||
rc = -EBUSY;
|
||||
}
|
||||
|
||||
err:
|
||||
mutex_unlock(&snd->lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static struct file_operations snd_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = snd_open,
|
||||
.release = snd_release,
|
||||
.unlocked_ioctl = snd_ioctl,
|
||||
};
|
||||
|
||||
struct miscdevice snd_misc = {
|
||||
.minor = MISC_DYNAMIC_MINOR,
|
||||
.name = "msm_snd",
|
||||
.fops = &snd_fops,
|
||||
};
|
||||
|
||||
static int snd_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_ctxt *snd = &the_snd;
|
||||
mutex_init(&snd->lock);
|
||||
snd->snd_epts = (struct msm_snd_endpoints *)pdev->dev.platform_data;
|
||||
return misc_register(&snd_misc);
|
||||
}
|
||||
|
||||
static struct platform_driver snd_plat_driver = {
|
||||
.probe = snd_probe,
|
||||
.driver = {
|
||||
.name = "msm_snd",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init snd_init(void)
|
||||
{
|
||||
return platform_driver_register(&snd_plat_driver);
|
||||
}
|
||||
|
||||
module_init(snd_init);
|
Loading…
Reference in New Issue
Block a user