htcleo: add 1550 audio
All credits to Cotulla
This commit is contained in:
parent
8137654e8f
commit
dd777b0b10
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
||||
|
247
arch/arm/mach-msm/board-htcleo-acoustic.c
Normal file
247
arch/arm/mach-msm/board-htcleo-acoustic.c
Normal 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);
|
||||
|
330
arch/arm/mach-msm/board-htcleo-audio.c
Normal file
330
arch/arm/mach-msm/board-htcleo-audio.c
Normal 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
|
@ -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());
|
||||
|
@ -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 {
|
||||
|
11
arch/arm/mach-msm/qdsp6_1550/Makefile
Normal file
11
arch/arm/mach-msm/qdsp6_1550/Makefile
Normal 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
|
217
arch/arm/mach-msm/qdsp6_1550/aac_in.c
Normal file
217
arch/arm/mach-msm/qdsp6_1550/aac_in.c
Normal 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);
|
252
arch/arm/mach-msm/qdsp6_1550/audio_ctl.c
Normal file
252
arch/arm/mach-msm/qdsp6_1550/audio_ctl.c
Normal 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);
|
746
arch/arm/mach-msm/qdsp6_1550/dal.c
Normal file
746
arch/arm/mach-msm/qdsp6_1550/dal.c
Normal 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
|
65
arch/arm/mach-msm/qdsp6_1550/dal.h
Normal file
65
arch/arm/mach-msm/qdsp6_1550/dal.h
Normal 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
|
90
arch/arm/mach-msm/qdsp6_1550/dal_acdb.h
Normal file
90
arch/arm/mach-msm/qdsp6_1550/dal_acdb.h
Normal 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
|
110
arch/arm/mach-msm/qdsp6_1550/dal_adie.h
Normal file
110
arch/arm/mach-msm/qdsp6_1550/dal_adie.h
Normal 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
|
690
arch/arm/mach-msm/qdsp6_1550/dal_audio.h
Normal file
690
arch/arm/mach-msm/qdsp6_1550/dal_audio.h
Normal 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
|
285
arch/arm/mach-msm/qdsp6_1550/dal_audio_format.h
Normal file
285
arch/arm/mach-msm/qdsp6_1550/dal_audio_format.h
Normal 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
|
||||
|
229
arch/arm/mach-msm/qdsp6_1550/mp3.c
Normal file
229
arch/arm/mach-msm/qdsp6_1550/mp3.c
Normal 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);
|
940
arch/arm/mach-msm/qdsp6_1550/msm_q6vdec.c
Normal file
940
arch/arm/mach-msm/qdsp6_1550/msm_q6vdec.c
Normal 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);
|
637
arch/arm/mach-msm/qdsp6_1550/msm_q6venc.c
Normal file
637
arch/arm/mach-msm/qdsp6_1550/msm_q6venc.c
Normal 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);
|
207
arch/arm/mach-msm/qdsp6_1550/pcm_in.c
Normal file
207
arch/arm/mach-msm/qdsp6_1550/pcm_in.c
Normal 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);
|
242
arch/arm/mach-msm/qdsp6_1550/pcm_out.c
Normal file
242
arch/arm/mach-msm/qdsp6_1550/pcm_out.c
Normal 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);
|
2634
arch/arm/mach-msm/qdsp6_1550/q6audio.c
Normal file
2634
arch/arm/mach-msm/qdsp6_1550/q6audio.c
Normal file
File diff suppressed because it is too large
Load Diff
268
arch/arm/mach-msm/qdsp6_1550/q6audio_devices.h
Normal file
268
arch/arm/mach-msm/qdsp6_1550/q6audio_devices.h
Normal 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,
|
||||
},
|
||||
};
|
||||
|
212
arch/arm/mach-msm/qdsp6_1550/qcelp_in.c
Normal file
212
arch/arm/mach-msm/qdsp6_1550/qcelp_in.c
Normal 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);
|
71
arch/arm/mach-msm/qdsp6_1550/routing.c
Normal file
71
arch/arm/mach-msm/qdsp6_1550/routing.c
Normal 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
125
include/linux/msm_q6venc_1550.h
Executable 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_ */
|
Loading…
x
Reference in New Issue
Block a user