htcleo: add 1550 audio

All credits to Cotulla
This commit is contained in:
Markinus 2010-08-30 13:36:24 +02:00
parent 8137654e8f
commit dd777b0b10
26 changed files with 8624 additions and 3 deletions

View File

@ -212,6 +212,7 @@ CONFIG_MSM_DEBUG_UART_NONE=y
#
# MSM Board Type
#
CONFIG_AAT1271_FLASHLIGHT=y
CONFIG_MACH_HTCLEO=y
# CONFIG_MACH_SWORDFISH is not set
# CONFIG_MACH_MAHIMAHI is not set
@ -251,6 +252,9 @@ CONFIG_MSM_RPCSERVERS=y
# CONFIG_MSM_CPU_FREQ_SCREEN is not set
# CONFIG_MSM_HW3D is not set
CONFIG_MSM_QDSP6=y
# CONFIG_HTC_ACOUSTIC is not set
CONFIG_QSD_AUDIO=y
CONFIG_ARCH_MSM_FLASHLIGHT=y
# CONFIG_MSM_CLOCK_CTRL_DEBUG is not set
CONFIG_WIFI_CONTROL_FUNC=y
CONFIG_WIFI_MEM_PREALLOC=y

View File

@ -894,7 +894,7 @@ config VIRTUAL_KPANIC_PARTITION
config QSD_AUDIO
bool "QSD audio"
depends on (ARCH_QSD8X50 && MSM_DALRPC)
depends on (ARCH_QSD8X50)
default y
help
Provides PCM, MP3, and AAC audio playback.

View File

@ -58,7 +58,11 @@ obj-$(CONFIG_ARCH_MSM7X30) += dal_axi.o
obj-$(CONFIG_MSM_ADSP) += qdsp5/
obj-$(CONFIG_MSM_ADSP_COMP) += qdsp5_comp/
obj-$(CONFIG_MSM7KV2_AUDIO) += qdsp5v2/
ifdef CONFIG_MSM_AMSS_VERSION_1550
obj-$(CONFIG_QSD_AUDIO) += qdsp6_1550/
else
obj-$(CONFIG_QSD_AUDIO) += qdsp6/
endif
obj-$(CONFIG_MSM_HW3D) += hw3d.o
obj-$(CONFIG_PM) += pm.o
obj-$(CONFIG_CPU_FREQ) += cpufreq.o
@ -113,7 +117,7 @@ obj-$(CONFIG_MACH_BRAVOC) += board-bravoc-microp.o clock.o
obj-$(CONFIG_MACH_HTCLEO) += board-htcleo.o board-htcleo-spi.o board-htcleo-panel.o board-htcleo-keypad.o
obj-$(CONFIG_MACH_HTCLEO) += board-htcleo-ts.o board-htcleo-mmc.o ieee754-df.o board-htcleo-power.o
obj-$(CONFIG_MACH_HTCLEO) += board-htcleo-battery.o board-htcleo-log.o
obj-$(CONFIG_MACH_HTCLEO) += board-htcleo-battery.o board-htcleo-log.o board-htcleo-audio.o board-htcleo-acoustic.o
obj-$(CONFIG_MACH_HTCLEO) += board-htcleo-bt.o board-htcleo-microp.o board-htcleo-wifi.o
obj-$(CONFIG_MACH_HTCLEO) += clock-wince.o

View File

@ -0,0 +1,247 @@
/* arch/arm/mach-msm/board-htcleo-acoustic.c
*
* Copyright (C) 2010 Cotulla
*
* 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/device.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/mm.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/uaccess.h>
#include <linux/mutex.h>
#include <linux/gpio.h>
#include <mach/msm_smd.h>
#include <mach/msm_rpcrouter.h>
#include <mach/msm_iomap.h>
#include <mach/htc_acoustic_qsd.h>
#include <mach/msm_qdsp6_audio.h>
#include "smd_private.h"
#include "dex_comm.h"
#define ACOUSTIC_IOCTL_MAGIC 'p'
#define ACOUSTIC_UPDATE_ADIE \
_IOW(ACOUSTIC_IOCTL_MAGIC, 24, unsigned int)
#define HTC_ACOUSTIC_TABLE_SIZE (0x20000)
#define D(fmt, args...) printk(KERN_INFO "htc-acoustic: "fmt, ##args)
#define E(fmt, args...) printk(KERN_ERR "htc-acoustic: "fmt, ##args)
static uint32_t htc_acoustic_vir_addr;
static struct mutex api_lock;
static struct qsd_acoustic_ops *the_ops;
void acoustic_register_ops(struct qsd_acoustic_ops *ops)
{
the_ops = ops;
}
int turn_mic_bias_on(int on)
{
D("%s called %d\n", __func__, on);
if (the_ops->enable_mic_bias)
the_ops->enable_mic_bias(on);
return 0;
}
EXPORT_SYMBOL(turn_mic_bias_on);
int force_headset_speaker_on(int enable)
{
printk("force_headset_speaker_on((%d)\n", enable);
if (enable)
dex_audio(0x53);
else
dex_audio(0x54);
return 0;
}
EXPORT_SYMBOL(force_headset_speaker_on);
int enable_aux_loopback(uint32_t enable)
{
printk("enable_aux_loopback(%d)\n", enable);
if (enable)
dex_audio(0x51);
else
dex_audio(0x52);
return 0;
}
EXPORT_SYMBOL(enable_aux_loopback);
int set_aux_gain(int level)
{
/* struct aux_gain_req {
struct rpc_request_hdr hdr;
int level;
} aux_req;
D("%s called %d\n", __func__, level);
if (is_rpc_connect() == -1)
return -1;
aux_req.level = cpu_to_be32(level);
return msm_rpc_call(endpoint,
ONCRPC_SET_AUX_PGA_GAIN_PROC,
&aux_req, sizeof(aux_req), 5 * HZ);*/
return 0;
}
EXPORT_SYMBOL(set_aux_gain);
static int acoustic_mmap(struct file *file, struct vm_area_struct *vma)
{
unsigned long pgoff;
int rc = -EINVAL;
size_t size;
D("mmap\n");
mutex_lock(&api_lock);
size = vma->vm_end - vma->vm_start;
if (vma->vm_pgoff != 0) {
E("mmap failed: page offset %lx\n", vma->vm_pgoff);
goto done;
}
if (!htc_acoustic_vir_addr) {
E("mmap failed: smem region not allocated\n");
rc = -EIO;
goto done;
}
pgoff = MSM_SHARED_RAM_PHYS +
(htc_acoustic_vir_addr - (uint32_t)MSM_SHARED_RAM_BASE);
pgoff = ((pgoff + 4095) & ~4095);
htc_acoustic_vir_addr = ((htc_acoustic_vir_addr + 4095) & ~4095);
if (pgoff <= 0) {
E("pgoff wrong. %ld\n", pgoff);
goto done;
}
if (size <= HTC_ACOUSTIC_TABLE_SIZE) {
pgoff = pgoff >> PAGE_SHIFT;
} else {
E("size > HTC_ACOUSTIC_TABLE_SIZE %d\n", size);
goto done;
}
vma->vm_flags |= VM_IO | VM_RESERVED;
rc = io_remap_pfn_range(vma, vma->vm_start, pgoff,
size, vma->vm_page_prot);
if (rc < 0)
E("mmap failed: remap error %d\n", rc);
done: mutex_unlock(&api_lock);
return rc;
}
static int acoustic_open(struct inode *inode, struct file *file)
{
int rc = -EIO;
D("open\n");
mutex_lock(&api_lock);
if (!htc_acoustic_vir_addr)
{
htc_acoustic_vir_addr = (uint32_t)MSM_SHARED_RAM_BASE + 0xF8000;
}
rc = 0;
mutex_unlock(&api_lock);
return rc;
}
static int acoustic_release(struct inode *inode, struct file *file)
{
D("release\n");
return 0;
}
static long acoustic_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
int rc = 0;
D("ioctl\n");
mutex_lock(&api_lock);
switch (cmd) {
case ACOUSTIC_UPDATE_ADIE:
{
unsigned data = 0xE5;
D("ioctl: ACOUSTIC_UPDATE_ADIE called %d.\n", current->pid);
// CotullaTODO: finish this code. if we need android tables really...
D("ioctl: done.\n");
break;
}
default:
E("ioctl: invalid command\n");
rc = -EINVAL;
}
mutex_unlock(&api_lock);
return rc;
}
struct rpc_set_uplink_mute_args {
int mute;
};
static struct file_operations acoustic_fops = {
.owner = THIS_MODULE,
.open = acoustic_open,
.release = acoustic_release,
.mmap = acoustic_mmap,
.unlocked_ioctl = acoustic_ioctl,
};
static struct miscdevice acoustic_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "htc-acoustic",
.fops = &acoustic_fops,
};
static int __init acoustic_init(void)
{
mutex_init(&api_lock);
return misc_register(&acoustic_misc);
}
static void __exit acoustic_exit(void)
{
misc_deregister(&acoustic_misc);
}
module_init(acoustic_init);
module_exit(acoustic_exit);

View File

@ -0,0 +1,330 @@
/* arch/arm/mach-msm/board-htcleo-audio.c
*
* Copyright (C) 2010 Cotulla
*
* 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/gpio.h>
#include <linux/delay.h>
#include <mach/msm_qdsp6_audio.h>
#include <mach/htc_acoustic_qsd.h>
#include <asm/gpio.h>
#include <mach/gpio.h>
#include "board-htcleo.h"
#include "devices.h"
#include "dex_comm.h"
#include "proc_comm.h"
#include "pmic.h"
#if 1
#define D(fmt, args...) printk(KERN_INFO "Audio: "fmt, ##args)
#else
#define D(fmt, args...) do {} while (0)
#endif
static struct mutex mic_lock;
static struct mutex bt_sco_lock;
#if 1
// LEO
static struct q6_hw_info q6_audio_hw[Q6_HW_COUNT] =
{
[Q6_HW_HANDSET] =
{
.min_gain = -2000,
.max_gain = 0,
},
[Q6_HW_HEADSET] =
{
.min_gain = -2000,
.max_gain = 0,
},
[Q6_HW_SPEAKER] =
{
.min_gain = -1000,
.max_gain = 500,
},
[Q6_HW_TTY] =
{
.min_gain = 0,
.max_gain = 0,
},
[Q6_HW_BT_SCO] =
{
.min_gain = -1100,
.max_gain = 400,
},
[Q6_HW_BT_A2DP] =
{
.min_gain = -1100,
.max_gain = 400,
},
};
#else
// old desire one
static struct q6_hw_info q6_audio_hw[Q6_HW_COUNT] =
{
[Q6_HW_HANDSET] = {
.min_gain = -2000,
.max_gain = 0,
},
[Q6_HW_HEADSET] = {
.min_gain = -2000,
.max_gain = 0,
},
[Q6_HW_SPEAKER] = {
.min_gain = -1500,
.max_gain = 0,
},
[Q6_HW_TTY] = {
.min_gain = -2000,
.max_gain = 0,
},
[Q6_HW_BT_SCO] = {
.min_gain = -2000,
.max_gain = 0,
},
[Q6_HW_BT_A2DP] = {
.min_gain = -2000,
.max_gain = 0,
},
};
#endif
void htcleo_headset_enable(int en)
{
D("%s %d\n", __func__, en);
/* enable audio amp */
if (en) mdelay(60);
gpio_set_value(HTCLEO_AUD_JACKHP_EN, !!en);
}
void htcleo_speaker_enable(int en)
{
struct spkr_config_mode scm;
memset(&scm, 0, sizeof(scm));
D("%s %d\n", __func__, en);
if (en)
{
scm.is_right_chan_en = 0;
scm.is_left_chan_en = 1;
scm.is_stereo_en = 0;
scm.is_hpf_en = 0; //1; // CotullaTODO: check it
pmic_spkr_en_mute(LEFT_SPKR, 0);
pmic_spkr_en_mute(RIGHT_SPKR, 0);
pmic_set_spkr_configuration(&scm);
pmic_spkr_set_mux_hpf_corner_freq(SPKR_FREQ_0_76KHZ);
pmic_spkr_en(LEFT_SPKR, 1);
pmic_spkr_en(RIGHT_SPKR, 0);
pmic_spkr_en_hpf(ON_CMD); // +LEO
/* unmute */
pmic_spkr_en_mute(LEFT_SPKR, 1);
mdelay(40);
}
else
{
pmic_spkr_en_mute(LEFT_SPKR, 0);
pmic_spkr_en_hpf(OFF_CMD); // +LEO
pmic_spkr_en(LEFT_SPKR, 0);
pmic_spkr_en(RIGHT_SPKR, 0);
pmic_set_spkr_configuration(&scm);
}
}
void htcleo_receiver_enable(int en)
{
// if (is_cdma_version(system_rev) &&
// ((system_rev == 0xC1) || (system_rev == 0xC2))) {
struct spkr_config_mode scm;
memset(&scm, 0, sizeof(scm));
D("%s %d\n", __func__, en);
if (en)
{
scm.is_right_chan_en = 1;
scm.is_left_chan_en = 0;
scm.is_stereo_en = 0;
scm.is_hpf_en = 1;
pmic_spkr_en_mute(RIGHT_SPKR, 0);
pmic_set_spkr_configuration(&scm);
pmic_spkr_en(RIGHT_SPKR, 1);
/* unmute */
pmic_spkr_en_mute(RIGHT_SPKR, 1);
}
else
{
pmic_spkr_en_mute(RIGHT_SPKR, 0);
pmic_spkr_en(RIGHT_SPKR, 0);
pmic_set_spkr_configuration(&scm);
}
// }
}
static uint32_t bt_sco_enable[] =
{
PCOM_GPIO_CFG(HTCLEO_BT_PCM_OUT, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA),
PCOM_GPIO_CFG(HTCLEO_BT_PCM_IN, 1, GPIO_INPUT, GPIO_NO_PULL, GPIO_2MA),
PCOM_GPIO_CFG(HTCLEO_BT_PCM_SYNC, 2, GPIO_INPUT, GPIO_NO_PULL, GPIO_2MA),
PCOM_GPIO_CFG(HTCLEO_BT_PCM_CLK, 2, GPIO_INPUT, GPIO_NO_PULL, GPIO_2MA)
};
static uint32_t bt_sco_disable[] =
{
PCOM_GPIO_CFG(HTCLEO_BT_PCM_OUT, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA),
PCOM_GPIO_CFG(HTCLEO_BT_PCM_IN, 0, GPIO_INPUT, GPIO_NO_PULL, GPIO_2MA),
PCOM_GPIO_CFG(HTCLEO_BT_PCM_SYNC, 0, GPIO_INPUT, GPIO_NO_PULL, GPIO_2MA),
PCOM_GPIO_CFG(HTCLEO_BT_PCM_CLK, 0, GPIO_INPUT, GPIO_NO_PULL, GPIO_2MA)
};
void htcleo_bt_sco_enable(int en)
{
static int bt_sco_refcount;
D("%s %d\n", __func__, en);
mutex_lock(&bt_sco_lock);
if (en)
{
if (++bt_sco_refcount == 1)
{
config_gpio_table(bt_sco_enable, ARRAY_SIZE(bt_sco_enable));
}
}
else
{
if (--bt_sco_refcount == 0)
{
config_gpio_table(bt_sco_disable, ARRAY_SIZE(bt_sco_disable));
gpio_set_value(HTCLEO_BT_PCM_OUT, 0);
}
}
mutex_unlock(&bt_sco_lock);
}
void htcleo_mic_enable(int en)
{
static int old_state = 0, new_state = 0;
D("%s %d\n", __func__, en);
mutex_lock(&mic_lock);
if (!!en)
new_state++;
else
new_state--;
if (new_state == 1 && old_state == 0)
{
gpio_set_value(HTCLEO_AUD_2V5_EN, 1);
mdelay(60);
}
else if (new_state == 0 && old_state == 1)
{
gpio_set_value(HTCLEO_AUD_2V5_EN, 0);
}
else
{
D("%s: do nothing %d %d\n", __func__, old_state, new_state);
}
old_state = new_state;
mutex_unlock(&mic_lock);
}
void htcleo_analog_init(void)
{
D("%s\n", __func__);
/* stereo pmic init */
pmic_spkr_set_gain(LEFT_SPKR, SPKR_GAIN_PLUS12DB);
pmic_spkr_set_gain(RIGHT_SPKR, SPKR_GAIN_PLUS12DB);
pmic_spkr_en_right_chan(OFF_CMD);
pmic_spkr_en_left_chan(OFF_CMD);
pmic_spkr_add_right_left_chan(OFF_CMD);
pmic_spkr_en_stereo(OFF_CMD);
pmic_spkr_select_usb_with_hpf_20hz(OFF_CMD);
pmic_spkr_bypass_mux(OFF_CMD);
pmic_spkr_en_hpf(OFF_CMD);
pmic_spkr_en_sink_curr_from_ref_volt_cir(OFF_CMD);
pmic_spkr_set_mux_hpf_corner_freq(SPKR_FREQ_0_76KHZ);
pmic_mic_set_volt(MIC_VOLT_1_80V);
gpio_request(HTCLEO_AUD_JACKHP_EN, "aud_jackhp_en");
gpio_request(HTCLEO_BT_PCM_OUT, "bt_pcm_out");
gpio_direction_output(HTCLEO_AUD_JACKHP_EN, 0);
mutex_lock(&bt_sco_lock);
config_gpio_table(bt_sco_disable, ARRAY_SIZE(bt_sco_disable));
gpio_direction_output(HTCLEO_BT_PCM_OUT, 0);
mutex_unlock(&bt_sco_lock);
}
int htcleo_get_rx_vol(uint8_t hw, int level)
{
int vol;
if (level > 100)
level = 100;
else if (level < 0)
level = 0;
// TODO: is it correct?
struct q6_hw_info *info;
info = &q6_audio_hw[hw];
vol = info->min_gain + ((info->max_gain - info->min_gain) * level) / 100;
D("%s %d\n", __func__, vol);
return vol;
}
static struct qsd_acoustic_ops acoustic =
{
.enable_mic_bias = htcleo_mic_enable,
};
static struct q6audio_analog_ops ops =
{
.init = htcleo_analog_init,
.speaker_enable = htcleo_speaker_enable,
.headset_enable = htcleo_headset_enable,
.receiver_enable = htcleo_receiver_enable,
.bt_sco_enable = htcleo_bt_sco_enable,
.int_mic_enable = htcleo_mic_enable,
.ext_mic_enable = htcleo_mic_enable,
.get_rx_vol = htcleo_get_rx_vol,
};
void __init htcleo_audio_init(void)
{
mutex_init(&mic_lock);
mutex_init(&bt_sco_lock);
q6audio_register_analog_ops(&ops);
acoustic_register_ops(&acoustic);
// q6audio_set_acdb_file("default_PMIC.acdb");
}
// END OF FILE

View File

@ -58,6 +58,7 @@ void __init htcleo_microp_init(void);
#endif
extern int __init htcleo_init_mmc(unsigned debug_uart);
extern void __init htcleo_audio_init(void);
///////////////////////////////////////////////////////////////////////
// SPI
@ -740,6 +741,7 @@ static void __init htcleo_init(void)
platform_device_register(&htcleo_timed_gpios);
htcleo_bt_init();
htcleo_audio_init();
#ifdef CONFIG_USB_ANDROID
msm_hsusb_set_vbus_state(htcleo_get_vbus_state());

View File

@ -33,7 +33,9 @@ struct audio_client {
int dsp_buf; /* next buffer the DSP will touch */
int running;
int session;
int open_done;
int open_status;
wait_queue_head_t wait;
struct dal_client *client;
@ -91,6 +93,7 @@ int q6audio_set_tx_mute(int mute);
int q6audio_reinit_acdb(char* filename);
int q6audio_update_acdb(uint32_t id_src, uint32_t id_dst);
int q6audio_set_rx_volume(int level);
int q6audio_set_rx_mute(int mute);
int q6audio_set_stream_volume(struct audio_client *ac, int vol);
struct q6audio_analog_ops {

View File

@ -0,0 +1,11 @@
obj-y += dal.o
obj-y += q6audio.o
obj-y += pcm_out.o
obj-y += pcm_in.o
obj-y += mp3.o
obj-y += routing.o
obj-y += audio_ctl.o
obj-y += msm_q6vdec.o
obj-y += msm_q6venc.o
obj-y += aac_in.o
obj-y += qcelp_in.o

View File

@ -0,0 +1,217 @@
/* arch/arm/mach-msm/qdsp6/aac_in.c
*
* Copyright (C) 2009 Google, Inc.
* Copyright (C) 2009 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/fs.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/mutex.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/uaccess.h>
#include <linux/msm_audio.h>
#include <mach/msm_qdsp6_audio.h>
#define BUFSZ (4096)
#define DMASZ (BUFSZ * 2)
#if 0
#define TRACE(x...) pr_info("Q6: "x)
#else
#define TRACE(x...) do{}while(0)
#endif
static DEFINE_MUTEX(aac_in_lock);
static int aac_in_opened = 0;
static struct aac_format *af;
void audio_client_dump(struct audio_client *ac);
static long aac_in_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int rc = 0;
switch (cmd) {
case AUDIO_SET_VOLUME:
break;
case AUDIO_GET_STATS: {
struct msm_audio_stats stats;
memset(&stats, 0, sizeof(stats));
if (copy_to_user((void*) arg, &stats, sizeof(stats)))
return -EFAULT;
return 0;
}
case AUDIO_START: {
uint32_t acdb_id;
rc = 0;
if (arg == 0) {
acdb_id = 0;
} else if (copy_from_user(&acdb_id,
(void*) arg, sizeof(acdb_id))) {
rc = -EFAULT;
break;
}
mutex_lock(&aac_in_lock);
if (file->private_data) {
rc = -EBUSY;
} else {
file->private_data = q6audio_open_aac(
BUFSZ, 48000, AUDIO_FLAG_READ, af, acdb_id);
if (!file->private_data)
rc = -ENOMEM;
}
mutex_unlock(&aac_in_lock);
break;
}
case AUDIO_STOP:
break;
case AUDIO_FLUSH:
break;
case AUDIO_SET_CONFIG: {
struct msm_audio_config config;
if (copy_from_user(&config, (void*) arg, sizeof(config))) {
rc = -EFAULT;
break;
}
if (config.sample_rate != 48000)
pr_info("only 48KHz AAC encode supported\n");
af->channel_config = config.channel_count;
break;
}
case AUDIO_GET_CONFIG: {
struct msm_audio_config config;
config.buffer_size = BUFSZ;
config.buffer_count = 2;
config.sample_rate = 48000;
config.channel_count = af->channel_config;
config.unused[0] = 0;
config.unused[1] = 0;
config.unused[2] = 0;
if (copy_to_user((void*) arg, &config, sizeof(config))) {
rc = -EFAULT;
}
break;
}
default:
rc = -EINVAL;
}
return rc;
}
static int aac_in_open(struct inode *inode, struct file *file)
{
int rc;
pr_info("aac_in: open\n");
mutex_lock(&aac_in_lock);
if (aac_in_opened) {
pr_err("aac_in: busy\n");
rc = -EBUSY;
} else {
af = kzalloc(sizeof(*af), GFP_KERNEL);
memset(af, 0, sizeof(struct aac_format));
af->sample_rate = 3; /* 48000 */
af->channel_config = 1;
af->block_formats = AUDIO_AAC_FORMAT_ADTS;
af->audio_object_type = 2; /* CAD to ADSP format */
af->bit_rate = 192000;
aac_in_opened = 1;
rc = 0;
}
mutex_unlock(&aac_in_lock);
return rc;
}
static ssize_t aac_in_read(struct file *file, char __user *buf,
size_t count, loff_t *pos)
{
struct audio_client *ac;
struct audio_buffer *ab;
const char __user *start = buf;
int xfer, res = 0;
mutex_lock(&aac_in_lock);
ac = file->private_data;
if (!ac) {
res = -ENODEV;
goto fail;
}
while (count > 0) {
ab = ac->buf + ac->cpu_buf;
if (ab->used)
if (!wait_event_timeout(ac->wait, (ab->used == 0), 5*HZ)) {
audio_client_dump(ac);
pr_err("aac_read: timeout. dsp dead?\n");
BUG();
}
xfer = count;
if (xfer > ab->size)
xfer = ab->size;
if (copy_to_user(buf, ab->data, xfer)) {
res = -EFAULT;
goto fail;
}
buf += xfer;
count -= xfer;
ab->used = 1;
q6audio_read(ac, ab);
ac->cpu_buf ^= 1;
}
fail:
res = buf - start;
mutex_unlock(&aac_in_lock);
return res;
}
static int aac_in_release(struct inode *inode, struct file *file)
{
int rc = 0;
pr_info("aac_in: release\n");
mutex_lock(&aac_in_lock);
if (file->private_data)
rc = q6audio_close(file->private_data);
kfree(af);
aac_in_opened = 0;
mutex_unlock(&aac_in_lock);
return rc;
}
static struct file_operations aac_in_fops = {
.owner = THIS_MODULE,
.open = aac_in_open,
.read = aac_in_read,
.release = aac_in_release,
.unlocked_ioctl = aac_in_ioctl,
};
struct miscdevice aac_in_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "msm_aac_in",
.fops = &aac_in_fops,
};
static int __init aac_in_init(void) {
return misc_register(&aac_in_misc);
}
device_initcall(aac_in_init);

View File

@ -0,0 +1,252 @@
/* arch/arm/mach-msm/qdsp6/audio_ctrl.c
*
* Copyright (C) 2009 Google, Inc.
* Copyright (C) 2009 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/fs.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/msm_audio.h>
#include <mach/msm_qdsp6_audio.h>
#include <mach/htc_acoustic_qsd.h>
#define BUFSZ (0)
#if 1
#define AUDIO_INFO(x...) pr_info("Audio: "x)
#else
#define AUDIO_INFO(x...) do{}while(0)
#endif
// from board-htcleo-accoustic
extern int set_aux_gain(int level);
static DEFINE_MUTEX(voice_lock);
static DEFINE_MUTEX(fm_lock);
static int voice_started;
static int fm_started;
int global_now_phone_call;
static struct audio_client *voc_tx_clnt;
static struct audio_client *voc_rx_clnt;
static struct audio_client *fm_clnt;
static int q6_voice_start(uint32_t rx_acdb_id, uint32_t tx_acdb_id)
{
int rc = 0;
printk("VOICE START (%d %d)\n", rx_acdb_id, tx_acdb_id);
mutex_lock(&voice_lock);
if (voice_started) {
pr_err("voice: busy\n");
rc = -EBUSY;
goto done;
}
global_now_phone_call = 1;
voc_rx_clnt = q6voice_open(AUDIO_FLAG_WRITE, rx_acdb_id);
if (!voc_rx_clnt) {
pr_err("voice: open voice rx failed.\n");
rc = -ENOMEM;
goto done;
}
voc_tx_clnt = q6voice_open(AUDIO_FLAG_READ, tx_acdb_id);
if (!voc_tx_clnt) {
pr_err("voice: open voice tx failed.\n");
q6voice_close(voc_rx_clnt);
rc = -ENOMEM;
}
voice_started = 1;
done:
mutex_unlock(&voice_lock);
return rc;
}
static int q6_voice_stop(void)
{
mutex_lock(&voice_lock);
global_now_phone_call = 0;
if (voice_started)
{
q6voice_close(voc_tx_clnt);
q6voice_close(voc_rx_clnt);
voice_started = 0;
}
mutex_unlock(&voice_lock);
return 0;
}
static int q6_fm_start(void)
{
int rc = 0;
mutex_lock(&fm_lock);
if (fm_started) {
pr_err("fm: busy\n");
rc = -EBUSY;
goto done;
}
fm_clnt = q6fm_open();
if (!fm_clnt) {
pr_err("fm: open failed.\n");
rc = -ENOMEM;
goto done;
}
fm_started = 1;
done:
mutex_unlock(&fm_lock);
return rc;
}
static int q6_fm_stop(void)
{
mutex_lock(&fm_lock);
if (fm_started) {
q6fm_close(fm_clnt);
fm_started = 0;
}
mutex_unlock(&fm_lock);
return 0;
}
static int q6_open(struct inode *inode, struct file *file)
{
return 0;
}
static int q6_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
int rc;
uint32_t n;
uint32_t id[2];
char filename[64];
// printk("$$$ AUDIO IOCTL=%08X\n", cmd);
switch (cmd) {
case AUDIO_SWITCH_DEVICE:
rc = copy_from_user(&id, (void *)arg, sizeof(id));
AUDIO_INFO("SWITCH DEVICE %d, acdb %d\n", id[0], id[1]);
if (!rc)
rc = q6audio_do_routing(id[0], id[1]);
break;
case AUDIO_SET_VOLUME:
rc = copy_from_user(&n, (void *)arg, sizeof(n));
if (!rc)
rc = q6audio_set_rx_volume(n);
break;
case AUDIO_SET_MUTE:
rc = copy_from_user(&n, (void *)arg, sizeof(n));
if (!rc)
rc = q6audio_set_tx_mute(n);
break;
case AUDIO_UPDATE_ACDB:
rc = copy_from_user(&id, (void *)arg, sizeof(id));
if (!rc)
rc = q6audio_update_acdb(id[0], id[1]);
break;
case AUDIO_START_VOICE:
if (arg == 0) {
id[0] = id[1] = 0;
} else if (copy_from_user(&id, (void*) arg, sizeof(id))) {
pr_info("voice: copy acdb_id from user failed\n");
rc = -EFAULT;
break;
}
AUDIO_INFO("voice: start\n");
rc = q6_voice_start(id[0], id[1]);
break;
case AUDIO_STOP_VOICE:
AUDIO_INFO("voice: stop\n");
rc = q6_voice_stop();
break;
case AUDIO_START_FM:
AUDIO_INFO("FM: start\n");
rc = q6_fm_start();
break;
case AUDIO_STOP_FM:
AUDIO_INFO("FM: stop\n");
rc = q6_fm_stop();
break;
case AUDIO_REINIT_ACDB:
rc = copy_from_user(&filename, (void *)arg, sizeof(filename));
if (!rc)
rc = q6audio_reinit_acdb(filename);
break;
case AUDIO_ENABLE_AUXPGA_LOOPBACK: {
uint32_t enable;
if (copy_from_user(&enable, (void*) arg, sizeof(enable))) {
rc = -EFAULT;
break;
}
AUDIO_INFO("audio_ctl: enable aux loopback %d\n", enable);
rc = enable_aux_loopback(enable);
break;
}
case AUDIO_SET_AUXPGA_GAIN: {
int level;
if (copy_from_user(&level, (void*) arg, sizeof(level))) {
rc = -EFAULT;
break;
}
AUDIO_INFO("audio_ctl: set aux gain %d\n", level);
rc = set_aux_gain(level);
break;
}
case AUDIO_SET_RX_MUTE:
rc = copy_from_user(&n, (void *)arg, sizeof(n));
if (!rc)
rc = q6audio_set_rx_mute(n);
break;
default:
rc = -EINVAL;
}
return rc;
}
static int q6_release(struct inode *inode, struct file *file)
{
return 0;
}
static struct file_operations q6_dev_fops = {
.owner = THIS_MODULE,
.open = q6_open,
.ioctl = q6_ioctl,
.release = q6_release,
};
struct miscdevice q6_control_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = "msm_audio_ctl",
.fops = &q6_dev_fops,
};
static int __init q6_audio_ctl_init(void) {
return misc_register(&q6_control_device);
}
device_initcall(q6_audio_ctl_init);

View File

@ -0,0 +1,746 @@
/* arch/arm/mach-msm/qdsp6/dal.c
*
* Copyright (C) 2009 Google, Inc.
* Author: Brian Swetland <swetland@google.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/kernel.h>
#include <linux/spinlock.h>
#include <linux/mutex.h>
#include <linux/list.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <mach/msm_smd.h>
#include "dal.h"
#define DAL_TRACE 0
struct dal_hdr {
uint32_t length:16; /* message length (header inclusive) */
uint32_t version:8; /* DAL protocol version */
uint32_t priority:7;
uint32_t async:1;
uint32_t ddi:16; /* DDI method number */
uint32_t prototype:8; /* DDI serialization format */
uint32_t msgid:8; /* message id (DDI, ATTACH, DETACH, ...) */
void *from;
void *to;
} __attribute__((packed));
#define TRACE_DATA_MAX 128
#define TRACE_LOG_MAX 32
#define TRACE_LOG_MASK (TRACE_LOG_MAX - 1)
struct dal_trace {
unsigned timestamp;
struct dal_hdr hdr;
uint32_t data[TRACE_DATA_MAX];
};
#define DAL_HDR_SIZE (sizeof(struct dal_hdr))
#define DAL_DATA_MAX 512
#define DAL_MSG_MAX (DAL_HDR_SIZE + DAL_DATA_MAX)
#define DAL_VERSION 0x11
#define DAL_MSGID_DDI 0x00
#define DAL_MSGID_ATTACH 0x01
#define DAL_MSGID_DETACH 0x02
#define DAL_MSGID_ASYNCH 0xC0
#define DAL_MSGID_REPLY 0x80
struct dal_channel {
struct list_head list;
struct list_head clients;
/* synchronization for changing channel state,
* adding/removing clients, smd callbacks, etc
*/
spinlock_t lock;
struct smd_channel *sch;
char *name;
/* events are delivered at IRQ context immediately, so
* we only need one assembly buffer for the entire channel
*/
struct dal_hdr hdr;
unsigned char data[DAL_DATA_MAX];
unsigned count;
void *ptr;
/* client which the current inbound message is for */
struct dal_client *active;
};
struct dal_client {
struct list_head list;
struct dal_channel *dch;
void *cookie;
dal_event_func_t event;
/* opaque handle for the far side */
void *remote;
/* dal rpc calls are fully synchronous -- only one call may be
* active per client at a time
*/
struct mutex write_lock;
wait_queue_head_t wait;
unsigned char data[DAL_DATA_MAX];
void *reply;
int reply_max;
int status;
unsigned msgid; /* msgid of expected reply */
spinlock_t tr_lock;
unsigned tr_head;
unsigned tr_tail;
struct dal_trace *tr_log;
};
static unsigned now(void)
{
struct timespec ts;
ktime_get_ts(&ts);
return (ts.tv_nsec / 1000000) + (ts.tv_sec * 1000);
}
void dal_trace(struct dal_client *c)
{
if (c->tr_log)
return;
c->tr_log = kzalloc(sizeof(struct dal_trace) * TRACE_LOG_MAX,
GFP_KERNEL);
}
void dal_trace_print(struct dal_hdr *hdr, unsigned *data, int len, unsigned when)
{
int i;
printk("DAL %08x -> %08x L=%03x A=%d D=%04x P=%02x M=%02x T=%d",
(unsigned) hdr->from, (unsigned) hdr->to,
hdr->length, hdr->async,
hdr->ddi, hdr->prototype, hdr->msgid,
when);
len /= 4;
for (i = 0; i < len; i++) {
if (!(i & 7))
printk("\n%03x", i * 4);
printk(" %08x", data[i]);
}
printk("\n");
}
void dal_trace_dump(struct dal_client *c)
{
struct dal_trace *dt;
unsigned n, len;
if (!c->tr_log)
return;
for (n = c->tr_tail; n != c->tr_head; n = (n + 1) & TRACE_LOG_MASK) {
dt = c->tr_log + n;
len = dt->hdr.length;
if (len > TRACE_DATA_MAX)
len = TRACE_DATA_MAX;
dal_trace_print(&dt->hdr, dt->data, len, dt->timestamp);
}
}
static void dal_trace_log(struct dal_client *c,
struct dal_hdr *hdr, void *data, unsigned len)
{
unsigned long flags;
unsigned t, n;
struct dal_trace *dt;
t = now();
if (len > TRACE_DATA_MAX)
len = TRACE_DATA_MAX;
spin_lock_irqsave(&c->tr_lock, flags);
n = (c->tr_head + 1) & TRACE_LOG_MASK;
if (c->tr_tail == n)
c->tr_tail = (c->tr_tail + 1) & TRACE_LOG_MASK;
dt = c->tr_log + n;
dt->timestamp = t;
memcpy(&dt->hdr, hdr, sizeof(struct dal_hdr));
memcpy(dt->data, data, len);
c->tr_head = n;
spin_unlock_irqrestore(&c->tr_lock, flags);
}
static void dal_channel_notify(void *priv, unsigned event)
{
struct dal_channel *dch = priv;
struct dal_hdr *hdr = &dch->hdr;
struct dal_client *client;
unsigned long flags;
int len;
int r;
spin_lock_irqsave(&dch->lock, flags);
again:
if (dch->count == 0) {
if (smd_read_avail(dch->sch) < DAL_HDR_SIZE)
goto done;
smd_read(dch->sch, hdr, DAL_HDR_SIZE);
if (hdr->length < DAL_HDR_SIZE)
goto done;
if (hdr->length > DAL_MSG_MAX)
panic("oversize message");
dch->count = hdr->length - DAL_HDR_SIZE;
/* locate the client this message is targeted to */
list_for_each_entry(client, &dch->clients, list) {
if (dch->hdr.to == client) {
dch->active = client;
dch->ptr = client->data;
goto check_data;
}
}
pr_err("$$$ receiving unknown message len = %d $$$\n",
dch->count);
dch->active = 0;
dch->ptr = dch->data;
}
check_data:
len = dch->count;
if (len > 0) {
if (smd_read_avail(dch->sch) < len)
goto done;
r = smd_read(dch->sch, dch->ptr, len);
if (r != len)
panic("invalid read");
#if DAL_TRACE
pr_info("dal recv %p <- %p %02x:%04x:%02x %d\n",
hdr->to, hdr->from, hdr->msgid, hdr->ddi,
hdr->prototype, hdr->length - sizeof(*hdr));
print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, dch->ptr, len);
#endif
dch->count = 0;
client = dch->active;
if (!client) {
pr_err("dal: message to %p discarded\n", dch->hdr.to);
goto again;
}
if (client->tr_log)
dal_trace_log(client, hdr, dch->ptr, len);
if (hdr->msgid == DAL_MSGID_ASYNCH) {
if (client->event)
client->event(dch->ptr, len, client->cookie);
else
pr_err("dal: client %p has no event handler\n", client);
goto again;
}
if (hdr->msgid == client->msgid) {
if (!client->remote)
client->remote = hdr->from;
if (len > client->reply_max)
len = client->reply_max;
memcpy(client->reply, client->data, len);
client->status = len;
wake_up(&client->wait);
goto again;
}
pr_err("dal: cannot find client %p\n", dch->hdr.to);
goto again;
}
done:
spin_unlock_irqrestore(&dch->lock, flags);
}
static LIST_HEAD(dal_channel_list);
static DEFINE_MUTEX(dal_channel_list_lock);
static struct dal_channel *dal_open_channel(const char *name)
{
struct dal_channel *dch;
/* quick sanity check to avoid trying to talk to
* some non-DAL channel...
*/
if (strncmp(name, "DSP_DAL", 7) && strncmp(name, "SMD_DAL", 7))
return 0;
mutex_lock(&dal_channel_list_lock);
list_for_each_entry(dch, &dal_channel_list, list) {
if (!strcmp(dch->name, name))
goto found_it;
}
dch = kzalloc(sizeof(*dch) + strlen(name) + 1, GFP_KERNEL);
if (!dch)
goto fail;
dch->name = (char *) (dch + 1);
strcpy(dch->name, name);
spin_lock_init(&dch->lock);
INIT_LIST_HEAD(&dch->clients);
list_add(&dch->list, &dal_channel_list);
found_it:
if (!dch->sch) {
if (smd_open(name, &dch->sch, dch, dal_channel_notify))
dch = NULL;
/* FIXME: wait for channel to open before returning */
msleep(100);
}
fail:
mutex_unlock(&dal_channel_list_lock);
return dch;
}
int dal_call_raw(struct dal_client *client,
struct dal_hdr *hdr,
void *data, int data_len,
void *reply, int reply_max)
{
struct dal_channel *dch = client->dch;
unsigned long flags;
client->reply = reply;
client->reply_max = reply_max;
client->msgid = hdr->msgid | DAL_MSGID_REPLY;
client->status = -EBUSY;
#if DAL_TRACE
pr_info("dal send %p -> %p %02x:%04x:%02x %d\n",
hdr->from, hdr->to, hdr->msgid, hdr->ddi,
hdr->prototype, hdr->length - sizeof(*hdr));
print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, data, data_len);
#endif
if (client->tr_log)
dal_trace_log(client, hdr, data, data_len);
spin_lock_irqsave(&dch->lock, flags);
/* FIXME: ensure entire message is written or none. */
smd_write(dch->sch, hdr, sizeof(*hdr));
smd_write(dch->sch, data, data_len);
spin_unlock_irqrestore(&dch->lock, flags);
if (!wait_event_timeout(client->wait, (client->status != -EBUSY), 5*HZ))
{
dal_trace_dump(client);
pr_err("dal: call timed out. dsp is probably dead.\n");
dal_trace_print(hdr, data, data_len, 0);
// BUG();
}
return client->status;
}
int dal_call(struct dal_client *client,
unsigned ddi, unsigned prototype,
void *data, int data_len,
void *reply, int reply_max)
{
struct dal_hdr hdr;
int r;
memset(&hdr, 0, sizeof(hdr));
hdr.length = data_len + sizeof(hdr);
hdr.version = DAL_VERSION;
hdr.msgid = DAL_MSGID_DDI;
hdr.ddi = ddi;
hdr.prototype = prototype;
hdr.from = client;
hdr.to = client->remote;
if (hdr.length > DAL_MSG_MAX)
return -EINVAL;
mutex_lock(&client->write_lock);
r = dal_call_raw(client, &hdr, data, data_len, reply, reply_max);
mutex_unlock(&client->write_lock);
#if 1
if ((r > 3) && (((uint32_t*) reply)[0] == 0)) {
// pr_info("dal call OK\n");
} else {
pr_info("dal call %d %d ERROR\n", ddi, prototype);
}
#endif
return r;
}
struct dal_msg_attach {
uint32_t device_id;
char attach[64];
char service_name[32];
} __attribute__((packed));
struct dal_reply_attach {
uint32_t status;
char name[64];
};
struct dal_client *dal_attach_ex(uint32_t device_id, const char *aname, const char *name, dal_event_func_t func, void *cookie)
{
struct dal_hdr hdr;
struct dal_msg_attach msg;
struct dal_reply_attach reply;
struct dal_channel *dch;
struct dal_client *client;
unsigned long flags;
int r;
dch = dal_open_channel(name);
if (!dch)
return 0;
client = kzalloc(sizeof(*client), GFP_KERNEL);
if (!client)
return 0;
client->dch = dch;
client->event = func;
client->cookie = cookie;
mutex_init(&client->write_lock);
spin_lock_init(&client->tr_lock);
init_waitqueue_head(&client->wait);
spin_lock_irqsave(&dch->lock, flags);
list_add(&client->list, &dch->clients);
spin_unlock_irqrestore(&dch->lock, flags);
memset(&hdr, 0, sizeof(hdr));
memset(&msg, 0, sizeof(msg));
hdr.length = sizeof(hdr) + sizeof(msg);
hdr.version = DAL_VERSION;
hdr.msgid = DAL_MSGID_ATTACH;
hdr.from = client;
msg.device_id = device_id;
if (aname)
strcpy(msg.attach, aname);
r = dal_call_raw(client, &hdr, &msg, sizeof(msg),
&reply, sizeof(reply));
if ((r == sizeof(reply)) && (reply.status == 0)) {
reply.name[63] = 0;
pr_info("dal_attach: status = %d, name = '%s'\n",
reply.status, reply.name);
return client;
}
pr_err("dal_attach: failure\n");
dal_detach(client);
return 0;
}
struct dal_client *dal_attach(uint32_t device_id, const char *name,
dal_event_func_t func, void *cookie)
{
struct dal_hdr hdr;
struct dal_msg_attach msg;
struct dal_reply_attach reply;
struct dal_channel *dch;
struct dal_client *client;
unsigned long flags;
int r;
dch = dal_open_channel(name);
if (!dch)
return 0;
client = kzalloc(sizeof(*client), GFP_KERNEL);
if (!client)
return 0;
client->dch = dch;
client->event = func;
client->cookie = cookie;
mutex_init(&client->write_lock);
spin_lock_init(&client->tr_lock);
init_waitqueue_head(&client->wait);
spin_lock_irqsave(&dch->lock, flags);
list_add(&client->list, &dch->clients);
spin_unlock_irqrestore(&dch->lock, flags);
memset(&hdr, 0, sizeof(hdr));
memset(&msg, 0, sizeof(msg));
hdr.length = sizeof(hdr) + sizeof(msg);
hdr.version = DAL_VERSION;
hdr.msgid = DAL_MSGID_ATTACH;
hdr.from = client;
msg.device_id = device_id;
r = dal_call_raw(client, &hdr, &msg, sizeof(msg),
&reply, sizeof(reply));
if ((r == sizeof(reply)) && (reply.status == 0)) {
reply.name[63] = 0;
pr_info("dal_attach: status = %d, name = '%s'\n",
reply.status, reply.name);
return client;
}
pr_err("dal_attach: failure\n");
dal_detach(client);
return 0;
}
int dal_detach(struct dal_client *client)
{
struct dal_channel *dch;
unsigned long flags;
mutex_lock(&client->write_lock);
if (client->remote) {
struct dal_hdr hdr;
uint32_t data;
memset(&hdr, 0, sizeof(hdr));
hdr.length = sizeof(hdr) + sizeof(data);
hdr.version = DAL_VERSION;
hdr.msgid = DAL_MSGID_DETACH;
hdr.from = client;
hdr.to = client->remote;
data = (uint32_t) client;
dal_call_raw(client, &hdr, &data, sizeof(data),
&data, sizeof(data));
}
dch = client->dch;
spin_lock_irqsave(&dch->lock, flags);
if (dch->active == client) {
/* We have received a message header for this client
* but not the body of the message. Ensure that when
* the body arrives we don't write it into the now-closed
* client. In *theory* this should never happen.
*/
dch->active = 0;
dch->ptr = dch->data;
}
list_del(&client->list);
spin_unlock_irqrestore(&dch->lock, flags);
mutex_unlock(&client->write_lock);
kfree(client);
return 0;
}
void *dal_get_remote_handle(struct dal_client *client)
{
return client->remote;
}
/* convenience wrappers */
int dal_call_f0(struct dal_client *client, uint32_t ddi, uint32_t arg1)
{
uint32_t tmp = arg1;
int res;
res = dal_call(client, ddi, 0, &tmp, sizeof(tmp), &tmp, sizeof(tmp));
if (res >= 4)
return (int) tmp;
return res;
}
int dal_call_f1(struct dal_client *client, uint32_t ddi, uint32_t arg1, uint32_t arg2)
{
uint32_t tmp[2];
int res;
tmp[0] = arg1;
tmp[1] = arg2;
res = dal_call(client, ddi, 1, tmp, sizeof(tmp), tmp, sizeof(uint32_t));
if (res >= 4)
return (int) tmp[0];
return res;
}
int dal_call_f5(struct dal_client *client, uint32_t ddi, void *ibuf, uint32_t ilen)
{
// uint32_t tmp[128];
uint32_t tmp[DAL_DATA_MAX];
int res;
int param_idx = 0;
if (ilen + 4 > DAL_DATA_MAX)
return -EINVAL;
tmp[param_idx] = ilen;
param_idx++;
memcpy(&tmp[param_idx], ibuf, ilen);
param_idx += DIV_ROUND_UP(ilen, 4);
res = dal_call(client, ddi, 5, tmp, param_idx * 4, tmp, sizeof(tmp));
if (res >= 4)
return (int) tmp[0];
return res;
}
int dal_call_f6(struct dal_client *client, uint32_t ddi, uint32_t cmd, void *ibuf, uint32_t ilen)
{
uint32_t tmp[DAL_DATA_MAX];
int res;
int param_idx = 0;
if (ilen + 4 > DAL_DATA_MAX)
return -EINVAL;
tmp[param_idx] = cmd;
param_idx++;
tmp[param_idx] = ilen;
param_idx++;
memcpy(&tmp[param_idx], ibuf, ilen);
param_idx += DIV_ROUND_UP(ilen, 4);
res = dal_call(client, ddi, 6, tmp, param_idx * 4, tmp, sizeof(tmp));
if (res >= 4)
return (int) tmp[0];
return res;
}
int dal_call_f8(struct dal_client *client, uint32_t ddi, void *ibuf, uint32_t ilen, void *obuf, uint32_t olen)
{
uint32_t tmp[128];
int res;
int param_idx = 0;
if (ilen + 8 > DAL_DATA_MAX)
return -EINVAL;
tmp[param_idx] = ilen;
param_idx++;
memcpy(&tmp[param_idx], ibuf, ilen);
param_idx += DIV_ROUND_UP(ilen, 4);
tmp[param_idx++] = olen;
res = dal_call(client, ddi, 8, tmp, param_idx * 4, tmp, sizeof(tmp));
if (res >= 4)
{
res = (int)tmp[0];
}
if (!res)
{
if (tmp[1] > olen)
return -EIO;
memcpy(obuf, &tmp[2], tmp[1]);
}
return res;
}
int dal_call_f9(struct dal_client *client, uint32_t ddi, void *obuf, uint32_t olen)
{
uint32_t tmp[128];
int res;
int param_idx = 0;
if (olen + 8 > DAL_DATA_MAX)
return -EINVAL;
tmp[param_idx++] = olen;
res = dal_call(client, ddi, 9, tmp, param_idx * 4, tmp, sizeof(tmp));
if (res >= 4)
{
res = (int)tmp[0];
}
if (!res)
{
if (tmp[1] > olen)
return -EIO;
memcpy(obuf, &tmp[2], tmp[1]);
}
return res;
}
int dal_call_f13(struct dal_client *client, uint32_t ddi, void *ibuf1,
uint32_t ilen1, void *ibuf2, uint32_t ilen2, void *obuf, uint32_t olen)
{
uint32_t tmp[128];
int res;
int param_idx = 0;
if (ilen1 + ilen2 + 8 > DAL_DATA_MAX)
return -EINVAL;
tmp[param_idx] = ilen1;
param_idx++;
memcpy(&tmp[param_idx], ibuf1, ilen1);
param_idx += DIV_ROUND_UP(ilen1, 4);
tmp[param_idx++] = ilen2;
memcpy(&tmp[param_idx], ibuf2, ilen2);
param_idx += DIV_ROUND_UP(ilen2, 4);
tmp[param_idx++] = olen;
res = dal_call(client, ddi, 13, tmp, param_idx * 4, tmp, sizeof(tmp));
if (res >= 4)
res = (int)tmp[0];
if (!res) {
if (tmp[1] > olen)
return -EIO;
memcpy(obuf, &tmp[2], tmp[1]);
}
return res;
}
// END OF FILE

View File

@ -0,0 +1,65 @@
/* arch/arm/mach-msm/qdsp6/dal.h
*
* Copyright (C) 2009 Google, Inc.
* Author: Brian Swetland <swetland@google.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 _MACH_MSM_DAL_
#define _MACH_MSM_DAL_
struct dal_client;
typedef void (*dal_event_func_t)(void *data, int len, void *cookie);
struct dal_client *dal_attach(uint32_t device_id, const char *name, dal_event_func_t func, void *cookie);
struct dal_client *dal_attach_ex(uint32_t device_id, const char *aname, const char *name, dal_event_func_t func, void *cookie);
int dal_detach(struct dal_client *client);
int dal_call(struct dal_client *client,
unsigned ddi, unsigned prototype,
void *data, int data_len,
void *reply, int reply_max);
void dal_trace(struct dal_client *client);
void dal_trace_dump(struct dal_client *client);
/* function to call before panic on stalled dal calls */
void dal_set_oops(struct dal_client *client, void (*oops)(void));
/* convenience wrappers */
int dal_call_f0(struct dal_client *client, uint32_t ddi, uint32_t arg1);
int dal_call_f1(struct dal_client *client, uint32_t ddi, uint32_t arg1, uint32_t arg2);
int dal_call_f5(struct dal_client *client, uint32_t ddi, void *ibuf, uint32_t ilen);
int dal_call_f6(struct dal_client *client, uint32_t ddi, uint32_t cmd, void *ibuf, uint32_t ilen);
int dal_call_f8(struct dal_client *client, uint32_t ddi, void *ibuf, uint32_t ilen, void *obuf, uint32_t olen);
int dal_call_f9(struct dal_client *client, uint32_t ddi, void *obuf, uint32_t olen);
int dal_call_f13(struct dal_client *client, uint32_t ddi, void *ibuf1,
uint32_t ilen1, void *ibuf2, uint32_t ilen2, void *obuf, uint32_t olen);
/* common DAL operations */
enum {
DAL_OP_ATTACH = 0,
DAL_OP_DETACH,
DAL_OP_INIT,
DAL_OP_DEINIT,
DAL_OP_OPEN,
DAL_OP_CLOSE,
DAL_OP_INFO,
DAL_OP_POWEREVENT,
DAL_OP_SYSREQUEST,
DAL_OP_FIRST_DEVICE_API,
};
#endif

View File

@ -0,0 +1,90 @@
/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Code Aurora Forum nor
* the names of its contributors may be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#define ACDB_DAL_DEVICE 0x02000069
//#define ACDB_DAL_PORT "SMD_DAL_AM_AUD"
#define ACDB_DAL_PORT "SMD_DAL00"
#define ACDB_OP_IOCTL DAL_OP_FIRST_DEVICE_API
/* ioctls */
#define ACDB_GET_DEVICE 0x0108bb92
#define ACDB_SET_DEVICE 0x0108bb93
#define ACDB_GET_STREAM 0x0108bb95
#define ACDB_SET_STREAM 0x0108bb96
#define ACDB_GET_DEVICE_TABLE 0x0108bb97
#define ACDB_GET_STREAM_TABLE 0x0108bb98
#define ACDB_RES_SUCCESS 0
#define ACDB_RES_FAILURE -1
#define ACDB_RES_BADPARM -2
#define ACDB_RES_BADSTATE -3
struct acdb_cmd_device
{
uint32_t size;
uint32_t command_id;
uint32_t device_id;
uint32_t network_id;
uint32_t sample_rate_id;
uint32_t interface_id;
uint32_t algorithm_block_id;
/* physical page aligned buffer */
uint32_t total_bytes;
uint32_t unmapped_buf;
} __attribute__((packed));
struct acdb_cmd_device_table
{
uint32_t size;
uint32_t command_id;
uint32_t device_id;
uint32_t network_id;
uint32_t sample_rate_id;
/* physical page aligned buffer */
uint32_t total_bytes;
uint32_t unmapped_buf;
uint32_t res_size;
} __attribute__((packed));
struct acdb_result
{
uint32_t dal_status;
uint32_t size;
uint32_t unmapped_buf;
uint32_t used_bytes;
uint32_t result;
} __attribute__((packed));
// END OF FILE

View File

@ -0,0 +1,110 @@
/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Code Aurora Forum nor
* the names of its contributors may be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef _MACH_MSM_QDSP6_ADIE_
#define _MACH_MSM_QDSP6_ADIE_
#include "dal.h"
#define ADIE_DAL_DEVICE 0x02000029
#define ADIE_DAL_PORT "SMD_DAL00"
//#define ADIE_DAL_PORT "SMD_DAL_AM_AUD"
enum {
ADIE_OP_GET_NUM_PATHS = DAL_OP_FIRST_DEVICE_API,
ADIE_OP_GET_ALL_PATH_IDS,
ADIE_OP_SET_PATH,
ADIE_OP_GET_NUM_PATH_FREQUENCY_PLANS,
ADIE_OP_GET_PATH_FREQUENCY_PLANS,
ADIE_OP_SET_PATH_FREQUENCY_PLAN,
ADIE_OP_PROCEED_TO_STAGE,
ADIE_OP_MUTE_PATH
};
/* Path IDs for normal operation. */
#define ADIE_PATH_HANDSET_TX 0x010740f6
#define ADIE_PATH_HANDSET_RX 0x010740f7
#define ADIE_PATH_HEADSET_MONO_TX 0x010740f8
#define ADIE_PATH_HEADSET_STEREO_TX 0x010740f9
#define ADIE_PATH_HEADSET_MONO_RX 0x010740fa
#define ADIE_PATH_HEADSET_STEREO_RX 0x010740fb
#define ADIE_PATH_SPEAKER_TX 0x010740fc
#define ADIE_PATH_SPEAKER_RX 0x010740fd
#define ADIE_PATH_SPEAKER_STEREO_RX 0x01074101
/* Path IDs used for TTY */
#define ADIE_PATH_TTY_HEADSET_TX 0x010740fe
#define ADIE_PATH_TTY_HEADSET_RX 0x010740ff
/* Path IDs used by Factory Test Mode. */
#define ADIE_PATH_FTM_MIC1_TX 0x01074108
#define ADIE_PATH_FTM_MIC2_TX 0x01074107
#define ADIE_PATH_FTM_HPH_L_RX 0x01074106
#define ADIE_PATH_FTM_HPH_R_RX 0x01074104
#define ADIE_PATH_FTM_EAR_RX 0x01074103
#define ADIE_PATH_FTM_SPKR_RX 0x01074102
/* Path IDs for Loopback */
/* Path IDs used for Line in -> AuxPGA -> Line Out Stereo Mode*/
#define ADIE_PATH_AUXPGA_LINEOUT_STEREO_LB 0x01074100
/* Line in -> AuxPGA -> LineOut Mono */
#define ADIE_PATH_AUXPGA_LINEOUT_MONO_LB 0x01073d82
/* Line in -> AuxPGA -> Stereo Headphone */
#define ADIE_PATH_AUXPGA_HDPH_STEREO_LB 0x01074109
/* Line in -> AuxPGA -> Mono Headphone */
#define ADIE_PATH_AUXPGA_HDPH_MONO_LB 0x01073d85
/* Line in -> AuxPGA -> Earpiece */
#define ADIE_PATH_AUXPGA_EAP_LB 0x01073d81
/* Line in -> AuxPGA -> AuxOut */
#define ADIE_PATH_AUXPGA_AUXOUT_LB 0x01073d86
/* Concurrency Profiles */
#define ADIE_PATH_SPKR_STEREO_HDPH_MONO_RX 0x01073d83
#define ADIE_PATH_SPKR_MONO_HDPH_MONO_RX 0x01073d84
#define ADIE_PATH_SPKR_MONO_HDPH_STEREO_RX 0x01073d88
#define ADIE_PATH_SPKR_STEREO_HDPH_STEREO_RX 0x01073d89
/* stages */
#define ADIE_STAGE_PATH_OFF 0x0050
#define ADIE_STAGE_DIGITAL_READY 0x0100
#define ADIE_STAGE_DIGITAL_ANALOG_READY 0x1000
#define ADIE_STAGE_ANALOG_OFF 0x0750
#define ADIE_STAGE_DIGITAL_OFF 0x0600
/* path types */
#define ADIE_PATH_RX 0
#define ADIE_PATH_TX 1
#define ADIE_PATH_LOOPBACK 2
/* mute states */
#define ADIE_MUTE_OFF 0
#define ADIE_MUTE_ON 1
#endif

View File

@ -0,0 +1,690 @@
/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Code Aurora Forum nor
* the names of its contributors may be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef __DAL_AUDIO_H__
#define __DAL_AUDIO_H__
#include "dal_audio_format.h"
#include "dal.h"
#include <mach/msm_qdsp6_audio.h>
#define AUDIO_DAL_DEVICE 0x02000028
#define AUDIO_DAL_PORT "DSP_DAL_AQ_AUD"
enum
{
#if defined(CONFIG_MACH_HTCLEO)
AUDIO_OP_OPEN = DAL_OP_FIRST_DEVICE_API,
AUDIO_OP_WRITE,
AUDIO_OP_READ,
AUDIO_OP_IOCTL,
AUDIO_OP_INIT,
AUDIO_OP_CLOSE,
AUDIO_OP_FLUSH
#else
AUDIO_OP_CONTROL = DAL_OP_FIRST_DEVICE_API,
AUDIO_OP_DATA,
AUDIO_OP_INIT,
#endif
};
/* ---- common audio structures ---- */
/* This flag, if set, indicates that the beginning of the data in the*/
/* buffer is a synchronization point or key frame, meaning no data */
/* before it in the stream is required in order to render the stream */
/* from this point onward. */
#define ADSP_AUDIO_BUFFER_FLAG_SYNC_POINT 0x01
/* This flag, if set, indicates that the buffer object is using valid */
/* physical address used to store the media data */
#define ADSP_AUDIO_BUFFER_FLAG_PHYS_ADDR 0x04
/* This flag, if set, indicates that a media start timestamp has been */
/* set for a buffer. */
#define ADSP_AUDIO_BUFFER_FLAG_START_SET 0x08
/* This flag, if set, indicates that a media stop timestamp has been set */
/* for a buffer. */
#define ADSP_AUDIO_BUFFER_FLAG_STOP_SET 0x10
/* This flag, if set, indicates that a preroll timestamp has been set */
/* for a buffer. */
#define ADSP_AUDIO_BUFFER_FLAG_PREROLL_SET 0x20
/* This flag, if set, indicates that the data in the buffer is a fragment of */
/* a larger block of data, and will be continued by the data in the next */
/* buffer to be delivered. */
#define ADSP_AUDIO_BUFFER_FLAG_CONTINUATION 0x40
struct adsp_audio_buffer {
u32 addr; /* Physical Address of buffer */
u32 max_size; /* Maximum size of buffer */
u32 actual_size; /* Actual size of valid data in the buffer */
u32 offset; /* Offset to the first valid byte */
u32 flags; /* ADSP_AUDIO_BUFFER_FLAGs that has been set */
s64 start; /* Start timestamp, if any */
s64 stop; /* Stop timestamp, if any */
s64 preroll; /* Preroll timestamp, if any */
} __attribute__ ((packed));
/* ---- audio commands ---- */
/* Command/event response types */
#define ADSP_AUDIO_RESPONSE_COMMAND 0
#define ADSP_AUDIO_RESPONSE_ASYNC 1
#if !defined(CONFIG_MACH_HTCLEO)
struct adsp_command_hdr {
u32 size; /* sizeof(cmd) - sizeof(u32) */
u32 dst;
u32 src;
u32 opcode;
u32 response_type;
u32 seq_number;
u32 context; /* opaque to DSP */
u32 data;
u32 padding;
} __attribute__ ((packed));
#else
struct adsp_command_hdr
{
u32 context;
u32 data;
} __attribute__ ((packed));
#endif
#define AUDIO_DOMAIN_APP 0
#define AUDIO_DOMAIN_MODEM 1
#define AUDIO_DOMAIN_DSP 2
#define AUDIO_SERVICE_AUDIO 0
#define AUDIO_SERVICE_VIDEO 1 /* really? */
/* adsp audio addresses are (byte order) domain, service, major, minor */
//#define AUDIO_ADDR(maj,min) ( (((maj) & 0xff) << 16) | (((min) & 0xff) << 24) | (1) )
#define AUDIO_ADDR(maj,min,dom) ( (((min) & 0xff) << 24) | (((maj) & 0xff) << 16) | ((AUDIO_SERVICE_AUDIO) << 8) | (dom) )
/* AAC Encoder modes */
#define ADSP_AUDIO_ENC_AAC_LC_ONLY_MODE 0
#define ADSP_AUDIO_ENC_AAC_PLUS_MODE 1
#define ADSP_AUDIO_ENC_ENHANCED_AAC_PLUS_MODE 2
struct adsp_audio_aac_enc_cfg {
u32 bit_rate; /* bits per second */
u32 encoder_mode; /* ADSP_AUDIO_ENC_* */
} __attribute__ ((packed));
#define ADSP_AUDIO_ENC_SBC_ALLOCATION_METHOD_LOUNDNESS 0
#define ADSP_AUDIO_ENC_SBC_ALLOCATION_METHOD_SNR 1
#define ADSP_AUDIO_ENC_SBC_CHANNEL_MODE_MONO 1
#define ADSP_AUDIO_ENC_SBC_CHANNEL_MODE_STEREO 2
#define ADSP_AUDIO_ENC_SBC_CHANNEL_MODE_DUAL 8
#define ADSP_AUDIO_ENC_SBC_CHANNEL_MODE_JOINT_STEREO 9
struct adsp_audio_sbc_encoder_cfg {
u32 num_subbands;
u32 block_len;
u32 channel_mode;
u32 allocation_method;
u32 bit_rate;
} __attribute__ ((packed));
/* AMR NB encoder modes */
#define ADSP_AUDIO_AMR_MR475 0
#define ADSP_AUDIO_AMR_MR515 1
#define ADSP_AUDIO_AMR_MMR59 2
#define ADSP_AUDIO_AMR_MMR67 3
#define ADSP_AUDIO_AMR_MMR74 4
#define ADSP_AUDIO_AMR_MMR795 5
#define ADSP_AUDIO_AMR_MMR102 6
#define ADSP_AUDIO_AMR_MMR122 7
/* The following are valid AMR NB DTX modes */
#define ADSP_AUDIO_AMR_DTX_MODE_OFF 0
#define ADSP_AUDIO_AMR_DTX_MODE_ON_VAD1 1
#define ADSP_AUDIO_AMR_DTX_MODE_ON_VAD2 2
#define ADSP_AUDIO_AMR_DTX_MODE_ON_AUTO 3
/* AMR Encoder configuration */
struct adsp_audio_amr_enc_cfg {
u32 mode; /* ADSP_AUDIO_AMR_MR* */
u32 dtx_mode; /* ADSP_AUDIO_AMR_DTX_MODE* */
u32 enable; /* 1 = enable, 0 = disable */
} __attribute__ ((packed));
struct adsp_audio_qcelp13k_enc_cfg {
u16 min_rate;
u16 max_rate;
} __attribute__ ((packed));
struct adsp_audio_evrc_enc_cfg {
u16 min_rate;
u16 max_rate;
} __attribute__ ((packed));
union adsp_audio_codec_config {
struct adsp_audio_amr_enc_cfg amr;
struct adsp_audio_aac_enc_cfg aac;
struct adsp_audio_qcelp13k_enc_cfg qcelp13k;
struct adsp_audio_evrc_enc_cfg evrc;
struct adsp_audio_sbc_encoder_cfg sbc;
} __attribute__ ((packed));
/* This is the default value. */
#define ADSP_AUDIO_OPEN_STREAM_MODE_NONE 0x0000
/* This bit, if set, indicates that the AVSync mode is activated. */
#define ADSP_AUDIO_OPEN_STREAM_MODE_AVSYNC 0x0001
/* This bit, if set, indicates that the Sample Rate/Channel Mode */
/* Change Notification mode is activated. */
#define ADSP_AUDIO_OPEN_STREAM_MODE_SR_CM_NOTIFY 0x0002
/* This bit, if set, indicates that the sync clock is enabled */
#define ADSP_AUDIO_OPEN_STREAM_MODE_ENABLE_SYNC_CLOCK 0x0004
#if !defined(CONFIG_MACH_HTCLEO)
struct adsp_open_command {
struct adsp_command_hdr hdr;
u32 device;
u32 endpoint; /* address */
u32 stream_context;
u32 mode;
u32 buf_max_size;
union adsp_audio_format format;
union adsp_audio_codec_config config;
} __attribute__ ((packed));
/* Opcode to open a device stream session to capture audio */
#define ADSP_AUDIO_IOCTL_CMD_OPEN_READ 0x0108dd79
/* Opcode to open a device stream session to render audio */
#define ADSP_AUDIO_IOCTL_CMD_OPEN_WRITE 0x0108dd7a
/* Opcode to open a device session, must open a device */
#define ADSP_AUDIO_IOCTL_CMD_OPEN_DEVICE 0x0108dd7b
/* Close an existing stream or device */
#define ADSP_AUDIO_IOCTL_CMD_CLOSE 0x0108d8bc
#else
struct adsp_open_command
{
struct adsp_command_hdr hdr;
u32 opcode;
u32 numdev;
u32 dev[4];
u32 stream_context;
u32 format;
u32 pblock;
u32 blocklen;
u32 buf_max_size;
u32 priority;
union adsp_audio_codec_config config;
u32 mode;
} __attribute__ ((packed));
/* --- audio control and stream session ioctls ---- */
/* Opcode to open a device stream session to capture audio */
#define ADSP_AUDIO_OPCODE_OPEN_READ 0x01
/* Opcode to open a device stream session to render audio */
#define ADSP_AUDIO_OPCODE_OPEN_WRITE 0x02
/* Opcode to open a device session, must open a device */
#define ADSP_AUDIO_OPCODE_OPEN_DEV 0x04
#endif
/* A device switch requires three IOCTL */
/* commands in the following sequence: PREPARE, STANDBY, COMMIT */
/* adsp_audio_device_switch_command structure is needed for */
/* DEVICE_SWITCH_PREPARE */
/* Device switch protocol step #1. Pause old device and */
/* generate silence for the old device. */
#define ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_PREPARE 0x010815c4
/* Device switch protocol step #2. Release old device, */
/* create new device and generate silence for the new device. */
/* When client receives ack for this IOCTL, the client can */
/* start sending IOCTL commands to configure, calibrate and */
/* change filter settings on the new device. */
#define ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_STANDBY 0x010815c5
/* Device switch protocol step #3. Start normal operations on new device */
#define ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_COMMIT 0x01075ee7
struct adsp_device_switch_command {
struct adsp_command_hdr hdr;
u32 old_device;
u32 new_device;
u8 device_class; /* 0 = i.rx, 1 = i.tx, 2 = e.rx, 3 = e.tx */
u8 device_type; /* 0 = rx, 1 = tx, 2 = both */
} __attribute__ ((packed));
/* --- audio control session ioctls ---- */
#define ADSP_PATH_RX 0
#define ADSP_PATH_TX 1
#define ADSP_PATH_BOTH 2
/* These commands will affect a logical device and all its associated */
/* streams. */
/* Set device volume. */
#define ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_VOL 0x0107605c
struct adsp_set_dev_volume_command {
struct adsp_command_hdr hdr;
u32 device_id;
u32 path; /* 0 = rx, 1 = tx, 2 = both */
s32 volume;
} __attribute__ ((packed));
/* Set Device stereo volume. This command has data payload, */
/* struct adsp_audio_set_dev_stereo_volume_command. */
#define ADSP_AUDIO_IOCTL_SET_DEVICE_STEREO_VOL 0x0108df3e
/* Set L, R cross channel gain for a Device. This command has */
/* data payload, struct adsp_audio_set_dev_x_chan_gain_command. */
#define ADSP_AUDIO_IOCTL_SET_DEVICE_XCHAN_GAIN 0x0108df40
/* Set device mute state. */
#define ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_MUTE 0x0107605f
struct adsp_set_dev_mute_command {
struct adsp_command_hdr hdr;
u32 device_id;
u32 path; /* 0 = rx, 1 = tx, 2 = both */
u32 mute; /* 1 = mute */
} __attribute__ ((packed));
/* Configure Equalizer for a device. */
/* This command has payload struct adsp_audio_set_dev_equalizer_command. */
#define ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_EQ_CONFIG 0x0108b10e
/* Set configuration data for an algorithm aspect of a device. */
/* This command has payload struct adsp_audio_set_dev_cfg_command. */
#define ADSP_AUDIO_IOCTL_SET_DEVICE_CONFIG 0x0108b6cb
struct adsp_set_dev_cfg_command {
struct adsp_command_hdr hdr;
u32 device_id;
u32 block_id;
u32 interface_id;
u32 phys_addr;
u32 phys_size;
u32 phys_used;
} __attribute__ ((packed));
/* Set configuration data for all interfaces of a device. */
#define ADSP_AUDIO_IOCTL_SET_DEVICE_CONFIG_TABLE 0x0108b6bf
struct adsp_set_dev_cfg_table_command {
struct adsp_command_hdr hdr;
u32 device_id;
u32 phys_addr;
u32 phys_size;
u32 phys_used;
} __attribute__ ((packed));
/* ---- audio stream data commands ---- */
#define ADSP_AUDIO_IOCTL_CMD_DATA_TX 0x0108dd7f
#define ADSP_AUDIO_IOCTL_CMD_DATA_RX 0x0108dd80
struct adsp_buffer_command {
struct adsp_command_hdr hdr;
struct adsp_audio_buffer buffer;
} __attribute__ ((packed));
/* ---- audio stream ioctls (only affect a single stream in a session) ---- */
/* Stop stream for audio device. */
#define ADSP_AUDIO_IOCTL_CMD_STREAM_STOP 0x01075c54
/* End of stream reached. Client will not send any more data. */
#define ADSP_AUDIO_IOCTL_CMD_STREAM_EOS 0x0108b150
/* Do sample slipping/stuffing on AAC outputs. The payload of */
/* this command is struct adsp_audio_slip_sample_command. */
#define ADSP_AUDIO_IOCTL_CMD_STREAM_SLIPSAMPLE 0x0108d40e
/* Set stream volume. */
/* This command has data payload, struct adsp_audio_set_volume_command. */
#define ADSP_AUDIO_IOCTL_CMD_SET_STREAM_VOL 0x0108c0de
/* Set stream stereo volume. This command has data payload, */
/* struct adsp_audio_set_stereo_volume_command. */
#define ADSP_AUDIO_IOCTL_SET_STREAM_STEREO_VOL 0x0108dd7c
/* Set L, R cross channel gain for a Stream. This command has */
/* data payload, struct adsp_audio_set_x_chan_gain_command. */
#define ADSP_AUDIO_IOCTL_SET_STREAM_XCHAN_GAIN 0x0108dd7d
/* Set stream mute state. */
/* This command has data payload, struct adsp_audio_set_stream_mute. */
#define ADSP_AUDIO_IOCTL_CMD_SET_STREAM_MUTE 0x0108c0df
/* Reconfigure bit rate information. This command has data */
/* payload, struct adsp_audio_set_bit_rate_command */
#define ADSP_AUDIO_IOCTL_SET_STREAM_BITRATE 0x0108ccf1
/* Set Channel Mapping. This command has data payload, struct */
/* This command has data payload struct adsp_audio_set_channel_map_command. */
#define ADSP_AUDIO_IOCTL_SET_STREAM_CHANNELMAP 0x0108d32a
/* Enable/disable AACPlus SBR. */
/* This command has data payload struct adsp_audio_set_sbr_command */
#define ADSP_AUDIO_IOCTL_SET_STREAM_SBR 0x0108d416
/* Enable/disable WMA Pro Chex and Fex. This command has data payload */
/* struct adsp_audio_stream_set_wma_command. */
#define ADSP_AUDIO_IOCTL_SET_STREAM_WMAPRO 0x0108d417
/* ---- audio session ioctls (affect all streams in a session) --- */
/* Start stream for audio device. */
#define ADSP_AUDIO_IOCTL_CMD_SESSION_START 0x010815c6
/* Stop all stream(s) for audio session as indicated by major id. */
#define ADSP_AUDIO_IOCTL_CMD_SESSION_STOP 0x0108dd7e
/* Pause the data flow for a session as indicated by major id. */
#define ADSP_AUDIO_IOCTL_CMD_SESSION_PAUSE 0x01075ee8
/* Resume the data flow for a session as indicated by major id. */
#define ADSP_AUDIO_IOCTL_CMD_SESSION_RESUME 0x01075ee9
/* Drop any unprocessed data buffers for a session as indicated by major id. */
#define ADSP_AUDIO_IOCTL_CMD_SESSION_FLUSH 0x01075eea
/* Start Stream DTMF tone */
#define ADSP_AUDIO_IOCTL_CMD_SESSION_DTMF_START 0x0108c0dd
/* Stop Stream DTMF tone */
#define ADSP_AUDIO_IOCTL_CMD_SESSION_DTMF_STOP 0x01087554
/* Set Session volume. */
/* This command has data payload, struct adsp_audio_set_volume_command. */
#define ADSP_AUDIO_IOCTL_SET_SESSION_VOL 0x0108d8bd
/* Set session stereo volume. This command has data payload, */
/* struct adsp_audio_set_stereo_volume_command. */
#define ADSP_AUDIO_IOCTL_SET_SESSION_STEREO_VOL 0x0108df3d
/* Set L, R cross channel gain for a session. This command has */
/* data payload, struct adsp_audio_set_x_chan_gain_command. */
#define ADSP_AUDIO_IOCTL_SET_SESSION_XCHAN_GAIN 0x0108df3f
/* Set Session mute state. */
/* This command has data payload, struct adsp_audio_set_mute_command. */
#define ADSP_AUDIO_IOCTL_SET_SESSION_MUTE 0x0108d8be
/* Configure Equalizer for a stream. */
/* This command has payload struct adsp_audio_set_equalizer_command. */
#define ADSP_AUDIO_IOCTL_SET_SESSION_EQ_CONFIG 0x0108c0e0
#define ADSP_AUDIO_MAX_EQ_BANDS 12
/* Definition for any one band of Equalizer. */
struct adsp_audio_eq_band {
u16 band_idx;
u32 filter_type;
u32 center_freq_hz;
s32 filter_gain;
s32 q_factor;
} __attribute__ ((packed));
struct adsp_audio_set_equalizer_command {
struct adsp_command_hdr hdr;
u32 enable;
u32 num_bands;
struct adsp_audio_eq_band eq_bands[ADSP_AUDIO_MAX_EQ_BANDS];
} __attribute__ ((packed));
/* Set Audio Video sync information. */
/* This command has data payload, struct adsp_audio_set_av_sync_command. */
#define ADSP_AUDIO_IOCTL_SET_SESSION_AVSYNC 0x0108d1e2
/* Get Audio Media Session time. */
/* This command returns the audioTime in adsp_audio_unsigned64_event */
#define ADSP_AUDIO_IOCTL_CMD_GET_AUDIO_TIME 0x0108c26c
/* these command structures are used for both STREAM and SESSION ioctls */
struct adsp_set_volume_command {
struct adsp_command_hdr hdr;
s32 volume;
} __attribute__ ((packed));
struct adsp_set_mute_command {
struct adsp_command_hdr hdr;
u32 mute; /* 1 == mute */
} __attribute__ ((packed));
/* ---- audio events ---- */
/* All IOCTL commands generate an event with the IOCTL opcode as the */
/* event id after the IOCTL command has been executed. */
/* This event is generated after a media stream session is opened. */
#define ADSP_AUDIO_EVT_STATUS_OPEN 0x0108c0d6
/* This event is generated after a media stream session is closed. */
#define ADSP_AUDIO_EVT_STATUS_CLOSE 0x0108c0d7
/* Asyncronous buffer consumption. This event is generated after a */
/* recived buffer is consumed during rendering or filled during */
/* capture opeartion. */
#define ADSP_AUDIO_EVT_STATUS_BUF_DONE 0x0108c0d8
/* This event is generated when rendering operation is starving for */
/* data. In order to avoid audio loss at the end of a plauback, the */
/* client should wait for this event before issuing the close command. */
#define ADSP_AUDIO_EVT_STATUS_BUF_UNDERRUN 0x0108c0d9
/* This event is generated during capture operation when there are no */
/* buffers available to copy the captured audio data */
#define ADSP_AUDIO_EVT_STATUS_BUF_OVERFLOW 0x0108c0da
/* This asynchronous event is generated as a result of an input */
/* sample rate change and/or channel mode change detected by the */
/* decoder. The event payload data is an array of 2 uint32 */
/* values containing the sample rate in Hz and channel mode. */
#define ADSP_AUDIO_EVT_SR_CM_CHANGE 0x0108d329
struct adsp_event_hdr
{
u32 evt_handle; /* DAL common header */
u32 evt_cookie;
u32 evt_length;
u32 src; /* "source" audio address */
u32 dst; /* "destination" audio address */
u32 event_id;
u32 response_type;
u32 seq_number;
u32 context; /* opaque to DSP */
u32 data;
u32 status;
} __attribute__ ((packed));
#if defined(CONFIG_MACH_HTCLEO)
union adsp_event_data
{
u32 val32;
uint8_t data[32];
} __attribute__ ((packed));
struct adsp_audio_event
{
struct adsp_command_hdr hdr;
u32 session;
u32 event_id;
u32 status;
u32 datalen;
union adsp_event_data data;
} __attribute__ ((packed));
struct adsp_audio_dal_event
{
u32 cb_evt;
u32 loc_evt;
u32 size;
struct adsp_audio_event ae;
} __attribute__ ((packed));
#else
struct adsp_buffer_event
{
struct adsp_event_hdr hdr;
struct adsp_audio_buffer buffer;
} __attribute__ ((packed));
#endif
/* ---- audio device IDs ---- */
/* Device direction Rx/Tx flag */
#define ADSP_AUDIO_RX_DEVICE 0x00
#define ADSP_AUDIO_TX_DEVICE 0x01
/* Default RX or TX device */
#define ADSP_AUDIO_DEVICE_ID_DEFAULT 0x1081679
/* Source (TX) devices */
#define ADSP_AUDIO_DEVICE_ID_HANDSET_MIC 0x107ac8d
#define ADSP_AUDIO_DEVICE_ID_HEADSET_MIC 0x1081510
#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MIC 0x1081512
#define ADSP_AUDIO_DEVICE_ID_BT_SCO_MIC 0x1081518
#define ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_MIC 0x108151b
#define ADSP_AUDIO_DEVICE_ID_I2S_MIC 0x1089bf3
/* Special loopback pseudo device to be paired with an RX device */
/* with usage ADSP_AUDIO_DEVICE_USAGE_MIXED_PCM_LOOPBACK */
#define ADSP_AUDIO_DEVICE_ID_MIXED_PCM_LOOPBACK_TX 0x1089bf2
/* Sink (RX) devices */
#define ADSP_AUDIO_DEVICE_ID_HANDSET_SPKR 0x107ac88
#define ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_MONO 0x1081511
#define ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_STEREO 0x107ac8a
#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO 0x1081513
#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_MONO_HEADSET 0x108c508
#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_STEREO_HEADSET 0x108c894
#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO 0x1081514
#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_MONO_HEADSET 0x108c895
#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_STEREO_HEADSET 0x108c509
#define ADSP_AUDIO_DEVICE_ID_BT_SCO_SPKR 0x1081519
#define ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_SPKR 0x108151c
#define ADSP_AUDIO_DEVICE_ID_I2S_SPKR 0x1089bf4
#define ADSP_AUDIO_DEVICE_ID_NULL_SINK 0x108e512
/* BT A2DP playback device. */
/* This device must be paired with */
/* ADSP_AUDIO_DEVICE_ID_MIXED_PCM_LOOPBACK_TX using */
/* ADSP_AUDIO_DEVICE_USAGE_MIXED_PCM_LOOPBACK mode */
#define ADSP_AUDIO_DEVICE_ID_BT_A2DP_SPKR 0x108151a
/* Voice Destination identifier - specifically used for */
/* controlling Voice module from the Device Control Session */
#define ADSP_AUDIO_DEVICE_ID_VOICE 0x0108df3c
/* Audio device usage types. */
/* This is a bit mask to determine which topology to use in the */
/* device session */
#define ADSP_AUDIO_DEVICE_CONTEXT_VOICE 0x01
#define ADSP_AUDIO_DEVICE_CONTEXT_PLAYBACK 0x02
#define ADSP_AUDIO_DEVICE_CONTEXT_MIXED_RECORD 0x10
#define ADSP_AUDIO_DEVICE_CONTEXT_RECORD 0x20
#define ADSP_AUDIO_DEVICE_CONTEXT_PCM_LOOPBACK 0x40
/* for EQ */
#define CAD_EQ_INVALID_DATA 0xFFFFFFFF
/* Equalizer filter band types */
#define ADSP_AUDIO_EQUALIZER_TYPE_NONE 0
#define ADSP_AUDIO_EQUALIZER_BASS_BOOST 1
#define ADSP_AUDIO_EQUALIZER_BASS_CUT 2
#define ADSP_AUDIO_EQUALIZER_TREBLE_BOOST 3
#define ADSP_AUDIO_EQUALIZER_TREBLE_CUT 4
#define ADSP_AUDIO_EQUALIZER_BAND_BOOST 5
#define ADSP_AUDIO_EQUALIZER_BAND_CUT 6
struct cad_audio_eq_cfg {
u32 enable;
u32 num_bands;
struct adsp_audio_eq_band eq_bands[ADSP_AUDIO_MAX_EQ_BANDS];
} __attribute__ ((packed));
int q6audio_set_stream_eq(struct audio_client *ac, struct cad_audio_eq_cfg *eq_cfg);
#endif

View File

@ -0,0 +1,285 @@
/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Code Aurora nor
* the names of its contributors may be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef __ADSP_AUDIO_MEDIA_FORMAT_H
#define __ADSP_AUDIO_MEDIA_FORMAT_H
/* Supported audio media formats */
/* format block in shmem */
#define ADSP_AUDIO_FORMAT_SHAREDMEMORY 0x01091a78
/* adsp_audio_format_raw_pcm type */
#define ADSP_AUDIO_FORMAT_PCM 0x0103d2fd
/* adsp_audio_format_raw_pcm type */
#define ADSP_AUDIO_FORMAT_DTMF 0x01087725
/* adsp_audio_format_adpcm type */
#define ADSP_AUDIO_FORMAT_ADPCM 0x0103d2ff
/* Yamaha PCM format */
#define ADSP_AUDIO_FORMAT_YADPCM 0x0108dc07
/* ISO/IEC 11172 */
#define ADSP_AUDIO_FORMAT_MP3 0x0103d308
/* ISO/IEC 14496 */
#define ADSP_AUDIO_FORMAT_MPEG4_AAC 0x010422f1
/* AMR-NB audio in FS format */
#define ADSP_AUDIO_FORMAT_AMRNB_FS 0x0105c16c
/* AMR-WB audio in FS format */
#define ADSP_AUDIO_FORMAT_AMRWB_FS 0x0105c16e
/* QCELP 13k, IS733 */
#define ADSP_AUDIO_FORMAT_V13K_FS 0x01080b8a
/* EVRC 8k, IS127 */
#define ADSP_AUDIO_FORMAT_EVRC_FS 0x01080b89
/* EVRC-B 8k, 4GV */
#define ADSP_AUDIO_FORMAT_EVRCB_FS 0x0108f2a3
/* MIDI command stream */
#define ADSP_AUDIO_FORMAT_MIDI 0x0103d300
/* A2DP SBC stream */
#define ADSP_AUDIO_FORMAT_SBC 0x0108c4d8
/* Version 10 Professional */
#define ADSP_AUDIO_FORMAT_WMA_V10PRO 0x0108aa92
/* Version 9 Starndard */
#define ADSP_AUDIO_FORMAT_WMA_V9 0x0108d430
/* AMR WideBand Plus */
#define ADSP_AUDIO_FORMAT_AMR_WB_PLUS 0x0108f3da
/* AC3 Decoder */
#define ADSP_AUDIO_FORMAT_AC3_DECODER 0x0108d5f9
/* Not yet supported audio media formats */
/* ISO/IEC 13818 */
#define ADSP_AUDIO_FORMAT_MPEG2_AAC 0x0103d309
/* 3GPP TS 26.101 Sec 4.0 */
#define ADSP_AUDIO_FORMAT_AMRNB_IF1 0x0103d305
/* 3GPP TS 26.101 Annex A */
#define ADSP_AUDIO_FORMAT_AMRNB_IF2 0x01057b31
/* 3GPP TS 26.201 */
#define ADSP_AUDIO_FORMAT_AMRWB_IF1 0x0103d306
/* 3GPP TS 26.201 */
#define ADSP_AUDIO_FORMAT_AMRWB_IF2 0x0105c16d
/* G.711 */
#define ADSP_AUDIO_FORMAT_G711 0x0106201d
/* QCELP 8k, IS96A */
#define ADSP_AUDIO_FORMAT_V8K_FS 0x01081d29
/* Version 1 codec */
#define ADSP_AUDIO_FORMAT_WMA_V1 0x01055b2b
/* Version 2, 7 & 8 codec */
#define ADSP_AUDIO_FORMAT_WMA_V8 0x01055b2c
/* Version 9 Professional codec */
#define ADSP_AUDIO_FORMAT_WMA_V9PRO 0x01055b2d
/* Version 9 Voice codec */
#define ADSP_AUDIO_FORMAT_WMA_SP1 0x01055b2e
/* Version 9 Lossless codec */
#define ADSP_AUDIO_FORMAT_WMA_LOSSLESS 0x01055b2f
/* Real Media content, low-bitrate */
#define ADSP_AUDIO_FORMAT_RA_SIPR 0x01042a0f
/* Real Media content */
#define ADSP_AUDIO_FORMAT_RA_COOK 0x01042a0e
/* For all of the audio formats, unless specified otherwise, */
/* the following apply: */
/* Format block bits are arranged in bytes and words in little-endian */
/* order, i.e., least-significant bit first and least-significant */
/* byte first. */
/* AAC Format Block. */
/* AAC format block consist of a format identifier followed by */
/* AudioSpecificConfig formatted according to ISO/IEC 14496-3 */
/* The following AAC format identifiers are supported */
#define ADSP_AUDIO_AAC_ADTS 0x010619cf
#define ADSP_AUDIO_AAC_MPEG4_ADTS 0x010619d0
#define ADSP_AUDIO_AAC_LOAS 0x010619d1
#define ADSP_AUDIO_AAC_ADIF 0x010619d2
#define ADSP_AUDIO_AAC_RAW 0x010619d3
#define ADSP_AUDIO_AAC_FRAMED_RAW 0x0108c1fb
#define ADSP_AUDIO_COMPANDING_ALAW 0x10619cd
#define ADSP_AUDIO_COMPANDING_MLAW 0x10619ce
/* Maxmum number of bytes allowed in a format block */
#define ADSP_AUDIO_FORMAT_DATA_MAX 16
struct adsp_audio_no_payload_format {
/* Media Format Code (must always be first element) */
u32 format;
/* no payload for this format type */
} __attribute__ ((packed));
/* For convenience, to be used as a standard format block */
/* for various media types that don't need a unique format block */
/* ie. PCM, DTMF, etc. */
struct adsp_audio_standard_format {
/* Media Format Code (must always be first element) */
// u32 format;
/* payload */
u16 channels;
u16 bits_per_sample;
u32 sampling_rate;
u8 is_signed;
u8 is_interleaved;
} __attribute__ ((packed));
/* ADPCM format block */
struct adsp_audio_adpcm_format {
/* Media Format Code (must always be first element) */
// u32 format;
/* payload */
u16 channels;
u16 bits_per_sample;
u32 sampling_rate;
u8 is_signed;
u8 is_interleaved;
u32 block_size;
} __attribute__ ((packed));
/* MIDI format block */
struct adsp_audio_midi_format {
/* Media Format Code (must always be first element) */
// u32 format;
/* payload */
u32 sampling_rate;
u16 channels;
u16 mode;
} __attribute__ ((packed));
/* G711 format block */
struct adsp_audio_g711_format {
/* Media Format Code (must always be first element) */
// u32 format;
/* payload */
u32 companding;
} __attribute__ ((packed));
struct adsp_audio_wma_pro_format {
/* Media Format Code (must always be first element) */
// u32 format;
/* payload */
u16 format_tag;
u16 channels;
u32 samples_per_sec;
u32 avg_bytes_per_sec;
u16 block_align;
u16 valid_bits_per_sample;
u32 channel_mask;
u16 encode_opt;
u16 advanced_encode_opt;
u32 advanced_encode_opt2;
u32 drc_peak_reference;
u32 drc_peak_target;
u32 drc_average_reference;
u32 drc_average_target;
} __attribute__ ((packed));
struct adsp_audio_amrwb_plus_format {
/* Media Format Code (must always be first element) */
// u32 format;
/* payload */
u32 size;
u32 version;
u32 channels;
u32 amr_band_mode;
u32 amr_dtx_mode;
u32 amr_frame_format;
u32 amr_isf_index;
} __attribute__ ((packed));
/* Binary Byte Stream Format */
/* Binary format type that defines a byte stream, */
/* can be used to specify any format (ie. AAC) */
struct adsp_audio_binary_format {
/* Media Format Code (must always be first element) */
// u32 format;
/* payload */
/* number of bytes set in byte stream */
// u32 num_bytes;
/* Byte stream binary data */
u8 data[ADSP_AUDIO_FORMAT_DATA_MAX];
} __attribute__ ((packed));
struct adsp_audio_shared_memory_format {
/* Media Format Code (must always be first element) */
// u32 format;
/* Number of bytes in shared memory */
u32 len;
/* Phyisical address to data in shared memory */
u32 address;
} __attribute__ ((packed));
/* Union of all format types */
union adsp_audio_format {
/* Basic format block with no payload */
struct adsp_audio_no_payload_format no_payload;
/* Generic format block PCM, DTMF */
struct adsp_audio_standard_format standard;
/* ADPCM format block */
struct adsp_audio_adpcm_format adpcm;
/* MIDI format block */
struct adsp_audio_midi_format midi;
/* G711 format block */
struct adsp_audio_g711_format g711;
/* WmaPro format block */
struct adsp_audio_wma_pro_format wma_pro;
/* WmaPro format block */
struct adsp_audio_amrwb_plus_format amrwb_plus;
/* binary (byte stream) format block, used for AAC */
struct adsp_audio_binary_format binary;
/* format block in shared memory */
struct adsp_audio_shared_memory_format shared_mem;
};
#endif

View File

@ -0,0 +1,229 @@
/* arch/arm/mach-msm/qdsp6/mp3.c
*
* Copyright (C) 2009 Google, Inc.
* Copyright (C) 2009 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/fs.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/mutex.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/uaccess.h>
#include <linux/msm_audio.h>
#include <mach/msm_qdsp6_audio.h>
#include "dal_audio.h"
#define BUFSZ (8192)
#define DMASZ (BUFSZ * 2)
struct mp3 {
struct mutex lock;
struct audio_client *ac;
uint32_t sample_rate;
uint32_t channel_count;
};
static long mp3_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct mp3 *mp3 = file->private_data;
struct cad_audio_eq_cfg eq_cfg;
int rc = 0;
if (cmd == AUDIO_GET_STATS) {
struct msm_audio_stats stats;
memset(&stats, 0, sizeof(stats));
if (copy_to_user((void*) arg, &stats, sizeof(stats)))
return -EFAULT;
return 0;
}
mutex_lock(&mp3->lock);
switch (cmd) {
case AUDIO_SET_VOLUME: {
int vol;
if (copy_from_user(&vol, (void*) arg, sizeof(vol))) {
rc = -EFAULT;
break;
}
rc = q6audio_set_stream_volume(mp3->ac, vol);
break;
}
case AUDIO_SET_EQ: {
if (copy_from_user(&eq_cfg, (void *)arg, sizeof(struct cad_audio_eq_cfg))) {
rc = -EFAULT;
break;
}
rc = q6audio_set_stream_eq(mp3->ac, &eq_cfg);
break;
}
case AUDIO_START: {
uint32_t acdb_id;
if (arg == 0) {
acdb_id = 0;
} else if (copy_from_user(&acdb_id, (void*) arg, sizeof(acdb_id))) {
pr_info("pcm_out: copy acdb_id from user failed\n");
rc = -EFAULT;
break;
}
if (mp3->ac) {
rc = -EBUSY;
} else {
mp3->ac = q6audio_open_mp3(BUFSZ,
mp3->sample_rate, mp3->channel_count, acdb_id);
if (!mp3->ac)
rc = -ENOMEM;
}
break;
}
case AUDIO_STOP:
break;
case AUDIO_FLUSH:
break;
case AUDIO_SET_CONFIG: {
struct msm_audio_config config;
if (mp3->ac) {
rc = -EBUSY;
break;
}
if (copy_from_user(&config, (void*) arg, sizeof(config))) {
rc = -EFAULT;
break;
}
if (config.channel_count < 1 || config.channel_count > 2) {
rc = -EINVAL;
break;
}
mp3->sample_rate = config.sample_rate;
mp3->channel_count = config.channel_count;
break;
}
case AUDIO_GET_CONFIG: {
struct msm_audio_config config;
config.buffer_size = BUFSZ;
config.buffer_count = 2;
config.sample_rate = mp3->sample_rate;
config.channel_count = mp3->channel_count;
config.unused[0] = 0;
config.unused[1] = 0;
config.unused[2] = 0;
if (copy_to_user((void*) arg, &config, sizeof(config))) {
rc = -EFAULT;
}
break;
}
default:
rc = -EINVAL;
}
mutex_unlock(&mp3->lock);
return rc;
}
static int mp3_open(struct inode *inode, struct file *file)
{
int rc = 0;
struct mp3 *mp3;
mp3 = kzalloc(sizeof(struct mp3), GFP_KERNEL);
if (!mp3)
return -ENOMEM;
mutex_init(&mp3->lock);
mp3->channel_count = 2;
mp3->sample_rate = 44100;
file->private_data = mp3;
return rc;
}
static ssize_t mp3_write(struct file *file, const char __user *buf,
size_t count, loff_t *pos)
{
struct mp3 *mp3 = file->private_data;
struct audio_client *ac;
struct audio_buffer *ab;
const char __user *start = buf;
int xfer;
if (!mp3->ac)
mp3_ioctl(file, AUDIO_START, 0);
ac = mp3->ac;
if (!ac)
return -ENODEV;
while (count > 0) {
ab = ac->buf + ac->cpu_buf;
if (ab->used)
wait_event(ac->wait, (ab->used == 0));
xfer = count;
if (xfer > ab->size)
xfer = ab->size;
if (copy_from_user(ab->data, buf, xfer))
return -EFAULT;
buf += xfer;
count -= xfer;
ab->used = xfer;
q6audio_write(ac, ab);
ac->cpu_buf ^= 1;
}
return buf - start;
}
static int mp3_fsync(struct file *f, struct dentry *dentry, int datasync)
{
struct mp3 *mp3 = f->private_data;
if (mp3->ac)
return q6audio_async(mp3->ac);
return -ENODEV;
}
static int mp3_release(struct inode *inode, struct file *file)
{
struct mp3 *mp3 = file->private_data;
if (mp3->ac)
q6audio_mp3_close(mp3->ac);
kfree(mp3);
return 0;
}
static struct file_operations mp3_fops = {
.owner = THIS_MODULE,
.open = mp3_open,
.write = mp3_write,
.fsync = mp3_fsync,
.release = mp3_release,
.unlocked_ioctl = mp3_ioctl,
};
struct miscdevice mp3_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "msm_mp3",
.fops = &mp3_fops,
};
static int __init mp3_init(void) {
return misc_register(&mp3_misc);
}
device_initcall(mp3_init);

View File

@ -0,0 +1,940 @@
/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Code Aurora Forum nor
* the names of its contributors may be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
/*
#define DEBUG_TRACE_VDEC
#define DEBUG
*/
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/list.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/uaccess.h>
#include <linux/wakelock.h>
#include <linux/android_pmem.h>
#include <linux/msm_q6vdec.h>
#include "dal.h"
#define DALDEVICEID_VDEC_DEVICE 0x02000026
#define DALDEVICEID_VDEC_PORTNAME "DSP_DAL_AQ_VID"
#define VDEC_INTERFACE_VERSION 0x00020000
#define MAJOR_MASK 0xFFFF0000
#define MINOR_MASK 0x0000FFFF
#define VDEC_GET_MAJOR_VERSION(version) (((version)&MAJOR_MASK)>>16)
#define VDEC_GET_MINOR_VERSION(version) ((version)&MINOR_MASK)
#ifdef DEBUG_TRACE_VDEC
#define TRACE(fmt,x...) \
do { pr_debug("%s:%d " fmt, __func__, __LINE__, ##x); } while (0)
#else
#define TRACE(fmt,x...) do { } while (0)
#endif
static DEFINE_MUTEX(idlecount_lock);
static int idlecount;
static struct wake_lock wakelock;
static struct wake_lock idlelock;
static void prevent_sleep(void)
{
mutex_lock(&idlecount_lock);
if (++idlecount == 1) {
wake_lock(&idlelock);
wake_lock(&wakelock);
}
mutex_unlock(&idlecount_lock);
}
static void allow_sleep(void)
{
mutex_lock(&idlecount_lock);
if (--idlecount == 0) {
wake_unlock(&idlelock);
wake_unlock(&wakelock);
}
mutex_unlock(&idlecount_lock);
}
enum {
VDEC_DALRPC_INITIALIZE = DAL_OP_FIRST_DEVICE_API,
VDEC_DALRPC_SETBUFFERS,
VDEC_DALRPC_FREEBUFFERS,
VDEC_DALRPC_QUEUE,
VDEC_DALRPC_SIGEOFSTREAM,
VDEC_DALRPC_FLUSH,
VDEC_DALRPC_REUSEFRAMEBUFFER,
VDEC_DALRPC_GETDECATTRIBUTES,
};
enum {
VDEC_ASYNCMSG_DECODE_DONE = 0xdec0de00,
VDEC_ASYNCMSG_REUSE_FRAME,
};
struct vdec_init_cfg {
u32 decode_done_evt;
u32 reuse_frame_evt;
struct vdec_config cfg;
};
struct vdec_buffer_status {
u32 data;
u32 status;
};
#define VDEC_MSG_MAX 128
struct vdec_msg_list {
struct list_head list;
struct vdec_msg vdec_msg;
};
struct vdec_mem_info {
u32 buf_type;
u32 id;
unsigned long phys_addr;
unsigned long len;
struct file *file;
};
struct vdec_mem_list {
struct list_head list;
struct vdec_mem_info mem;
};
struct vdec_data {
struct dal_client *vdec_handle;
struct list_head vdec_msg_list_head;
struct list_head vdec_msg_list_free;
wait_queue_head_t vdec_msg_evt;
spinlock_t vdec_list_lock;
struct list_head vdec_mem_list_head;
spinlock_t vdec_mem_list_lock;
int mem_initialized;
int running;
int close_decode;
};
static struct class *driver_class;
static dev_t vdec_device_no;
static struct cdev vdec_cdev;
static int ref_cnt;
static DEFINE_MUTEX(vdec_ref_lock);
static inline int vdec_check_version(u32 client, u32 server)
{
int ret = -EINVAL;
if ((VDEC_GET_MAJOR_VERSION(client) == VDEC_GET_MAJOR_VERSION(server))
&& (VDEC_GET_MINOR_VERSION(client) <=
VDEC_GET_MINOR_VERSION(server)))
ret = 0;
return ret;
}
static int vdec_get_msg(struct vdec_data *vd, void *msg)
{
struct vdec_msg_list *l;
unsigned long flags;
int ret = 0;
if (!vd->running)
return -EPERM;
spin_lock_irqsave(&vd->vdec_list_lock, flags);
list_for_each_entry_reverse(l, &vd->vdec_msg_list_head, list) {
if (copy_to_user(msg, &l->vdec_msg, sizeof(struct vdec_msg)))
pr_err("vdec_get_msg failed to copy_to_user!\n");
if (l->vdec_msg.id == VDEC_MSG_REUSEINPUTBUFFER)
TRACE("reuse_input_buffer %d\n", l->vdec_msg.buf_id);
else if (l->vdec_msg.id == VDEC_MSG_FRAMEDONE)
TRACE("frame_done (stat=%d)\n",
l->vdec_msg.vfr_info.status);
else
TRACE("unknown msg (msgid=%d)\n", l->vdec_msg.id);
list_del(&l->list);
list_add(&l->list, &vd->vdec_msg_list_free);
ret = 1;
break;
}
spin_unlock_irqrestore(&vd->vdec_list_lock, flags);
if (vd->close_decode)
ret = 1;
return ret;
}
static void vdec_put_msg(struct vdec_data *vd, struct vdec_msg *msg)
{
struct vdec_msg_list *l;
unsigned long flags;
int found = 0;
spin_lock_irqsave(&vd->vdec_list_lock, flags);
list_for_each_entry(l, &vd->vdec_msg_list_free, list) {
memcpy(&l->vdec_msg, msg, sizeof(struct vdec_msg));
list_del(&l->list);
list_add(&l->list, &vd->vdec_msg_list_head);
found = 1;
break;
}
spin_unlock_irqrestore(&vd->vdec_list_lock, flags);
if (found)
wake_up(&vd->vdec_msg_evt);
else
pr_err("vdec_put_msg can't find free list!\n");
}
static struct vdec_mem_list *vdec_get_mem_from_list(struct vdec_data *vd,
u32 pmem_id, u32 buf_type)
{
struct vdec_mem_list *l;
unsigned long flags;
int found = 0;
spin_lock_irqsave(&vd->vdec_mem_list_lock, flags);
list_for_each_entry(l, &vd->vdec_mem_list_head, list) {
if (l->mem.buf_type == buf_type && l->mem.id == pmem_id) {
found = 1;
break;
}
}
spin_unlock_irqrestore(&vd->vdec_mem_list_lock, flags);
if (found)
return l;
else
return NULL;
}
static int vdec_initialize(struct vdec_data *vd, void *argp)
{
struct vdec_config_sps vdec_cfg_sps;
struct vdec_init_cfg vi_cfg;
struct vdec_buf_req vdec_buf_req;
struct u8 *header;
int ret = 0;
ret = copy_from_user(&vdec_cfg_sps,
&((struct vdec_init *)argp)->sps_cfg,
sizeof(vdec_cfg_sps));
if (ret) {
pr_err("%s: copy_from_user failed\n", __func__);
return ret;
}
vi_cfg.decode_done_evt = VDEC_ASYNCMSG_DECODE_DONE;
vi_cfg.reuse_frame_evt = VDEC_ASYNCMSG_REUSE_FRAME;
memcpy(&vi_cfg.cfg, &vdec_cfg_sps.cfg, sizeof(struct vdec_config));
header = kmalloc(vdec_cfg_sps.seq.len, GFP_KERNEL);
if (!header) {
pr_err("%s: kmalloc failed\n", __func__);
return -ENOMEM;
}
ret = copy_from_user(header,
((struct vdec_init *)argp)->sps_cfg.seq.header,
vdec_cfg_sps.seq.len);
if (ret) {
pr_err("%s: copy_from_user failed\n", __func__);
kfree(header);
return ret;
}
TRACE("vi_cfg: handle=%p fourcc=0x%x w=%d h=%d order=%d notify_en=%d "
"vc1_rb=%d h264_sd=%d h264_nls=%d pp_flag=%d fruc_en=%d\n",
vd->vdec_handle, vi_cfg.cfg.fourcc, vi_cfg.cfg.width,
vi_cfg.cfg.height, vi_cfg.cfg.order, vi_cfg.cfg.notify_enable,
vi_cfg.cfg.vc1_rowbase, vi_cfg.cfg.h264_startcode_detect,
vi_cfg.cfg.h264_nal_len_size, vi_cfg.cfg.postproc_flag,
vi_cfg.cfg.fruc_enable);
ret = dal_call_f13(vd->vdec_handle, VDEC_DALRPC_INITIALIZE,
&vi_cfg, sizeof(vi_cfg),
header, vdec_cfg_sps.seq.len,
&vdec_buf_req, sizeof(vdec_buf_req));
kfree(header);
if (ret)
pr_err("%s: remote function failed (%d)\n", __func__, ret);
else
ret = copy_to_user(((struct vdec_init *)argp)->buf_req,
&vdec_buf_req, sizeof(vdec_buf_req));
vd->close_decode = 0;
return ret;
}
static int vdec_setbuffers(struct vdec_data *vd, void *argp)
{
struct vdec_buffer vmem;
struct vdec_mem_list *l;
unsigned long vstart;
unsigned long flags;
struct {
uint32_t size;
struct vdec_buf_info buf;
} rpc;
uint32_t res;
int ret = 0;
vd->mem_initialized = 0;
ret = copy_from_user(&vmem, argp, sizeof(vmem));
if (ret) {
pr_err("%s: copy_from_user failed\n", __func__);
return ret;
}
l = kzalloc(sizeof(struct vdec_mem_list), GFP_KERNEL);
if (!l) {
pr_err("%s: kzalloc failed!\n", __func__);
return -ENOMEM;
}
l->mem.id = vmem.pmem_id;
l->mem.buf_type = vmem.buf.buf_type;
ret = get_pmem_file(l->mem.id, &l->mem.phys_addr, &vstart,
&l->mem.len, &l->mem.file);
if (ret) {
pr_err("%s: get_pmem_fd failed\n", __func__);
goto err_get_pmem_file;
}
TRACE("pmem_id=%d (phys=0x%08lx len=0x%lx) buftype=%d num_buf=%d "
"islast=%d src_id=%d offset=0x%08x size=0x%x\n",
vmem.pmem_id, l->mem.phys_addr, l->mem.len,
vmem.buf.buf_type, vmem.buf.num_buf, vmem.buf.islast,
vmem.buf.region.src_id, vmem.buf.region.offset,
vmem.buf.region.size);
/* input buffers */
if ((vmem.buf.region.offset + vmem.buf.region.size) > l->mem.len) {
pr_err("%s: invalid input buffer offset!\n", __func__);
ret = -EINVAL;
goto err_bad_offset;
}
vmem.buf.region.offset += l->mem.phys_addr;
rpc.size = sizeof(vmem.buf);
memcpy(&rpc.buf, &vmem.buf, sizeof(struct vdec_buf_info));
ret = dal_call(vd->vdec_handle, VDEC_DALRPC_SETBUFFERS, 5,
&rpc, sizeof(rpc), &res, sizeof(res));
if (ret < 4) {
pr_err("%s: remote function failed (%d)\n", __func__, ret);
ret = -EIO;
goto err_dal_call;
}
spin_lock_irqsave(&vd->vdec_mem_list_lock, flags);
list_add(&l->list, &vd->vdec_mem_list_head);
spin_unlock_irqrestore(&vd->vdec_mem_list_lock, flags);
vd->mem_initialized = 1;
return ret;
err_dal_call:
err_bad_offset:
put_pmem_file(l->mem.file);
err_get_pmem_file:
kfree(l);
return ret;
}
static int vdec_queue(struct vdec_data *vd, void *argp)
{
struct {
uint32_t size;
struct vdec_input_buf_info buf_info;
uint32_t osize;
} rpc;
struct vdec_mem_list *l;
struct {
uint32_t result;
uint32_t size;
struct vdec_queue_status status;
} rpc_res;
u32 pmem_id;
int ret = 0;
if (!vd->mem_initialized) {
pr_err("%s: memory is not being initialized!\n", __func__);
return -EPERM;
}
ret = copy_from_user(&rpc.buf_info,
&((struct vdec_input_buf *)argp)->buffer,
sizeof(rpc.buf_info));
if (ret) {
pr_err("%s: copy_from_user failed\n", __func__);
return ret;
}
ret = copy_from_user(&pmem_id,
&((struct vdec_input_buf *)argp)->pmem_id,
sizeof(u32));
if (ret) {
pr_err("%s: copy_from_user failed\n", __func__);
return ret;
}
l = vdec_get_mem_from_list(vd, pmem_id, VDEC_BUFFER_TYPE_INPUT);
if (NULL == l) {
pr_err("%s: not able to find the buffer from list\n", __func__);
return -EPERM;
}
if ((rpc.buf_info.size + rpc.buf_info.offset) >= l->mem.len) {
pr_err("%s: invalid queue buffer offset!\n", __func__);
return -EINVAL;
}
rpc.buf_info.offset += l->mem.phys_addr;
rpc.size = sizeof(struct vdec_input_buf_info);
rpc.osize = sizeof(struct vdec_queue_status);
ret = dal_call(vd->vdec_handle, VDEC_DALRPC_QUEUE, 8,
&rpc, sizeof(rpc), &rpc_res, sizeof(rpc_res));
if (ret < 4) {
pr_err("%s: remote function failed (%d)\n", __func__, ret);
ret = -EIO;
}
return ret;
}
static int vdec_reuse_framebuffer(struct vdec_data *vd, void *argp)
{
u32 buf_id;
int ret = 0;
ret = copy_from_user(&buf_id, argp, sizeof(buf_id));
if (ret) {
pr_err("%s: copy_from_user failed\n", __func__);
return ret;
}
ret = dal_call_f0(vd->vdec_handle, VDEC_DALRPC_REUSEFRAMEBUFFER,
buf_id);
if (ret)
pr_err("%s: remote function failed (%d)\n", __func__, ret);
return ret;
}
static int vdec_flush(struct vdec_data *vd, void *argp)
{
u32 flush_type;
int ret = 0;
ret = copy_from_user(&flush_type, argp, sizeof(flush_type));
if (ret) {
pr_err("%s: copy_from_user failed\n", __func__);
return ret;
}
TRACE("flush_type=%d\n", flush_type);
ret = dal_call_f0(vd->vdec_handle, VDEC_DALRPC_FLUSH, flush_type);
if (ret) {
pr_err("%s: remote function failed (%d)\n", __func__, ret);
return ret;
}
return ret;
}
static int vdec_close(struct vdec_data *vd, void *argp)
{
struct vdec_mem_list *l;
int ret = 0;
pr_info("q6vdec_close()\n");
vd->close_decode = 1;
wake_up(&vd->vdec_msg_evt);
ret = dal_call_f0(vd->vdec_handle, DAL_OP_CLOSE, 0);
if (ret)
pr_err("%s: failed to close daldevice (%d)\n", __func__, ret);
if (vd->mem_initialized) {
list_for_each_entry(l, &vd->vdec_mem_list_head, list)
put_pmem_file(l->mem.file);
}
return ret;
}
static int vdec_getdecattributes(struct vdec_data *vd, void *argp)
{
struct {
uint32_t status;
uint32_t size;
struct vdec_dec_attributes dec_attr;
} rpc;
uint32_t inp;
int ret = 0;
inp = sizeof(struct vdec_dec_attributes);
ret = dal_call(vd->vdec_handle, VDEC_DALRPC_GETDECATTRIBUTES, 9,
&inp, sizeof(inp), &rpc, sizeof(rpc));
if (ret < 4 || rpc.size != sizeof(struct vdec_dec_attributes)) {
pr_err("%s: remote function failed (%d)\n", __func__, ret);
ret = -EIO;
} else
ret =
copy_to_user(((struct vdec_dec_attributes *)argp),
&rpc.dec_attr, sizeof(rpc.dec_attr));
return ret;
}
static int vdec_freebuffers(struct vdec_data *vd, void *argp)
{
struct vdec_buffer vmem;
struct vdec_mem_list *l;
struct {
uint32_t size;
struct vdec_buf_info buf;
} rpc;
uint32_t res;
int ret = 0;
if (!vd->mem_initialized) {
pr_err("%s: memory is not being initialized!\n", __func__);
return -EPERM;
}
ret = copy_from_user(&vmem, argp, sizeof(vmem));
if (ret) {
pr_err("%s: copy_from_user failed\n", __func__);
return ret;
}
l = vdec_get_mem_from_list(vd, vmem.pmem_id, vmem.buf.buf_type);
if (NULL == l) {
pr_err("%s: not able to find the buffer from list\n", __func__);
return -EPERM;
}
/* input buffers */
if ((vmem.buf.region.offset + vmem.buf.region.size) > l->mem.len) {
pr_err("%s: invalid input buffer offset!\n", __func__);
return -EINVAL;
}
vmem.buf.region.offset += l->mem.phys_addr;
rpc.size = sizeof(vmem.buf);
memcpy(&rpc.buf, &vmem.buf, sizeof(struct vdec_buf_info));
ret = dal_call(vd->vdec_handle, VDEC_DALRPC_FREEBUFFERS, 5,
&rpc, sizeof(rpc), &res, sizeof(res));
if (ret < 4) {
pr_err("%s: remote function failed (%d)\n", __func__, ret);
}
return ret;
}
static long vdec_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct vdec_data *vd = file->private_data;
void __user *argp = (void __user *)arg;
int ret = 0;
if (!vd->running)
return -EPERM;
switch (cmd) {
case VDEC_IOCTL_INITIALIZE:
ret = vdec_initialize(vd, argp);
break;
case VDEC_IOCTL_SETBUFFERS:
ret = vdec_setbuffers(vd, argp);
break;
case VDEC_IOCTL_QUEUE:
TRACE("VDEC_IOCTL_QUEUE (pid=%d tid=%d)\n",
current->group_leader->pid, current->pid);
ret = vdec_queue(vd, argp);
break;
case VDEC_IOCTL_REUSEFRAMEBUFFER:
TRACE("VDEC_IOCTL_REUSEFRAMEBUFFER (pid=%d tid=%d)\n",
current->group_leader->pid, current->pid);
ret = vdec_reuse_framebuffer(vd, argp);
break;
case VDEC_IOCTL_FLUSH:
ret = vdec_flush(vd, argp);
break;
case VDEC_IOCTL_EOS:
TRACE("VDEC_IOCTL_EOS (pid=%d tid=%d)\n",
current->group_leader->pid, current->pid);
ret = dal_call_f0(vd->vdec_handle, VDEC_DALRPC_SIGEOFSTREAM, 0);
if (ret)
pr_err("%s: remote function failed (%d)\n",
__func__, ret);
break;
case VDEC_IOCTL_GETMSG:
TRACE("VDEC_IOCTL_GETMSG (pid=%d tid=%d)\n",
current->group_leader->pid, current->pid);
wait_event_interruptible(vd->vdec_msg_evt,
vdec_get_msg(vd, argp));
if (vd->close_decode)
ret = -EINTR;
break;
case VDEC_IOCTL_CLOSE:
ret = vdec_close(vd, argp);
break;
case VDEC_IOCTL_GETDECATTRIBUTES:
TRACE("VDEC_IOCTL_GETDECATTRIBUTES (pid=%d tid=%d)\n",
current->group_leader->pid, current->pid);
ret = vdec_getdecattributes(vd, argp);
if (ret)
pr_err("%s: remote function failed (%d)\n",
__func__, ret);
break;
case VDEC_IOCTL_FREEBUFFERS:
TRACE("VDEC_IOCTL_FREEBUFFERS (pid=%d tid=%d)\n",
current->group_leader->pid, current->pid);
ret = vdec_freebuffers(vd, argp);
if (ret)
pr_err("%s: remote function failed (%d)\n",
__func__, ret);
break;
default:
pr_err("%s: invalid ioctl!\n", __func__);
ret = -EINVAL;
break;
}
TRACE("ioctl done (pid=%d tid=%d)\n",
current->group_leader->pid, current->pid);
return ret;
}
static void vdec_dcdone_handler(struct vdec_data *vd, void *frame,
uint32_t frame_size)
{
struct vdec_msg msg;
struct vdec_mem_list *l;
unsigned long flags;
int found = 0;
if (frame_size != sizeof(struct vdec_frame_info)) {
pr_warning("%s: msg size mismatch %d != %d\n", __func__,
frame_size, sizeof(struct vdec_frame_info));
return;
}
memcpy(&msg.vfr_info, (struct vdec_frame_info *)frame,
sizeof(struct vdec_frame_info));
if (msg.vfr_info.status == VDEC_FRAME_DECODE_OK) {
spin_lock_irqsave(&vd->vdec_mem_list_lock, flags);
list_for_each_entry(l, &vd->vdec_mem_list_head, list) {
if ((l->mem.buf_type == VDEC_BUFFER_TYPE_OUTPUT) &&
(msg.vfr_info.offset >= l->mem.phys_addr) &&
(msg.vfr_info.offset <
(l->mem.phys_addr + l->mem.len))) {
found = 1;
msg.vfr_info.offset -= l->mem.phys_addr;
msg.vfr_info.data2 = l->mem.id;
break;
}
}
spin_unlock_irqrestore(&vd->vdec_mem_list_lock, flags);
}
if (found || (msg.vfr_info.status != VDEC_FRAME_DECODE_OK)) {
msg.id = VDEC_MSG_FRAMEDONE;
vdec_put_msg(vd, &msg);
} else {
pr_err("%s: invalid phys addr = 0x%x\n",
__func__, msg.vfr_info.offset);
}
}
static void vdec_reuseibuf_handler(struct vdec_data *vd, void *bufstat,
uint32_t bufstat_size)
{
struct vdec_buffer_status *vdec_bufstat;
struct vdec_msg msg;
/* TODO: how do we signal the client? If they are waiting on a
* message in an ioctl, they may block forever */
if (bufstat_size != sizeof(struct vdec_buffer_status)) {
pr_warning("%s: msg size mismatch %d != %d\n", __func__,
bufstat_size, sizeof(struct vdec_buffer_status));
return;
}
vdec_bufstat = (struct vdec_buffer_status *)bufstat;
msg.id = VDEC_MSG_REUSEINPUTBUFFER;
msg.buf_id = vdec_bufstat->data;
vdec_put_msg(vd, &msg);
}
static void callback(void *data, int len, void *cookie)
{
struct vdec_data *vd = (struct vdec_data *)cookie;
uint32_t *tmp = (uint32_t *) data;
if (!vd->mem_initialized) {
pr_err("%s:memory not initialize but callback called!\n",
__func__);
return;
}
TRACE("vdec_async: tmp=0x%08x 0x%08x 0x%08x\n", tmp[0], tmp[1], tmp[2]);
switch (tmp[0]) {
case VDEC_ASYNCMSG_DECODE_DONE:
vdec_dcdone_handler(vd, &tmp[3], tmp[2]);
break;
case VDEC_ASYNCMSG_REUSE_FRAME:
vdec_reuseibuf_handler(vd, &tmp[3], tmp[2]);
break;
default:
pr_err("%s: Unknown async message from DSP id=0x%08x sz=%u\n",
__func__, tmp[0], tmp[2]);
}
}
static int vdec_open(struct inode *inode, struct file *file)
{
int ret;
int i;
struct vdec_msg_list *l;
struct vdec_data *vd;
pr_info("q6vdec_open()\n");
mutex_lock(&vdec_ref_lock);
if (ref_cnt > 0) {
pr_err("%s: Instance alredy running\n", __func__);
mutex_unlock(&vdec_ref_lock);
return -ENOMEM;
}
ref_cnt++;
mutex_unlock(&vdec_ref_lock);
vd = kmalloc(sizeof(struct vdec_data), GFP_KERNEL);
if (!vd) {
pr_err("%s: kmalloc failed\n", __func__);
ret = -ENOMEM;
goto vdec_open_err_handle_vd;
}
file->private_data = vd;
vd->mem_initialized = 0;
INIT_LIST_HEAD(&vd->vdec_msg_list_head);
INIT_LIST_HEAD(&vd->vdec_msg_list_free);
INIT_LIST_HEAD(&vd->vdec_mem_list_head);
init_waitqueue_head(&vd->vdec_msg_evt);
spin_lock_init(&vd->vdec_list_lock);
spin_lock_init(&vd->vdec_mem_list_lock);
for (i = 0; i < VDEC_MSG_MAX; i++) {
l = kzalloc(sizeof(struct vdec_msg_list), GFP_KERNEL);
if (!l) {
pr_err("%s: kzalloc failed!\n", __func__);
ret = -ENOMEM;
goto vdec_open_err_handle_list;
}
list_add(&l->list, &vd->vdec_msg_list_free);
}
vd->vdec_handle = dal_attach(DALDEVICEID_VDEC_DEVICE,
DALDEVICEID_VDEC_PORTNAME,
callback, vd);
if (!vd->vdec_handle) {
pr_err("%s: failed to attach \n", __func__);
ret = -EIO;
goto vdec_open_err_handle_list;
}
vd->running = 1;
prevent_sleep();
return 0;
vdec_open_err_handle_list:
{
struct vdec_msg_list *l, *n;
list_for_each_entry_safe(l, n, &vd->vdec_msg_list_free, list) {
list_del(&l->list);
kfree(l);
}
}
vdec_open_err_handle_vd:
kfree(vd);
return ret;
}
static int vdec_release(struct inode *inode, struct file *file)
{
int ret;
struct vdec_msg_list *l, *n;
struct vdec_mem_list *m, *k;
struct vdec_data *vd = file->private_data;
vd->running = 0;
wake_up_all(&vd->vdec_msg_evt);
if (!vd->close_decode)
vdec_close(vd, NULL);
ret = dal_detach(vd->vdec_handle);
if (ret)
printk(KERN_INFO "%s: failed to detach (%d)\n", __func__, ret);
list_for_each_entry_safe(l, n, &vd->vdec_msg_list_free, list) {
list_del(&l->list);
kfree(l);
}
list_for_each_entry_safe(l, n, &vd->vdec_msg_list_head, list) {
list_del(&l->list);
kfree(l);
}
list_for_each_entry_safe(m, k, &vd->vdec_mem_list_head, list) {
list_del(&m->list);
kfree(m);
}
mutex_lock(&vdec_ref_lock);
BUG_ON(ref_cnt <= 0);
ref_cnt--;
mutex_unlock(&vdec_ref_lock);
kfree(vd);
allow_sleep();
return 0;
}
static const struct file_operations vdec_fops = {
.owner = THIS_MODULE,
.open = vdec_open,
.release = vdec_release,
.unlocked_ioctl = vdec_ioctl,
};
static int __init vdec_init(void)
{
struct device *class_dev;
int rc = 0;
wake_lock_init(&idlelock, WAKE_LOCK_IDLE, "vdec_idle");
wake_lock_init(&wakelock, WAKE_LOCK_SUSPEND, "vdec_suspend");
rc = alloc_chrdev_region(&vdec_device_no, 0, 1, "vdec");
if (rc < 0) {
pr_err("%s: alloc_chrdev_region failed %d\n", __func__, rc);
return rc;
}
driver_class = class_create(THIS_MODULE, "vdec");
if (IS_ERR(driver_class)) {
rc = -ENOMEM;
pr_err("%s: class_create failed %d\n", __func__, rc);
goto vdec_init_err_unregister_chrdev_region;
}
class_dev = device_create(driver_class, NULL,
vdec_device_no, NULL, "vdec");
if (!class_dev) {
pr_err("%s: class_device_create failed %d\n", __func__, rc);
rc = -ENOMEM;
goto vdec_init_err_class_destroy;
}
cdev_init(&vdec_cdev, &vdec_fops);
vdec_cdev.owner = THIS_MODULE;
rc = cdev_add(&vdec_cdev, MKDEV(MAJOR(vdec_device_no), 0), 1);
if (rc < 0) {
pr_err("%s: cdev_add failed %d\n", __func__, rc);
goto vdec_init_err_class_device_destroy;
}
return 0;
vdec_init_err_class_device_destroy:
device_destroy(driver_class, vdec_device_no);
vdec_init_err_class_destroy:
class_destroy(driver_class);
vdec_init_err_unregister_chrdev_region:
unregister_chrdev_region(vdec_device_no, 1);
return rc;
}
static void __exit vdec_exit(void)
{
device_destroy(driver_class, vdec_device_no);
class_destroy(driver_class);
unregister_chrdev_region(vdec_device_no, 1);
}
MODULE_DESCRIPTION("video decoder driver for QSD platform");
MODULE_VERSION("2.00");
module_init(vdec_init);
module_exit(vdec_exit);

View File

@ -0,0 +1,637 @@
/*
* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
* Copyright (c) 2009, Google Inc.
*
* Original authors: Code Aurora Forum
* Major cleanup: Dima Zavin <dima@android.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Code Aurora Forum nor
* the names of its contributors may be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
//#define DEBUG 1
#include <linux/device.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/uaccess.h>
#include <linux/android_pmem.h>
#include <linux/msm_q6venc_1550.h>
#include <asm/cacheflush.h>
#include "dal.h"
#define DALDEVICEID_VENC_DEVICE 0x0200002D
#define DALDEVICEID_VENC_PORTNAME "DSP_DAL_AQ_VID"
enum {
VENC_DALRPC_INITIALIZE = DAL_OP_FIRST_DEVICE_API,
VENC_DALRPC_SET_CB_CHANNEL,
VENC_DALRPC_ENCODE,
VENC_DALRPC_INTRA_REFRESH,
VENC_DALRPC_RC_CONFIG,
VENC_DALRPC_ENCODE_CONFIG,
VENC_DALRPC_STOP,
};
struct callback_event_data {
u32 data_notify_event;
u32 enc_cb_handle;
u32 empty_input_buffer_event;
};
struct buf_info {
unsigned long paddr;
unsigned long vaddr;
struct file *file;
struct venc_buf venc_buf;
};
#define VENC_MAX_BUF_NUM 15
#define RLC_MAX_BUF_NUM 2
#define BITS_PER_PIXEL 12
#define PIXELS_PER_MACROBLOCK 16
#define VENC_CB_EVENT_ID 0xd0e4c0de
struct q6venc_dev {
struct dal_client *venc;
struct callback_event_data cb_ev_data;
bool stop_encode;
struct buf_info rlc_bufs[RLC_MAX_BUF_NUM];
unsigned int rlc_buf_index;
unsigned int rlc_buf_len;
unsigned int enc_buf_size;
struct buf_info enc_bufs[VENC_MAX_BUF_NUM];
unsigned int num_enc_bufs;
wait_queue_head_t encode_wq;
/* protects all state in q6venc_dev except for cb stuff below */
struct mutex lock;
/* protects encode_done and done_frame inside the callback */
spinlock_t done_lock;
struct frame_type done_frame;
bool encode_done;
};
static int get_buf_info(struct buf_info *buf_info, struct venc_buf *venc_buf)
{
unsigned long len;
unsigned long vaddr;
unsigned long paddr;
struct file *file;
int ret;
ret = get_pmem_file(venc_buf->fd, &paddr, &vaddr, &len, &file);
if (ret) {
pr_err("%s: get_pmem_file failed for fd=%d offset=%ld\n",
__func__, venc_buf->fd, venc_buf->offset);
return ret;
} else if (venc_buf->offset >= len) {
/* XXX: we really should check venc_buf->size too, but userspace
* sometimes leaves this uninitialized (in encode ioctl) */
pr_err("%s: invalid offset/size (%ld + %ld > %ld) for fd=%d\n",
__func__, venc_buf->offset, venc_buf->size, len,
venc_buf->fd);
put_pmem_file(file);
return -EINVAL;
}
buf_info->file = file;
buf_info->paddr = paddr + venc_buf->offset;
buf_info->vaddr = vaddr;
memcpy(&buf_info->venc_buf, venc_buf, sizeof(struct venc_buf));
return 0;
}
static void put_buf_info(struct buf_info *buf_info)
{
if (!buf_info || !buf_info->file)
return;
put_pmem_file(buf_info->file);
buf_info->file = NULL;
}
static void q6venc_callback(void *context, void *data, uint32_t len)
{
struct q6venc_dev *q6venc = context;
struct q6_frame_type *q6frame = data;
struct buf_info *rlc_buf;
unsigned long flags;
int i;
pr_debug("%s \n", __func__);
spin_lock_irqsave(&q6venc->done_lock, flags);
q6venc->encode_done = true;
for (i = 0; i < RLC_MAX_BUF_NUM; ++i) {
rlc_buf = &q6venc->rlc_bufs[i];
if (rlc_buf->paddr == q6frame->frame_addr)
goto frame_found;
}
pr_err("%s: got incorrect phy address 0x%08x from q6 \n", __func__,
q6frame->frame_addr);
q6venc->done_frame.q6_frame_type.frame_len = 0;
wake_up_interruptible(&q6venc->encode_wq);
goto done;
frame_found:
memcpy(&q6venc->done_frame.frame_addr, &rlc_buf->venc_buf,
sizeof(struct venc_buf));
memcpy(&q6venc->done_frame.q6_frame_type, q6frame,
sizeof(struct q6_frame_type));
dmac_inv_range((const void *)q6venc->rlc_bufs[i].vaddr,
(const void *)(q6venc->rlc_bufs[i].vaddr +
q6venc->rlc_buf_len));
wake_up_interruptible(&q6venc->encode_wq);
done:
spin_unlock_irqrestore(&q6venc->done_lock, flags);
}
static void callback(void *data, int len, void *cookie)
{
struct q6venc_dev *ve = (struct q6venc_dev *)cookie;
uint32_t *tmp = (uint32_t *) data;
if (tmp[0] == VENC_CB_EVENT_ID)
q6venc_callback(ve, &tmp[3], tmp[2]);
else
pr_err("%s: Unknown callback received for %p\n", __func__, ve);
}
static int q6venc_open(struct inode *inode, struct file *file)
{
struct q6venc_dev *q6venc;
int err;
q6venc = kzalloc(sizeof(struct q6venc_dev), GFP_KERNEL);
if (!q6venc) {
pr_err("%s: Unable to allocate memory for q6venc_dev\n",
__func__);
return -ENOMEM;
}
file->private_data = q6venc;
init_waitqueue_head(&q6venc->encode_wq);
mutex_init(&q6venc->lock);
spin_lock_init(&q6venc->done_lock);
q6venc->venc = dal_attach(DALDEVICEID_VENC_DEVICE,
DALDEVICEID_VENC_PORTNAME,
callback, q6venc);
if (!q6venc->venc) {
pr_err("%s: dal_attach failed\n", __func__);
err = -EIO;
goto err_dal_attach;
}
q6venc->cb_ev_data.enc_cb_handle = VENC_CB_EVENT_ID;
err = dal_call_f5(q6venc->venc, VENC_DALRPC_SET_CB_CHANNEL,
&q6venc->cb_ev_data, sizeof(q6venc->cb_ev_data));
if (err) {
pr_err("%s: set_cb_channgel failed\n", __func__);
goto err_dal_call_set_cb;
}
pr_info("%s() handle=%p enc_cb=%08x\n", __func__, q6venc->venc,
q6venc->cb_ev_data.enc_cb_handle);
return 0;
err_dal_call_set_cb:
dal_detach(q6venc->venc);
err_dal_attach:
file->private_data = NULL;
mutex_destroy(&q6venc->lock);
kfree(q6venc);
return err;
}
static int q6venc_release(struct inode *inode, struct file *file)
{
struct q6venc_dev *q6venc;
int id, err;
q6venc = file->private_data;
file->private_data = NULL;
pr_info("q6venc_close() handle=%p\n", q6venc->venc);
for (id = 0; id < q6venc->num_enc_bufs; id++)
put_buf_info(&q6venc->enc_bufs[id]);
put_buf_info(&q6venc->rlc_bufs[0]);
put_buf_info(&q6venc->rlc_bufs[1]);
if(!q6venc->stop_encode)
{
err = dal_call_f0(q6venc->venc, VENC_DALRPC_STOP, 1);
if (err)
pr_err("%s: dal_rpc STOP call failed\n", __func__);
q6venc->stop_encode = true;
}
dal_detach(q6venc->venc);
mutex_destroy(&q6venc->lock);
kfree(q6venc);
return 0;
}
static int q6_config_encode(struct q6venc_dev *q6venc, uint32_t type,
struct init_config *init_config)
{
struct q6_init_config *q6_init_config = &init_config->q6_init_config;
int ret;
int i;
mutex_lock(&q6venc->lock);
if (q6venc->num_enc_bufs != 0) {
pr_err("%s: multiple sessions not supported\n", __func__);
ret = -EBUSY;
goto err_busy;
}
ret = get_buf_info(&q6venc->enc_bufs[0], &init_config->ref_frame_buf1);
if (ret) {
pr_err("%s: can't get ref_frame_buf1\n", __func__);
goto err_get_ref_frame_buf1;
}
ret = get_buf_info(&q6venc->enc_bufs[1], &init_config->ref_frame_buf2);
if (ret) {
pr_err("%s: can't get ref_frame_buf2\n", __func__);
goto err_get_ref_frame_buf2;
}
ret = get_buf_info(&q6venc->rlc_bufs[0], &init_config->rlc_buf1);
if (ret) {
pr_err("%s: can't get rlc_buf1\n", __func__);
goto err_get_rlc_buf1;
}
ret = get_buf_info(&q6venc->rlc_bufs[1], &init_config->rlc_buf2);
if (ret) {
pr_err("%s: can't get rlc_buf2\n", __func__);
goto err_get_rlc_buf2;
}
q6venc->rlc_buf_len = 2 * q6_init_config->rlc_buf_length;
q6venc->num_enc_bufs = 2;
q6venc->enc_buf_size =
(q6_init_config->enc_frame_width_inmb * PIXELS_PER_MACROBLOCK) *
(q6_init_config->enc_frame_height_inmb * PIXELS_PER_MACROBLOCK) *
BITS_PER_PIXEL / 8;
q6_init_config->ref_frame_buf1_phy = q6venc->enc_bufs[0].paddr;
q6_init_config->ref_frame_buf2_phy = q6venc->enc_bufs[1].paddr;
q6_init_config->rlc_buf1_phy = q6venc->rlc_bufs[0].paddr;
q6_init_config->rlc_buf2_phy = q6venc->rlc_bufs[1].paddr;
// The DSP may use the rlc_bufs during initialization,
for (i=0; i<RLC_MAX_BUF_NUM; i++)
{
dmac_inv_range((const void *)q6venc->rlc_bufs[i].vaddr,
(const void *)(q6venc->rlc_bufs[i].vaddr +
q6venc->rlc_buf_len));
}
ret = dal_call_f5(q6venc->venc, type, q6_init_config,
sizeof(struct q6_init_config));
if (ret) {
pr_err("%s: rpc failed \n", __func__);
goto err_dal_rpc_init;
}
mutex_unlock(&q6venc->lock);
return 0;
err_dal_rpc_init:
q6venc->num_enc_bufs = 0;
put_pmem_file(q6venc->rlc_bufs[1].file);
err_get_rlc_buf2:
put_pmem_file(q6venc->rlc_bufs[0].file);
err_get_rlc_buf1:
put_pmem_file(q6venc->enc_bufs[1].file);
err_get_ref_frame_buf2:
put_pmem_file(q6venc->enc_bufs[0].file);
err_get_ref_frame_buf1:
err_busy:
mutex_unlock(&q6venc->lock);
return ret;
}
static int q6_encode(struct q6venc_dev *q6venc, struct encode_param *enc_param)
{
struct q6_encode_param *q6_param = &enc_param->q6_encode_param;
struct file *file;
struct buf_info *buf;
int i;
int ret;
int rlc_buf_index;
pr_debug("y_addr fd=%d offset=0x%08lx uv_offset=0x%08lx\n",
enc_param->y_addr.fd, enc_param->y_addr.offset,
enc_param->uv_offset);
file = fget(enc_param->y_addr.fd);
if (!file) {
pr_err("%s: invalid encode buffer fd %d\n", __func__,
enc_param->y_addr.fd);
return -EBADF;
}
mutex_lock(&q6venc->lock);
for (i = 0; i < q6venc->num_enc_bufs; i++) {
buf = &q6venc->enc_bufs[i];
if (buf->file == file
&& buf->venc_buf.offset == enc_param->y_addr.offset)
break;
}
if (i == q6venc->num_enc_bufs) {
if (q6venc->num_enc_bufs == VENC_MAX_BUF_NUM) {
pr_err("%s: too many input buffers\n", __func__);
ret = -ENOMEM;
goto done;
}
buf = &q6venc->enc_bufs[q6venc->num_enc_bufs];
ret = get_buf_info(buf, &enc_param->y_addr);
if (ret) {
pr_err("%s: can't get encode buffer\n", __func__);
ret = -EINVAL;
goto done;
}
if (!IS_ALIGNED(buf->paddr, PAGE_SIZE)) {
pr_err("%s: input buffer not 4k aligned\n", __func__);
put_buf_info(buf);
ret = -EINVAL;
goto done;
}
q6venc->num_enc_bufs++;
}
/* We must invalidate the buffer that the DSP will write to
* to ensure that a dirty cache line doesn't get flushed on
* top of the data that the DSP is writing.
* Unfortunately, we have to predict which rlc_buf index the
* DSP is going to write to. We assume it will write to buf
* 0 the first time we call q6_encode, and alternate afterwards
* */
rlc_buf_index = q6venc->rlc_buf_index;
dmac_inv_range((const void *)q6venc->rlc_bufs[rlc_buf_index].vaddr,
(const void *)(q6venc->rlc_bufs[rlc_buf_index].vaddr +
q6venc->rlc_buf_len));
q6venc->rlc_buf_index = (q6venc->rlc_buf_index + 1) % RLC_MAX_BUF_NUM;
q6_param->luma_addr = buf->paddr;
q6_param->chroma_addr = q6_param->luma_addr + enc_param->uv_offset;
pr_debug("luma_addr=0x%08x chroma_addr=0x%08x\n", q6_param->luma_addr,
q6_param->chroma_addr);
/* Ideally, each ioctl that passed in a data buffer would include the size
* of the input buffer, so we can properly flush the cache on it. Since
* userspace does not fill in the size fields, we have to assume the size
* based on the encoder configuration for now.
*/
flush_pmem_file(buf->file, enc_param->y_addr.offset,
q6venc->enc_buf_size);
ret = dal_call_f5(q6venc->venc, VENC_DALRPC_ENCODE, q6_param,
sizeof(struct q6_encode_param));
if (ret) {
pr_err("%s: encode rpc failed\n", __func__);
goto done;
}
ret = 0;
done:
mutex_unlock(&q6venc->lock);
fput(file);
return ret;
}
static int q6venc_ioctl(struct inode *inode, struct file *file,
unsigned cmd, unsigned long arg)
{
struct q6venc_dev *q6venc = file->private_data;
struct init_config config;
struct encode_param encode_param;
struct intra_refresh intra_refresh;
struct rc_config rc_config;
struct frame_type frame_done;
unsigned int id;
unsigned long flags;
int err = 0;
if (!q6venc) {
pr_err("%s: file has no private data\n", __func__);
return -ENODEV;
}
pr_debug("%s\n", __func__);
switch (cmd) {
case VENC_IOCTL_INITIALIZE:
pr_debug("%s: VENC_IOCTL_INITIALIZE\n", __func__);
if (copy_from_user(&config, (void __user *)arg, sizeof(config)))
return -EFAULT;
err = q6_config_encode(q6venc, VENC_DALRPC_INITIALIZE, &config);
break;
case VENC_IOCTL_ENCODE_CONFIG:
pr_debug("%s: VENC_IOCTL_ENCODE_CONFIG\n", __func__);
if (copy_from_user(&config, (void __user *)arg, sizeof(config)))
return -EFAULT;
err = q6_config_encode(q6venc, VENC_DALRPC_ENCODE_CONFIG,
&config);
break;
case VENC_IOCTL_ENCODE:
pr_debug("%s: VENC_IOCTL_ENCODE\n", __func__);
if (copy_from_user(&encode_param, (void __user *)arg,
sizeof(encode_param)))
return -EFAULT;
err = q6_encode(q6venc, &encode_param);
break;
case VENC_IOCTL_INTRA_REFRESH:
pr_debug("%s: VENC_IOCTL_INTRA_REFRESH\n", __func__);
if (copy_from_user(&intra_refresh, (void __user *)arg,
sizeof(intra_refresh)))
return -EFAULT;
mutex_lock(&q6venc->lock);
err = dal_call_f5(q6venc->venc, VENC_DALRPC_INTRA_REFRESH,
&intra_refresh, sizeof(struct intra_refresh));
mutex_unlock(&q6venc->lock);
if (err)
pr_err("%s: intra_refresh rpc failed\n", __func__);
break;
case VENC_IOCTL_RC_CONFIG:
pr_debug("%s: VENC_IOCTL_RC_CONFIG\n", __func__);
if (copy_from_user(&rc_config, (void __user *)arg,
sizeof(rc_config)))
return -EFAULT;
mutex_lock(&q6venc->lock);
err = dal_call_f5(q6venc->venc, VENC_DALRPC_RC_CONFIG,
&rc_config, sizeof(rc_config));
mutex_unlock(&q6venc->lock);
if (err)
pr_err("%s: dal_call_f5 failed\n", __func__);
break;
case VENC_IOCTL_STOP:
pr_debug("%s: VENC_IOCTL_STOP\n", __func__);
mutex_lock(&q6venc->lock);
err = dal_call_f0(q6venc->venc, VENC_DALRPC_STOP, 1);
if (err)
pr_err("%s: dal_rpc STOP call failed\n", __func__);
/* XXX: if the dal call fails we still want to continue to free
* the buffers. Is this correct? */
for (id = 0; id < q6venc->num_enc_bufs; id++)
put_buf_info(&q6venc->enc_bufs[id]);
put_buf_info(&q6venc->rlc_bufs[0]);
put_buf_info(&q6venc->rlc_bufs[1]);
q6venc->num_enc_bufs = 0;
q6venc->stop_encode = true;
mutex_unlock(&q6venc->lock);
break;
case VENC_IOCTL_WAIT_FOR_ENCODE:
pr_debug("%s: waiting for encode done event \n", __func__);
err = wait_event_interruptible(q6venc->encode_wq,
(q6venc->encode_done || q6venc->stop_encode));
if (err < 0) {
err = -ERESTARTSYS;
break;
}
mutex_lock(&q6venc->lock);
if (q6venc->stop_encode) {
q6venc->stop_encode = false;
mutex_unlock(&q6venc->lock);
pr_debug("%s: Received Stop encode event \n", __func__);
err = -EINTR;
break;
}
spin_lock_irqsave(&q6venc->done_lock, flags);
if (!q6venc->encode_done) {
spin_unlock_irqrestore(&q6venc->done_lock, flags);
pr_err("%s: encoding not stopped, and is not done.\n",
__func__);
err = -EIO;
break;
}
memcpy(&frame_done, &q6venc->done_frame,
sizeof(struct frame_type));
q6venc->encode_done = false;
spin_unlock_irqrestore(&q6venc->done_lock, flags);
mutex_unlock(&q6venc->lock);
if (frame_done.q6_frame_type.frame_len == 0) {
pr_debug("%s: got incorrect address from q6\n",
__func__);
err = -EIO;
break;
}
pr_debug("%s: done encoding \n", __func__);
if (copy_to_user((void __user *)arg, &frame_done,
sizeof(struct frame_type)))
err = -EFAULT;
break;
case VENC_IOCTL_STOP_ENCODE:
pr_debug("%s: Stop encode event \n", __func__);
mutex_lock(&q6venc->lock);
q6venc->stop_encode = true;
wake_up_interruptible(&q6venc->encode_wq);
mutex_unlock(&q6venc->lock);
break;
default:
err = -ENOTTY;
break;
}
return err;
}
static const struct file_operations q6venc_dev_fops = {
.owner = THIS_MODULE,
.open = q6venc_open,
.release = q6venc_release,
.ioctl = q6venc_ioctl,
};
static struct miscdevice q6venc_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "q6venc",
.fops = &q6venc_dev_fops,
};
static int __init q6venc_init(void)
{
int rc = 0;
rc = misc_register(&q6venc_misc);
if (rc)
pr_err("%s: Unable to register q6venc misc device\n", __func__);
return rc;
}
static void __exit q6venc_exit(void)
{
misc_deregister(&q6venc_misc);
}
MODULE_DESCRIPTION("video encoder driver for QSD platform");
MODULE_VERSION("2.0");
module_init(q6venc_init);
module_exit(q6venc_exit);

View File

@ -0,0 +1,207 @@
/* arch/arm/mach-msm/qdsp6/pcm_in.c
*
* Copyright (C) 2009 Google, Inc.
* Copyright (C) 2009 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/fs.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/mutex.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/uaccess.h>
#include <linux/msm_audio.h>
#include <mach/msm_qdsp6_audio.h>
#define BUFSZ (4096)
#define DMASZ (BUFSZ * 2)
static DEFINE_MUTEX(pcm_in_lock);
static uint32_t sample_rate = 8000;
static uint32_t channel_count = 1;
static int pcm_in_opened = 0;
void audio_client_dump(struct audio_client *ac);
static long q6_in_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int rc = 0;
switch (cmd) {
case AUDIO_SET_VOLUME:
break;
case AUDIO_GET_STATS: {
struct msm_audio_stats stats;
memset(&stats, 0, sizeof(stats));
if (copy_to_user((void*) arg, &stats, sizeof(stats)))
return -EFAULT;
return 0;
}
case AUDIO_START: {
uint32_t acdb_id;
rc = 0;
if (arg == 0) {
acdb_id = 0;
} else if (copy_from_user(&acdb_id, (void*) arg, sizeof(acdb_id))) {
rc = -EFAULT;
break;
}
mutex_lock(&pcm_in_lock);
if (file->private_data) {
rc = -EBUSY;
} else {
file->private_data = q6audio_open_pcm(
BUFSZ, sample_rate, channel_count, AUDIO_FLAG_READ, acdb_id);
if (!file->private_data)
rc = -ENOMEM;
}
mutex_unlock(&pcm_in_lock);
break;
}
case AUDIO_STOP:
break;
case AUDIO_FLUSH:
break;
case AUDIO_SET_CONFIG: {
struct msm_audio_config config;
if (copy_from_user(&config, (void*) arg, sizeof(config))) {
rc = -EFAULT;
break;
}
sample_rate = config.sample_rate;
channel_count = config.channel_count;
break;
}
case AUDIO_GET_CONFIG: {
struct msm_audio_config config;
config.buffer_size = BUFSZ;
config.buffer_count = 2;
config.sample_rate = sample_rate;
config.channel_count = channel_count;
config.unused[0] = 0;
config.unused[1] = 0;
config.unused[2] = 0;
if (copy_to_user((void*) arg, &config, sizeof(config))) {
rc = -EFAULT;
}
break;
}
default:
rc = -EINVAL;
}
return rc;
}
static int q6_in_open(struct inode *inode, struct file *file)
{
int rc;
pr_info("pcm_in: open\n");
mutex_lock(&pcm_in_lock);
if (pcm_in_opened) {
pr_err("pcm_in: busy\n");
rc = -EBUSY;
} else {
pcm_in_opened = 1;
rc = 0;
}
mutex_unlock(&pcm_in_lock);
return rc;
}
static ssize_t q6_in_read(struct file *file, char __user *buf,
size_t count, loff_t *pos)
{
struct audio_client *ac;
struct audio_buffer *ab;
const char __user *start = buf;
int xfer;
int res;
mutex_lock(&pcm_in_lock);
ac = file->private_data;
if (!ac) {
res = -ENODEV;
goto fail;
}
while (count > 0) {
ab = ac->buf + ac->cpu_buf;
if (ab->used)
if (!wait_event_timeout(ac->wait, (ab->used == 0), 5*HZ)) {
audio_client_dump(ac);
pr_err("pcm_read: timeout. dsp dead?\n");
//BUG();
res = -EFAULT;
goto fail;
}
xfer = count;
if (xfer > ab->size)
xfer = ab->size;
if (copy_to_user(buf, ab->data, xfer)) {
res = -EFAULT;
goto fail;
}
buf += xfer;
count -= xfer;
ab->used = 1;
q6audio_read(ac, ab);
ac->cpu_buf ^= 1;
}
fail:
res = buf - start;
mutex_unlock(&pcm_in_lock);
return res;
}
static int q6_in_release(struct inode *inode, struct file *file)
{
int rc = 0;
mutex_lock(&pcm_in_lock);
if (file->private_data)
rc = q6audio_close(file->private_data);
pcm_in_opened = 0;
mutex_unlock(&pcm_in_lock);
pr_info("pcm_in: release\n");
return rc;
}
static struct file_operations q6_in_fops = {
.owner = THIS_MODULE,
.open = q6_in_open,
.read = q6_in_read,
.release = q6_in_release,
.unlocked_ioctl = q6_in_ioctl,
};
struct miscdevice q6_in_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "msm_pcm_in",
.fops = &q6_in_fops,
};
static int __init q6_in_init(void) {
return misc_register(&q6_in_misc);
}
device_initcall(q6_in_init);

View File

@ -0,0 +1,242 @@
/* arch/arm/mach-msm/qdsp6/pcm_out.c
*
* Copyright (C) 2009 Google, Inc.
* Author: Brian Swetland <swetland@google.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/fs.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/mutex.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/uaccess.h>
#include <linux/msm_audio.h>
#include <mach/msm_qdsp6_audio.h>
#include "dal_audio.h"
#if 0
#define AUDIO_INFO(x...) pr_info("Audio: "x)
#else
#define AUDIO_INFO(x...) do{}while(0)
#endif
void audio_client_dump(struct audio_client *ac);
#define BUFSZ (4096) //(3072)
#define DMASZ (BUFSZ * 2)
struct pcm {
struct mutex lock;
struct audio_client *ac;
uint32_t sample_rate;
uint32_t channel_count;
};
static long pcm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct pcm *pcm = file->private_data;
struct cad_audio_eq_cfg eq_cfg;
int rc = 0;
AUDIO_INFO("%s: %X %X\n", __func__, cmd, arg);
if (cmd == AUDIO_GET_STATS) {
struct msm_audio_stats stats;
memset(&stats, 0, sizeof(stats));
if (copy_to_user((void*) arg, &stats, sizeof(stats)))
return -EFAULT;
return 0;
}
mutex_lock(&pcm->lock);
switch (cmd) {
case AUDIO_SET_VOLUME: {
int vol;
if (copy_from_user(&vol, (void*) arg, sizeof(vol))) {
rc = -EFAULT;
break;
}
rc = q6audio_set_stream_volume(pcm->ac, vol);
break;
}
case AUDIO_SET_EQ: {
if (copy_from_user(&eq_cfg, (void *)arg, sizeof(struct cad_audio_eq_cfg))) {
rc = -EFAULT;
break;
}
rc = q6audio_set_stream_eq(pcm->ac, &eq_cfg);
break;
}
case AUDIO_START: {
uint32_t acdb_id;
if (arg == 0) {
acdb_id = 0;
} else if (copy_from_user(&acdb_id, (void*) arg, sizeof(acdb_id))) {
pr_info("pcm_out: copy acdb_id from user failed\n");
rc = -EFAULT;
break;
}
if (pcm->ac) {
rc = -EBUSY;
} else {
pcm->ac = q6audio_open_pcm(BUFSZ, pcm->sample_rate,
pcm->channel_count,
AUDIO_FLAG_WRITE, acdb_id);
if (!pcm->ac)
rc = -ENOMEM;
}
break;
}
case AUDIO_STOP:
break;
case AUDIO_FLUSH:
break;
case AUDIO_SET_CONFIG: {
struct msm_audio_config config;
if (pcm->ac) {
rc = -EBUSY;
break;
}
if (copy_from_user(&config, (void*) arg, sizeof(config))) {
rc = -EFAULT;
break;
}
if (config.channel_count < 1 || config.channel_count > 2) {
rc = -EINVAL;
break;
}
pcm->sample_rate = config.sample_rate;
pcm->channel_count = config.channel_count;
break;
}
case AUDIO_GET_CONFIG: {
struct msm_audio_config config;
config.buffer_size = BUFSZ;
config.buffer_count = 2;
config.sample_rate = pcm->sample_rate;
config.channel_count = pcm->channel_count;
config.unused[0] = 0;
config.unused[1] = 0;
config.unused[2] = 0;
if (copy_to_user((void*) arg, &config, sizeof(config))) {
rc = -EFAULT;
}
break;
}
default:
rc = -EINVAL;
}
mutex_unlock(&pcm->lock);
return rc;
}
static int pcm_open(struct inode *inode, struct file *file)
{
struct pcm *pcm;
AUDIO_INFO("%s\n", __func__);
pr_info("pcm_out: open\n");
pcm = kzalloc(sizeof(struct pcm), GFP_KERNEL);
if (!pcm)
return -ENOMEM;
mutex_init(&pcm->lock);
pcm->channel_count = 2;
pcm->sample_rate = 44100;
file->private_data = pcm;
return 0;
}
static ssize_t pcm_write(struct file *file, const char __user *buf,
size_t count, loff_t *pos)
{
struct pcm *pcm = file->private_data;
struct audio_client *ac;
struct audio_buffer *ab;
const char __user *start = buf;
int xfer;
AUDIO_INFO("%s: size = %d\n", __func__, count);
if (!pcm->ac)
pcm_ioctl(file, AUDIO_START, 0);
ac = pcm->ac;
if (!ac)
return -ENODEV;
while (count > 0)
{
ab = ac->buf + ac->cpu_buf;
if (ab->used)
if (!wait_event_timeout(ac->wait, (ab->used == 0), 5*HZ))
{
audio_client_dump(ac);
pr_err("pcm_write: timeout. dsp dead?\n");
//BUG();
return -EFAULT;
}
xfer = count;
if (xfer > ab->size)
xfer = ab->size;
if (copy_from_user(ab->data, buf, xfer))
return -EFAULT;
buf += xfer;
count -= xfer;
ab->used = xfer;
q6audio_write(ac, ab);
ac->cpu_buf ^= 1;
}
return buf - start;
}
static int pcm_release(struct inode *inode, struct file *file)
{
struct pcm *pcm = file->private_data;
AUDIO_INFO("%s\n", __func__);
if (pcm->ac)
q6audio_close(pcm->ac);
kfree(pcm);
pr_info("pcm_out: release\n");
return 0;
}
static struct file_operations pcm_fops = {
.owner = THIS_MODULE,
.open = pcm_open,
.write = pcm_write,
.release = pcm_release,
.unlocked_ioctl = pcm_ioctl,
};
struct miscdevice pcm_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "msm_pcm_out",
.fops = &pcm_fops,
};
static int __init pcm_init(void) {
return misc_register(&pcm_misc);
}
device_initcall(pcm_init);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,268 @@
/* arch/arm/mach-msm/qdsp6/q6audio_devices.h
*
* Copyright (C) 2009 Google, Inc.
* Author: Brian Swetland <swetland@google.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.
*
*/
struct q6_device_info {
uint32_t id;
uint32_t cad_id;
uint32_t path;
uint32_t rate;
uint8_t dir;
uint8_t codec;
uint8_t hw;
};
#define Q6_ICODEC_RX 0
#define Q6_ICODEC_TX 1
#define Q6_ECODEC_RX 2
#define Q6_ECODEC_TX 3
#define Q6_SDAC_RX 6
#define Q6_SDAC_TX 7
#define Q6_CODEC_NONE 255
#define Q6_TX 1
#define Q6_RX 2
#define Q6_TX_RX 3
#define CAD_HW_DEVICE_ID_HANDSET_MIC 0x01
#define CAD_HW_DEVICE_ID_HANDSET_SPKR 0x02
#define CAD_HW_DEVICE_ID_HEADSET_MIC 0x03
#define CAD_HW_DEVICE_ID_HEADSET_SPKR_MONO 0x04
#define CAD_HW_DEVICE_ID_HEADSET_SPKR_STEREO 0x05
#define CAD_HW_DEVICE_ID_SPKR_PHONE_MIC 0x06
#define CAD_HW_DEVICE_ID_SPKR_PHONE_MONO 0x07
#define CAD_HW_DEVICE_ID_SPKR_PHONE_STEREO 0x08
#define CAD_HW_DEVICE_ID_BT_SCO_MIC 0x09
#define CAD_HW_DEVICE_ID_BT_SCO_SPKR 0x0A
#define CAD_HW_DEVICE_ID_BT_A2DP_SPKR 0x0B
#define CAD_HW_DEVICE_ID_TTY_HEADSET_MIC 0x0C
#define CAD_HW_DEVICE_ID_TTY_HEADSET_SPKR 0x0D
#define CAD_HW_DEVICE_ID_DEFAULT_TX 0x0E
#define CAD_HW_DEVICE_ID_DEFAULT_RX 0x0F
/* Logical Device to indicate A2DP routing */
#define CAD_HW_DEVICE_ID_BT_A2DP_TX 0x10
#define CAD_HW_DEVICE_ID_HEADSET_MONO_PLUS_SPKR_MONO_RX 0x11
#define CAD_HW_DEVICE_ID_HEADSET_MONO_PLUS_SPKR_STEREO_RX 0x12
#define CAD_HW_DEVICE_ID_HEADSET_STEREO_PLUS_SPKR_MONO_RX 0x13
#define CAD_HW_DEVICE_ID_HEADSET_STEREO_PLUS_SPKR_STEREO_RX 0x14
#define CAD_HW_DEVICE_ID_VOICE 0x15
#define CAD_HW_DEVICE_ID_I2S_RX 0x20
#define CAD_HW_DEVICE_ID_I2S_TX 0x21
/* AUXPGA */
#define CAD_HW_DEVICE_ID_HEADSET_SPKR_STEREO_LB 0x22
#define CAD_HW_DEVICE_ID_HEADSET_SPKR_MONO_LB 0x23
#define CAD_HW_DEVICE_ID_SPEAKER_SPKR_STEREO_LB 0x24
#define CAD_HW_DEVICE_ID_SPEAKER_SPKR_MONO_LB 0x25
#define CAD_HW_DEVICE_ID_NULL_RX 0x2A
#define CAD_HW_DEVICE_ID_MAX_NUM 0x2F
#define CAD_HW_DEVICE_ID_INVALID 0xFF
#define CAD_RX_DEVICE 0x00
#define CAD_TX_DEVICE 0x01
static struct q6_device_info q6_audio_devices[] = {
{
.id = ADSP_AUDIO_DEVICE_ID_HANDSET_SPKR,
.cad_id = CAD_HW_DEVICE_ID_HANDSET_SPKR,
.path = ADIE_PATH_HANDSET_RX,
.rate = 48000,
.dir = Q6_RX,
.codec = Q6_ICODEC_RX,
.hw = Q6_HW_HANDSET,
},
{
.id = ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_MONO,
.cad_id = CAD_HW_DEVICE_ID_HEADSET_SPKR_MONO,
.path = ADIE_PATH_HEADSET_MONO_RX,
.rate = 48000,
.dir = Q6_RX,
.codec = Q6_ICODEC_RX,
.hw = Q6_HW_HEADSET,
},
{
.id = ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_STEREO,
.cad_id = CAD_HW_DEVICE_ID_HEADSET_SPKR_STEREO,
.path = ADIE_PATH_HEADSET_STEREO_RX,
.rate = 48000,
.dir = Q6_RX,
.codec = Q6_ICODEC_RX,
.hw = Q6_HW_HEADSET,
},
{
.id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO,
.cad_id = CAD_HW_DEVICE_ID_SPKR_PHONE_MONO,
.path = ADIE_PATH_SPEAKER_RX,
.rate = 48000,
.dir = Q6_RX,
.codec = Q6_ICODEC_RX,
.hw = Q6_HW_SPEAKER,
},
{
.id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO,
.cad_id = CAD_HW_DEVICE_ID_SPKR_PHONE_STEREO,
.path = ADIE_PATH_SPEAKER_STEREO_RX,
.rate = 48000,
.dir = Q6_RX,
.codec = Q6_ICODEC_RX,
.hw = Q6_HW_SPEAKER,
},
{
.id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_MONO_HEADSET,
.cad_id = CAD_HW_DEVICE_ID_HEADSET_MONO_PLUS_SPKR_MONO_RX,
.path = ADIE_PATH_SPKR_MONO_HDPH_MONO_RX,
.rate = 48000,
.dir = Q6_RX,
.codec = Q6_ICODEC_RX,
.hw = Q6_HW_SPEAKER,
},
{
.id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_STEREO_HEADSET,
.cad_id = CAD_HW_DEVICE_ID_HEADSET_STEREO_PLUS_SPKR_MONO_RX,
.path = ADIE_PATH_SPKR_MONO_HDPH_STEREO_RX,
.rate = 48000,
.dir = Q6_RX,
.codec = Q6_ICODEC_RX,
.hw = Q6_HW_SPEAKER,
},
{
.id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_MONO_HEADSET,
.cad_id = CAD_HW_DEVICE_ID_HEADSET_MONO_PLUS_SPKR_STEREO_RX,
.path = ADIE_PATH_SPKR_STEREO_HDPH_MONO_RX,
.rate = 48000,
.dir = Q6_RX,
.codec = Q6_ICODEC_RX,
.hw = Q6_HW_SPEAKER,
},
{
.id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_STEREO_HEADSET,
.cad_id = CAD_HW_DEVICE_ID_HEADSET_STEREO_PLUS_SPKR_STEREO_RX,
.path = ADIE_PATH_SPKR_STEREO_HDPH_STEREO_RX,
.rate = 48000,
.dir = Q6_RX,
.codec = Q6_ICODEC_RX,
.hw = Q6_HW_SPEAKER,
},
{
.id = ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_SPKR,
.cad_id = CAD_HW_DEVICE_ID_TTY_HEADSET_SPKR,
.path = ADIE_PATH_TTY_HEADSET_RX,
.rate = 48000,
.dir = Q6_RX,
.codec = Q6_ICODEC_RX,
.hw = Q6_HW_TTY,
},
{
.id = ADSP_AUDIO_DEVICE_ID_HANDSET_MIC,
.cad_id = CAD_HW_DEVICE_ID_HANDSET_MIC,
.path = ADIE_PATH_HANDSET_TX,
.rate = 8000,
.dir = Q6_TX,
.codec = Q6_ICODEC_TX,
.hw = Q6_HW_HANDSET,
},
{
.id = ADSP_AUDIO_DEVICE_ID_HEADSET_MIC,
.cad_id = CAD_HW_DEVICE_ID_HEADSET_MIC,
.path = ADIE_PATH_HEADSET_MONO_TX,
.rate = 8000,
.dir = Q6_TX,
.codec = Q6_ICODEC_TX,
.hw = Q6_HW_HEADSET,
},
{
.id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MIC,
.cad_id = CAD_HW_DEVICE_ID_SPKR_PHONE_MIC,
.path = ADIE_PATH_SPEAKER_TX,
.rate = 8000,
.dir = Q6_TX,
.codec = Q6_ICODEC_TX,
.hw = Q6_HW_SPEAKER,
},
{
.id = ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_MIC,
.cad_id = CAD_HW_DEVICE_ID_TTY_HEADSET_MIC,
.path = ADIE_PATH_TTY_HEADSET_TX,
.rate = 8000,
.dir = Q6_TX,
.codec = Q6_ICODEC_TX,
.hw = Q6_HW_HEADSET,
},
{
.id = ADSP_AUDIO_DEVICE_ID_BT_SCO_SPKR,
.cad_id = CAD_HW_DEVICE_ID_BT_SCO_SPKR,
.path = 0, /* XXX */
.rate = 48000,
.dir = Q6_RX,
.codec = Q6_ECODEC_RX,
.hw = Q6_HW_BT_SCO,
},
{
.id = ADSP_AUDIO_DEVICE_ID_BT_A2DP_SPKR,
.cad_id = CAD_HW_DEVICE_ID_BT_A2DP_SPKR,
.path = 0, /* XXX */
.rate = 48000,
.dir = Q6_RX,
.codec = Q6_ECODEC_RX,
.hw = Q6_HW_BT_A2DP,
},
{
.id = ADSP_AUDIO_DEVICE_ID_BT_SCO_MIC,
.cad_id = CAD_HW_DEVICE_ID_BT_SCO_MIC,
.path = 0, /* XXX */
.rate = 8000,
.dir = Q6_TX,
.codec = Q6_ECODEC_TX,
.hw = Q6_HW_BT_SCO,
},
{
.id = ADSP_AUDIO_DEVICE_ID_I2S_SPKR,
.cad_id = CAD_HW_DEVICE_ID_I2S_RX,
.path = 0, /* XXX */
.rate = 48000,
.dir = Q6_RX,
.codec = Q6_SDAC_RX,
.hw = Q6_HW_SPEAKER,
},
{
.id = ADSP_AUDIO_DEVICE_ID_I2S_MIC,
.cad_id = CAD_HW_DEVICE_ID_I2S_TX,
.path = 0, /* XXX */
.rate = 16000,
.dir = Q6_TX,
.codec = Q6_SDAC_TX,
.hw = Q6_HW_SPEAKER,
},
{
.id = 0,
.cad_id = 0,
.path = 0,
.rate = 8000,
.dir = 0,
.codec = Q6_CODEC_NONE,
.hw = 0,
},
};

View File

@ -0,0 +1,212 @@
/* arch/arm/mach-msm/qdsp6/qcelp_in.c
*
* Copyright (C) 2009 Google, Inc.
* Copyright (C) 2009 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/fs.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/mutex.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/uaccess.h>
#include <linux/msm_audio.h>
#include <mach/msm_qdsp6_audio.h>
#include <mach/msm_audio_qcp.h>
#define BUFSZ (734)
#define DMASZ (BUFSZ * 2)
#if 0
#define TRACE(x...) pr_info("Q6: "x)
#else
#define TRACE(x...) do{}while(0)
#endif
static DEFINE_MUTEX(qcelp_in_lock);
static int qcelp_in_opened = 0;
static struct msm_audio_qcelp_config *qf;
void audio_client_dump(struct audio_client *ac);
static long qcelp_in_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int rc = 0;
switch (cmd) {
case AUDIO_SET_VOLUME:
break;
case AUDIO_GET_STATS: {
struct msm_audio_stats stats;
memset(&stats, 0, sizeof(stats));
if (copy_to_user((void*) arg, &stats, sizeof(stats)))
return -EFAULT;
return 0;
}
case AUDIO_START: {
uint32_t acdb_id;
rc = 0;
if (arg == 0) {
acdb_id = 0;
} else if (copy_from_user(&acdb_id, (void*) arg, sizeof(acdb_id))) {
rc = -EFAULT;
break;
}
mutex_lock(&qcelp_in_lock);
if (file->private_data) {
rc = -EBUSY;
} else {
file->private_data = q6audio_open_qcelp(
BUFSZ, 8000, qf, acdb_id);
if (!file->private_data)
rc = -ENOMEM;
}
mutex_unlock(&qcelp_in_lock);
break;
}
case AUDIO_STOP:
break;
case AUDIO_FLUSH:
break;
case AUDIO_GET_CONFIG:
if (copy_to_user((void *)arg, qf,
sizeof(struct msm_audio_qcelp_config)))
return -EFAULT;
break;
case AUDIO_SET_CONFIG:
if (copy_from_user(qf, (void *)arg,
sizeof(struct msm_audio_qcelp_config)))
return -EFAULT;
if (qf->min_bit_rate > 4 || qf->min_bit_rate < 1) {
pr_err("invalid min bitrate\n");
return -EINVAL;
}
if (qf->max_bit_rate > 4 || qf->max_bit_rate < 1) {
pr_err("invalid max bitrate\n");
return -EINVAL;
}
if (qf->cdma_rate > CDMA_RATE_ERASURE ||
qf->cdma_rate < CDMA_RATE_BLANK) {
pr_err("invalid qcelp cdma rate\n");
return -EINVAL;
}
break;
default:
rc = -EINVAL;
}
return rc;
}
static int qcelp_in_open(struct inode *inode, struct file *file)
{
int rc;
pr_info("qcelp_in: open\n");
mutex_lock(&qcelp_in_lock);
if (qcelp_in_opened) {
pr_err("qcelp_in: busy\n");
rc = -EBUSY;
} else {
qf = kzalloc(sizeof(*qf), GFP_KERNEL);
memset(qf, 0, sizeof(struct msm_audio_qcelp_config));
qf->channels = 1;
qf->cdma_rate = 0x04; /* CDMA_RATE_FULL */
qf->min_bit_rate = 1;
qf->max_bit_rate = 4;
qcelp_in_opened = 1;
rc = 0;
}
mutex_unlock(&qcelp_in_lock);
return rc;
}
static ssize_t qcelp_in_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
{
struct audio_client *ac;
struct audio_buffer *ab;
const char __user *start = buf;
int xfer, res = 0;
mutex_lock(&qcelp_in_lock);
ac = file->private_data;
if (!ac) {
res = -ENODEV;
pr_err("qcelp_in_read ac NULL\n");
goto fail;
}
while (count > 0) {
ab = ac->buf + ac->cpu_buf;
TRACE("qcelp_in_read wait count=%d ab=%d ac->buf=%d cpu_buf=%d ac->buf[1]=%d\n",
count, ab, ac->buf, ac->cpu_buf, &(ac->buf[1]));
if (ab->used)
wait_event(ac->wait, (ab->used == 0));
TRACE(" qcelp_in_read event arrive ab->size=%d\n", ab->size);
xfer = count;
if (xfer > ab->size)
xfer = ab->size;
if (copy_to_user(buf, ab->data, xfer)) {
res = -EFAULT;
pr_err("Tomdbg copy to user failed \n");
goto fail;
}
TRACE("qcelp_in read buf = %d,xfer = %d,cnt = %d\n", buf, xfer, count);
buf += xfer;
count -= xfer;
ab->used = 1;
q6audio_read(ac, ab);
ac->cpu_buf ^= 1;
}
fail:
res = buf - start;
mutex_unlock(&qcelp_in_lock);
return res;
}
static int qcelp_in_release(struct inode *inode, struct file *file)
{
int rc = 0;
pr_info("qcelp_in: release\n");
mutex_lock(&qcelp_in_lock);
if (file->private_data)
rc = q6audio_close(file->private_data);
kfree(qf);
qcelp_in_opened = 0;
mutex_unlock(&qcelp_in_lock);
return rc;
}
static struct file_operations qcelp_in_fops = {
.owner = THIS_MODULE,
.open = qcelp_in_open,
.read = qcelp_in_read,
.release = qcelp_in_release,
.unlocked_ioctl = qcelp_in_ioctl,
};
struct miscdevice qcelp_in_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "msm_qcelp_in",
.fops = &qcelp_in_fops,
};
static int __init qcelp_in_init(void) {
return misc_register(&qcelp_in_misc);
}
device_initcall(qcelp_in_init);

View File

@ -0,0 +1,71 @@
/* arch/arm/mach-msm/qdsp6/routing.c
*
* Copyright (C) 2009 Google, Inc.
* Author: Brian Swetland <swetland@google.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/fs.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
extern int q6audio_set_route(const char *name);
static int q6_open(struct inode *inode, struct file *file)
{
return 0;
}
static ssize_t q6_write(struct file *file, const char __user *buf,
size_t count, loff_t *pos)
{
char cmd[32];
if (count >= sizeof(cmd))
return -EINVAL;
if (copy_from_user(cmd, buf, count))
return -EFAULT;
cmd[count] = 0;
if ((count > 1) && (cmd[count-1] == '\n'))
cmd[count-1] = 0;
q6audio_set_route(cmd);
return count;
}
static int q6_release(struct inode *inode, struct file *file)
{
return 0;
}
static struct file_operations q6_fops = {
.owner = THIS_MODULE,
.open = q6_open,
.write = q6_write,
.release = q6_release,
};
static struct miscdevice q6_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "msm_audio_route",
.fops = &q6_fops,
};
static int __init q6_init(void) {
return misc_register(&q6_misc);
}
device_initcall(q6_init);

125
include/linux/msm_q6venc_1550.h Executable file
View File

@ -0,0 +1,125 @@
/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Code Aurora Forum nor
* the names of its contributors may be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef _MSM_VENC_H_
#define _MSM_VENC_H_
#include <linux/types.h>
struct venc_buf {
unsigned int src_id;
int fd;
unsigned long offset;
unsigned long size;
};
struct q6_init_config {
unsigned short venc_standard;
unsigned short partial_run_length_flag;
unsigned short h263_annex_ispt;
unsigned short h263_annex_jspt;
unsigned short h263_annex_tspt;
unsigned short rc_flag;
unsigned short one_mv_flag;
unsigned short acdc_pred_enable;
unsigned short rounding_bit_ctrl;
unsigned short rotation_flag;
unsigned short max_mvx;
unsigned short max_mvy;
unsigned short enc_frame_height_inmb;
unsigned short enc_frame_width_inmb;
unsigned short dvs_frame_height;
unsigned short dvs_frame_width;
/* unused by userspace, filled in by kernel */
unsigned int ref_frame_buf1_phy;
unsigned int ref_frame_buf2_phy;
unsigned int rlc_buf1_phy;
unsigned int rlc_buf2_phy;
unsigned int rlc_buf_length;
};
struct init_config {
struct venc_buf ref_frame_buf1;
struct venc_buf ref_frame_buf2;
struct venc_buf rlc_buf1;
struct venc_buf rlc_buf2;
struct q6_init_config q6_init_config;
};
struct q6_encode_param {
unsigned int luma_addr;
unsigned int chroma_addr;
unsigned int x_offset;
unsigned int y_offset;
unsigned int frame_rho_budget;
unsigned int frame_type;
unsigned int qp;
};
struct encode_param {
struct venc_buf y_addr;
unsigned long uv_offset;
struct q6_encode_param q6_encode_param;
};
struct intra_refresh {
unsigned int intra_refresh_enable;
unsigned int intra_mb_num;
};
struct rc_config {
unsigned short max_frame_qp_up_delta;
unsigned short max_frame_qp_down_delta;
unsigned short min_frame_qp;
unsigned short max_frame_qp;
};
struct q6_frame_type {
unsigned int frame_type;
unsigned int frame_len;
unsigned int frame_addr;
unsigned int map_table;
};
struct frame_type {
struct venc_buf frame_addr;
struct q6_frame_type q6_frame_type;
};
#define VENC_IOCTL_MAGIC 'V'
#define VENC_IOCTL_INITIALIZE _IOW(VENC_IOCTL_MAGIC, 1, struct init_config)
#define VENC_IOCTL_ENCODE _IOW(VENC_IOCTL_MAGIC, 2, struct encode_param)
#define VENC_IOCTL_INTRA_REFRESH _IOW(VENC_IOCTL_MAGIC, 3, struct intra_refresh)
#define VENC_IOCTL_RC_CONFIG _IOW(VENC_IOCTL_MAGIC, 4, struct rc_config)
#define VENC_IOCTL_ENCODE_CONFIG _IOW(VENC_IOCTL_MAGIC, 5, struct init_config)
#define VENC_IOCTL_STOP _IO(VENC_IOCTL_MAGIC, 6)
#define VENC_IOCTL_WAIT_FOR_ENCODE _IOR(VENC_IOCTL_MAGIC, 7, struct frame_type)
#define VENC_IOCTL_STOP_ENCODE _IO(VENC_IOCTL_MAGIC, 8)
#endif /* _MSM_VENC_H_ */