From b739bce6290e5e21b4a90a3efaef4b3178c873fc Mon Sep 17 00:00:00 2001 From: Markinus Date: Sat, 28 Aug 2010 19:22:09 +0200 Subject: [PATCH] htcleo: add audio drivers, qdsp6 modifications and enhanced venc driver Merged from cotulla's wince commits, integrated in the normal qdsp6 dir --- arch/arm/configs/htcleo_defconfig | 8 +- arch/arm/mach-msm/Makefile | 2 +- arch/arm/mach-msm/board-htcleo-acoustic.c | 247 ++ arch/arm/mach-msm/board-htcleo-audio.c | 330 +++ arch/arm/mach-msm/board-htcleo-hds.c | 351 +++ arch/arm/mach-msm/board-htcleo.c | 3 + .../mach-msm/include/mach/msm_qdsp6_audio.h | 4 +- arch/arm/mach-msm/qdsp6/Makefile | 10 +- arch/arm/mach-msm/qdsp6/aac_in.c | 217 ++ arch/arm/mach-msm/qdsp6/audio_ctl.c | 39 +- arch/arm/mach-msm/qdsp6/dal.c | 228 +- arch/arm/mach-msm/qdsp6/dal.h | 18 +- arch/arm/mach-msm/qdsp6/dal_acdb.h | 6 +- arch/arm/mach-msm/qdsp6/dal_adie.h | 6 +- arch/arm/mach-msm/qdsp6/dal_audio.h | 295 +- arch/arm/mach-msm/qdsp6/mp3.c | 10 + arch/arm/mach-msm/qdsp6/msm_q6vdec.c | 113 +- arch/arm/mach-msm/qdsp6/msm_q6venc.c | 1695 ++++------- arch/arm/mach-msm/qdsp6/pcm_in.c | 23 +- arch/arm/mach-msm/qdsp6/pcm_out.c | 39 +- arch/arm/mach-msm/qdsp6/q6audio.c | 303 +- arch/arm/mach-msm/qdsp6/q6audio_htcleo.c | 2634 +++++++++++++++++ arch/arm/mach-msm/qdsp6/qcelp_in.c | 212 ++ include/linux/msm_q6venc.h | 368 +-- 24 files changed, 5135 insertions(+), 2026 deletions(-) create mode 100644 arch/arm/mach-msm/board-htcleo-acoustic.c create mode 100644 arch/arm/mach-msm/board-htcleo-audio.c create mode 100644 arch/arm/mach-msm/board-htcleo-hds.c create mode 100644 arch/arm/mach-msm/qdsp6/aac_in.c create mode 100644 arch/arm/mach-msm/qdsp6/q6audio_htcleo.c create mode 100644 arch/arm/mach-msm/qdsp6/qcelp_in.c diff --git a/arch/arm/configs/htcleo_defconfig b/arch/arm/configs/htcleo_defconfig index 8eb7dfe3..136431b1 100644 --- a/arch/arm/configs/htcleo_defconfig +++ b/arch/arm/configs/htcleo_defconfig @@ -220,7 +220,7 @@ CONFIG_MACH_HTCLEO=y CONFIG_HTC_35MM_JACK=y # CONFIG_HTC_PWRSPLY is not set # CONFIG_HTC_PWRSINK is not set -# CONFIG_MSM_DALRPC is not set +CONFIG_MSM_DALRPC=y CONFIG_CACHE_FLUSH_RANGE_LIMIT=0x40000 CONFIG_MSM7X00A_USE_GP_TIMER=y # CONFIG_MSM7X00A_USE_DG_TIMER is not set @@ -250,7 +250,10 @@ CONFIG_MSM_RPCSERVERS=y # CONFIG_MSM_CPU_FREQ_SCREEN is not set # CONFIG_MSM_HW3D is not set CONFIG_MSM_QDSP6=y +CONFIG_QSD_AUDIO=y +CONFIG_QSD_HTC_FM=y # CONFIG_MSM_CLOCK_CTRL_DEBUG is not set +CONFIG_ARCH_MSM_FLASHLIGHT=y CONFIG_WIFI_CONTROL_FUNC=y # CONFIG_WIFI_MEM_PREALLOC is not set # CONFIG_VIRTUAL_KPANIC_PARTITION is not set @@ -260,7 +263,8 @@ CONFIG_HTCLEO_ENABLE_MULTI_TOUCH=y # CONFIG_ENABLE_BRAVO_UART_DRV is not set # CONFIG_ENABLE_USE_DESIRE_AMSS is not set CONFIG_PHYS_OFFSET=0x11800000 - +# CONFIG_HTC_ACOUSTIC is not set +# CONFIG_HTC_ACOUSTIC_QSD is not set # # Processor Type # diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 6670f40f..5de28164 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -101,7 +101,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-acoustic.o board-htcleo-audio.o obj-$(CONFIG_MACH_HTCLEO) += clock-wince.o # MSM7x30 boards diff --git a/arch/arm/mach-msm/board-htcleo-acoustic.c b/arch/arm/mach-msm/board-htcleo-acoustic.c new file mode 100644 index 00000000..2760e847 --- /dev/null +++ b/arch/arm/mach-msm/board-htcleo-acoustic.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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); + diff --git a/arch/arm/mach-msm/board-htcleo-audio.c b/arch/arm/mach-msm/board-htcleo-audio.c new file mode 100644 index 00000000..c92ce2de --- /dev/null +++ b/arch/arm/mach-msm/board-htcleo-audio.c @@ -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 +#include +#include +#include +#include +#include + +#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 diff --git a/arch/arm/mach-msm/board-htcleo-hds.c b/arch/arm/mach-msm/board-htcleo-hds.c new file mode 100644 index 00000000..3cbd27a6 --- /dev/null +++ b/arch/arm/mach-msm/board-htcleo-hds.c @@ -0,0 +1,351 @@ +/* board-htcleo-hds.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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "board-htcleo.h" +#include "gpio_chip.h" + + +int microp_get_remote_adc(uint32_t *val); +int microp_set_adc_req(uint8_t value, int enable); + + +static int headset_has_mic(void); +static int enable_headset_plug_event(void); +static int enable_key_event(void); +static int disable_key_event(void); + +static struct h35mm_platform_data htcleo_h35mm_data = +{ + .plug_event_enable = enable_headset_plug_event, + .headset_has_mic = headset_has_mic, + .key_event_enable = enable_key_event, + .key_event_disable = disable_key_event, +}; + +static struct platform_device htcleo_h35mm = +{ + .name = "htc_headset", + .id = -1, + .dev = + { + .platform_data = &htcleo_h35mm_data, + }, +}; + +///////////////////////////////////////////////////////////////////////////////////////////// + +struct hds_data +{ + int inited; + int gpio_mic; + int gpio_det; + int irq_mic; + int irq_det; + int headset_is_in; + int is_hpin_pin_stable; + int last_pressed_key; + struct work_struct work_det; + struct work_struct work_mic; + struct delayed_work hpin_debounce_work; +}; + +static struct hds_data hds; +///////////////////////////////////////////////////////////////////////////////////////////// + +static int headset_detect_mic(void) +{ + return !gpio_get_value(hds.gpio_mic); +} + +static int headset_is_in(void) +{ + return !gpio_get_value(hds.gpio_det); +} + +static int get_remote_keycode(int *keycode) +{ + uint32_t val; + uint32_t btn = 0; + + microp_set_adc_req(1, 1); + if (microp_get_remote_adc(&val)) + { + // failed. who know why? ignore + *keycode = 0; + return 1; + } + + if((val >= 0) && (val <= 33)) + { + btn = 1; + } + else if((val >= 38) && (val <= 82)) + { + btn = 2; + } + else if((val >= 95) && (val <= 200)) + { + btn = 3; + } + else if(val > 200) + { + // check previous key + if (hds.last_pressed_key) + { + *keycode = hds.last_pressed_key | 0x80; + hds.last_pressed_key = 0; + return 0; + } + *keycode = 0; + return 1; + } + + hds.last_pressed_key = btn; + *keycode = btn; + return 0; +} + +static int headset_has_mic(void) +{ + int mic1 = -1; + int mic2 = -1; + int count = 0; + + mic2 = headset_detect_mic(); + + /* debounce the detection wait until 2 consecutive read are equal */ + while ((mic1 != mic2) && (count < 10)) + { + mic1 = mic2; + msleep(600); + mic2 = headset_detect_mic(); + count++; + } + + printk("%s: microphone (%d) %s\n", __func__, count, mic1 ? "present" : "not present"); + + return mic1; +} + +static int enable_headset_plug_event(void) +{ + uint16_t stat; + + enable_irq(hds.irq_det); + + // see if headset state has changed + stat = headset_is_in(); + printk("headsetisin: old %d new %d\n", hds.headset_is_in, stat); + if (hds.headset_is_in != stat) + { + hds.headset_is_in = stat; + printk("Headset state changed\n"); + htc_35mm_jack_plug_event(stat, &hds.is_hpin_pin_stable); + } + return 1; +} + +static int enable_key_event(void) +{ + printk("enable_key_event\n"); + enable_irq(hds.irq_mic); + return 0; +} + +static int disable_key_event(void) +{ + printk("disable_key_event\n"); + disable_irq(hds.irq_mic); + return 0; +} + +static void hpin_debounce_do_work(struct work_struct *work) +{ + int insert = 0; + + insert = headset_is_in(); + printk("debonce new %d old %d\n", insert, hds.headset_is_in); + if (insert != hds.headset_is_in) + { + // clear keypress state + hds.last_pressed_key = 0; + + hds.headset_is_in = insert; + printk("headset %s\n", insert ? "inserted" : "removed"); + htc_35mm_jack_plug_event(hds.headset_is_in, &hds.is_hpin_pin_stable); + } +} + + +static void det_intr_work_func(struct work_struct *work) +{ + int value1; + + printk("det_intr_work_func\n"); + hds.is_hpin_pin_stable = 0; + // TODO: + // wake_lock_timeout(µp_i2c_wakelock, 3 * HZ); + if (!hds.headset_is_in) + schedule_delayed_work(&hds.hpin_debounce_work, msecs_to_jiffies(500)); + else + schedule_delayed_work(&hds.hpin_debounce_work, msecs_to_jiffies(300)); +} + +static void mic_intr_work_func(struct work_struct *work) +{ + int keycode = 0; + int value1; + + printk("mic_intr_work_func\n"); + if ((get_remote_keycode(&keycode) == 0) && (hds.is_hpin_pin_stable)) + { + printk("keycode %d\n", keycode); + htc_35mm_key_event(keycode, &hds.is_hpin_pin_stable); + } +} + + +static irqreturn_t detect_irq_handler(int irq, void *dev_id) +{ + int value1, value2; + int retry_limit = 10; + + do + { + value1 = gpio_get_value(hds.gpio_det); + set_irq_type(hds.irq_det, value1 ? IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH); + value2 = gpio_get_value(hds.gpio_det); + + } while (value1 != value2 && retry_limit-- > 0); + + schedule_work(&hds.work_det); + return IRQ_HANDLED; +} + +static irqreturn_t mic_irq_handler(int irq, void *dev_id) +{ + int value1, value2; + int retry_limit = 10; + + do + { + value1 = gpio_get_value(hds.gpio_mic); + set_irq_type(hds.irq_mic, value1 ? IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH); + value2 = gpio_get_value(hds.gpio_mic); + + } while (value1 != value2 && retry_limit-- > 0); + + schedule_work(&hds.work_mic); + return IRQ_HANDLED; +} + +///////////////////////////////////////////////////////////////////////////////////////////// + +static int htcleo_hds_probe(struct platform_device *pdev) +{ + int rc = 0; + + printk("htcleo_hds_probe()\n"); + + // allow run it only once + if (hds.inited != 0) + { + return -1; + } + hds.last_pressed_key = 0; + hds.inited = 1; + hds.gpio_mic = HTCLEO_GPIO_HDS_MIC; + hds.gpio_det = HTCLEO_GPIO_HDS_DET; + + hds.irq_mic = gpio_to_irq(hds.gpio_mic); + hds.irq_det = gpio_to_irq(hds.gpio_det); + + gpio_request(hds.gpio_det, "hds_detect"); + gpio_request(hds.gpio_mic, "hds_mic"); + + gpio_direction_input(hds.gpio_det); + gpio_direction_input(hds.gpio_mic); + + hds.headset_is_in = 0; + hds.is_hpin_pin_stable = 1; + INIT_WORK(&hds.work_det, det_intr_work_func); + INIT_WORK(&hds.work_mic, mic_intr_work_func); + INIT_DELAYED_WORK(&hds.hpin_debounce_work, hpin_debounce_do_work); + + + rc = request_irq(hds.irq_det, detect_irq_handler, IRQF_DISABLED | IRQF_TRIGGER_LOW, "hds_detect_intr", 0); + rc = request_irq(hds.irq_mic, mic_irq_handler, IRQF_DISABLED | IRQF_TRIGGER_LOW, "hds_mic_intr", 0); + disable_irq(hds.irq_mic); + + platform_device_register(&htcleo_h35mm); + + return rc; +} + +static int htcleo_hds_remove(struct platform_device *dev) +{ + platform_device_unregister(&htcleo_h35mm); + + free_irq(hds.irq_det, 0); + free_irq(hds.irq_mic, 0); + + gpio_free(hds.gpio_det); + gpio_free(hds.gpio_mic); + + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////////// + + +static struct platform_driver htcleo_hds_driver = +{ + .probe = htcleo_hds_probe, + .remove = htcleo_hds_remove, + .driver = + { + .name = "htcleo_hds", + .owner = THIS_MODULE, + }, +}; + +static int __init htcleo_hds_init(void) +{ + return platform_driver_register(&htcleo_hds_driver); +} + +static void __exit htcleo_hds_exit(void) +{ + platform_driver_unregister(&htcleo_hds_driver); +} + +module_init(htcleo_hds_init); +module_exit(htcleo_hds_exit); + +MODULE_AUTHOR("Cotulla"); +MODULE_DESCRIPTION("HTC LEO headset driver"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/board-htcleo.c b/arch/arm/mach-msm/board-htcleo.c index 1ef7fd5f..8676bd16 100644 --- a/arch/arm/mach-msm/board-htcleo.c +++ b/arch/arm/mach-msm/board-htcleo.c @@ -51,6 +51,7 @@ #include "dex_comm.h" extern int __init htcleo_init_mmc(unsigned debug_uart); +extern void __init htcleo_audio_init(void); /////////////////////////////////////////////////////////////////////// // SPI @@ -477,6 +478,8 @@ static void __init htcleo_init(void) htcleo_init_mmc(0); platform_device_register(&htcleo_timed_gpios); + + htcleo_audio_init(); #ifdef CONFIG_USB_ANDROID msm_hsusb_set_vbus_state(htcleo_get_vbus_state()); diff --git a/arch/arm/mach-msm/include/mach/msm_qdsp6_audio.h b/arch/arm/mach-msm/include/mach/msm_qdsp6_audio.h index ca5e735e..dfc62f06 100644 --- a/arch/arm/mach-msm/include/mach/msm_qdsp6_audio.h +++ b/arch/arm/mach-msm/include/mach/msm_qdsp6_audio.h @@ -33,7 +33,8 @@ 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 +92,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 { diff --git a/arch/arm/mach-msm/qdsp6/Makefile b/arch/arm/mach-msm/qdsp6/Makefile index 87ccfed7..624db17e 100644 --- a/arch/arm/mach-msm/qdsp6/Makefile +++ b/arch/arm/mach-msm/qdsp6/Makefile @@ -1,11 +1,15 @@ obj-y += dal.o +ifndef CONFIG_ARCH_HTCLEO +obj-y += q6audio_htcleo.o +else obj-y += q6audio.o +endif obj-y += pcm_out.o obj-y += pcm_in.o obj-y += mp3.o -#obj-y += routing.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 +obj-y += aac_in.o +obj-y += qcelp_in.o diff --git a/arch/arm/mach-msm/qdsp6/aac_in.c b/arch/arm/mach-msm/qdsp6/aac_in.c new file mode 100644 index 00000000..5c7042bc --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/aac_in.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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); diff --git a/arch/arm/mach-msm/qdsp6/audio_ctl.c b/arch/arm/mach-msm/qdsp6/audio_ctl.c index 90188c3a..8606f183 100644 --- a/arch/arm/mach-msm/qdsp6/audio_ctl.c +++ b/arch/arm/mach-msm/qdsp6/audio_ctl.c @@ -25,10 +25,20 @@ #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; @@ -38,6 +48,7 @@ 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) { @@ -45,6 +56,7 @@ static int q6_voice_start(uint32_t rx_acdb_id, uint32_t tx_acdb_id) rc = -EBUSY; goto done; } + global_now_phone_call = 1; voc_rx_clnt = q6voice_open(AUDIO_FLAG_WRITE, rx_acdb_id); if (!voc_rx_clnt) { @@ -69,7 +81,9 @@ done: static int q6_voice_stop(void) { mutex_lock(&voice_lock); - if (voice_started) { + global_now_phone_call = 0; + if (voice_started) + { q6voice_close(voc_tx_clnt); q6voice_close(voc_rx_clnt); voice_started = 0; @@ -127,9 +141,12 @@ static int q6_ioctl(struct inode *inode, struct file *file, 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; @@ -156,15 +173,19 @@ static int q6_ioctl(struct inode *inode, struct file *file, 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: @@ -178,9 +199,25 @@ static int q6_ioctl(struct inode *inode, struct file *file, 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; } diff --git a/arch/arm/mach-msm/qdsp6/dal.c b/arch/arm/mach-msm/qdsp6/dal.c index cb2f3fe1..d05ccfb0 100644 --- a/arch/arm/mach-msm/qdsp6/dal.c +++ b/arch/arm/mach-msm/qdsp6/dal.c @@ -31,20 +31,20 @@ #define DAL_TRACE 0 struct dal_hdr { - uint32_t length:16; /* message length (header inclusive) */ - uint32_t version:8; /* DAL protocol version */ + 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, ...) */ + 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) +#define TRACE_DATA_MAX 128 +#define TRACE_LOG_MAX 32 +#define TRACE_LOG_MASK (TRACE_LOG_MAX - 1) struct dal_trace { unsigned timestamp; @@ -129,17 +129,17 @@ 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); + 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); + (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)) @@ -226,7 +226,7 @@ again: } } pr_err("$$$ receiving unknown message len = %d $$$\n", - dch->count); + dch->count); dch->active = 0; dch->ptr = dch->data; } @@ -262,8 +262,7 @@ check_data: if (client->event) client->event(dch->ptr, len, client->cookie); else - pr_err("dal: client %p has no event handler\n", - client); + pr_err("dal: client %p has no event handler\n", client); goto again; } @@ -360,20 +359,21 @@ int dal_call_raw(struct dal_client *client, smd_write(dch->sch, data, data_len); spin_unlock_irqrestore(&dch->lock, flags); - if (!wait_event_timeout(client->wait, (client->status != -EBUSY), 5*HZ)) { + 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(); +// 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) + unsigned ddi, unsigned prototype, + void *data, int data_len, + void *reply, int reply_max) { struct dal_hdr hdr; int r; @@ -394,11 +394,11 @@ int dal_call(struct dal_client *client, mutex_lock(&client->write_lock); r = dal_call_raw(client, &hdr, data, data_len, reply, reply_max); mutex_unlock(&client->write_lock); -#if 0 +#if 1 if ((r > 3) && (((uint32_t*) reply)[0] == 0)) { - pr_info("dal call OK\n"); + // pr_info("dal call OK\n"); } else { - pr_info("dal call ERROR\n"); + pr_info("dal call %d %d ERROR\n", ddi, prototype); } #endif return r; @@ -415,8 +415,67 @@ struct dal_reply_attach { 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) + dal_event_func_t func, void *cookie) { struct dal_hdr hdr; struct dal_msg_attach msg; @@ -489,7 +548,7 @@ int dal_detach(struct dal_client *client) data = (uint32_t) client; dal_call_raw(client, &hdr, &data, sizeof(data), - &data, sizeof(data)); + &data, sizeof(data)); } dch = client->dch; @@ -543,7 +602,8 @@ int dal_call_f1(struct dal_client *client, uint32_t ddi, uint32_t arg1, uint32_t int dal_call_f5(struct dal_client *client, uint32_t ddi, void *ibuf, uint32_t ilen) { - uint32_t tmp[128]; +// uint32_t tmp[128]; + uint32_t tmp[DAL_DATA_MAX]; int res; int param_idx = 0; @@ -563,23 +623,55 @@ int dal_call_f5(struct dal_client *client, uint32_t ddi, void *ibuf, uint32_t il return res; } -int dal_call_f9(struct dal_client *client, uint32_t ddi, void *obuf, - uint32_t olen) +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 (olen > sizeof(tmp) - 8) + if (ilen + 8 > DAL_DATA_MAX) return -EINVAL; - tmp[0] = olen; - res = dal_call(client, ddi, 9, tmp, sizeof(uint32_t), tmp, - sizeof(tmp)); + 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 (!res) + { if (tmp[1] > olen) return -EIO; memcpy(obuf, &tmp[2], tmp[1]); @@ -587,9 +679,37 @@ int dal_call_f9(struct dal_client *client, uint32_t ddi, void *obuf, 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 ilen1, void *ibuf2, uint32_t ilen2, void *obuf, uint32_t olen) { uint32_t tmp[128]; int res; @@ -622,41 +742,5 @@ int dal_call_f13(struct dal_client *client, uint32_t ddi, void *ibuf1, return res; } -int dal_call_f14(struct dal_client *client, uint32_t ddi, void *ibuf, - uint32_t ilen, void *obuf1, uint32_t olen1, void *obuf2, - uint32_t olen2, uint32_t *oalen2) -{ - uint32_t tmp[128]; - int res; - int param_idx = 0; - if (olen1 + olen2 + 8 > DAL_DATA_MAX || - ilen + 12 > 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++] = olen1; - tmp[param_idx++] = olen2; - res = dal_call(client, ddi, 14, tmp, param_idx * 4, tmp, sizeof(tmp)); - - if (res >= 4) - res = (int)tmp[0]; - - if (!res) { - if (tmp[1] > olen1) - return -EIO; - param_idx = DIV_ROUND_UP(tmp[1], 4) + 2; - if (tmp[param_idx] > olen2) - return -EIO; - - memcpy(obuf1, &tmp[2], tmp[1]); - memcpy(obuf2, &tmp[param_idx+1], tmp[param_idx]); - *oalen2 = tmp[param_idx]; - } - return res; -} +// END OF FILE diff --git a/arch/arm/mach-msm/qdsp6/dal.h b/arch/arm/mach-msm/qdsp6/dal.h index 92ccec0e..196053d6 100644 --- a/arch/arm/mach-msm/qdsp6/dal.h +++ b/arch/arm/mach-msm/qdsp6/dal.h @@ -19,16 +19,12 @@ struct dal_client; -struct dal_info { - uint32_t size; - uint32_t version; - char name[32]; -}; - 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); @@ -50,14 +46,16 @@ 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_f9(struct dal_client *client, uint32_t ddi, +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); -int dal_call_f14(struct dal_client *client, uint32_t ddi, void *ibuf, - uint32_t ilen, void *obuf1, uint32_t olen1, void *obuf2, - uint32_t olen2, uint32_t *oalen2); /* common DAL operations */ enum { diff --git a/arch/arm/mach-msm/qdsp6/dal_acdb.h b/arch/arm/mach-msm/qdsp6/dal_acdb.h index 0e95b3b8..58db5ac2 100644 --- a/arch/arm/mach-msm/qdsp6/dal_acdb.h +++ b/arch/arm/mach-msm/qdsp6/dal_acdb.h @@ -27,7 +27,11 @@ */ #define ACDB_DAL_DEVICE 0x02000069 -#define ACDB_DAL_PORT "SMD_DAL_AM_AUD" +#if defined(CONFIG_MACH_HTCLEO) + #define ACDB_DAL_PORT "SMD_DAL00" +#else + #define ACDB_DAL_PORT "SMD_DAL_AM_AUD" +#endif #define ACDB_OP_IOCTL DAL_OP_FIRST_DEVICE_API diff --git a/arch/arm/mach-msm/qdsp6/dal_adie.h b/arch/arm/mach-msm/qdsp6/dal_adie.h index b7f58456..08b938a0 100644 --- a/arch/arm/mach-msm/qdsp6/dal_adie.h +++ b/arch/arm/mach-msm/qdsp6/dal_adie.h @@ -32,7 +32,11 @@ #include "dal.h" #define ADIE_DAL_DEVICE 0x02000029 -#define ADIE_DAL_PORT "SMD_DAL_AM_AUD" +#if defined(CONFIG_MACH_HTCLEO) + #define ADIE_DAL_PORT "SMD_DAL00" +#else + #define ADIE_DAL_PORT "SMD_DAL_AM_AUD" +#endif enum { ADIE_OP_GET_NUM_PATHS = DAL_OP_FIRST_DEVICE_API, diff --git a/arch/arm/mach-msm/qdsp6/dal_audio.h b/arch/arm/mach-msm/qdsp6/dal_audio.h index b1ad07db..674f0e80 100644 --- a/arch/arm/mach-msm/qdsp6/dal_audio.h +++ b/arch/arm/mach-msm/qdsp6/dal_audio.h @@ -2,15 +2,15 @@ * * 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. + * * 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 @@ -30,15 +30,29 @@ #define __DAL_AUDIO_H__ #include "dal_audio_format.h" +#include "dal.h" +#include #define AUDIO_DAL_DEVICE 0x02000028 #define AUDIO_DAL_PORT "DSP_DAL_AQ_AUD" -enum { +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 ---- */ @@ -46,37 +60,37 @@ enum { /* 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 +#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 +#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 +#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 +#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 +#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 +#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 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 */ + 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)); @@ -86,10 +100,11 @@ struct adsp_audio_buffer { /* Command/event response types */ #define ADSP_AUDIO_RESPONSE_COMMAND 0 -#define ADSP_AUDIO_RESPONSE_ASYNC 1 +#define ADSP_AUDIO_RESPONSE_ASYNC 1 +#if !defined(CONFIG_MACH_HTCLEO) struct adsp_command_hdr { - u32 size; /* sizeof(cmd) - sizeof(u32) */ + u32 size; /* sizeof(cmd) - sizeof(u32) */ u32 dst; u32 src; @@ -104,13 +119,21 @@ struct adsp_command_hdr { 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_MODEM 1 #define AUDIO_DOMAIN_DSP 2 -#define AUDIO_SERVICE_AUDIO 0 -#define AUDIO_SERVICE_VIDEO 1 /* really? */ +#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) ) @@ -124,17 +147,17 @@ struct adsp_command_hdr { #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_* */ + 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_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 +#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; @@ -145,36 +168,36 @@ struct adsp_audio_sbc_encoder_cfg { } __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 +#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 +#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 */ + 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; + u16 min_rate; + u16 max_rate; } __attribute__ ((packed)); struct adsp_audio_evrc_enc_cfg { - u16 min_rate; - u16 max_rate; + u16 min_rate; + u16 max_rate; } __attribute__ ((packed)); union adsp_audio_codec_config { @@ -197,8 +220,9 @@ union adsp_audio_codec_config { #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 +#define ADSP_AUDIO_OPEN_STREAM_MODE_ENABLE_SYNC_CLOCK 0x0004 +#if !defined(CONFIG_MACH_HTCLEO) struct adsp_open_command { struct adsp_command_hdr hdr; @@ -214,22 +238,52 @@ struct adsp_open_command { 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_IOCTL_CMD_OPEN_READ 0x0108dd79 +#define ADSP_AUDIO_OPCODE_OPEN_READ 0x01 /* Opcode to open a device stream session to render audio */ -#define ADSP_AUDIO_IOCTL_CMD_OPEN_WRITE 0x0108dd7a +#define ADSP_AUDIO_OPCODE_OPEN_WRITE 0x02 /* 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 - +#define ADSP_AUDIO_OPCODE_OPEN_DEV 0x04 +#endif /* A device switch requires three IOCTL */ /* commands in the following sequence: PREPARE, STANDBY, COMMIT */ @@ -266,7 +320,7 @@ struct adsp_device_switch_command { #define ADSP_PATH_RX 0 #define ADSP_PATH_TX 1 -#define ADSP_PATH_BOTH 2 +#define ADSP_PATH_BOTH 2 /* These commands will affect a logical device and all its associated */ /* streams. */ @@ -351,13 +405,13 @@ struct adsp_buffer_command { /* 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 +#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, */ +/* Set stream stereo volume. This command has data payl oad, */ /* struct adsp_audio_set_stereo_volume_command. */ #define ADSP_AUDIO_IOCTL_SET_STREAM_STEREO_VOL 0x0108dd7c @@ -415,20 +469,39 @@ struct adsp_buffer_command { /* 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 +#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 +#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 +#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 @@ -458,33 +531,34 @@ struct adsp_set_mute_command { /* 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 +#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 +#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 +#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 +#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 +#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 +#define ADSP_AUDIO_EVT_SR_CM_CHANGE 0x0108d329 -struct adsp_event_hdr { - u32 evt_handle; /* DAL common header */ +struct adsp_event_hdr +{ + u32 evt_handle; /* DAL common header */ u32 evt_cookie; u32 evt_length; @@ -501,17 +575,48 @@ struct adsp_event_hdr { u32 status; } __attribute__ ((packed)); -struct adsp_buffer_event { +#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 +#define ADSP_AUDIO_RX_DEVICE 0x00 +#define ADSP_AUDIO_TX_DEVICE 0x01 /* Default RX or TX device */ #define ADSP_AUDIO_DEVICE_ID_DEFAULT 0x1081679 @@ -526,18 +631,18 @@ struct adsp_buffer_event { /* 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 +#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_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 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_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 @@ -547,11 +652,11 @@ struct adsp_buffer_event { /* 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 +#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 +#define ADSP_AUDIO_DEVICE_ID_VOICE 0x0108df3c /* Audio device usage types. */ /* This is a bit mask to determine which topology to use in the */ @@ -562,4 +667,24 @@ struct adsp_buffer_event { #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 diff --git a/arch/arm/mach-msm/qdsp6/mp3.c b/arch/arm/mach-msm/qdsp6/mp3.c index 7a3adf13..92e8f0f0 100644 --- a/arch/arm/mach-msm/qdsp6/mp3.c +++ b/arch/arm/mach-msm/qdsp6/mp3.c @@ -25,6 +25,7 @@ #include #include +#include "dal_audio.h" #define BUFSZ (8192) #define DMASZ (BUFSZ * 2) @@ -39,6 +40,7 @@ struct mp3 { 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) { @@ -60,6 +62,14 @@ static long mp3_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 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) { diff --git a/arch/arm/mach-msm/qdsp6/msm_q6vdec.c b/arch/arm/mach-msm/qdsp6/msm_q6vdec.c index 73837529..d3fcfe77 100644 --- a/arch/arm/mach-msm/qdsp6/msm_q6vdec.c +++ b/arch/arm/mach-msm/qdsp6/msm_q6vdec.c @@ -66,10 +66,35 @@ #define TRACE(fmt,x...) \ do { pr_debug("%s:%d " fmt, __func__, __LINE__, ##x); } while (0) #else -#define TRACE(fmt, x...) do { } while (0) +#define TRACE(fmt,x...) do { } while (0) #endif -#define MAX_SUPPORTED_INSTANCES 2 + +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, @@ -137,31 +162,6 @@ static struct cdev vdec_cdev; static int ref_cnt; static DEFINE_MUTEX(vdec_ref_lock); -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); -} - static inline int vdec_check_version(u32 client, u32 server) { int ret = -EINVAL; @@ -447,8 +447,6 @@ static int vdec_queue(struct vdec_data *vd, void *argp) rpc.size = sizeof(struct vdec_input_buf_info); rpc.osize = sizeof(struct vdec_queue_status); - /* complete the writes to the buffer */ - wmb(); ret = dal_call(vd->vdec_handle, VDEC_DALRPC_QUEUE, 8, &rpc, sizeof(rpc), &rpc_res, sizeof(rpc_res)); if (ret < 4) { @@ -589,22 +587,6 @@ static int vdec_freebuffers(struct vdec_data *vd, void *argp) return ret; } - -static int vdec_getversion(struct vdec_data *vd, void *argp) -{ - struct vdec_version ver_info; - int ret = 0; - - ver_info.major = VDEC_GET_MAJOR_VERSION(VDEC_INTERFACE_VERSION); - ver_info.minor = VDEC_GET_MINOR_VERSION(VDEC_INTERFACE_VERSION); - - ret = copy_to_user(((struct vdec_version *)argp), - &ver_info, sizeof(ver_info)); - - return ret; - -} - static long vdec_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct vdec_data *vd = file->private_data; @@ -656,9 +638,6 @@ static long vdec_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (vd->close_decode) ret = -EINTR; - else - /* order the reads from the buffer */ - rmb(); break; case VDEC_IOCTL_CLOSE: @@ -684,15 +663,7 @@ static long vdec_ioctl(struct file *file, unsigned int cmd, unsigned long arg) pr_err("%s: remote function failed (%d)\n", __func__, ret); break; - case VDEC_IOCTL_GETVERSION: - TRACE("VDEC_IOCTL_GETVERSION (pid=%d tid=%d)\n", - current->group_leader->pid, current->pid); - ret = vdec_getversion(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; @@ -713,8 +684,7 @@ static void vdec_dcdone_handler(struct vdec_data *vd, void *frame, unsigned long flags; int found = 0; -/*if (frame_size != sizeof(struct vdec_frame_info)) {*/ - if (frame_size < sizeof(struct vdec_frame_info)) { + 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; @@ -798,14 +768,13 @@ static int vdec_open(struct inode *inode, struct file *file) int i; struct vdec_msg_list *l; struct vdec_data *vd; - struct dal_info version_info; pr_info("q6vdec_open()\n"); mutex_lock(&vdec_ref_lock); - if (ref_cnt >= MAX_SUPPORTED_INSTANCES) { - pr_err("%s: Max allowed instances exceeded \n", __func__); + if (ref_cnt > 0) { + pr_err("%s: Instance alredy running\n", __func__); mutex_unlock(&vdec_ref_lock); - return -EBUSY; + return -ENOMEM; } ref_cnt++; mutex_unlock(&vdec_ref_lock); @@ -844,26 +813,11 @@ static int vdec_open(struct inode *inode, struct file *file) ret = -EIO; goto vdec_open_err_handle_list; } - ret = dal_call_f9(vd->vdec_handle, DAL_OP_INFO, - &version_info, sizeof(struct dal_info)); - - if (ret) { - pr_err("%s: failed to get version \n", __func__); - goto vdec_open_err_handle_version; - } - - TRACE("q6vdec_open() interface version 0x%x\n", version_info.version); - if (vdec_check_version(VDEC_INTERFACE_VERSION, - version_info.version)) { - pr_err("%s: driver version mismatch !\n", __func__); - goto vdec_open_err_handle_version; - } vd->running = 1; prevent_sleep(); return 0; -vdec_open_err_handle_version: - dal_detach(vd->vdec_handle); + vdec_open_err_handle_list: { struct vdec_msg_list *l, *n; @@ -873,9 +827,6 @@ vdec_open_err_handle_list: } } vdec_open_err_handle_vd: - mutex_lock(&vdec_ref_lock); - ref_cnt--; - mutex_unlock(&vdec_ref_lock); kfree(vd); return ret; } diff --git a/arch/arm/mach-msm/qdsp6/msm_q6venc.c b/arch/arm/mach-msm/qdsp6/msm_q6venc.c index dbbffd22..4f2db90a 100644 --- a/arch/arm/mach-msm/qdsp6/msm_q6venc.c +++ b/arch/arm/mach-msm/qdsp6/msm_q6venc.c @@ -1,4 +1,9 @@ -/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. +/* + * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * Copyright (c) 2009, Google Inc. + * + * Original authors: Code Aurora Forum + * Major cleanup: Dima Zavin * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -12,35 +17,6 @@ * products derived from this software without specific prior written * permission. * - * Alternatively, provided that this notice is retained in full, this software - * may be relicensed by the recipient under the terms of the GNU General Public - * License version 2 ("GPL") and only version 2, in which case the provisions of - * the GPL apply INSTEAD OF those given above. If the recipient relicenses the - * software under the GPL, then the identification text in the MODULE_LICENSE - * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a - * recipient changes the license terms to the GPL, subsequent recipients shall - * not relicense under alternate licensing terms, including the BSD or dual - * BSD/GPL terms. In addition, the following license statement immediately - * below and between the words START and END shall also then apply when this - * software is relicensed under the GPL: - * - * START - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License version 2 and only version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * END - * * 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 @@ -55,1198 +31,607 @@ * */ -#include -#include +//#define DEBUG 1 + #include +#include #include +#include #include +#include #include +#include #include #include #include -#include #include #include + +#include + #include "dal.h" -#define DALDEVICEID_VENC_DEVICE 0x0200002D -/*#define DALDEVICEID_VENC_PORTNAME "DAL_AQ_VID"*/ -#define DALDEVICEID_VENC_PORTNAME "DSP_DAL_AQ_VID" - -#define VENC_NAME "q6venc" -#define VENC_MSG_MAX 128 - -#define VENC_INTERFACE_VERSION 0x00020000 -#define MAJOR_MASK 0xFFFF0000 -#define MINOR_MASK 0x0000FFFF -#define VENC_GET_MAJOR_VERSION(version) ((version & MAJOR_MASK)>>16) -#define VENC_GET_MINOR_VERSION(version) (version & MINOR_MASK) - -uint32_t kpi_start[5]; -uint32_t kpi_end; -static uint32_t cnt = 0; +#define DALDEVICEID_VENC_DEVICE 0x0200002D +#define DALDEVICEID_VENC_PORTNAME "DSP_DAL_AQ_VID" enum { - VENC_BUFFER_TYPE_INPUT, - VENC_BUFFER_TYPE_OUTPUT, - VENC_BUFFER_TYPE_QDSP6, - VENC_BUFFER_TYPE_HDR -}; -enum { - VENC_DALRPC_GET_SYNTAX_HEADER = DAL_OP_FIRST_DEVICE_API, - VENC_DALRPC_UPDATE_INTRA_REFRESH, - VENC_DALRPC_UPDATE_FRAME_RATE, - VENC_DALRPC_UPDATE_BITRATE, - VENC_DALRPC_UPDATE_QP_RANGE, - VENC_DALRPC_UPDATE_INTRA_PERIOD, - VENC_DALRPC_REQUEST_IFRAME, - VENC_DALRPC_START, + 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, - VENC_DALRPC_SUSPEND, - VENC_DALRPC_RESUME, - VENC_DALRPC_FLUSH, - VENC_DALRPC_QUEUE_INPUT, - VENC_DALRPC_QUEUE_OUTPUT -}; -struct venc_input_payload { - u32 data; -}; -struct venc_output_payload { - u32 size; - long long time_stamp; - u32 flags; - u32 data; - u32 client_data_from_input; -}; -union venc_payload { - struct venc_input_payload input_payload; - struct venc_output_payload output_payload; -}; -struct venc_msg_type { - u32 event; - u32 status; - union venc_payload payload; -}; -struct venc_input_buf { - struct venc_buf_type yuv_buf; - u32 data_size; - long long time_stamp; - u32 flags; - u32 dvs_offsetx; - u32 dvs_offsety; - u32 client_data; - u32 op_client_data; -}; -struct venc_output_buf { - struct venc_buf_type bit_stream_buf; - u32 client_data; }; -struct venc_msg_list { - struct list_head list; - struct venc_msg msg_data; -}; -struct venc_buf { - int fd; - u32 offset; - u32 size; - u32 btype; - unsigned long paddr; - struct file *file; -}; -struct venc_pmem_list { - struct list_head list; - struct venc_buf buf; -}; -struct venc_dev { - bool is_active; - bool stop_called; - enum venc_state_type state; - struct list_head venc_msg_list_head; - struct list_head venc_msg_list_free; - spinlock_t venc_msg_list_lock; - struct list_head venc_pmem_list_head; - spinlock_t venc_pmem_list_lock; - struct dal_client *q6_handle; - wait_queue_head_t venc_msg_evt; - struct device *class_devp; +struct callback_event_data { + u32 data_notify_event; + u32 enc_cb_handle; + u32 empty_input_buffer_event; }; -#define DEBUG_VENC 0 -#if DEBUG_VENC -#define TRACE(fmt, x...) \ - do { pr_debug("%s:%d " fmt, __func__, __LINE__, ##x); } while (0) -#else -#define TRACE(fmt, x...) do { } while (0) -#endif +struct buf_info { + unsigned long paddr; + unsigned long vaddr; + struct file *file; + struct venc_buf venc_buf; +}; -static struct cdev cdev; -static dev_t venc_dev_num; -static struct class *venc_class; -static struct venc_dev *venc_device_p; -static int venc_ref; +#define VENC_MAX_BUF_NUM 15 +#define RLC_MAX_BUF_NUM 2 +#define BITS_PER_PIXEL 12 +#define PIXELS_PER_MACROBLOCK 16 -static DEFINE_MUTEX(idlecount_lock); -static int idlecount; -static struct wake_lock wakelock; -static struct wake_lock idlelock; +#define VENC_CB_EVENT_ID 0xd0e4c0de -static void prevent_sleep(void) +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) { - 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); -} - -static inline int venc_check_version(u32 client, u32 server) -{ - int ret = -EINVAL; - - if ((VENC_GET_MAJOR_VERSION(client) == VENC_GET_MAJOR_VERSION(server)) - && (VENC_GET_MINOR_VERSION(client) <= - VENC_GET_MINOR_VERSION(server))) - ret = 0; - - return ret; -} - -static int venc_get_msg(struct venc_dev *dvenc, void *msg) -{ - struct venc_msg_list *l; - unsigned long flags; - int ret = 0; - struct venc_msg qdsp_msg; - - if (!dvenc->is_active) - return -EPERM; - spin_lock_irqsave(&dvenc->venc_msg_list_lock, flags); - list_for_each_entry_reverse(l, &dvenc->venc_msg_list_head, list) { - memcpy(&qdsp_msg, &l->msg_data, sizeof(struct venc_msg)); - list_del(&l->list); - list_add(&l->list, &dvenc->venc_msg_list_free); - ret = 1; - break; - } - spin_unlock_irqrestore(&dvenc->venc_msg_list_lock, flags); - if (copy_to_user(msg, &qdsp_msg, sizeof(struct venc_msg))) - pr_err("%s failed to copy_to_user\n", __func__); - return ret; -} - -static void venc_put_msg(struct venc_dev *dvenc, struct venc_msg *msg) -{ - struct venc_msg_list *l; - unsigned long flags; - int found = 0; - - spin_lock_irqsave(&dvenc->venc_msg_list_lock, flags); - list_for_each_entry(l, &dvenc->venc_msg_list_free, list) { - memcpy(&l->msg_data, msg, sizeof(struct venc_msg)); - list_del(&l->list); - list_add(&l->list, &dvenc->venc_msg_list_head); - found = 1; - break; - } - spin_unlock_irqrestore(&dvenc->venc_msg_list_lock, flags); - if (found) - wake_up(&dvenc->venc_msg_evt); - else - pr_err("%s: failed to find a free node\n", __func__); - -} - -static struct venc_pmem_list *venc_add_pmem_to_list(struct venc_dev *dvenc, - struct venc_pmem *mptr, - u32 btype) -{ - int ret = 0; - unsigned long flags; unsigned long len; unsigned long vaddr; - struct venc_pmem_list *plist = NULL; - - plist = kzalloc(sizeof(struct venc_pmem_list), GFP_KERNEL); - if (!plist) { - pr_err("%s: kzalloc failed\n", __func__); - return NULL; - } - - ret = get_pmem_file(mptr->fd, &(plist->buf.paddr), - &vaddr, &len, &(plist->buf.file)); - if (ret) { - pr_err("%s: get_pmem_file failed for fd=%d offset=%d\n", - __func__, mptr->fd, mptr->offset); - goto err_venc_add_pmem; - } else if (mptr->offset >= len) { - pr_err("%s: invalid offset (%d > %ld) for fd=%d\n", - __func__, mptr->offset, len, mptr->fd); - ret = -EINVAL; - goto err_venc_get_pmem; - } - - plist->buf.fd = mptr->fd; - plist->buf.paddr += mptr->offset; - plist->buf.size = mptr->size; - plist->buf.btype = btype; - plist->buf.offset = mptr->offset; - - spin_lock_irqsave(&dvenc->venc_pmem_list_lock, flags); - list_add(&plist->list, &dvenc->venc_pmem_list_head); - spin_unlock_irqrestore(&dvenc->venc_pmem_list_lock, flags); - return plist; - -err_venc_get_pmem: - put_pmem_file(plist->buf.file); -err_venc_add_pmem: - kfree(plist); - return NULL; -} - -static struct venc_pmem_list *venc_get_pmem_from_list( - struct venc_dev *dvenc, u32 pmem_fd, - u32 offset, u32 btype) -{ - struct venc_pmem_list *plist; - unsigned long flags; + unsigned long paddr; struct file *file; - int found = 0; + int ret; - file = fget(pmem_fd); - if (!file) { - pr_err("%s: invalid encoder buffer fd(%d)\n", __func__, - pmem_fd); - return NULL; - } - spin_lock_irqsave(&dvenc->venc_pmem_list_lock, flags); - list_for_each_entry(plist, &dvenc->venc_pmem_list_head, list) { - if (plist->buf.btype == btype && plist->buf.file == file && - plist->buf.offset == offset) { - found = 1; - break; - } - } - spin_unlock_irqrestore(&dvenc->venc_pmem_list_lock, flags); - fput(file); - if (found) - return plist; - - else - return NULL; -} - -static int venc_set_buffer(struct venc_dev *dvenc, void *argp, - u32 btype) -{ - struct venc_pmem pmem; - struct venc_pmem_list *plist; - int ret = 0; - - ret = copy_from_user(&pmem, argp, sizeof(pmem)); + ret = get_pmem_file(venc_buf->fd, &paddr, &vaddr, &len, &file); if (ret) { - pr_err("%s: copy_from_user failed\n", __func__); + pr_err("%s: get_pmem_file failed for fd=%d offset=%ld\n", + __func__, venc_buf->fd, venc_buf->offset); return ret; - } - plist = venc_add_pmem_to_list(dvenc, &pmem, btype); - if (plist == NULL) { - pr_err("%s: buffer add_to_pmem_list failed\n", - __func__); - return -EPERM; - } - return ret; -} - -static int venc_assign_q6_buffers(struct venc_dev *dvenc, - struct venc_buffers *pbufs, - struct venc_nonio_buf_config *pcfg) -{ - int ret = 0; - struct venc_pmem_list *plist; - - plist = venc_add_pmem_to_list(dvenc, &(pbufs->recon_buf[0]), - VENC_BUFFER_TYPE_QDSP6); - if (plist == NULL) { - pr_err("%s: recon_buf0 failed to add_to_pmem_list\n", - __func__); - return -EPERM; - } - pcfg->recon_buf1.region = pbufs->recon_buf[0].src; - pcfg->recon_buf1.phys = plist->buf.paddr; - pcfg->recon_buf1.size = plist->buf.size; - pcfg->recon_buf1.offset = 0; - - plist = venc_add_pmem_to_list(dvenc, &(pbufs->recon_buf[1]), - VENC_BUFFER_TYPE_QDSP6); - if (plist == NULL) { - pr_err("%s: recons_buf1 failed to add_to_pmem_list\n", - __func__); - return -EPERM; - } - pcfg->recon_buf2.region = pbufs->recon_buf[1].src; - pcfg->recon_buf2.phys = plist->buf.paddr; - pcfg->recon_buf2.size = plist->buf.size; - pcfg->recon_buf2.offset = 0; - - plist = venc_add_pmem_to_list(dvenc, &(pbufs->wb_buf), - VENC_BUFFER_TYPE_QDSP6); - if (plist == NULL) { - pr_err("%s: wb_buf failed to add_to_pmem_list\n", - __func__); - return -EPERM; - } - pcfg->wb_buf.region = pbufs->wb_buf.src; - pcfg->wb_buf.phys = plist->buf.paddr; - pcfg->wb_buf.size = plist->buf.size; - pcfg->wb_buf.offset = 0; - - plist = venc_add_pmem_to_list(dvenc, &(pbufs->cmd_buf), - VENC_BUFFER_TYPE_QDSP6); - if (plist == NULL) { - pr_err("%s: cmd_buf failed to add_to_pmem_list\n", - __func__); - return -EPERM; - } - pcfg->cmd_buf.region = pbufs->cmd_buf.src; - pcfg->cmd_buf.phys = plist->buf.paddr; - pcfg->cmd_buf.size = plist->buf.size; - pcfg->cmd_buf.offset = 0; - - plist = venc_add_pmem_to_list(dvenc, &(pbufs->vlc_buf), - VENC_BUFFER_TYPE_QDSP6); - if (plist == NULL) { - pr_err("%s: vlc_buf failed to add_to_pmem_list" - " failed\n", __func__); - return -EPERM; - } - pcfg->vlc_buf.region = pbufs->vlc_buf.src; - pcfg->vlc_buf.phys = plist->buf.paddr; - pcfg->vlc_buf.size = plist->buf.size; - pcfg->vlc_buf.offset = 0; - - return ret; -} - -static int venc_start(struct venc_dev *dvenc, void *argp) -{ - int ret = 0; - struct venc_q6_config q6_config; - struct venc_init_config vconfig; - - dvenc->state = VENC_STATE_START; - ret = copy_from_user(&vconfig, argp, sizeof(struct venc_init_config)); - if (ret) { - pr_err("%s: copy_from_user failed\n", __func__); - return ret; - } - memcpy(&q6_config, &(vconfig.q6_config), sizeof(q6_config)); - ret = venc_assign_q6_buffers(dvenc, &(vconfig.q6_bufs), - &(q6_config.buf_params)); - if (ret != 0) { - pr_err("%s: assign_q6_buffers failed\n", __func__); - return -EPERM; - } - - q6_config.callback_event = dvenc->q6_handle; - TRACE("%s: parameters: handle:%p, config:%p, callback:%p \n", __func__, - dvenc->q6_handle, &q6_config, q6_config.callback_event); - TRACE("%s: parameters:recon1:0x%x, recon2:0x%x," - " wb_buf:0x%x, cmd:0x%x, vlc:0x%x\n", __func__, - q6_config.buf_params.recon_buf1.phys, - q6_config.buf_params.recon_buf2.phys, - q6_config.buf_params.wb_buf.phys, - q6_config.buf_params.cmd_buf.phys, - q6_config.buf_params.vlc_buf.phys); - TRACE("%s: size of param:%d \n", __func__, sizeof(q6_config)); - ret = dal_call_f5(dvenc->q6_handle, VENC_DALRPC_START, &q6_config, - sizeof(q6_config)); - if (ret != 0) { - pr_err("%s: remote function failed (%d)\n", __func__, ret); - return ret; - } - return ret; -} - -static int venc_encode_frame(struct venc_dev *dvenc, void *argp) -{ - int ret = 0; - struct venc_pmem buf; - struct venc_input_buf q6_input; - struct venc_pmem_list *plist; - struct venc_buffer input; - - ret = copy_from_user(&input, argp, sizeof(struct venc_buffer)); - if (ret) { - pr_err("%s: copy_from_user failed\n", __func__); - return ret; - } - ret = copy_from_user(&buf, - ((struct venc_buffer *)argp)->ptr_buffer, - sizeof(struct venc_pmem)); - if (ret) { - pr_err("%s: copy_from_user failed\n", __func__); - return ret; - } - - plist = venc_get_pmem_from_list(dvenc, buf.fd, buf.offset, - VENC_BUFFER_TYPE_INPUT); - if (NULL == plist) { - plist = venc_add_pmem_to_list(dvenc, &buf, - VENC_BUFFER_TYPE_INPUT); - if (plist == NULL) { - pr_err("%s: buffer add_to_pmem_list failed\n", - __func__); - return -EPERM; - } - } - - q6_input.flags = 0; - if (input.flags & VENC_FLAG_EOS) - q6_input.flags |= 0x00000001; - q6_input.yuv_buf.region = 0; - q6_input.yuv_buf.phys = plist->buf.paddr; - q6_input.yuv_buf.size = plist->buf.size; - q6_input.yuv_buf.offset = 0; - q6_input.data_size = plist->buf.size; - q6_input.client_data = (u32)input.client_data; - q6_input.time_stamp = input.time_stamp; - q6_input.dvs_offsetx = 0; - q6_input.dvs_offsety = 0; - - -kpi_start[cnt] = ktime_to_ns(ktime_get()); -TRACE("kpi_start %d, %u \n", cnt, kpi_start[cnt]); -cnt++; - - TRACE("Pushing down input phys=0x%x fd= %d, client_data: 0x%x," - " time_stamp:%lld \n", q6_input.yuv_buf.phys, plist->buf.fd, - input.client_data, input.time_stamp); - ret = dal_call_f5(dvenc->q6_handle, VENC_DALRPC_QUEUE_INPUT, - &q6_input, sizeof(q6_input)); - - if (ret != 0) - pr_err("%s: Q6 queue_input failed (%d)\n", __func__, - (int)ret); - return ret; -} - -static int venc_fill_output(struct venc_dev *dvenc, void *argp) -{ - int ret = 0; - struct venc_pmem buf; - struct venc_output_buf q6_output; - struct venc_pmem_list *plist; - struct venc_buffer output; - - ret = copy_from_user(&output, argp, sizeof(struct venc_buffer)); - if (ret) { - pr_err("%s: copy_from_user failed\n", __func__); - return ret; - } - ret = copy_from_user(&buf, - ((struct venc_buffer *)argp)->ptr_buffer, - sizeof(struct venc_pmem)); - if (ret) { - pr_err("%s: copy_from_user failed\n", __func__); - return ret; - } - plist = venc_get_pmem_from_list(dvenc, buf.fd, buf.offset, - VENC_BUFFER_TYPE_OUTPUT); - if (NULL == plist) { - plist = venc_add_pmem_to_list(dvenc, &buf, - VENC_BUFFER_TYPE_OUTPUT); - if (NULL == plist) { - pr_err("%s: output buffer failed to add_to_pmem_list" - "\n", __func__); - return -EPERM; - } - } - q6_output.bit_stream_buf.region = 0; - q6_output.bit_stream_buf.phys = (u32)plist->buf.paddr; - q6_output.bit_stream_buf.size = plist->buf.size; - q6_output.bit_stream_buf.offset = 0; - q6_output.client_data = (u32)output.client_data; - ret = - dal_call_f5(dvenc->q6_handle, VENC_DALRPC_QUEUE_OUTPUT, &q6_output, - sizeof(q6_output)); - if (ret != 0) - pr_err("%s: remote function failed (%d)\n", __func__, ret); - return ret; -} - -static int venc_stop(struct venc_dev *dvenc) -{ - int ret = 0; - struct venc_msg msg; - - dvenc->stop_called = 1; - ret = dal_call_f0(dvenc->q6_handle, VENC_DALRPC_STOP, 1); - if (ret) { - pr_err("%s: remote runction failed (%d)\n", __func__, ret); - msg.msg_code = VENC_MSG_STOP; - msg.msg_data_size = 0; - msg.status_code = VENC_S_EFAIL; - venc_put_msg(dvenc, &msg); - } - return ret; -} - -static int venc_pause(struct venc_dev *dvenc) -{ - int ret = 0; - struct venc_msg msg; - - ret = dal_call_f0(dvenc->q6_handle, VENC_DALRPC_SUSPEND, 1); - if (ret) { - pr_err("%s: remote function failed (%d)\n", __func__, ret); - msg.msg_code = VENC_MSG_PAUSE; - msg.status_code = VENC_S_EFAIL; - msg.msg_data_size = 0; - venc_put_msg(dvenc, &msg); - } - return ret; -} - -static int venc_resume(struct venc_dev *dvenc) -{ - int ret = 0; - struct venc_msg msg; - - ret = dal_call_f0(dvenc->q6_handle, VENC_DALRPC_RESUME, 1); - if (ret) { - pr_err("%s: remote function failed (%d)\n", __func__, ret); - msg.msg_code = VENC_MSG_RESUME; - msg.msg_data_size = 0; - msg.status_code = VENC_S_EFAIL; - venc_put_msg(dvenc, &msg); - } - return ret; -} - -static int venc_flush(struct venc_dev *dvenc, void *argp) -{ - int ret = 0; - struct venc_msg msg; - union venc_msg_data smsg; - int status = VENC_S_SUCCESS; - struct venc_buffer_flush flush; - - if (copy_from_user(&flush, argp, sizeof(struct venc_buffer_flush))) - return -EFAULT; - if (flush.flush_mode == VENC_FLUSH_ALL) { - ret = dal_call_f0(dvenc->q6_handle, VENC_DALRPC_FLUSH, 1); - if (ret) - status = VENC_S_EFAIL; - } else - status = VENC_S_ENOTSUPP; - - if (status != VENC_S_SUCCESS) { - if ((flush.flush_mode == VENC_FLUSH_INPUT) || - (flush.flush_mode == VENC_FLUSH_ALL)) { - smsg.flush_ret.flush_mode = VENC_FLUSH_INPUT; - msg.msg_data = smsg; - msg.status_code = status; - msg.msg_code = VENC_MSG_FLUSH; - msg.msg_data_size = sizeof(union venc_msg_data); - venc_put_msg(dvenc, &msg); - } - if (flush.flush_mode == VENC_FLUSH_OUTPUT || - (flush.flush_mode == VENC_FLUSH_ALL)) { - smsg.flush_ret.flush_mode = VENC_FLUSH_OUTPUT; - msg.msg_data = smsg; - msg.status_code = status; - msg.msg_code = VENC_MSG_FLUSH; - msg.msg_data_size = sizeof(union venc_msg_data); - venc_put_msg(dvenc, &msg); - } - return -EIO; - } - return ret; -} - -static int venc_get_sequence_hdr(struct venc_dev *dvenc, void *argp) -{ - pr_err("%s not supported\n", __func__); - return -EIO; -} - -static int venc_set_qp_range(struct venc_dev *dvenc, void *argp) -{ - int ret = 0; - struct venc_qp_range qp; - - ret = copy_from_user(&qp, argp, sizeof(struct venc_qp_range)); - if (ret) { - pr_err("%s: copy_from_user failed\n", __func__); - return ret; - } - - if (dvenc->state == VENC_STATE_START || - dvenc->state == VENC_STATE_PAUSE) { - ret = - dal_call_f5(dvenc->q6_handle, VENC_DALRPC_UPDATE_QP_RANGE, - &qp, sizeof(struct venc_qp_range)); - if (ret) { - pr_err("%s: remote function failed (%d) \n", __func__, - ret); - return ret; - } - } - return ret; -} - -static int venc_set_intra_period(struct venc_dev *dvenc, void *argp) -{ - int ret = 0; - u32 pnum = 0; - - ret = copy_from_user(&pnum, argp, sizeof(int)); - if (ret) { - pr_err("%s: copy_from_user failed\n", __func__); - return ret; - } - if (dvenc->state == VENC_STATE_START || - dvenc->state == VENC_STATE_PAUSE) { - ret = dal_call_f0(dvenc->q6_handle, - VENC_DALRPC_UPDATE_INTRA_PERIOD, pnum); - if (ret) - pr_err("%s: remote function failed (%d)\n", __func__, - ret); - } - return ret; -} - -static int venc_set_intra_refresh(struct venc_dev *dvenc, void *argp) -{ - int ret = 0; - u32 mb_num = 0; - - ret = copy_from_user(&mb_num, argp, sizeof(int)); - if (ret) { - pr_err("%s: copy_from_user failed\n", __func__); - return ret; - } - if (dvenc->state == VENC_STATE_START || - dvenc->state == VENC_STATE_PAUSE) { - ret = dal_call_f0(dvenc->q6_handle, - VENC_DALRPC_UPDATE_INTRA_REFRESH, mb_num); - if (ret) - pr_err("%s: remote function failed (%d)\n", __func__, - ret); - } - return ret; -} - -static int venc_set_frame_rate(struct venc_dev *dvenc, void *argp) -{ - int ret = 0; - struct venc_frame_rate pdata; - ret = copy_from_user(&pdata, argp, sizeof(struct venc_frame_rate)); - if (ret) { - pr_err("%s: copy_from_user failed\n", __func__); - return ret; - } - if (dvenc->state == VENC_STATE_START || - dvenc->state == VENC_STATE_PAUSE) { - ret = dal_call_f5(dvenc->q6_handle, - VENC_DALRPC_UPDATE_FRAME_RATE, - (void *)&(pdata), - sizeof(struct venc_frame_rate)); - if (ret) - pr_err("%s: remote function failed (%d)\n", __func__, - ret); - } - return ret; -} - -static int venc_set_target_bitrate(struct venc_dev *dvenc, void *argp) -{ - int ret = 0; - u32 pdata = 0; - - ret = copy_from_user(&pdata, argp, sizeof(int)); - if (ret) { - pr_err("%s: copy_from_user failed\n", __func__); - return ret; - } - if (dvenc->state == VENC_STATE_START || - dvenc->state == VENC_STATE_PAUSE) { - ret = dal_call_f0(dvenc->q6_handle, - VENC_DALRPC_UPDATE_BITRATE, pdata); - if (ret) - pr_err("%s: remote function failed (%d)\n", __func__, - ret); - } - return ret; -} - -static int venc_request_iframe(struct venc_dev *dvenc) -{ - int ret = 0; - - if (dvenc->state != VENC_STATE_START) + } 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; - - ret = dal_call_f0(dvenc->q6_handle, VENC_DALRPC_REQUEST_IFRAME, 1); - if (ret) - pr_err("%s: remote function failed (%d)\n", __func__, ret); - return ret; -} - -static int venc_stop_read_msg(struct venc_dev *dvenc) -{ - struct venc_msg msg; - int ret = 0; - - msg.status_code = 0; - msg.msg_code = VENC_MSG_STOP_READING_MSG; - msg.msg_data_size = 0; - venc_put_msg(dvenc, &msg); - return ret; -} - -static int venc_q6_stop(struct venc_dev *dvenc) -{ - int ret = 0; - struct venc_pmem_list *plist; - - wake_up(&dvenc->venc_msg_evt); - list_for_each_entry(plist, &dvenc->venc_pmem_list_head, list) - put_pmem_file(plist->buf.file); - dvenc->state = VENC_STATE_STOP; - return ret; -} - -static int venc_translate_error(enum venc_status_code q6_status) -{ - int ret = 0; - - switch (q6_status) { - case VENC_STATUS_SUCCESS: - ret = VENC_S_SUCCESS; - break; - case VENC_STATUS_ERROR: - ret = VENC_S_EFAIL; - break; - case VENC_STATUS_INVALID_STATE: - ret = VENC_S_EINVALSTATE; - break; - case VENC_STATUS_FLUSHING: - ret = VENC_S_EFLUSHED; - break; - case VENC_STATUS_INVALID_PARAM: - ret = VENC_S_EBADPARAM; - break; - case VENC_STATUS_CMD_QUEUE_FULL: - ret = VENC_S_ECMDQFULL; - break; - case VENC_STATUS_CRITICAL: - ret = VENC_S_EFATAL; - break; - case VENC_STATUS_INSUFFICIENT_RESOURCES: - ret = VENC_S_ENOHWRES; - break; - case VENC_STATUS_TIMEOUT: - ret = VENC_S_ETIMEOUT; - break; } - if (q6_status != VENC_STATUS_SUCCESS) - pr_err("%s: Q6 failed (%d)", __func__, (int)q6_status); - return ret; + + 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 venc_q6_callback(void *data, int len, void *cookie) +static void put_buf_info(struct buf_info *buf_info) { - int status = 0; - struct venc_dev *dvenc = (struct venc_dev *)cookie; - struct venc_msg_type *q6_msg = NULL; - struct venc_msg msg, msg1; - union venc_msg_data smsg1, smsg2; - unsigned long msg_code; - struct venc_input_payload *pload1; - struct venc_output_payload *pload2; + 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 (dvenc == NULL) { - pr_err("%s: empty driver parameter\n", __func__); - return; - } - if (tmp[2] == sizeof(struct venc_msg_type)) { - q6_msg = (struct venc_msg_type *)&tmp[3]; - } else { - pr_err("%s: callback with empty message (%d, %d)\n", - __func__, tmp[2], sizeof(struct venc_msg_type)); - return; - } - msg.msg_data_size = 0; - status = venc_translate_error(q6_msg->status); - switch ((enum venc_event_type_enum)q6_msg->event) { - case VENC_EVENT_START_STATUS: - dvenc->state = VENC_STATE_START; - msg_code = VENC_MSG_START; - break; - case VENC_EVENT_STOP_STATUS: - venc_q6_stop(dvenc); - msg_code = VENC_MSG_STOP; - break; - case VENC_EVENT_SUSPEND_STATUS: - dvenc->state = VENC_STATE_PAUSE; - msg_code = VENC_MSG_PAUSE; - break; - case VENC_EVENT_RESUME_STATUS: - dvenc->state = VENC_STATE_START; - msg_code = VENC_MSG_RESUME; - break; - case VENC_EVENT_FLUSH_STATUS: - smsg1.flush_ret.flush_mode = VENC_FLUSH_INPUT; - msg1.status_code = status; - msg1.msg_code = VENC_MSG_FLUSH; - msg1.msg_data = smsg1; - msg1.msg_data_size = sizeof(union venc_msg_data); - venc_put_msg(dvenc, &msg1); - smsg2.flush_ret.flush_mode = VENC_FLUSH_OUTPUT; - msg_code = VENC_MSG_FLUSH; - msg.msg_data = smsg2; - msg.msg_data_size = sizeof(union venc_msg_data); - break; - case VENC_EVENT_RELEASE_INPUT: - -kpi_end = ktime_to_ns(ktime_get()); -TRACE("KPI : encode a frame, %u ms\n", (kpi_end - kpi_start[0])/(1000*1000)); -if (cnt > 0) { - int i = 0; - for (i = 0; i < cnt; i++) - kpi_start[i] = kpi_start[i+1]; -} -cnt--; - pload1 = &((q6_msg->payload).input_payload); - TRACE("Release_input: data: 0x%x \n", pload1->data); - if (pload1 != NULL) { - msg.msg_data.buf.client_data = pload1->data; - msg_code = VENC_MSG_INPUT_BUFFER_DONE; - msg.msg_data_size = sizeof(union venc_msg_data); - } - break; - case VENC_EVENT_DELIVER_OUTPUT: - pload2 = &((q6_msg->payload).output_payload); - smsg1.buf.flags = 0; - if (pload2->flags & VENC_FLAG_SYNC_FRAME) - smsg1.buf.flags |= VENC_FLAG_SYNC_FRAME; - if (pload2->flags & VENC_FLAG_CODEC_CONFIG) - smsg1.buf.flags |= VENC_FLAG_CODEC_CONFIG; - if (pload2->flags & VENC_FLAG_END_OF_FRAME) - smsg1.buf.flags |= VENC_FLAG_END_OF_FRAME; - if (pload2->flags & VENC_FLAG_EOS) - smsg1.buf.flags |= VENC_FLAG_EOS; - smsg1.buf.len = pload2->size; - smsg1.buf.offset = 0; - smsg1.buf.time_stamp = pload2->time_stamp; - smsg1.buf.client_data = pload2->data; - msg_code = VENC_MSG_OUTPUT_BUFFER_DONE; - msg.msg_data = smsg1; - msg.msg_data_size = sizeof(union venc_msg_data); - break; - default: - pr_err("%s: invalid response from Q6 (%d)\n", __func__, - (int)q6_msg->event); - return; - } - msg.status_code = status; - msg.msg_code = msg_code; - venc_put_msg(dvenc, &msg); - return; -} - -static int venc_get_version(struct venc_dev *dvenc, void *argp) -{ - struct venc_version ver_info; - int ret = 0; - - ver_info.major = VENC_GET_MAJOR_VERSION(VENC_INTERFACE_VERSION); - ver_info.minor = VENC_GET_MINOR_VERSION(VENC_INTERFACE_VERSION); - - ret = copy_to_user(((struct venc_version *)argp), - &ver_info, sizeof(ver_info)); - if (ret) - pr_err("%s failed to copy_to_user\n", __func__); - - return ret; - -} - -static long q6venc_ioctl(struct file *file, u32 cmd, - unsigned long arg) -{ - long ret = 0; - void __user *argp = (void __user *)arg; - struct venc_dev *dvenc = file->private_data; - - if (!dvenc || !dvenc->is_active) - return -EPERM; - - switch (cmd) { - case VENC_IOCTL_SET_INPUT_BUFFER: - ret = venc_set_buffer(dvenc, argp, VENC_BUFFER_TYPE_INPUT); - break; - case VENC_IOCTL_SET_OUTPUT_BUFFER: - ret = venc_set_buffer(dvenc, argp, VENC_BUFFER_TYPE_OUTPUT); - break; - case VENC_IOCTL_GET_SEQUENCE_HDR: - ret = venc_get_sequence_hdr(dvenc, argp); - break; - case VENC_IOCTL_SET_QP_RANGE: - ret = venc_set_qp_range(dvenc, argp); - break; - case VENC_IOCTL_SET_INTRA_PERIOD: - ret = venc_set_intra_period(dvenc, argp); - break; - case VENC_IOCTL_SET_INTRA_REFRESH: - ret = venc_set_intra_refresh(dvenc, argp); - break; - case VENC_IOCTL_SET_FRAME_RATE: - ret = venc_set_frame_rate(dvenc, argp); - break; - case VENC_IOCTL_SET_TARGET_BITRATE: - ret = venc_set_target_bitrate(dvenc, argp); - break; - case VENC_IOCTL_CMD_REQUEST_IFRAME: - if (dvenc->state == VENC_STATE_START) - ret = venc_request_iframe(dvenc); - break; - case VENC_IOCTL_CMD_START: - ret = venc_start(dvenc, argp); - break; - case VENC_IOCTL_CMD_STOP: - ret = venc_stop(dvenc); - break; - case VENC_IOCTL_CMD_PAUSE: - ret = venc_pause(dvenc); - break; - case VENC_IOCTL_CMD_RESUME: - ret = venc_resume(dvenc); - break; - case VENC_IOCTL_CMD_ENCODE_FRAME: - ret = venc_encode_frame(dvenc, argp); - break; - case VENC_IOCTL_CMD_FILL_OUTPUT_BUFFER: - ret = venc_fill_output(dvenc, argp); - break; - case VENC_IOCTL_CMD_FLUSH: - ret = venc_flush(dvenc, argp); - break; - case VENC_IOCTL_CMD_READ_NEXT_MSG: - wait_event_interruptible(dvenc->venc_msg_evt, - venc_get_msg(dvenc, argp)); - break; - case VENC_IOCTL_CMD_STOP_READ_MSG: - ret = venc_stop_read_msg(dvenc); - break; - case VENC_IOCTL_GET_VERSION: - ret = venc_get_version(dvenc, argp); - break; - default: - pr_err("%s: invalid ioctl code (%d)\n", __func__, cmd); - ret = -ENOTTY; - break; - } - return ret; + 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) { - int i; - int ret = 0; - struct venc_dev *dvenc; - struct venc_msg_list *plist; - struct dal_info version_info; + struct q6venc_dev *q6venc; + int err; - dvenc = kzalloc(sizeof(struct venc_dev), GFP_KERNEL); - if (!dvenc) { - pr_err("%s: unable to allocate memory for struct venc_dev\n", - __func__); + 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 = dvenc; - INIT_LIST_HEAD(&dvenc->venc_msg_list_head); - INIT_LIST_HEAD(&dvenc->venc_msg_list_free); - INIT_LIST_HEAD(&dvenc->venc_pmem_list_head); - init_waitqueue_head(&dvenc->venc_msg_evt); - spin_lock_init(&dvenc->venc_msg_list_lock); - spin_lock_init(&dvenc->venc_pmem_list_lock); - venc_ref++; - for (i = 0; i < VENC_MSG_MAX; i++) { - plist = kzalloc(sizeof(struct venc_msg_list), GFP_KERNEL); - if (!plist) { - pr_err("%s: kzalloc failed\n", __func__); - ret = -ENOMEM; - goto err_venc_create_msg_list; - } - list_add(&plist->list, &dvenc->venc_msg_list_free); - } - dvenc->q6_handle = - dal_attach(DALDEVICEID_VENC_DEVICE, DALDEVICEID_VENC_PORTNAME, - venc_q6_callback, (void *)dvenc); - if (!(dvenc->q6_handle)) { - pr_err("%s: daldevice_attach failed (%d)\n", __func__, ret); - goto err_venc_dal_attach; - } - ret = dal_call_f9(dvenc->q6_handle, DAL_OP_INFO, &version_info, - sizeof(struct dal_info)); - if (ret) { - pr_err("%s: failed to get version\n", __func__); - goto err_venc_dal_open; + + 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; } - pr_info("VENC_INTERFACE_VERSION %X, version_info.version %X\n", - VENC_INTERFACE_VERSION, version_info.version); -#if 0 - if (venc_check_version(VENC_INTERFACE_VERSION, version_info.version)) { - pr_err("%s: driver version mismatch\n", __func__); - goto err_venc_dal_open; + 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; } -#endif - ret = dal_call_f0(dvenc->q6_handle, DAL_OP_OPEN, 1); - if (ret) { - pr_err("%s: dal_call_open failed (%d)\n", __func__, ret); - goto err_venc_dal_open; - } - dvenc->state = VENC_STATE_STOP; - dvenc->is_active = 1; - prevent_sleep(); - return ret; -err_venc_dal_open: - dal_detach(dvenc->q6_handle); -err_venc_dal_attach: - list_for_each_entry(plist, &dvenc->venc_msg_list_free, list) { - list_del(&plist->list); - kfree(plist); - } -err_venc_create_msg_list: - kfree(dvenc); - venc_ref--; - return ret; + + 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) { - int ret = 0; - struct venc_msg_list *l, *n; - struct venc_pmem_list *plist, *m; - struct venc_dev *dvenc; + struct q6venc_dev *q6venc; + int id, err; - venc_ref--; - dvenc = file->private_data; - dvenc->is_active = 0; - wake_up_all(&dvenc->venc_msg_evt); - if (!dvenc->stop_called) - dal_call_f0(dvenc->q6_handle, VENC_DALRPC_STOP, 1); - dal_call_f0(dvenc->q6_handle, DAL_OP_CLOSE, 1); - dal_detach(dvenc->q6_handle); - list_for_each_entry_safe(l, n, &dvenc->venc_msg_list_free, list) { - list_del(&l->list); - kfree(l); - } - list_for_each_entry_safe(l, n, &dvenc->venc_msg_list_head, list) { - list_del(&l->list); - kfree(l); - } - if (!dvenc->stop_called) { - list_for_each_entry(plist, &dvenc->venc_pmem_list_head, list) - put_pmem_file(plist->buf.file); - dvenc->stop_called = 1; + 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; } - list_for_each_entry_safe(plist, m, &dvenc->venc_pmem_list_head, list) { - list_del(&plist->list); - kfree(plist); + 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; } - kfree(dvenc); - allow_sleep(); + + 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; irlc_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; } -const struct file_operations q6venc_fops = { - .owner = THIS_MODULE, - .open = q6venc_open, - .release = q6venc_release, - .unlocked_ioctl = q6venc_ioctl, +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 ret = 0; + int rc = 0; - wake_lock_init(&idlelock, WAKE_LOCK_IDLE, "venc_idle"); - wake_lock_init(&wakelock, WAKE_LOCK_SUSPEND, "venc_suspend"); - - venc_device_p = kzalloc(sizeof(struct venc_dev), GFP_KERNEL); - if (!venc_device_p) { - pr_err("%s: unable to allocate memory for venc_device_p\n", - __func__); - return -ENOMEM; - } - ret = alloc_chrdev_region(&venc_dev_num, 0, 1, VENC_NAME); - if (ret < 0) { - pr_err("%s: alloc_chrdev_region failed (%d)\n", __func__, - ret); - return ret; - } - venc_class = class_create(THIS_MODULE, VENC_NAME); - if (IS_ERR(venc_class)) { - ret = PTR_ERR(venc_class); - pr_err("%s: failed to create venc_class (%d)\n", - __func__, ret); - goto err_venc_class_create; - } - venc_device_p->class_devp = - device_create(venc_class, NULL, venc_dev_num, NULL, - VENC_NAME); - if (IS_ERR(venc_device_p->class_devp)) { - ret = PTR_ERR(venc_device_p->class_devp); - pr_err("%s: failed to create class_device (%d)\n", __func__, - ret); - goto err_venc_class_device_create; - } - cdev_init(&cdev, &q6venc_fops); - cdev.owner = THIS_MODULE; - ret = cdev_add(&cdev, venc_dev_num, 1); - if (ret < 0) { - pr_err("%s: cdev_add failed (%d)\n", __func__, ret); - goto err_venc_cdev_add; - } - init_waitqueue_head(&venc_device_p->venc_msg_evt); - return ret; - -err_venc_cdev_add: - device_destroy(venc_class, venc_dev_num); -err_venc_class_device_create: - class_destroy(venc_class); -err_venc_class_create: - unregister_chrdev_region(venc_dev_num, 1); - return ret; + 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) { - cdev_del(&(cdev)); - device_destroy(venc_class, venc_dev_num); - class_destroy(venc_class); - unregister_chrdev_region(venc_dev_num, 1); + misc_deregister(&q6venc_misc); } -MODULE_LICENSE("Dual BSD/GPL"); -MODULE_DESCRIPTION("Video encoder driver for QDSP6"); +MODULE_DESCRIPTION("video encoder driver for QSD platform"); MODULE_VERSION("2.0"); + module_init(q6venc_init); module_exit(q6venc_exit); diff --git a/arch/arm/mach-msm/qdsp6/pcm_in.c b/arch/arm/mach-msm/qdsp6/pcm_in.c index 3b3d23bf..60ef98df 100644 --- a/arch/arm/mach-msm/qdsp6/pcm_in.c +++ b/arch/arm/mach-msm/qdsp6/pcm_in.c @@ -26,12 +26,12 @@ #include -#define BUFSZ (256) +#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 uint32_t buffer_size = BUFSZ; static int pcm_in_opened = 0; void audio_client_dump(struct audio_client *ac); @@ -66,7 +66,7 @@ static long q6_in_ioctl(struct file *file, unsigned int cmd, unsigned long arg) rc = -EBUSY; } else { file->private_data = q6audio_open_pcm( - buffer_size, sample_rate, channel_count, + BUFSZ, sample_rate, channel_count, AUDIO_FLAG_READ, acdb_id); if (!file->private_data) rc = -ENOMEM; @@ -84,26 +84,13 @@ static long q6_in_ioctl(struct file *file, unsigned int cmd, unsigned long arg) rc = -EFAULT; break; } - if (!config.channel_count || config.channel_count > 2) { - rc = -EINVAL; - break; - } - if (config.sample_rate < 8000 || config.sample_rate > 48000) { - rc = -EINVAL; - break; - } - if (config.buffer_size < 128 || config.buffer_size > 8192) { - rc = -EINVAL; - break; - } sample_rate = config.sample_rate; channel_count = config.channel_count; - buffer_size = config.buffer_size; break; } case AUDIO_GET_CONFIG: { struct msm_audio_config config; - config.buffer_size = buffer_size; + config.buffer_size = BUFSZ; config.buffer_count = 2; config.sample_rate = sample_rate; config.channel_count = channel_count; @@ -160,7 +147,7 @@ static ssize_t q6_in_read(struct file *file, char __user *buf, if (!wait_event_timeout(ac->wait, (ab->used == 0), 5*HZ)) { audio_client_dump(ac); pr_err("pcm_read: timeout. dsp dead?\n"); - BUG(); +// BUG(); } xfer = count; diff --git a/arch/arm/mach-msm/qdsp6/pcm_out.c b/arch/arm/mach-msm/qdsp6/pcm_out.c index efe22f21..814216f1 100644 --- a/arch/arm/mach-msm/qdsp6/pcm_out.c +++ b/arch/arm/mach-msm/qdsp6/pcm_out.c @@ -25,24 +25,33 @@ #include #include +#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 (3072) +#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; - size_t buffer_size; }; 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)); @@ -62,6 +71,14 @@ static long pcm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 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) { @@ -74,7 +91,7 @@ static long pcm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (pcm->ac) { rc = -EBUSY; } else { - pcm->ac = q6audio_open_pcm(pcm->buffer_size, pcm->sample_rate, + pcm->ac = q6audio_open_pcm(BUFSZ, pcm->sample_rate, pcm->channel_count, AUDIO_FLAG_WRITE, acdb_id); if (!pcm->ac) @@ -100,22 +117,13 @@ static long pcm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) rc = -EINVAL; break; } - if (config.sample_rate < 8000 || config.sample_rate > 48000) { - rc = -EINVAL; - break; - } - if (config.buffer_size < 128 || config.buffer_size > 8192) { - rc = -EINVAL; - break; - } pcm->sample_rate = config.sample_rate; pcm->channel_count = config.channel_count; - pcm->buffer_size = config.buffer_size; break; } case AUDIO_GET_CONFIG: { struct msm_audio_config config; - config.buffer_size = pcm->buffer_size; + config.buffer_size = BUFSZ; config.buffer_count = 2; config.sample_rate = pcm->sample_rate; config.channel_count = pcm->channel_count; @@ -138,6 +146,7 @@ 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); @@ -147,7 +156,6 @@ static int pcm_open(struct inode *inode, struct file *file) mutex_init(&pcm->lock); pcm->channel_count = 2; pcm->sample_rate = 44100; - pcm->buffer_size = BUFSZ; file->private_data = pcm; return 0; @@ -176,7 +184,7 @@ static ssize_t pcm_write(struct file *file, const char __user *buf, if (!wait_event_timeout(ac->wait, (ab->used == 0), 5*HZ)) { audio_client_dump(ac); pr_err("pcm_write: timeout. dsp dead?\n"); - BUG(); +// BUG(); } xfer = count; @@ -199,6 +207,7 @@ static ssize_t pcm_write(struct file *file, const char __user *buf, static int pcm_release(struct inode *inode, struct file *file) { + AUDIO_INFO("%s\n", __func__); struct pcm *pcm = file->private_data; if (pcm->ac) q6audio_close(pcm->ac); diff --git a/arch/arm/mach-msm/qdsp6/q6audio.c b/arch/arm/mach-msm/qdsp6/q6audio.c index 2df5b244..ecac00e6 100644 --- a/arch/arm/mach-msm/qdsp6/q6audio.c +++ b/arch/arm/mach-msm/qdsp6/q6audio.c @@ -31,8 +31,7 @@ #include "dal_acdb.h" #include "dal_adie.h" #include -#include -#include + #include #include "q6audio_devices.h" @@ -348,7 +347,7 @@ static int audio_ioctl(struct audio_client *ac, void *ptr, uint32_t len) if (!wait_event_timeout(ac->wait, (ac->cb_status != -EBUSY), 5*HZ)) { dal_trace_dump(ac->client); pr_err("audio_ioctl: timeout. dsp dead?\n"); - BUG(); + q6audio_dsp_not_responding(); } return ac->cb_status; } @@ -438,156 +437,6 @@ static int audio_mp3_open(struct audio_client *ac, uint32_t bufsz, return audio_ioctl(ac, &rpc, sizeof(rpc)); } -static int audio_aac_open(struct audio_client *ac, uint32_t bufsz, - void *data) -{ - struct aac_format *af = data; - struct adsp_open_command rpc; - uint32_t *aac_type; - int idx = sizeof(uint32_t); - struct adsp_audio_binary_format *fmt = &(rpc.format.binary); - - memset(&rpc, 0, sizeof(rpc)); - - fmt->format = ADSP_AUDIO_FORMAT_MPEG4_AAC; - aac_type = (uint32_t *)(fmt->data); - switch (af->block_formats) { - case 0xffff: - if (ac->flags & AUDIO_FLAG_WRITE) - *aac_type = ADSP_AUDIO_AAC_ADTS; - else - *aac_type = ADSP_AUDIO_AAC_MPEG4_ADTS; - break; - case 0: - if (ac->flags & AUDIO_FLAG_WRITE) - *aac_type = ADSP_AUDIO_AAC_ADIF; - else - *aac_type = ADSP_AUDIO_AAC_RAW; - break; - case 1: - *aac_type = ADSP_AUDIO_AAC_RAW; - break; - case 2: - *aac_type = ADSP_AUDIO_AAC_LOAS; - break; - case 3: - *aac_type = ADSP_AUDIO_AAC_FRAMED_RAW; - break; - case 4: - *aac_type = ADSP_AUDIO_AAC_RAW; - break; - default: - pr_err("unsupported AAC type %d\n", af->block_formats); - return -EINVAL; - } - - TRACE("aac_open: type %x, obj %d, idx %d\n", - *aac_type, af->audio_object_type, idx); - fmt->data[idx++] = (u8)(((af->audio_object_type & 0x1F) << 3) | - ((af->sample_rate >> 1) & 0x7)); - fmt->data[idx] = (u8)(((af->sample_rate & 0x1) << 7) | - ((af->channel_config & 0x7) << 3)); - - switch (af->audio_object_type) { - case AAC_OBJECT_ER_LC: - case AAC_OBJECT_ER_LTP: - case AAC_OBJECT_ER_LD: - /* extension flag */ - fmt->data[idx++] |= 0x1; - fmt->data[idx] = (u8)( - ((af->aac_section_data_resilience_flag & 0x1) << 7) | - ((af->aac_scalefactor_data_resilience_flag & 0x1) << 6) | - ((af->aac_spectral_data_resilience_flag & 0x1) << 5) | - ((af->ep_config & 0x3) << 2)); - break; - - case AAC_OBJECT_ER_SCALABLE: - fmt->data[idx++] |= 0x1; - /* extension flag */ - fmt->data[idx++] = (u8)( - ((af->aac_section_data_resilience_flag & 0x1) << 4) | - ((af->aac_scalefactor_data_resilience_flag & 0x1) << 3) | - ((af->aac_spectral_data_resilience_flag & 0x1) << 2) | - ((af->ep_config >> 1) & 0x1)); - fmt->data[idx] = (u8)((af->ep_config & 0x1) - << 7); - break; - - case AAC_OBJECT_BSAC: - fmt->data[++idx] = (u8)((af->ep_config & 0x3) - << 6); - break; - - default: - pr_err("dbg unknown object type \n"); - break; - } - fmt->num_bytes = idx + 1; - - TRACE("aac_open: format %x%x %x%x%x%x %x%x, \n", - fmt->data[0], fmt->data[1], fmt->data[2], fmt->data[3], - fmt->data[4], fmt->data[5], fmt->data[6], fmt->data[7]); - - rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT; - rpc.config.aac.bit_rate = af->bit_rate; - if (ac->flags & AUDIO_FLAG_WRITE) { - rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_WRITE; - rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_PLAYBACK; - } else { - rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_READ; - rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_RECORD; - } - - if ((af->sbr_on_flag == 0) && (af->sbr_ps_on_flag == 0)) { - rpc.config.aac.encoder_mode = - ADSP_AUDIO_ENC_AAC_LC_ONLY_MODE; - } else if ((af->sbr_on_flag == 1) && (af->sbr_ps_on_flag == 0)) { - rpc.config.aac.encoder_mode = - ADSP_AUDIO_ENC_AAC_PLUS_MODE; - } else if ((af->sbr_on_flag == 1) && (af->sbr_ps_on_flag == 1)) { - rpc.config.aac.encoder_mode = - ADSP_AUDIO_ENC_ENHANCED_AAC_PLUS_MODE; - } else { - pr_err("unsupported SBR flag\n"); - return -EINVAL; - } - rpc.buf_max_size = bufsz; /* XXX ??? */ - rpc.hdr.response_type = 0; - - TRACE("aac_open: opcode %x, stream_context 0x%x, " - "mode %d, bytes %d, bbuffer size %d\n", - rpc.hdr.opcode, rpc.stream_context, - rpc.config.aac.encoder_mode, fmt->num_bytes, bufsz); - - return audio_ioctl(ac, &rpc, sizeof(rpc)); -} - -static int audio_qcelp_open(struct audio_client *ac, uint32_t bufsz, - void *data) -{ - struct msm_audio_qcelp_config *qf = data; - struct adsp_open_command rpc; - struct adsp_audio_standard_format *fmt = &(rpc.format.standard); - - memset(&rpc, 0, sizeof(rpc)); - - fmt->format = ADSP_AUDIO_FORMAT_V13K_FS; - fmt->sampling_rate = 8000; - fmt->channels = 1; - fmt->bits_per_sample = 16; - fmt->is_signed = 1; - fmt->is_interleaved = 0; - - rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT; - rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_READ; - rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_RECORD; - rpc.config.qcelp13k.min_rate = (uint16_t) qf->min_bit_rate; - rpc.config.qcelp13k.max_rate = (uint16_t) qf->max_bit_rate; - rpc.buf_max_size = bufsz; /* XXX ??? */ - - return audio_ioctl(ac, &rpc, sizeof(rpc)); -} - static int audio_close(struct audio_client *ac) { TRACE("%p: close\n", ac); @@ -734,10 +583,7 @@ static void callback(void *data, int len, void *cookie) { struct adsp_event_hdr *e = data; struct audio_client *ac; - struct adsp_buffer_event *abe = data; - TRACE("audio callback: context %d, event 0x%x, status %d\n", - e->context, e->event_id, e->status); if (e->context >= SESSION_MAX) { pr_err("audio callback: bogus session %d\n", @@ -764,9 +610,6 @@ static void callback(void *data, int len, void *cookie) if (e->event_id == ADSP_AUDIO_EVT_STATUS_BUF_DONE) { TRACE("%p: CB done (%d)\n", ac, e->status); - TRACE("%p: actual_size %d, buffer_size %d\n", - ac, abe->buffer.actual_size, ac->buf[ac->dsp_buf].size); - if (e->status) pr_err("buffer status %d\n", e->status); ac->buf[ac->dsp_buf].used = 0; @@ -1048,10 +891,6 @@ static void audio_rx_analog_enable(int en) if (analog_ops->receiver_enable) analog_ops->receiver_enable(en); break; - case ADSP_AUDIO_DEVICE_ID_I2S_SPKR: - if (analog_ops->i2s_enable) - analog_ops->i2s_enable(en); - break; } } @@ -1097,8 +936,7 @@ static int audio_update_acdb(uint32_t adev, uint32_t acdb_id) return -EINVAL; } - if (sz > 0) - audio_set_table(ac_control, adev, sz); + audio_set_table(ac_control, adev, sz); return 0; } @@ -1375,7 +1213,7 @@ int q6audio_reinit_acdb(char* filename) { return 0; mutex_lock(&audio_path_lock); - if (strlen(filename) < 0) { + if (strlen(filename) < 0 || !strcmp(filename, acdb_file)) { res = -EINVAL; goto done; } @@ -1609,7 +1447,7 @@ struct audio_client *q6audio_open_pcm(uint32_t bufsz, uint32_t rate, if (rc == 0) break; if (retry == 0) - BUG(); + q6audio_dsp_not_responding(); pr_err("q6audio: open pcm error %d, retrying\n", rc); msleep(1); } @@ -1634,7 +1472,7 @@ struct audio_client *q6audio_open_pcm(uint32_t bufsz, uint32_t rate, if (rc == 0) break; if (retry == 0) - BUG(); + q6audio_dsp_not_responding(); pr_err("q6audio: stream start error %d, retrying\n", rc); } @@ -1674,10 +1512,9 @@ struct audio_client *q6voice_open(uint32_t flags, uint32_t acdb_id) return 0; ac->flags = flags; - if (ac->flags & AUDIO_FLAG_WRITE) { + if (ac->flags & AUDIO_FLAG_WRITE) audio_rx_path_enable(1, acdb_id); - audio_rx_mute(ac_control, ADSP_AUDIO_DEVICE_ID_VOICE, 0); - } else { + else { tx_clk_freq = 8000; audio_tx_path_enable(1, acdb_id); } @@ -1687,10 +1524,9 @@ struct audio_client *q6voice_open(uint32_t flags, uint32_t acdb_id) int q6voice_close(struct audio_client *ac) { - if (ac->flags & AUDIO_FLAG_WRITE) { - audio_rx_mute(ac_control, ADSP_AUDIO_DEVICE_ID_VOICE, 1); + if (ac->flags & AUDIO_FLAG_WRITE) audio_rx_path_enable(0, 0); - } else + else audio_tx_path_enable(0, 0); audio_client_free(ac); @@ -1736,122 +1572,3 @@ int q6audio_async(struct audio_client *ac) rpc.response_type = ADSP_AUDIO_RESPONSE_ASYNC; return audio_ioctl(ac, &rpc, sizeof(rpc)); } - -struct audio_client *q6audio_open_aac(uint32_t bufsz, uint32_t rate, - uint32_t flags, void *data, uint32_t acdb_id) -{ - struct audio_client *ac; - - TRACE("q6audio_open_aac flags=%d rate=%d\n", flags, rate); - - if (q6audio_init()) - return 0; - - ac = audio_client_alloc(bufsz); - if (!ac) - return 0; - - ac->flags = flags; - if (ac->flags & AUDIO_FLAG_WRITE) - audio_rx_path_enable(1, acdb_id); - else { - /* TODO: consider concourrency with voice call */ - tx_clk_freq = rate; - audio_tx_path_enable(1, acdb_id); - } - - audio_aac_open(ac, bufsz, data); - audio_command(ac, ADSP_AUDIO_IOCTL_CMD_SESSION_START); - - if (!(ac->flags & AUDIO_FLAG_WRITE)) { - ac->buf[0].used = 1; - ac->buf[1].used = 1; - q6audio_read(ac, &ac->buf[0]); - q6audio_read(ac, &ac->buf[1]); - } - audio_prevent_sleep(); - return ac; -} - -int q6audio_aac_close(struct audio_client *ac) -{ - audio_close(ac); - if (ac->flags & AUDIO_FLAG_WRITE) - audio_rx_path_enable(0, 0); - else - audio_tx_path_enable(0, 0); - - audio_client_free(ac); - audio_allow_sleep(); - return 0; -} - -struct audio_client *q6fm_open(void) -{ - struct audio_client *ac; - - printk("q6fm_open()\n"); - - if (q6audio_init()) - return 0; - - if (audio_rx_device_id != ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_STEREO && - audio_rx_device_id != ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO) - return 0; - - ac = audio_client_alloc(0); - if (!ac) - return 0; - - ac->flags = AUDIO_FLAG_WRITE; - audio_rx_path_enable(1, 0); - enable_aux_loopback(1); - - return ac; -} - -int q6fm_close(struct audio_client *ac) -{ - audio_rx_path_enable(0, 0); - enable_aux_loopback(0); - audio_client_free(ac); - return 0; -} - -struct audio_client *q6audio_open_qcelp(uint32_t bufsz, uint32_t rate, - void *data, uint32_t acdb_id) -{ - struct audio_client *ac; - - if (q6audio_init()) - return 0; - - ac = audio_client_alloc(bufsz); - if (!ac) - return 0; - - ac->flags = AUDIO_FLAG_READ; - tx_clk_freq = rate; - audio_tx_path_enable(1, acdb_id); - - audio_qcelp_open(ac, bufsz, data); - audio_command(ac, ADSP_AUDIO_IOCTL_CMD_SESSION_START); - - ac->buf[0].used = 1; - ac->buf[1].used = 1; - q6audio_read(ac, &ac->buf[0]); - q6audio_read(ac, &ac->buf[1]); - - audio_prevent_sleep(); - return ac; -} - -int q6audio_qcelp_close(struct audio_client *ac) -{ - audio_close(ac); - audio_tx_path_enable(0, 0); - audio_client_free(ac); - audio_allow_sleep(); - return 0; -} - diff --git a/arch/arm/mach-msm/qdsp6/q6audio_htcleo.c b/arch/arm/mach-msm/qdsp6/q6audio_htcleo.c new file mode 100644 index 00000000..e5b75a2c --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/q6audio_htcleo.c @@ -0,0 +1,2634 @@ +/* arch/arm/mach-msm/qdsp6_10/q6audio.c + * + * Copyright (C) 2010 Cotulla + * Copyright (C) 2009 Google, Inc. + * Author: Brian Swetland + * + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "dal.h" +#include "dal_audio.h" +#include "dal_audio_format.h" +#include "dal_acdb.h" +#include "dal_adie.h" +#include +#include +#include +#include + +#include + +#include "q6audio_devices.h" +#include "../dex_comm.h" + +#if 0 +#define TRACE(x...) pr_info("Q6: "x) +#else +#define TRACE(x...) do{}while(0) +#endif + +#if 0 +#define AUDIO_INFO(x...) pr_info("Audio: "x) +#else +#define AUDIO_INFO(x...) do{}while(0) +#endif + + +#define CB_EVENT_COOKIE 0xC00CE13E + + +#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 one (from Desire) +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 + +extern int global_now_phone_call; + +static struct audio_client * audio_test(void); +static void callback(void *data, int len, void *cookie); +static int audio_init(struct audio_client *ac); +static int audio_info(struct audio_client *ac); +static int q6audio_init_rx_volumes(); + +static struct wake_lock wakelock; +static struct wake_lock idlelock; +static int idlecount; +static DEFINE_MUTEX(idlecount_lock); + +void audio_prevent_sleep(void) +{ + mutex_lock(&idlecount_lock); + if (++idlecount == 1) { + wake_lock(&wakelock); + wake_lock(&idlelock); + } + mutex_unlock(&idlecount_lock); +} + +void audio_allow_sleep(void) +{ + mutex_lock(&idlecount_lock); + if (--idlecount == 0) { + wake_unlock(&idlelock); + wake_unlock(&wakelock); + } + mutex_unlock(&idlecount_lock); +} + +static struct clk *icodec_rx_clk; +static struct clk *icodec_tx_clk; +static struct clk *ecodec_clk; +static struct clk *sdac_clk; + +static struct q6audio_analog_ops default_analog_ops; +static struct q6audio_analog_ops *analog_ops = &default_analog_ops; +static uint32_t tx_clk_freq = 8000; +static int tx_mute_status = 0; +static int rx_vol_level = 100; +static char acdb_file[64] = "default.acdb"; +static uint32_t tx_acdb = 0; +static uint32_t rx_acdb = 0; +static int acdb_use_rpc = 0; + +///////////////////////////////////////////////////////////////////////////////// +// helper functions for device parameters +///////////////////////////////////////////////////////////////////////////////// + +void q6audio_register_analog_ops(struct q6audio_analog_ops *ops) +{ + pr_info("register analog ops = %p\n", ops); + analog_ops = ops; +} + +void q6audio_set_acdb_file(char* filename) +{ + if (filename) { + pr_info("audio: set acdb file as %s\n", filename); + strncpy(acdb_file, filename, sizeof(acdb_file)-1); + } +} + +static struct q6_device_info *q6_lookup_device(uint32_t device_id) +{ + struct q6_device_info *di = q6_audio_devices; + for (;;) { + if (di->id == device_id) + return di; + if (di->id == 0) { + pr_err("q6_lookup_device: bogus id 0x%08x\n", + device_id); + return di; + } + di++; + } +} + +static uint32_t q6_device_to_codec(uint32_t device_id) +{ + struct q6_device_info *di = q6_lookup_device(device_id); + return di->codec; +} + +static uint32_t q6_device_to_dir(uint32_t device_id) +{ + struct q6_device_info *di = q6_lookup_device(device_id); + return di->dir; +} + +static uint32_t q6_device_to_cad_id(uint32_t device_id) +{ + struct q6_device_info *di = q6_lookup_device(device_id); + return di->cad_id; +} + +static uint32_t q6_device_to_path(uint32_t device_id) +{ + struct q6_device_info *di = q6_lookup_device(device_id); + return di->path; +} + +static uint32_t q6_device_to_rate(uint32_t device_id) +{ + struct q6_device_info *di = q6_lookup_device(device_id); + return di->rate; +} + +int q6_device_volume(uint32_t device_id, int level) +{ + struct q6_device_info *di = q6_lookup_device(device_id); + if (analog_ops->get_rx_vol) + return analog_ops->get_rx_vol(di->hw, level); + else { + struct q6_hw_info *hw; + hw = &q6_audio_hw[di->hw]; + return hw->min_gain + ((hw->max_gain - hw->min_gain) * level) / 100; + } +} + +///////////////////////////////////////////////////////////////////////////////// +// ADIE functions +///////////////////////////////////////////////////////////////////////////////// + +static inline int adie_open(struct dal_client *client) +{ + return dal_call_f0(client, DAL_OP_OPEN, 0); +} + +static inline int adie_close(struct dal_client *client) +{ + return dal_call_f0(client, DAL_OP_CLOSE, 0); +} + +static inline int adie_set_path(struct dal_client *client, + uint32_t id, uint32_t path_type) +{ + return dal_call_f1(client, ADIE_OP_SET_PATH, id, path_type); +} + +static inline int adie_set_path_freq_plan(struct dal_client *client, + uint32_t path_type, uint32_t plan) +{ + return dal_call_f1(client, ADIE_OP_SET_PATH_FREQUENCY_PLAN, + path_type, plan); +} + +static inline int adie_proceed_to_stage(struct dal_client *client, + uint32_t path_type, uint32_t stage) +{ + return dal_call_f1(client, ADIE_OP_PROCEED_TO_STAGE, + path_type, stage); +} + +static inline int adie_mute_path(struct dal_client *client, + uint32_t path_type, uint32_t mute_state) +{ + return dal_call_f1(client, ADIE_OP_MUTE_PATH, path_type, mute_state); +} + +static int adie_refcount; + +static struct dal_client *adie; +static struct dal_client *adsp; +static struct dal_client *acdb; + +static int adie_enable(void) +{ + adie_refcount++; + if (adie_refcount == 1) + adie_open(adie); + return 0; +} + +static int adie_disable(void) +{ + adie_refcount--; + if (adie_refcount == 0) + adie_close(adie); + return 0; +} + +/* 4k DMA scratch page used for exchanging acdb device config tables + * and stream format descriptions with the DSP. + */ +static void *audio_data; +static dma_addr_t audio_phys; +// this memory used to pass open params into DSP +static void *params_data; +static dma_addr_t params_phys; + + +#define SESSION_MIN 0 +#define SESSION_MAX 64 + +static DEFINE_MUTEX(session_lock); +static DEFINE_MUTEX(audio_lock); +static DEFINE_MUTEX(open_mem_lock); + + +static struct audio_client *session[SESSION_MAX]; + +static int session_alloc(struct audio_client *ac) +{ + int n; + + mutex_lock(&session_lock); + for (n = SESSION_MIN; n < SESSION_MAX; n++) { + if (!session[n]) { + session[n] = ac; + mutex_unlock(&session_lock); + return n; + } + } + mutex_unlock(&session_lock); + return -ENOMEM; +} + +static void session_free(int n, struct audio_client *ac) +{ + mutex_lock(&session_lock); + if (session[n] == ac) + session[n] = 0; + mutex_unlock(&session_lock); +} + +void audio_client_dump(struct audio_client *ac) +{ + dal_trace_dump(ac->client); +} + + +static void audio_client_free(struct audio_client *ac) +{ + AUDIO_INFO("%s: session %d\n", __func__, ac->session); + + session_free(ac->session, ac); + + if (ac->buf[0].data) + dma_free_coherent(NULL, ac->buf[0].size, ac->buf[0].data, ac->buf[0].phys); + if (ac->buf[1].data) + dma_free_coherent(NULL, ac->buf[1].size, ac->buf[1].data, ac->buf[1].phys); + + if (ac->client) + dal_detach(ac->client); + kfree(ac); +} + +static struct audio_client *audio_client_alloc(unsigned bufsz) +{ + struct audio_client *ac; + struct dal_client *dsp; + int n; + + AUDIO_INFO("%s\n", __func__); + ac = kzalloc(sizeof(*ac), GFP_KERNEL); + if (!ac) + { + AUDIO_INFO("%s: alloc error\n", __func__); + return 0; + } + + n = session_alloc(ac); + if (n < 0) + { + AUDIO_INFO("%s: session alloc error\n", __func__); + goto fail_session; + } + ac->session = n; + AUDIO_INFO("%s: session %d\n", __func__, ac->session); + + if (bufsz > 0) + { + ac->buf[0].data = dma_alloc_coherent(NULL, bufsz, &ac->buf[0].phys, GFP_KERNEL); + if (!ac->buf[0].data) + { + goto fail; + } + + ac->buf[1].data = dma_alloc_coherent(NULL, bufsz, &ac->buf[1].phys, GFP_KERNEL); + if (!ac->buf[1].data) + { + goto fail; + } + + ac->buf[0].size = bufsz; + ac->buf[1].size = bufsz; + } + + init_waitqueue_head(&ac->wait); + + +// dsp = dal_attach(AUDIO_DAL_DEVICE, AUDIO_DAL_PORT, callback, 0); + dsp = dal_attach_ex(AUDIO_DAL_DEVICE, "AudioQdsp", AUDIO_DAL_PORT, callback, 0); + if (!dsp) + { + pr_err("audio_client_alloc: cannot attach to adsp\n"); + goto fail; + } + ac->client = dsp; + + audio_init(ac); + audio_info(ac); + audio_client_dump(ac); + + return ac; + +fail: + if (ac->buf[0].data) + dma_free_coherent(NULL, ac->buf[0].size, ac->buf[0].data, ac->buf[0].phys); + if (ac->buf[1].data) + dma_free_coherent(NULL, ac->buf[1].size, ac->buf[1].data, ac->buf[1].phys); + session_free(n, ac); +fail_session: + audio_client_free(ac); + return 0; +} + + +static int audio_ioctl(struct audio_client *ac, uint32_t cmd, void *ptr, uint32_t len) +{ + int r; + r = dal_call_f6(ac->client, AUDIO_OP_IOCTL, cmd, ptr, len); + return r; +} + +static int audio_command(struct audio_client *ac, uint32_t cmd) +{ + struct adsp_command_hdr rpc; + memset(&rpc, 0, sizeof(rpc)); + return audio_ioctl(ac, cmd, NULL, 0); +} + +static int audio_open_control(struct audio_client *ac) +{ + int r; + struct adsp_open_command rpc; + + memset(&rpc, 0, sizeof(rpc)); + + rpc.opcode = ADSP_AUDIO_OPCODE_OPEN_DEV; + + // reset flag before call, callback() will set it later + ac->open_done = 0; + r = dal_call_f5(ac->client, AUDIO_OP_OPEN, &rpc, sizeof(rpc)); + if (r != 0) + return r; + +// wait for async event + if (!wait_event_timeout(ac->wait, (ac->open_done == 1), 3 * HZ)) + { + pr_err("wait for open async event failed!\n"); + return -1; + } + return ac->open_status; +} + +static int audio_out_open(struct audio_client *ac, uint32_t bufsz, + uint32_t rate, uint32_t channels) +{ + int r; + struct adsp_open_command rpc; + union adsp_audio_format *ptr; + + printk("audio_out_open: %x %d %d\n", bufsz, rate, channels); + mutex_lock(&open_mem_lock); + + memset(&rpc, 0, sizeof(rpc)); + ptr = (union adsp_audio_format*)params_data; + memset(ptr, 0, sizeof(union adsp_audio_format)); + + rpc.numdev = 1; +// rpc.dev[0] = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO; + rpc.dev[0] = ADSP_AUDIO_DEVICE_ID_DEFAULT; + rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_PLAYBACK; + rpc.buf_max_size = bufsz; + + rpc.format = ADSP_AUDIO_FORMAT_PCM; + rpc.pblock = params_phys; + rpc.blocklen = sizeof(struct adsp_audio_standard_format); +// rpc.pblock = 0; +// rpc.blocklen = 0; + + rpc.opcode = ADSP_AUDIO_OPCODE_OPEN_WRITE; + + ptr->standard.channels = channels; + ptr->standard.bits_per_sample = 16; + ptr->standard.sampling_rate = rate; + ptr->standard.is_signed = 1; + ptr->standard.is_interleaved = 1; + + AUDIO_INFO("open out %p\n", ac); + ac->open_done = 0; + r = dal_call_f5(ac->client, AUDIO_OP_OPEN, &rpc, sizeof(rpc)); + +// wait for async event + if (!wait_event_timeout(ac->wait, (ac->open_done == 1), 3 * HZ)) + { + mutex_unlock(&open_mem_lock); + pr_err("wait for open async event failed!\n"); + return -1; + } + mutex_unlock(&open_mem_lock); +// return r; + return ac->open_status; +} + +static int audio_in_open(struct audio_client *ac, uint32_t bufsz, + uint32_t rate, uint32_t channels) +{ + int r; + struct adsp_open_command rpc; + union adsp_audio_format *ptr; + + printk("audio_in_open: %x %d %d\n", bufsz, rate, channels); + mutex_lock(&open_mem_lock); + memset(&rpc, 0, sizeof(rpc)); + ptr = (union adsp_audio_format*)params_data; + memset(ptr, 0, sizeof(union adsp_audio_format)); + + rpc.numdev = 1; + rpc.dev[0] = ADSP_AUDIO_DEVICE_ID_DEFAULT; + rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_RECORD; + rpc.buf_max_size = bufsz; + + rpc.format = ADSP_AUDIO_FORMAT_PCM; + rpc.pblock = params_phys; + rpc.blocklen = sizeof(struct adsp_audio_standard_format); + rpc.opcode = ADSP_AUDIO_OPCODE_OPEN_READ; + + ptr->standard.channels = channels; + ptr->standard.bits_per_sample = 16; + ptr->standard.sampling_rate = rate; + ptr->standard.is_signed = 1; + ptr->standard.is_interleaved = 1; + + AUDIO_INFO("%p: open in\n", ac); + ac->open_done = 0; + r = dal_call_f5(ac->client, AUDIO_OP_OPEN, &rpc, sizeof(rpc)); +// wait for async event + if (!wait_event_timeout(ac->wait, (ac->open_done == 1), 3 * HZ)) + { + mutex_unlock(&open_mem_lock); + pr_err("wait for open async event failed (IN)!\n"); + return -1; + } + mutex_unlock(&open_mem_lock); +// return r; + return ac->open_status; +} + +static int audio_mp3_open(struct audio_client *ac, uint32_t bufsz, + uint32_t rate, uint32_t channels) +{ + int r; + struct adsp_open_command rpc; + union adsp_audio_format *ptr; + + printk("audio_mp3_open: %x %d %d\n", bufsz, rate, channels); + mutex_lock(&open_mem_lock); + memset(&rpc, 0, sizeof(rpc)); + ptr = (union adsp_audio_format*)params_data; + memset(ptr, 0, sizeof(union adsp_audio_format)); + + rpc.numdev = 1; + rpc.dev[0] = ADSP_AUDIO_DEVICE_ID_DEFAULT; + rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_PLAYBACK; + rpc.buf_max_size = bufsz; + + rpc.format = ADSP_AUDIO_FORMAT_MP3; + rpc.pblock = params_phys; + rpc.blocklen = sizeof(struct adsp_audio_standard_format); + rpc.opcode = ADSP_AUDIO_OPCODE_OPEN_WRITE; + + ptr->standard.channels = channels; + ptr->standard.bits_per_sample = 16; + ptr->standard.sampling_rate = rate; + ptr->standard.is_signed = 1; + ptr->standard.is_interleaved = 1; + + r = dal_call_f5(ac->client, AUDIO_OP_OPEN, &rpc, sizeof(rpc)); +// wait for async event + if (!wait_event_timeout(ac->wait, (ac->open_done == 1), 3 * HZ)) + { + mutex_unlock(&open_mem_lock); + pr_err("wait for open async event failed (MP3)!\n"); + return -1; + } + mutex_unlock(&open_mem_lock); +// return r; + return ac->open_status; +} + +static int audio_aac_open(struct audio_client *ac, uint32_t bufsz, void *data) +{ + int r; + struct aac_format *af = data; + struct adsp_open_command rpc; + uint32_t *aac_type; + int idx = 0; // sizeof(uint32_t); + struct adsp_audio_binary_format *fmt = (struct adsp_audio_binary_format *)params_data; + union adsp_audio_format *ptr; + + + mutex_lock(&open_mem_lock); + memset(&rpc, 0, sizeof(rpc)); + ptr = (union adsp_audio_format*)params_data; + memset(ptr, 0, sizeof(union adsp_audio_format)); + + rpc.numdev = 1; + rpc.dev[0] = ADSP_AUDIO_DEVICE_ID_DEFAULT; + + rpc.format = ADSP_AUDIO_FORMAT_MPEG4_AAC; + rpc.pblock = params_phys; + + aac_type = (uint32_t *)(fmt->data); + switch (af->block_formats) + { + case 0xffff: + if (ac->flags & AUDIO_FLAG_WRITE) + *aac_type = ADSP_AUDIO_AAC_ADTS; + else + *aac_type = ADSP_AUDIO_AAC_MPEG4_ADTS; + break; + case 0: + if (ac->flags & AUDIO_FLAG_WRITE) + *aac_type = ADSP_AUDIO_AAC_ADIF; + else + *aac_type = ADSP_AUDIO_AAC_RAW; + break; + case 1: + *aac_type = ADSP_AUDIO_AAC_RAW; + break; + case 2: + *aac_type = ADSP_AUDIO_AAC_LOAS; + break; + case 3: + *aac_type = ADSP_AUDIO_AAC_FRAMED_RAW; + break; + case 4: + *aac_type = ADSP_AUDIO_AAC_RAW; + break; + default: + pr_err("unsupported AAC type %d\n", af->block_formats); + mutex_unlock(&open_mem_lock); + return -EINVAL; + } + + AUDIO_INFO("aac_open: type %x, obj %d, idx %d\n", + *aac_type, af->audio_object_type, idx); + fmt->data[idx++] = (u8)(((af->audio_object_type & 0x1F) << 3) | + ((af->sample_rate >> 1) & 0x7)); + fmt->data[idx] = (u8)(((af->sample_rate & 0x1) << 7) | + ((af->channel_config & 0x7) << 3)); + + switch (af->audio_object_type) { + case AAC_OBJECT_ER_LC: + case AAC_OBJECT_ER_LTP: + case AAC_OBJECT_ER_LD: + /* extension flag */ + fmt->data[idx++] |= 0x1; + fmt->data[idx] = (u8)( + ((af->aac_section_data_resilience_flag & 0x1) << 7) | + ((af->aac_scalefactor_data_resilience_flag & 0x1) << 6) | + ((af->aac_spectral_data_resilience_flag & 0x1) << 5) | + ((af->ep_config & 0x3) << 2)); + break; + + case AAC_OBJECT_ER_SCALABLE: + fmt->data[idx++] |= 0x1; + /* extension flag */ + fmt->data[idx++] = (u8)( + ((af->aac_section_data_resilience_flag & 0x1) << 4) | + ((af->aac_scalefactor_data_resilience_flag & 0x1) << 3) | + ((af->aac_spectral_data_resilience_flag & 0x1) << 2) | + ((af->ep_config >> 1) & 0x1)); + fmt->data[idx] = (u8)((af->ep_config & 0x1) << 7); + break; + + case AAC_OBJECT_BSAC: + fmt->data[++idx] = (u8)((af->ep_config & 0x3) << 6); + break; + + default: + pr_err("dbg unknown object type \n"); + break; + } +// fmt->num_bytes = idx + 1; + rpc.blocklen = idx + 1; + + TRACE("aac_open: format %x%x %x%x%x%x %x%x, \n", + fmt->data[0], fmt->data[1], fmt->data[2], fmt->data[3], + fmt->data[4], fmt->data[5], fmt->data[6], fmt->data[7]); + + rpc.config.aac.bit_rate = af->bit_rate; + if (ac->flags & AUDIO_FLAG_WRITE) + { + rpc.opcode = ADSP_AUDIO_OPCODE_OPEN_WRITE; + rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_PLAYBACK; + } + else + { + rpc.opcode = ADSP_AUDIO_OPCODE_OPEN_READ; + rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_RECORD; + } + + if ((af->sbr_on_flag == 0) && (af->sbr_ps_on_flag == 0)) + { + rpc.config.aac.encoder_mode = ADSP_AUDIO_ENC_AAC_LC_ONLY_MODE; + } + else if ((af->sbr_on_flag == 1) && (af->sbr_ps_on_flag == 0)) + { + rpc.config.aac.encoder_mode = ADSP_AUDIO_ENC_AAC_PLUS_MODE; + } + else if ((af->sbr_on_flag == 1) && (af->sbr_ps_on_flag == 1)) + { + rpc.config.aac.encoder_mode = ADSP_AUDIO_ENC_ENHANCED_AAC_PLUS_MODE; + } + else + { + pr_err("unsupported SBR flag\n"); + mutex_unlock(&open_mem_lock); + return -EINVAL; + } + rpc.buf_max_size = bufsz; /* XXX ??? */ + + TRACE("aac_open: opcode %x, stream_context 0x%x, " + "mode %d, bytes %d, bbuffer size %d\n", + rpc.opcode, rpc.stream_context, + rpc.config.aac.encoder_mode, rpc.blocklen, bufsz); + + r = dal_call_f5(ac->client, AUDIO_OP_OPEN, &rpc, sizeof(rpc)); + mutex_unlock(&open_mem_lock); + return r; +} + +static int audio_qcelp_open(struct audio_client *ac, uint32_t bufsz, + void *data) +{ + int r; + struct msm_audio_qcelp_config *qf = data; + struct adsp_open_command rpc; + struct adsp_audio_standard_format *fmt; + union adsp_audio_format *ptr; + + + mutex_lock(&open_mem_lock); + memset(&rpc, 0, sizeof(rpc)); + fmt = (struct adsp_audio_standard_format *)params_data; + memset(ptr, 0, sizeof(struct adsp_audio_standard_format)); + + rpc.numdev = 1; + rpc.dev[0] = ADSP_AUDIO_DEVICE_ID_DEFAULT; + rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_RECORD; + + rpc.format = ADSP_AUDIO_FORMAT_V13K_FS; + rpc.pblock = params_phys; + rpc.blocklen = sizeof(struct adsp_audio_standard_format); + rpc.opcode = ADSP_AUDIO_OPCODE_OPEN_READ; + + fmt->sampling_rate = 8000; + fmt->channels = 1; + fmt->bits_per_sample = 16; + fmt->is_signed = 1; + fmt->is_interleaved = 0; + + rpc.config.qcelp13k.min_rate = (uint16_t) qf->min_bit_rate; + rpc.config.qcelp13k.max_rate = (uint16_t) qf->max_bit_rate; + rpc.buf_max_size = bufsz; /* XXX ??? */ + + r = dal_call_f5(ac->client, AUDIO_OP_OPEN, &rpc, sizeof(rpc)); + + mutex_unlock(&open_mem_lock); + return r; +} + +static int audio_close(struct audio_client *ac) +{ + AUDIO_INFO("%p: close\n", ac); + audio_command(ac, ADSP_AUDIO_IOCTL_CMD_STREAM_STOP); +// audio_command(ac, ADSP_AUDIO_IOCTL_CMD_CLOSE); + + dal_call_f0(ac->client, AUDIO_OP_CLOSE, ac->session); + return 0; +} + +static int audio_set_table(struct audio_client *ac,int32_t device_id, int size) +{ + struct adsp_set_dev_cfg_table_command rpc; + + AUDIO_INFO("%s: %x %d\n", __func__, device_id, size); + +// print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, audio_data, size); + + memset(&rpc, 0, sizeof(rpc)); + if (q6_device_to_dir(device_id) == Q6_TX) + rpc.hdr.data = tx_clk_freq; + rpc.device_id = device_id; + rpc.phys_addr = audio_phys; + rpc.phys_size = size; + rpc.phys_used = size; + + TRACE("control: set table %x\n", device_id); + return audio_ioctl(ac, ADSP_AUDIO_IOCTL_SET_DEVICE_CONFIG_TABLE, &rpc, sizeof(rpc)); +} + +int q6audio_read(struct audio_client *ac, struct audio_buffer *ab) +{ + struct adsp_buffer_command rpc; + int r; + + AUDIO_INFO("%s: %X %X %X\n", __func__, ab->phys , ab->size, ab->used); + memset(&rpc, 0, sizeof(rpc)); + rpc.hdr.context = ac->session; + rpc.hdr.data = (uint32_t)ab->data; + rpc.buffer.addr = ab->phys; + rpc.buffer.max_size = ab->size; + rpc.buffer.actual_size = ab->used; + rpc.buffer.flags = ADSP_AUDIO_BUFFER_FLAG_PHYS_ADDR | ADSP_AUDIO_BUFFER_FLAG_START_SET; + + TRACE("%p: read\n", ac); +// r = dal_call(ac->client, AUDIO_OP_DATA, 5, &rpc, sizeof(rpc), &res, sizeof(res)); + + r = dal_call_f5(ac->client, AUDIO_OP_READ, &rpc, sizeof(rpc)); + return 0; +} + +int q6audio_write(struct audio_client *ac, struct audio_buffer *ab) +{ + struct adsp_buffer_command rpc; + int r; + + AUDIO_INFO("%s: %X %X %X\n", __func__, ab->phys , ab->size, ab->used); + memset(&rpc, 0, sizeof(rpc)); +// rpc.hdr.context = ac->session; + rpc.hdr.data = (uint32_t)ab->data; + rpc.buffer.addr = ab->phys; + rpc.buffer.max_size = ab->size; + rpc.buffer.actual_size = ab->used; + rpc.buffer.start = (~0x7fffffffffffffffLL); + rpc.buffer.flags = ADSP_AUDIO_BUFFER_FLAG_PHYS_ADDR | ADSP_AUDIO_BUFFER_FLAG_START_SET; + + TRACE("%p: write\n", ac); +// r = dal_call(ac->client, AUDIO_OP_DATA, 5, &rpc, sizeof(rpc), &res, sizeof(res)); + + r = dal_call_f5(ac->client, AUDIO_OP_WRITE, &rpc, sizeof(rpc)); + return 0; +} + +static int audio_rx_volume(struct audio_client *ac, uint32_t dev_id, int32_t volume) +{ + struct adsp_set_dev_volume_command rpc; + + AUDIO_INFO("%s: dev_id 0x%08x, volume = %d\n", __func__, dev_id , volume); + + memset(&rpc, 0, sizeof(rpc)); + rpc.device_id = dev_id; + rpc.path = ADSP_PATH_RX; + rpc.volume = volume; + return audio_ioctl(ac, ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_VOL, &rpc, sizeof(rpc)); +} + +static int audio_tx_volume(struct audio_client *ac, uint32_t dev_id, int32_t volume) +{ + struct adsp_set_dev_volume_command rpc; + + AUDIO_INFO("%s: dev_id 0x%08x, volume = %d\n", __func__, dev_id , volume); + + memset(&rpc, 0, sizeof(rpc)); + rpc.device_id = dev_id; + rpc.path = ADSP_PATH_TX; + rpc.volume = volume; + return audio_ioctl(ac, ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_VOL, &rpc, sizeof(rpc)); +} + + +static int audio_rx_mute(struct audio_client *ac, uint32_t dev_id, int mute) +{ + struct adsp_set_dev_mute_command rpc; + + AUDIO_INFO("%s: dev_id 0x%08x, mute %d\n", __func__, dev_id , mute); + + memset(&rpc, 0, sizeof(rpc)); + rpc.device_id = dev_id; + rpc.path = ADSP_PATH_RX; + rpc.mute = !!mute; + return audio_ioctl(ac, ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_MUTE, &rpc, sizeof(rpc)); +} + + +static int audio_tx_mute(struct audio_client *ac, uint32_t dev_id, int mute) +{ + struct adsp_set_dev_mute_command rpc; + + AUDIO_INFO("%s: dev_id 0x%08x, mute %d\n", __func__, dev_id , mute); + + memset(&rpc, 0, sizeof(rpc)); + rpc.device_id = dev_id; + rpc.path = ADSP_PATH_TX; + rpc.mute = !!mute; + return audio_ioctl(ac, ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_MUTE, &rpc, sizeof(rpc)); +} + +static int audio_stream_volume(struct audio_client *ac, int volume) +{ + struct adsp_set_volume_command rpc; + int rc; + + AUDIO_INFO("%s: volume %d\n", __func__, volume); +// +// CotullaHACK: this function called from libaudio-qsd8k / AudioHardware.cpp +// status = ioctl(mFd, AUDIO_SET_VOLUME, &stream_volume); +// stream_volume have fixed -300 value, replace it to zero +// +// printk("$$$ audio_stream_volume(%d)\n", volume); + volume = 0; + + memset(&rpc, 0, sizeof(rpc)); + rpc.volume = volume; + rc = audio_ioctl(ac, ADSP_AUDIO_IOCTL_CMD_SET_STREAM_VOL, &rpc, sizeof(rpc)); + return rc; +} + +static int audio_stream_mute(struct audio_client *ac, int mute) +{ + struct adsp_set_mute_command rpc; + int rc; + + AUDIO_INFO("%s: mute %d\n", __func__, mute); + memset(&rpc, 0, sizeof(rpc)); + rpc.mute = mute; + rc = audio_ioctl(ac, ADSP_AUDIO_IOCTL_CMD_SET_STREAM_MUTE, &rpc, sizeof(rpc)); + return rc; +} + +static int audio_stream_eq(struct audio_client *ac, struct cad_audio_eq_cfg *eq_cfg) +{ + struct adsp_audio_set_equalizer_command rpc; + int rc; + uint32_t i = 0; + + AUDIO_INFO("%s\n", __func__); + memset(&rpc, 0, sizeof(rpc)); + rpc.enable = eq_cfg->enable; + rpc.num_bands = eq_cfg->num_bands; + for (i = 0; i < rpc.num_bands; i++) + { + rpc.eq_bands[i].band_idx = eq_cfg->eq_bands[i].band_idx; + rpc.eq_bands[i].filter_type = eq_cfg->eq_bands[i].filter_type; + rpc.eq_bands[i].center_freq_hz = eq_cfg->eq_bands[i].center_freq_hz; + rpc.eq_bands[i].filter_gain = eq_cfg->eq_bands[i].filter_gain; + rpc.eq_bands[i].q_factor = eq_cfg->eq_bands[i].q_factor; +#if 1 + pr_info(">>>>>> band_idx = %d\n", rpc.eq_bands[i].band_idx); + pr_info(">>>>>> filter_type = %d\n", rpc.eq_bands[i].filter_type); + pr_info(">>>>>> center_freq_hz = %d\n", rpc.eq_bands[i].center_freq_hz); + pr_info(">>>>>> filter_gain = %d\n", rpc.eq_bands[i].filter_gain); + pr_info(">>>>>> q_factor = %d\n\n", rpc.eq_bands[i].q_factor); +#endif + } + rc = audio_ioctl(ac, ADSP_AUDIO_IOCTL_SET_SESSION_EQ_CONFIG, &rpc, sizeof(rpc)); + return rc; +} + +static void callback(void *data, int len, void *cookie) +{ +// struct adsp_event_hdr *e = data; + struct adsp_audio_dal_event *e = data; + struct audio_client *ac; + struct adsp_audio_event* ae; + +// printk("dal event\n"); + TRACE("audio callback: CB: %X LOC: %X SIZE=%X\n", e->cb_evt, e->loc_evt, e->size); + + if (e->cb_evt != CB_EVENT_COOKIE) + { + pr_err("audio callback: bad cb_event %X\n", e->cb_evt); + return; + } + + ae = &e->ae; + if (ae->session >= SESSION_MAX) + { + pr_err("audio callback: bogus session %d\n", ae->session); + return; + } + ac = session[ae->session]; + if (!ac) + { + pr_err("audio callback: unknown session %d\n", ae->session); + return; + } + + if (ae->event_id == ADSP_AUDIO_EVT_STATUS_OPEN) + { + pr_info("open done!\n"); + ac->open_status = ae->status; + ac->open_done = 1; + wake_up(&ac->wait); + return; + } + + if (ae->event_id == ADSP_AUDIO_IOCTL_CMD_STREAM_EOS) + { + TRACE("%p: CB stream eos\n", ac); + if (ae->status) + { + pr_err("playback status %d\n", ae->status); + } + if (ac->cb_status == -EBUSY) + { + ac->cb_status = ae->status; + wake_up(&ac->wait); + } + return; + } + + if (ae->event_id == ADSP_AUDIO_EVT_STATUS_BUF_DONE) + { + TRACE("%p: CB done (%d)\n", ac, ae->status); +// TRACE("%p: actual_size %d, buffer_size %d\n", +// ac, abe->buffer.actual_size, ac->buf[ac->dsp_buf].size); + + if (ae->status) + { + pr_err("buffer status %d\n", ae->status); + } + ac->buf[ac->dsp_buf].used = 0; + ac->dsp_buf ^= 1; + wake_up(&ac->wait); + return; + } + + TRACE("%p: CB %08x status %d\n", ac, ae->event_id, ae->status); + if (ae->status) + { + pr_warning("audio_cb: s=%d e=%08x status=%d\n", ae->session, ae->event_id, ae->status); + } + + if (ac->cb_status == -EBUSY) + { + ac->cb_status = ae->status; + wake_up(&ac->wait); + } +} + + +struct rpc_config_info +{ + u32 cb_evt; + u32 loc_evt; + u32 session_id; + u32 processor_id; +}; + +struct rpc_info +{ + uint32_t size; + uint32_t version; + char name[32]; + +}; + + + + +static int audio_init(struct audio_client *ac) +{ + + int r; + struct rpc_config_info info; + + AUDIO_INFO("%s\n", __func__); + + info.session_id = ac->session; + info.processor_id = 1; // AARM + info.cb_evt = CB_EVENT_COOKIE; + info.loc_evt = (u32)(((u32)ac->session + 1) << 16); + + r = dal_call_f5(ac->client, AUDIO_OP_INIT, &info, sizeof(info)); + return r; +} + +static int audio_info(struct audio_client *ac) +{ + int r; + struct rpc_info info; + + AUDIO_INFO("%s\n", __func__); + + r = dal_call_f9(ac->client, DAL_OP_INFO, &info, sizeof(info)); + return r; +} + +static struct audio_client *ac_control; +static int zero_already_inited; + +struct audio_config_data { + uint32_t device_id; + uint32_t sample_rate; + uint32_t offset; + uint32_t length; +}; + +struct audio_config_database { + uint8_t magic[8]; + uint32_t entry_count; + uint32_t unused; + struct audio_config_data entry[0]; +}; + +void *acdb_data; +const struct firmware *acdb_fw; +extern struct miscdevice q6_control_device; + +static int acdb_init(char *filename) +{ + const struct audio_config_database *db; + const struct firmware *fw; + int n; + + return -ENODEV; + + pr_info("acdb: load '%s'\n", filename); + if (request_firmware(&fw, filename, q6_control_device.this_device) < 0) { + pr_err("acdb: load 'default.acdb' failed...\n"); + return -ENODEV; + } + db = (void*) fw->data; + + if (fw->size < sizeof(struct audio_config_database)) { + pr_err("acdb: undersized database\n"); + goto fail; + } + if (strcmp(db->magic, "ACDB1.0")) { + pr_err("acdb: invalid magic\n"); + goto fail; + } + if (db->entry_count > 1024) { + pr_err("acdb: too many entries\n"); + goto fail; + } + if (fw->size < (sizeof(struct audio_config_database) + + db->entry_count * sizeof(struct audio_config_data))) { + pr_err("acdb: undersized TOC\n"); + goto fail; + } + for (n = 0; n < db->entry_count; n++) { + if (db->entry[n].length > 4096) { + pr_err("acdb: entry %d too large (%d)\n", + n, db->entry[n].length); + goto fail; + } + if ((db->entry[n].offset + db->entry[n].length) > fw->size) { + pr_err("acdb: entry %d outside of data\n", n); + goto fail; + } + } + if (acdb_data) + release_firmware(acdb_fw); + acdb_data = (void*) fw->data; + acdb_fw = fw; + return 0; +fail: + release_firmware(fw); + return -ENODEV; +} + +static int q6audio_init(void) +{ + struct audio_client *ac = 0; + int res; + + AUDIO_INFO("%s\n", __func__); + mutex_lock(&audio_lock); + if (ac_control) + { + res = 0; + goto done; + } + + pr_info("q6audio_init\n"); + + icodec_rx_clk = clk_get(0, "icodec_rx_clk"); + icodec_tx_clk = clk_get(0, "icodec_tx_clk"); + ecodec_clk = clk_get(0, "ecodec_clk"); + sdac_clk = clk_get(0, "sdac_clk"); + audio_data = dma_alloc_coherent(NULL, 4096, &audio_phys, GFP_KERNEL); + params_data = dma_alloc_coherent(NULL, 4096, ¶ms_phys, GFP_KERNEL); +// printk("allocated: %p %x\n", params_data, params_phys); + +// pr_info("audio: init: INIT\n"); + + if (!zero_already_inited) + { + zero_already_inited = 1; + pr_info("audio: init: first run, init dummy control\n"); + audio_client_alloc(0); + } + + ac = audio_client_alloc(0); + if (!ac) + { + pr_err("audio_init: cannot allocate client\n"); + res = -ENOMEM; + goto done; + } + +// pr_info("audio: init: OPEN control\n"); + if (audio_open_control(ac)) + { + pr_err("audio_init: cannot open control channel\n"); + res = -ENODEV; + goto done; + } + +// pr_info("audio: init: attach ACDB\n"); + acdb = dal_attach(ACDB_DAL_DEVICE, ACDB_DAL_PORT, 0, 0); + if (!acdb) + { + pr_err("audio_init: cannot attach to acdb channel\n"); + res = -ENODEV; + goto done; + } + +// pr_info("audio: init: attach ADIE\n"); + adie = dal_attach(ADIE_DAL_DEVICE, ADIE_DAL_PORT, 0, 0); + if (!adie) { + pr_err("audio_init: cannot attach to adie\n"); + res = -ENODEV; + goto done; + } + + if (analog_ops->init) + analog_ops->init(); + + if (!acdb_data && !acdb_use_rpc) + { + if (acdb_init(acdb_file)) + { + pr_info("use RPC to query ACDB.\n"); + acdb_use_rpc = 1; + } + } + + res = 0; + ac_control = ac; + + wake_lock_init(&idlelock, WAKE_LOCK_IDLE, "audio_pcm_idle"); + wake_lock_init(&wakelock, WAKE_LOCK_SUSPEND, "audio_pcm_suspend"); + + q6audio_init_rx_volumes(); + +// TEST HERE +// pr_info("START TEST\n"); +// audio_tx_mute(ac_control, ADSP_AUDIO_DEVICE_ID_DEFAULT, 1); +// pr_info("END TEST\n"); +done: + if ((res < 0) && ac) + { + audio_client_free(ac); + printk(" init failed!\n"); + } + mutex_unlock(&audio_lock); + return res; +} + + +static int map_cad_dev_to_virtual(int cd) +{ + if (global_now_phone_call) + { + return cd; + } + + switch (cd) + { + case 1: return 507; + case 2: return 208; + case 3: return 307; + case 5: return 407; + case 6: return 507; + case 7: return 607; + case 17: return 408; + default: return cd; + } + return 0; +} + +static int acdb_get_config_table(uint32_t device_id, uint32_t sample_rate) +{ + AUDIO_INFO("%s\n", __func__); + + if (q6audio_init()) + return 0; + + if (acdb_use_rpc) + { + struct acdb_cmd_device_table rpc; + struct acdb_result res; + int r; + uint32_t new_device_id; + + memset(audio_data, 0, 4096); + memset(&rpc, 0, sizeof(rpc)); + + new_device_id = map_cad_dev_to_virtual(device_id); + printk(" ACDB MAP: %d -> %d\n", device_id, new_device_id); + + rpc.size = sizeof(rpc) - (2 * sizeof(uint32_t)); + rpc.command_id = ACDB_GET_DEVICE_TABLE; + rpc.device_id = new_device_id; //0x25F; //device_id; + rpc.sample_rate_id = sample_rate; + rpc.total_bytes = 4096; + rpc.unmapped_buf = audio_phys; + rpc.res_size = sizeof(res) - (2 * sizeof(uint32_t)); + +// printk("ACDB dal call\n"); + r = dal_call(acdb, ACDB_OP_IOCTL, 8, &rpc, sizeof(rpc), &res, sizeof(res)); +// r = dal_call_f8(acdb, ACDB_OP_IOCTL, &rpc, sizeof(rpc), &res, sizeof(res)); + + pr_info("acdb ret: %d %X %X %X %d\n", res. dal_status, res.size, res.unmapped_buf, res.used_bytes, res.result); + if ((r == sizeof(res)) && (res.result == 0)) + { + pr_info("acdb: %d bytes for device %d, rate %d.\n", res.used_bytes, device_id, sample_rate); + return res.used_bytes; + } + return 0; + } + else + { + struct audio_config_database *db; + int n; + + printk("table\n"); + db = acdb_data; + for (n = 0; n < db->entry_count; n++) + { + if (db->entry[n].device_id != device_id) + continue; + if (db->entry[n].sample_rate != sample_rate) + continue; + break; + } + + if (n == db->entry_count) + { + pr_err("acdb: no entry for device %d, rate %d.\n", + device_id, sample_rate); + return 0; + } + + pr_info("acdb: %d bytes for device %d, rate %d.\n", + db->entry[n].length, device_id, sample_rate); + + memcpy(audio_data, acdb_data + db->entry[n].offset, db->entry[n].length); + return db->entry[n].length; + } +} + +//static uint32_t audio_rx_path_id = ADIE_PATH_HANDSET_RX; +//static uint32_t audio_rx_device_id = ADSP_AUDIO_DEVICE_ID_HANDSET_SPKR; +static uint32_t audio_rx_path_id = ADIE_PATH_SPEAKER_RX; +static uint32_t audio_rx_device_id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO; +static uint32_t audio_rx_device_group = -1; +static uint32_t audio_tx_path_id = ADIE_PATH_HANDSET_TX; +static uint32_t audio_tx_device_id = ADSP_AUDIO_DEVICE_ID_HANDSET_MIC; +static uint32_t audio_tx_device_group = -1; + +static int qdsp6_devchg_notify(struct audio_client *ac, uint32_t dev_type, uint32_t dev_id) +{ + struct adsp_device_switch_command rpc; + + AUDIO_INFO("%s\n", __func__); + + if (dev_type != ADSP_AUDIO_RX_DEVICE && dev_type != ADSP_AUDIO_TX_DEVICE) + return -EINVAL; + + memset(&rpc, 0, sizeof(rpc)); + if (dev_type == ADSP_AUDIO_RX_DEVICE) + { + rpc.old_device = audio_rx_device_id; + rpc.new_device = dev_id; + } + else + { + rpc.old_device = audio_tx_device_id; + rpc.new_device = dev_id; + } + rpc.device_class = 0; + rpc.device_type = dev_type; + return audio_ioctl(ac, ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_PREPARE, &rpc, sizeof(rpc)); +} + +static int qdsp6_standby(struct audio_client *ac) +{ + AUDIO_INFO("%s\n", __func__); + return audio_command(ac, ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_STANDBY); +} + +static int qdsp6_start(struct audio_client *ac) +{ + AUDIO_INFO("%s\n", __func__); + return audio_command(ac, ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_COMMIT); +} + +static void audio_rx_analog_enable(int en) +{ + AUDIO_INFO("%s\n", __func__); + switch (audio_rx_device_id) { + case ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_MONO: + case ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_STEREO: + case ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_SPKR: + if (analog_ops->headset_enable) + analog_ops->headset_enable(en); + break; + case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_MONO_HEADSET: + case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_STEREO_HEADSET: + case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_MONO_HEADSET: + case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_STEREO_HEADSET: + if (analog_ops->headset_enable) + analog_ops->headset_enable(en); + if (analog_ops->speaker_enable) + analog_ops->speaker_enable(en); + break; + case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO: + case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO: + if (analog_ops->speaker_enable) + analog_ops->speaker_enable(en); + break; + case ADSP_AUDIO_DEVICE_ID_BT_SCO_SPKR: + if (analog_ops->bt_sco_enable) + analog_ops->bt_sco_enable(en); + break; + case ADSP_AUDIO_DEVICE_ID_HANDSET_SPKR: + if (analog_ops->receiver_enable) + analog_ops->receiver_enable(en); + break; +/* + TODO: we need this? + case ADSP_AUDIO_DEVICE_ID_I2S_SPKR: + if (analog_ops->i2s_enable) + analog_ops->i2s_enable(en); + break; +*/ + } + +} + +static void audio_tx_analog_enable(int en) +{ + AUDIO_INFO("%s\n", __func__); + switch (audio_tx_device_id) { + case ADSP_AUDIO_DEVICE_ID_HANDSET_MIC: + case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MIC: + if (analog_ops->int_mic_enable) + analog_ops->int_mic_enable(en); + break; + case ADSP_AUDIO_DEVICE_ID_HEADSET_MIC: + case ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_MIC: + if (analog_ops->ext_mic_enable) + analog_ops->ext_mic_enable(en); + break; + case ADSP_AUDIO_DEVICE_ID_BT_SCO_MIC: + if (analog_ops->bt_sco_enable) + analog_ops->bt_sco_enable(en); + break; + } +} + +static int audio_update_acdb(uint32_t adev, uint32_t acdb_id) +{ + uint32_t sample_rate; + int sz = -1; + + printk("%s (%d, %d)\n", __func__, adev, acdb_id); + AUDIO_INFO("%s (%d, %d)\n", __func__, adev, acdb_id); +// dex_comm(PCOM_UPDATE_ACDB, 0, 0); + sample_rate = q6_device_to_rate(adev); + + if (q6_device_to_dir(adev) == Q6_RX) + rx_acdb = acdb_id; + else + tx_acdb = acdb_id; + + if (acdb_id != 0) + { + sz = acdb_get_config_table(acdb_id, sample_rate); + } + printk("res1 = %d\n", sz); + if (sz <= 0) + { + acdb_id = q6_device_to_cad_id(adev); + printk(" new acdb = %d\n", acdb_id); + sz = acdb_get_config_table(acdb_id, sample_rate); + } + printk("res2 = %d\n", sz); + if (sz > 0) + { + printk("call audio_set_table\n"); + audio_set_table(ac_control, adev, sz); + } + return 0; +} + +static void _audio_rx_path_enable(int reconf, uint32_t acdb_id) +{ + AUDIO_INFO("%s\n", __func__); + adie_enable(); + adie_set_path(adie, audio_rx_path_id, ADIE_PATH_RX); + adie_set_path_freq_plan(adie, ADIE_PATH_RX, 48000); + + adie_proceed_to_stage(adie, ADIE_PATH_RX, ADIE_STAGE_DIGITAL_READY); + adie_proceed_to_stage(adie, ADIE_PATH_RX, ADIE_STAGE_DIGITAL_ANALOG_READY); + +// audio_update_acdb(audio_rx_device_id, acdb_id); + if (!reconf) + qdsp6_devchg_notify(ac_control, ADSP_AUDIO_RX_DEVICE, audio_rx_device_id); + qdsp6_standby(ac_control); + qdsp6_start(ac_control); + + audio_update_acdb(audio_rx_device_id, acdb_id); + + audio_rx_analog_enable(1); +} + +static void _audio_tx_path_enable(int reconf, uint32_t acdb_id) +{ + AUDIO_INFO("%s\n", __func__); + audio_tx_analog_enable(1); + + adie_enable(); + adie_set_path(adie, audio_tx_path_id, ADIE_PATH_TX); + + if (tx_clk_freq > 8000) + adie_set_path_freq_plan(adie, ADIE_PATH_TX, 48000); + else + adie_set_path_freq_plan(adie, ADIE_PATH_TX, 8000); + + adie_proceed_to_stage(adie, ADIE_PATH_TX, ADIE_STAGE_DIGITAL_READY); + adie_proceed_to_stage(adie, ADIE_PATH_TX, ADIE_STAGE_DIGITAL_ANALOG_READY); + + audio_update_acdb(audio_tx_device_id, acdb_id); + + if (!reconf) + qdsp6_devchg_notify(ac_control, ADSP_AUDIO_TX_DEVICE, audio_tx_device_id); + qdsp6_standby(ac_control); + qdsp6_start(ac_control); + + audio_tx_mute(ac_control, audio_tx_device_id, tx_mute_status); +} + +static void _audio_rx_path_disable(void) +{ + AUDIO_INFO("%s\n", __func__); + audio_rx_analog_enable(0); + + adie_proceed_to_stage(adie, ADIE_PATH_RX, ADIE_STAGE_ANALOG_OFF); + adie_proceed_to_stage(adie, ADIE_PATH_RX, ADIE_STAGE_DIGITAL_OFF); + adie_disable(); +} + +static void _audio_tx_path_disable(void) +{ + AUDIO_INFO("%s\n", __func__); + audio_tx_analog_enable(0); + + adie_proceed_to_stage(adie, ADIE_PATH_TX, ADIE_STAGE_ANALOG_OFF); + adie_proceed_to_stage(adie, ADIE_PATH_TX, ADIE_STAGE_DIGITAL_OFF); + adie_disable(); +} + +static int icodec_rx_clk_refcount; +static int icodec_tx_clk_refcount; +static int ecodec_clk_refcount; +static int sdac_clk_refcount; + +static void _audio_rx_clk_enable(void) +{ + uint32_t device_group = q6_device_to_codec(audio_rx_device_id); + AUDIO_INFO("%s: device 0x%08x, group %d\n", + __func__, audio_rx_device_id, device_group); + switch(device_group) { + case Q6_ICODEC_RX: + icodec_rx_clk_refcount++; + AUDIO_INFO("%s: icodec_rx_clk_refcount = %d\n", + __func__, icodec_rx_clk_refcount); + if (icodec_rx_clk_refcount == 1) { + clk_set_rate(icodec_rx_clk, 12288000); + clk_enable(icodec_rx_clk); + AUDIO_INFO("%s: icodec_rx_clk enabled\n", __func__); + } + break; + case Q6_ECODEC_RX: + ecodec_clk_refcount++; + AUDIO_INFO("%s: ecodec_clk_refcount = %d\n", + __func__, ecodec_clk_refcount); + if (ecodec_clk_refcount == 1) { + clk_set_rate(ecodec_clk, 2048000); + clk_enable(ecodec_clk); + AUDIO_INFO("%s: ecodec_clk enabled\n", __func__); + } + break; + case Q6_SDAC_RX: + sdac_clk_refcount++; + AUDIO_INFO("%s: sdac_clk_refcount = %d\n", + __func__, sdac_clk_refcount); + if (sdac_clk_refcount == 1) { + clk_set_rate(sdac_clk, 12288000); + clk_enable(sdac_clk); + AUDIO_INFO("%s: sdac_clk enabled\n", __func__); + } + break; + default: + return; + } + audio_rx_device_group = device_group; +} + +static void _audio_tx_clk_enable(void) +{ + uint32_t device_group = q6_device_to_codec(audio_tx_device_id); + AUDIO_INFO("%s: device 0x%08x, group %d\n", + __func__, audio_tx_device_id, device_group); + switch (device_group) { + case Q6_ICODEC_TX: + icodec_tx_clk_refcount++; + AUDIO_INFO("%s: icodec_tx_clk_refcount = %d\n", + __func__, icodec_tx_clk_refcount); + if (icodec_tx_clk_refcount == 1) { + clk_set_rate(icodec_tx_clk, tx_clk_freq * 256); + clk_enable(icodec_tx_clk); + AUDIO_INFO("%s: icodec_tx_clk enabled\n", __func__); + } + break; + case Q6_ECODEC_TX: + ecodec_clk_refcount++; + AUDIO_INFO("%s: ecodec_clk_refcount = %d\n", + __func__, ecodec_clk_refcount); + if (ecodec_clk_refcount == 1) { + clk_set_rate(ecodec_clk, 2048000); + clk_enable(ecodec_clk); + AUDIO_INFO("%s: ecodec_clk enabled\n", __func__); + } + break; + case Q6_SDAC_TX: + /* TODO: In QCT BSP, clk rate was set to 20480000 */ + sdac_clk_refcount++; + AUDIO_INFO("%s: sdac_clk_refcount = %d\n", + __func__, sdac_clk_refcount); + if (sdac_clk_refcount == 1) { + clk_set_rate(sdac_clk, 12288000); + clk_enable(sdac_clk); + AUDIO_INFO("%s: sdac_clk enabled\n", __func__); + } + break; + default: + return; + } + audio_tx_device_group = device_group; +} + +static void _audio_rx_clk_disable(void) +{ + AUDIO_INFO("%s: group %d\n", __func__, audio_rx_device_group); + switch (audio_rx_device_group) { + case Q6_ICODEC_RX: + icodec_rx_clk_refcount--; + AUDIO_INFO("%s: icodec_rx_clk_refcount = %d\n", + __func__, icodec_rx_clk_refcount); + if (icodec_rx_clk_refcount == 0) { + clk_disable(icodec_rx_clk); + audio_rx_device_group = -1; + AUDIO_INFO("%s: icodec_rx_clk disabled\n", __func__); + } + break; + case Q6_ECODEC_RX: + ecodec_clk_refcount--; + AUDIO_INFO("%s: ecodec_clk_refcount = %d\n", + __func__, ecodec_clk_refcount); + if (ecodec_clk_refcount == 0) { + clk_disable(ecodec_clk); + audio_rx_device_group = -1; + AUDIO_INFO("%s: ecodec_clk disabled\n", __func__); + } + break; + case Q6_SDAC_RX: + sdac_clk_refcount--; + AUDIO_INFO("%s: sdac_clk_refcount = %d\n", + __func__, sdac_clk_refcount); + if (sdac_clk_refcount == 0) { + clk_disable(sdac_clk); + audio_rx_device_group = -1; + AUDIO_INFO("%s: sdac_clk disabled\n", __func__); + } + break; + default: + pr_err("audiolib: invalid rx device group %d\n", + audio_rx_device_group); + break; + } +} + +static void _audio_tx_clk_disable(void) +{ + AUDIO_INFO("%s: group %d\n", __func__, audio_tx_device_group); + switch (audio_tx_device_group) { + case Q6_ICODEC_TX: + icodec_tx_clk_refcount--; + AUDIO_INFO("%s: icodec_tx_clk_refcount = %d\n", + __func__, icodec_tx_clk_refcount); + if (icodec_tx_clk_refcount == 0) { + clk_disable(icodec_tx_clk); + audio_tx_device_group = -1; + AUDIO_INFO("%s: icodec_tx_clk disabled\n", __func__); + } + break; + case Q6_ECODEC_TX: + ecodec_clk_refcount--; + AUDIO_INFO("%s: ecodec_clk_refcount = %d\n", + __func__, ecodec_clk_refcount); + if (ecodec_clk_refcount == 0) { + clk_disable(ecodec_clk); + audio_tx_device_group = -1; + AUDIO_INFO("%s: ecodec_clk disabled\n", __func__); + } + break; + case Q6_SDAC_TX: + sdac_clk_refcount--; + AUDIO_INFO("%s: sdac_clk_refcount = %d\n", + __func__, sdac_clk_refcount); + if (sdac_clk_refcount == 0) { + clk_disable(sdac_clk); + audio_tx_device_group = -1; + AUDIO_INFO("%s: sdac_clk disabled\n", __func__); + } + break; + default: + pr_err("audiolib: invalid tx device group %d\n", + audio_tx_device_group); + break; + } +} + +static void _audio_rx_clk_reinit(uint32_t rx_device) +{ + uint32_t device_group = q6_device_to_codec(rx_device); + + AUDIO_INFO("%s\n", __func__); + + if (device_group != audio_rx_device_group) + _audio_rx_clk_disable(); + + audio_rx_device_id = rx_device; + audio_rx_path_id = q6_device_to_path(rx_device); + + if (device_group != audio_rx_device_group) + _audio_rx_clk_enable(); + +} + +static void _audio_tx_clk_reinit(uint32_t tx_device) +{ + uint32_t device_group = q6_device_to_codec(tx_device); + + AUDIO_INFO("%s\n", __func__); + if (device_group != audio_tx_device_group) + _audio_tx_clk_disable(); + + audio_tx_device_id = tx_device; + audio_tx_path_id = q6_device_to_path(tx_device); + + if (device_group != audio_tx_device_group) + _audio_tx_clk_enable(); +} + +static DEFINE_MUTEX(audio_path_lock); +static int audio_rx_path_refcount; +static int audio_tx_path_refcount; + +static int audio_rx_path_enable(int en, uint32_t acdb_id) +{ + AUDIO_INFO("%s\n", __func__); + mutex_lock(&audio_path_lock); + if (en) { + audio_rx_path_refcount++; + if (audio_rx_path_refcount == 1) { + _audio_rx_clk_enable(); + _audio_rx_path_enable(0, acdb_id); + } + } else { + audio_rx_path_refcount--; + if (audio_rx_path_refcount == 0) { + _audio_rx_path_disable(); + _audio_rx_clk_disable(); + } + } + mutex_unlock(&audio_path_lock); + return 0; +} + +static int audio_tx_path_enable(int en, uint32_t acdb_id) +{ + AUDIO_INFO("%s\n", __func__); + mutex_lock(&audio_path_lock); + if (en) { + audio_tx_path_refcount++; + if (audio_tx_path_refcount == 1) { + _audio_tx_clk_enable(); + _audio_tx_path_enable(0, acdb_id); + } + } else { + audio_tx_path_refcount--; + if (audio_tx_path_refcount == 0) { + _audio_tx_path_disable(); + _audio_tx_clk_disable(); + } + } + mutex_unlock(&audio_path_lock); + return 0; +} + +int q6audio_reinit_acdb(char* filename) +{ + int res; + + if (q6audio_init()) + return 0; + + mutex_lock(&audio_path_lock); + if (strlen(filename) < 0) { + res = -EINVAL; + goto done; + } + res = acdb_init(filename); + if (!res) + strcpy(acdb_file, filename); +done: + mutex_unlock(&audio_path_lock); + return res; + +} + +int q6audio_update_acdb(uint32_t id_src, uint32_t id_dst) +{ + int res; + + if (q6audio_init()) + return 0; + + mutex_lock(&audio_path_lock); + res = audio_update_acdb(id_dst, id_src); + if (res) + goto done; + + if (q6_device_to_dir(id_dst) == Q6_RX) + qdsp6_devchg_notify(ac_control, ADSP_AUDIO_RX_DEVICE, id_dst); + else + qdsp6_devchg_notify(ac_control, ADSP_AUDIO_TX_DEVICE, id_dst); + qdsp6_standby(ac_control); + qdsp6_start(ac_control); +done: + mutex_unlock(&audio_path_lock); + return res; +} + +int q6audio_set_tx_mute(int mute) +{ + uint32_t adev; + int rc; + + AUDIO_INFO("%s\n", __func__); + if (q6audio_init()) + return 0; + + mutex_lock(&audio_path_lock); + + if (mute == tx_mute_status) { + mutex_unlock(&audio_path_lock); + return 0; + } + + adev = audio_tx_device_id; + rc = audio_tx_mute(ac_control, adev, mute); + tx_mute_status = mute; + mutex_unlock(&audio_path_lock); + return 0; +} + +int q6audio_set_stream_volume(struct audio_client *ac, int vol) +{ + AUDIO_INFO("%s\n", __func__); + if (vol > 1200 || vol < -4000) { + pr_err("unsupported volume level %d\n", vol); + return -EINVAL; + } + mutex_lock(&audio_path_lock); + audio_stream_mute(ac, 0); + audio_stream_volume(ac, vol); + mutex_unlock(&audio_path_lock); + return 0; +} + +int q6audio_set_stream_eq(struct audio_client *ac, struct cad_audio_eq_cfg *eq_cfg) +{ + AUDIO_INFO("%s\n", __func__); + mutex_lock(&audio_path_lock); + audio_stream_eq(ac, eq_cfg); + mutex_unlock(&audio_path_lock); + return 0; +} + + +int q6audio_set_rx_dev_volume(int level) +{ + int vol; + + AUDIO_INFO("%s\n", __func__); + + mutex_lock(&audio_path_lock); + + vol = q6_device_volume(audio_rx_device_id, level); + printk("$$ DEV=%08X: vol is %d\n", audio_rx_device_id, vol); + audio_rx_volume(ac_control, audio_rx_device_id, vol); + + mutex_unlock(&audio_path_lock); + return 0; +} + +int q6audio_set_rx_volume(int level) +{ +#if 0 + uint32_t adev; + int vol; + + AUDIO_INFO("%s\n", __func__); + if (q6audio_init()) + return 0; + + if (level < 0 || level > 100) + return -EINVAL; + + mutex_lock(&audio_path_lock); + adev = ADSP_AUDIO_DEVICE_ID_VOICE; + vol = q6_device_volume(audio_rx_device_id, level); + audio_rx_mute(ac_control, adev, 0); + printk("@@@@ rx volume: adev=%d, rx_dev_id=%d, level=%d @@@@\n", adev, audio_rx_device_id, vol); + audio_rx_volume(ac_control, adev, vol); + rx_vol_level = level; + mutex_unlock(&audio_path_lock); +#else + q6audio_set_rx_dev_volume(level); +#endif + return 0; +} + +static int q6audio_init_rx_volumes() +{ + int vol; + struct q6_device_info *di = q6_audio_devices; + + AUDIO_INFO("%s\n", __func__); + + mutex_lock(&audio_path_lock); + + printk("$$$ q6audio_init_rx_volumes\n"); + while (1) + { + if (di->id == 0) break; + + vol = q6_device_volume(di->id, 100); + audio_rx_volume(ac_control, di->id, vol); + printk("$$ DEV=%08X: vol is %d\n", di->id, vol); + + di++; + } + + mutex_unlock(&audio_path_lock); + return 0; +} + + +int q6audio_set_rx_mute(int mute) +{ + uint32_t adev; + + AUDIO_INFO("%s\n", __func__); + if (q6audio_init()) + return 0; + + if (mute < 0 || mute > 1) + return -EINVAL; + + mutex_lock(&audio_path_lock); + AUDIO_INFO("%s: set mute status %d\n", __func__, mute); + adev = ADSP_AUDIO_DEVICE_ID_VOICE; + audio_rx_mute(ac_control, adev, mute); + mutex_unlock(&audio_path_lock); + return 0; +} + +static void do_rx_routing(uint32_t device_id, uint32_t acdb_id) +{ + AUDIO_INFO("%s\n", __func__); + if (device_id == audio_rx_device_id) { + if (acdb_id != rx_acdb) { + audio_update_acdb(device_id, acdb_id); + qdsp6_devchg_notify(ac_control, ADSP_AUDIO_RX_DEVICE, device_id); + qdsp6_standby(ac_control); + qdsp6_start(ac_control); + } + return; + } + + if (audio_rx_path_refcount > 0) { + qdsp6_devchg_notify(ac_control, ADSP_AUDIO_RX_DEVICE, device_id); + _audio_rx_path_disable(); + _audio_rx_clk_reinit(device_id); + _audio_rx_path_enable(1, acdb_id); + } else { + audio_rx_device_id = device_id; + audio_rx_path_id = q6_device_to_path(device_id); + } +} + +static void do_tx_routing(uint32_t device_id, uint32_t acdb_id) +{ + AUDIO_INFO("%s\n", __func__); + if (device_id == audio_tx_device_id) { + if (acdb_id != tx_acdb) { + audio_update_acdb(device_id, acdb_id); + qdsp6_devchg_notify(ac_control, ADSP_AUDIO_TX_DEVICE, device_id); + qdsp6_standby(ac_control); + qdsp6_start(ac_control); + } + return; + } + + if (audio_tx_path_refcount > 0) { + qdsp6_devchg_notify(ac_control, ADSP_AUDIO_TX_DEVICE, device_id); + _audio_tx_path_disable(); + _audio_tx_clk_reinit(device_id); + _audio_tx_path_enable(1, acdb_id); + } else { + audio_tx_device_id = device_id; + audio_tx_path_id = q6_device_to_path(device_id); + } +} + +int q6audio_do_routing(uint32_t device_id, uint32_t acdb_id) +{ + AUDIO_INFO("%s\n", __func__); + if (q6audio_init()) + return 0; + + mutex_lock(&audio_path_lock); + + switch(q6_device_to_dir(device_id)) { + case Q6_RX: + do_rx_routing(device_id, acdb_id); + break; + case Q6_TX: + do_tx_routing(device_id, acdb_id); + break; + } + + mutex_unlock(&audio_path_lock); + return 0; +} + +int q6audio_set_route(const char *name) +{ + uint32_t route; + AUDIO_INFO("%s\n", __func__); + if (!strcmp(name, "speaker")) { + route = ADIE_PATH_SPEAKER_STEREO_RX; + } else if (!strcmp(name, "headphones")) { + route = ADIE_PATH_HEADSET_STEREO_RX; + } else if (!strcmp(name, "handset")) { + route = ADIE_PATH_HANDSET_RX; + } else { + return -EINVAL; + } + + mutex_lock(&audio_path_lock); + if (route == audio_rx_path_id) + goto done; + + audio_rx_path_id = route; + + if (audio_rx_path_refcount > 0) + { + _audio_rx_path_disable(); + _audio_rx_path_enable(1, 0); + } + if (audio_tx_path_refcount > 0) + { + _audio_tx_path_disable(); + _audio_tx_path_enable(1, 0); + } +done: + mutex_unlock(&audio_path_lock); + return 0; +} + +struct audio_client *q6audio_open_pcm(uint32_t bufsz, uint32_t rate, + uint32_t channels, uint32_t flags, uint32_t acdb_id) +{ +#if 1 + int rc, retry = 5; + struct audio_client *ac; + + AUDIO_INFO("%s\n", __func__); + if (q6audio_init()) + return 0; + +// printk("afer init();\n"); + ac = audio_client_alloc(bufsz); + if (!ac) + { + printk("audio_client_alloc failed\n"); + return 0; + } +// printk("after alloc);\n"); + ac->flags = flags; + + mutex_lock(&audio_path_lock); + + if (ac->flags & AUDIO_FLAG_WRITE) + { + audio_rx_path_refcount++; + if (audio_rx_path_refcount == 1) + { + _audio_rx_clk_enable(); +// audio_update_acdb(audio_rx_device_id, acdb_id); + qdsp6_devchg_notify(ac_control, ADSP_AUDIO_RX_DEVICE, audio_rx_device_id); + qdsp6_standby(ac_control); + qdsp6_start(ac_control); + audio_update_acdb(audio_rx_device_id, acdb_id); + } + } + else + { + /* TODO: consider concurrency with voice call */ + tx_clk_freq = rate; + audio_tx_path_refcount++; + if (audio_tx_path_refcount == 1) + { + _audio_tx_clk_enable(); + _audio_tx_path_enable(0, acdb_id); + } + } +// printk("about to open\n"); + + for (retry = 5;;retry--) + { + if (ac->flags & AUDIO_FLAG_WRITE) + rc = audio_out_open(ac, bufsz, rate, channels); + else + rc = audio_in_open(ac, bufsz, rate, channels); + if (rc == 0) + break; + if (retry == 0) + { + // BUG(); + break; + } + pr_err("q6audio: open pcm error %d, retrying\n", rc); + msleep(1); + } + + if (retry == 0) + { + if (ac->flags & AUDIO_FLAG_WRITE) + audio_rx_path_enable(0, 0); + else + audio_tx_path_enable(0, 0); + audio_client_free(ac); + pr_err("q6audio: open pcm error\n"); + return NULL; + } + +// printk("after open\n"); + + if (ac->flags & AUDIO_FLAG_WRITE) + { + if (audio_rx_path_refcount == 1) + { + adie_enable(); + adie_set_path(adie, audio_rx_path_id, ADIE_PATH_RX); + adie_set_path_freq_plan(adie, ADIE_PATH_RX, 48000); + + adie_proceed_to_stage(adie, ADIE_PATH_RX, ADIE_STAGE_DIGITAL_READY); + adie_proceed_to_stage(adie, ADIE_PATH_RX, ADIE_STAGE_DIGITAL_ANALOG_READY); + + audio_rx_analog_enable(1); + } + } +// printk("about to start session\n"); + + mutex_unlock(&audio_path_lock); + + for (retry = 5;;retry--) + { + rc = audio_command(ac, ADSP_AUDIO_IOCTL_CMD_SESSION_START); + if (rc == 0) + break; + if (retry == 0) + { +// BUG(); + break; + } + pr_err("q6audio: stream start error %d, retrying\n", rc); + } + + if (retry == 0) + { + audio_close(ac); + if (ac->flags & AUDIO_FLAG_WRITE) + audio_rx_path_enable(0, 0); + else + audio_tx_path_enable(0, 0); + audio_client_free(ac); + pr_err("q6audio: open pcm error2\n"); + return NULL; + } + + if (!(ac->flags & AUDIO_FLAG_WRITE)) + { + ac->buf[0].used = 1; + ac->buf[1].used = 1; + q6audio_read(ac, &ac->buf[0]); + q6audio_read(ac, &ac->buf[1]); + } + + audio_prevent_sleep(); + return ac; +#else + return audio_test(); +#endif +} + +int q6audio_close(struct audio_client *ac) +{ + AUDIO_INFO("%s\n", __func__); + audio_close(ac); + if (ac->flags & AUDIO_FLAG_WRITE) + audio_rx_path_enable(0, 0); + else + audio_tx_path_enable(0, 0); + + audio_client_free(ac); + audio_allow_sleep(); + return 0; +} + +struct audio_client *q6voice_open(uint32_t flags, uint32_t acdb_id) +{ + struct audio_client *ac; + + AUDIO_INFO("%s\n", __func__); + if (q6audio_init()) + return 0; + + ac = audio_client_alloc(0); + if (!ac) + return 0; + + ac->flags = flags; + if (ac->flags & AUDIO_FLAG_WRITE) { + audio_rx_path_enable(1, acdb_id); + audio_rx_mute(ac_control, ADSP_AUDIO_DEVICE_ID_VOICE, 0); + } else { + tx_clk_freq = 8000; + audio_tx_path_enable(1, acdb_id); + } + + return ac; +} + +int q6voice_close(struct audio_client *ac) +{ + AUDIO_INFO("%s\n", __func__); + if (ac->flags & AUDIO_FLAG_WRITE) { + audio_rx_mute(ac_control, ADSP_AUDIO_DEVICE_ID_VOICE, 1); + audio_rx_path_enable(0, 0); + } else + audio_tx_path_enable(0, 0); + + audio_client_free(ac); + return 0; +} + +struct audio_client *q6audio_open_mp3(uint32_t bufsz, uint32_t rate, + uint32_t channels, uint32_t acdb_id) +{ + struct audio_client *ac; + + printk("q6audio_open_mp3()\n"); + + if (q6audio_init()) + return 0; + + ac = audio_client_alloc(bufsz); + if (!ac) + return 0; + + ac->flags = AUDIO_FLAG_WRITE; + audio_rx_path_enable(1, acdb_id); + + audio_mp3_open(ac, bufsz, rate, channels); + audio_command(ac, ADSP_AUDIO_IOCTL_CMD_SESSION_START); + + return ac; +} + +int q6audio_mp3_close(struct audio_client *ac) +{ + AUDIO_INFO("%s\n", __func__); + audio_close(ac); + audio_rx_path_enable(0, 0); + audio_client_free(ac); + return 0; +} + +int q6audio_async(struct audio_client *ac) +{ + struct adsp_command_hdr rpc; + memset(&rpc, 0, sizeof(rpc)); +// rpc.response_type = ADSP_AUDIO_RESPONSE_ASYNC; + return audio_ioctl(ac, ADSP_AUDIO_IOCTL_CMD_STREAM_EOS, &rpc, sizeof(rpc)); +} + + + +struct audio_client *q6audio_open_aac(uint32_t bufsz, uint32_t rate, + uint32_t flags, void *data, uint32_t acdb_id) +{ + struct audio_client *ac; + + TRACE("q6audio_open_aac flags=%d rate=%d\n", flags, rate); + + if (q6audio_init()) + return 0; + + ac = audio_client_alloc(bufsz); + if (!ac) + return 0; + + ac->flags = flags; + if (ac->flags & AUDIO_FLAG_WRITE) + audio_rx_path_enable(1, acdb_id); + else { + /* TODO: consider concourrency with voice call */ + tx_clk_freq = rate; + audio_tx_path_enable(1, acdb_id); + } + + audio_aac_open(ac, bufsz, data); + audio_command(ac, ADSP_AUDIO_IOCTL_CMD_SESSION_START); + + if (!(ac->flags & AUDIO_FLAG_WRITE)) { + ac->buf[0].used = 1; + ac->buf[1].used = 1; + q6audio_read(ac, &ac->buf[0]); + q6audio_read(ac, &ac->buf[1]); + } + audio_prevent_sleep(); + return ac; +} + +int q6audio_aac_close(struct audio_client *ac) +{ + audio_close(ac); + if (ac->flags & AUDIO_FLAG_WRITE) + audio_rx_path_enable(0, 0); + else + audio_tx_path_enable(0, 0); + + audio_client_free(ac); + audio_allow_sleep(); + return 0; +} + +struct audio_client *q6fm_open(void) +{ + struct audio_client *ac; + + printk("q6fm_open()\n"); + + if (q6audio_init()) + return 0; + + if (audio_rx_device_id != ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_STEREO && + audio_rx_device_id != ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO) + return 0; + + ac = audio_client_alloc(0); + if (!ac) + return 0; + + ac->flags = AUDIO_FLAG_WRITE; + audio_rx_path_enable(1, 0); + enable_aux_loopback(1); + + return ac; +} + +int q6fm_close(struct audio_client *ac) +{ + printk("q6fm_close\n"); + + audio_rx_path_enable(0, 0); + enable_aux_loopback(0); + audio_client_free(ac); + return 0; +} + +struct audio_client *q6audio_open_qcelp(uint32_t bufsz, uint32_t rate, + void *data, uint32_t acdb_id) +{ + struct audio_client *ac; + + AUDIO_INFO("%s\n", __func__); + if (q6audio_init()) + return 0; + + ac = audio_client_alloc(bufsz); + if (!ac) + { + return 0; + } + + ac->flags = AUDIO_FLAG_READ; + tx_clk_freq = rate; + audio_tx_path_enable(1, acdb_id); + + audio_qcelp_open(ac, bufsz, data); + audio_command(ac, ADSP_AUDIO_IOCTL_CMD_SESSION_START); + + ac->buf[0].used = 1; + ac->buf[1].used = 1; + q6audio_read(ac, &ac->buf[0]); + q6audio_read(ac, &ac->buf[1]); + + audio_prevent_sleep(); + return ac; +} + +int q6audio_qcelp_close(struct audio_client *ac) +{ + audio_close(ac); + audio_tx_path_enable(0, 0); + audio_client_free(ac); + audio_allow_sleep(); + return 0; +} + +/////////////////////////////////////////////////////////////////////////// + +int acdb_get_table(int dev_id, int sample_rate) +{ + struct acdb_cmd_device_table rpc; + struct acdb_result res; + int r; + + memset(audio_data, 0, 4096); + memset(&rpc, 0, sizeof(rpc)); + + rpc.size = sizeof(rpc) - (2 * sizeof(uint32_t)); + rpc.command_id = ACDB_GET_DEVICE_TABLE; + rpc.device_id = dev_id; + rpc.sample_rate_id = sample_rate; + rpc.total_bytes = 4096; + rpc.unmapped_buf = audio_phys; + rpc.res_size = sizeof(res) - (2 * sizeof(uint32_t)); + + r = dal_call(acdb, ACDB_OP_IOCTL, 8, &rpc, sizeof(rpc), &res, sizeof(res)); + + if ((r == sizeof(res)) && (res.dal_status == 0)) + { + pr_info("acdb: %d bytes for device %d, rate %d.\n", + res.used_bytes, dev_id, sample_rate); + return res.used_bytes; + } + return 0; +} + +static struct audio_client * audio_test(void) +{ + struct audio_client *ac = 0; + struct audio_client *ac2 = 0; + int size; + struct rpc_info info; + + pr_info("audio: init: codecs\n"); + icodec_rx_clk = clk_get(0, "icodec_rx_clk"); + icodec_tx_clk = clk_get(0, "icodec_tx_clk"); + ecodec_clk = clk_get(0, "ecodec_clk"); + sdac_clk = clk_get(0, "sdac_clk"); + audio_data = dma_alloc_coherent(NULL, 4096, &audio_phys, GFP_KERNEL); + params_data = dma_alloc_coherent(NULL, 4096, ¶ms_phys, GFP_KERNEL); + printk("allocated: %p %08X\n", params_data, params_phys); + + clk_set_rate(icodec_rx_clk, 12288000); + clk_enable(icodec_rx_clk); + clk_set_rate(ecodec_clk, 2048000); + clk_enable(ecodec_clk); + clk_set_rate(sdac_clk, 12288000); + clk_enable(sdac_clk); + + // 1. attach ADIE + adie = dal_attach_ex(ADIE_DAL_DEVICE, "NULL", ADIE_DAL_PORT , 0, 0); + if (!adie) + { + pr_err("audio_init: cannot attach to adie\n"); + return 0; + } + + if (analog_ops->init) + analog_ops->init(); + + audio_rx_device_id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO; + audio_rx_analog_enable(1); + + dal_call_f9(adie, DAL_OP_INFO, &info, sizeof(info)); + + + acdb = dal_attach(ACDB_DAL_DEVICE, ACDB_DAL_PORT, 0, 0); + if (!acdb) + { + pr_err("audio_init: cannot attach to acdb channel\n"); + return 0; + } + + dal_call_f9(acdb, DAL_OP_INFO, &info, sizeof(info)); + + audio_client_alloc(0); + + // audio_client_alloc(0); + + ac = audio_client_alloc(0); + + audio_open_control(ac); +//mdelay(1000); + + audio_rx_mute(ac, ADSP_AUDIO_DEVICE_ID_HANDSET_SPKR, 0); + audio_rx_volume(ac, ADSP_AUDIO_DEVICE_ID_HANDSET_SPKR, 0); + + acdb_get_table(7, 48000); + + audio_rx_device_id = ADSP_AUDIO_DEVICE_ID_HANDSET_SPKR; + qdsp6_devchg_notify(ac, ADSP_AUDIO_RX_DEVICE, ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO); + + qdsp6_standby(ac); + qdsp6_start(ac); + + size = acdb_get_table(0x25F, 48000); + audio_set_table(ac, ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO, size); + + audio_rx_device_id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO; + qdsp6_devchg_notify(ac, ADSP_AUDIO_RX_DEVICE, ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO); + + qdsp6_standby(ac); + qdsp6_start(ac); + + audio_rx_mute(ac, ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO, 0); + audio_rx_volume(ac, ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO, 500); + + + size = acdb_get_table(507, 8000); + audio_set_table(ac, ADSP_AUDIO_DEVICE_ID_HANDSET_MIC, size); + + audio_rx_device_id = ADSP_AUDIO_DEVICE_ID_HANDSET_MIC; + qdsp6_devchg_notify(ac, ADSP_AUDIO_TX_DEVICE, ADSP_AUDIO_DEVICE_ID_HANDSET_MIC); + + qdsp6_standby(ac); + qdsp6_start(ac); + + size = acdb_get_table(0x25F, 48000); + audio_set_table(ac, ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO, size); + + ac2 = audio_client_alloc(4096); + + audio_out_open(ac2, 4096, 44100, 2); + + adie_open(adie); + + adie_set_path(adie, ADIE_PATH_SPEAKER_RX, ADIE_PATH_RX); + adie_set_path_freq_plan(adie, ADIE_PATH_RX, 48000); + adie_proceed_to_stage(adie, ADIE_PATH_RX, ADIE_STAGE_DIGITAL_READY); + adie_proceed_to_stage(adie, ADIE_PATH_RX, ADIE_STAGE_DIGITAL_ANALOG_READY); + + audio_command(ac2, ADSP_AUDIO_IOCTL_CMD_SESSION_START); + + audio_rx_mute(ac, ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO, 0); + audio_rx_volume(ac, ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO, 500); + + return ac2; +} + + +// END OF FILE diff --git a/arch/arm/mach-msm/qdsp6/qcelp_in.c b/arch/arm/mach-msm/qdsp6/qcelp_in.c new file mode 100644 index 00000000..e5843181 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/qcelp_in.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); diff --git a/include/linux/msm_q6venc.h b/include/linux/msm_q6venc.h index 097e2db8..2ca4832d 100644 --- a/include/linux/msm_q6venc.h +++ b/include/linux/msm_q6venc.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. +/* 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: @@ -7,325 +7,119 @@ * * 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 + * * 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, 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. + * 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 -#define VENC_MAX_RECON_BUFFERS 2 - -#define VENC_FLAG_EOS 0x00000001 -#define VENC_FLAG_END_OF_FRAME 0x00000010 -#define VENC_FLAG_SYNC_FRAME 0x00000020 -#define VENC_FLAG_EXTRA_DATA 0x00000040 -#define VENC_FLAG_CODEC_CONFIG 0x00000080 - -enum venc_flush_type { - VENC_FLUSH_INPUT, - VENC_FLUSH_OUTPUT, - VENC_FLUSH_ALL -}; - -enum venc_state_type { - VENC_STATE_PAUSE = 0x1, - VENC_STATE_START = 0x2, - VENC_STATE_STOP = 0x4 -}; - -enum venc_event_type_enum { - VENC_EVENT_START_STATUS, - VENC_EVENT_STOP_STATUS, - VENC_EVENT_SUSPEND_STATUS, - VENC_EVENT_RESUME_STATUS, - VENC_EVENT_FLUSH_STATUS, - VENC_EVENT_RELEASE_INPUT, - VENC_EVENT_DELIVER_OUTPUT, - VENC_EVENT_UNKNOWN_STATUS -}; - -enum venc_status_code { - VENC_STATUS_SUCCESS, - VENC_STATUS_ERROR, - VENC_STATUS_INVALID_STATE, - VENC_STATUS_FLUSHING, - VENC_STATUS_INVALID_PARAM, - VENC_STATUS_CMD_QUEUE_FULL, - VENC_STATUS_CRITICAL, - VENC_STATUS_INSUFFICIENT_RESOURCES, - VENC_STATUS_TIMEOUT -}; - -enum venc_msg_code { - VENC_MSG_INDICATION, - VENC_MSG_INPUT_BUFFER_DONE, - VENC_MSG_OUTPUT_BUFFER_DONE, - VENC_MSG_NEED_OUTPUT_BUFFER, - VENC_MSG_FLUSH, - VENC_MSG_START, - VENC_MSG_STOP, - VENC_MSG_PAUSE, - VENC_MSG_RESUME, - VENC_MSG_STOP_READING_MSG -}; - -enum venc_error_code { - VENC_S_SUCCESS, - VENC_S_EFAIL, - VENC_S_EFATAL, - VENC_S_EBADPARAM, - VENC_S_EINVALSTATE, - VENC_S_ENOSWRES, - VENC_S_ENOHWRES, - VENC_S_EBUFFREQ, - VENC_S_EINVALCMD, - VENC_S_ETIMEOUT, - VENC_S_ENOREATMPT, - VENC_S_ENOPREREQ, - VENC_S_ECMDQFULL, - VENC_S_ENOTSUPP, - VENC_S_ENOTIMPL, - VENC_S_ENOTPMEM, - VENC_S_EFLUSHED, - VENC_S_EINSUFBUF, - VENC_S_ESAMESTATE, - VENC_S_EINVALTRANS -}; - -enum venc_mem_region_enum { - VENC_PMEM_EBI1, - VENC_PMEM_SMI -}; - -struct venc_buf_type { - unsigned int region; - unsigned int phys; - unsigned int size; - int offset; -}; - -struct venc_qp_range { - unsigned int min_qp; - unsigned int max_qp; -}; - -struct venc_frame_rate { - unsigned int frame_rate_num; - unsigned int frame_rate_den; -}; - -struct venc_slice_info { - unsigned int slice_mode; - unsigned int units_per_slice; -}; - -struct venc_extra_data { - unsigned int slice_extra_data_flag; - unsigned int slice_client_data1; - unsigned int slice_client_data2; - unsigned int slice_client_data3; - unsigned int none_extra_data_flag; - unsigned int none_client_data1; - unsigned int none_client_data2; - unsigned int none_client_data3; -}; - -struct venc_common_config { - unsigned int standard; - unsigned int input_frame_height; - unsigned int input_frame_width; - unsigned int output_frame_height; - unsigned int output_frame_width; - unsigned int rotation_angle; - unsigned int intra_period; - unsigned int rate_control; - struct venc_frame_rate frame_rate; - unsigned int bitrate; - struct venc_qp_range qp_range; - unsigned int iframe_qp; - unsigned int pframe_qp; - struct venc_slice_info slice_config; - struct venc_extra_data extra_data; -}; - -struct venc_nonio_buf_config { - struct venc_buf_type recon_buf1; - struct venc_buf_type recon_buf2; - struct venc_buf_type wb_buf; - struct venc_buf_type cmd_buf; - struct venc_buf_type vlc_buf; -}; - -struct venc_mpeg4_config { - unsigned int profile; - unsigned int level; - unsigned int time_resolution; - unsigned int ac_prediction; - unsigned int hec_interval; - unsigned int data_partition; - unsigned int short_header; - unsigned int rvlc_enable; -}; - -struct venc_h263_config { - unsigned int profile; - unsigned int level; -}; - -struct venc_h264_config { - unsigned int profile; - unsigned int level; - unsigned int max_nal; - unsigned int idr_period; -}; - -struct venc_pmem { - int src; +struct venc_buf { + unsigned int src_id; int fd; - unsigned int offset; - void *virt; - void *phys; - unsigned int size; + unsigned long offset; + unsigned long size; }; -struct venc_buffer { - unsigned char *ptr_buffer; - unsigned int size; - unsigned int len; - unsigned int offset; - long long time_stamp; - unsigned int flags; - unsigned int client_data; +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 venc_buffers { - struct venc_pmem recon_buf[VENC_MAX_RECON_BUFFERS]; - struct venc_pmem wb_buf; - struct venc_pmem cmd_buf; - struct venc_pmem vlc_buf; +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 venc_buffer_flush { - unsigned int flush_mode; +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; }; -union venc_msg_data { - struct venc_buffer buf; - struct venc_buffer_flush flush_ret; - +struct encode_param { + struct venc_buf y_addr; + unsigned long uv_offset; + struct q6_encode_param q6_encode_param; }; -struct venc_msg { - unsigned int status_code; - unsigned int msg_code; - union venc_msg_data msg_data; - unsigned int msg_data_size; +struct intra_refresh { + unsigned int intra_refresh_enable; + unsigned int intra_mb_num; }; -union venc_codec_config { - struct venc_mpeg4_config mpeg4_params; - struct venc_h263_config h263_params; - struct venc_h264_config h264_params; +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 venc_q6_config { - struct venc_common_config config_params; - union venc_codec_config codec_params; - struct venc_nonio_buf_config buf_params; - void *callback_event; +struct q6_frame_type { + unsigned int frame_type; + unsigned int frame_len; + unsigned int frame_addr; + unsigned int map_table; }; -struct venc_hdr_config { - struct venc_common_config config_params; - union venc_codec_config codec_params; -}; - -struct venc_init_config { - struct venc_q6_config q6_config; - struct venc_buffers q6_bufs; -}; - -struct venc_seq_config { - int size; - struct venc_pmem buf; - struct venc_q6_config q6_config; -}; - -struct venc_version { - u32 major; - u32 minor; +struct frame_type { + struct venc_buf frame_addr; + struct q6_frame_type q6_frame_type; }; #define VENC_IOCTL_MAGIC 'V' -#define VENC_IOCTL_CMD_READ_NEXT_MSG \ - _IOWR(VENC_IOCTL_MAGIC, 1, struct venc_msg) +#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) -#define VENC_IOCTL_CMD_STOP_READ_MSG _IO(VENC_IOCTL_MAGIC, 2) - -#define VENC_IOCTL_SET_INPUT_BUFFER \ - _IOW(VENC_IOCTL_MAGIC, 3, struct venc_pmem) - -#define VENC_IOCTL_SET_OUTPUT_BUFFER \ - _IOW(VENC_IOCTL_MAGIC, 4, struct venc_pmem) - -#define VENC_IOCTL_CMD_START _IOW(VENC_IOCTL_MAGIC, 5, struct venc_init_config) - -#define VENC_IOCTL_CMD_ENCODE_FRAME \ - _IOW(VENC_IOCTL_MAGIC, 6, struct venc_buffer) - -#define VENC_IOCTL_CMD_FILL_OUTPUT_BUFFER \ - _IOW(VENC_IOCTL_MAGIC, 7, struct venc_buffer) - -#define VENC_IOCTL_CMD_FLUSH \ - _IOW(VENC_IOCTL_MAGIC, 8, struct venc_buffer_flush) - -#define VENC_IOCTL_CMD_PAUSE _IO(VENC_IOCTL_MAGIC, 9) - -#define VENC_IOCTL_CMD_RESUME _IO(VENC_IOCTL_MAGIC, 10) - -#define VENC_IOCTL_CMD_STOP _IO(VENC_IOCTL_MAGIC, 11) - -#define VENC_IOCTL_SET_INTRA_PERIOD \ - _IOW(VENC_IOCTL_MAGIC, 12, int) - -#define VENC_IOCTL_CMD_REQUEST_IFRAME _IO(VENC_IOCTL_MAGIC, 13) - -#define VENC_IOCTL_GET_SEQUENCE_HDR \ - _IOWR(VENC_IOCTL_MAGIC, 14, struct venc_seq_config) - -#define VENC_IOCTL_SET_INTRA_REFRESH \ - _IOW(VENC_IOCTL_MAGIC, 15, int) - -#define VENC_IOCTL_SET_FRAME_RATE \ - _IOW(VENC_IOCTL_MAGIC, 16, struct venc_frame_rate) - -#define VENC_IOCTL_SET_TARGET_BITRATE \ - _IOW(VENC_IOCTL_MAGIC, 17, int) - -#define VENC_IOCTL_SET_QP_RANGE \ - _IOW(VENC_IOCTL_MAGIC, 18, struct venc_qp_range) - -#define VENC_IOCTL_GET_VERSION \ - _IOR(VENC_IOCTL_MAGIC, 19, struct venc_version) - -#endif +#endif /* _MSM_VENC_H_ */