| /* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. |
| * |
| * 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. |
| */ |
| |
| #include <linux/init.h> |
| #include <linux/err.h> |
| #include <linux/module.h> |
| #include <linux/moduleparam.h> |
| #include <linux/mutex.h> |
| #include <linux/slab.h> |
| #include <linux/msm_ion.h> |
| #include <linux/mm.h> |
| #include <linux/msm_audio_ion.h> |
| #include <linux/vmalloc.h> |
| #include <sound/core.h> |
| #include <sound/soc.h> |
| #include <sound/pcm.h> |
| #include <sound/q6adm-v2.h> |
| #include <sound/q6asm-v2.h> |
| #include <sound/apr_audio-v2.h> |
| #include <sound/q6audio-v2.h> |
| #include <sound/audio_effects.h> |
| #include <sound/hwdep.h> |
| #include <sound/msm-dts-eagle.h> |
| #include <sound/q6core.h> |
| |
| #include "msm-pcm-routing-v2.h" |
| |
| #define ION_MEM_SIZE 131072 |
| #define DEPC_MAX_SIZE 524288 |
| |
| #define MPST AUDPROC_MODULE_ID_DTS_HPX_POSTMIX |
| #define MPRE AUDPROC_MODULE_ID_DTS_HPX_PREMIX |
| |
| #define eagle_vol_dbg(fmt, ...) \ |
| pr_debug("DTS_EAGLE_DRIVER_VOLUME: " fmt "\n", ##__VA_ARGS__) |
| #define eagle_vol_err(fmt, ...) \ |
| pr_err("DTS_EAGLE_DRIVER_VOLUME: " fmt "\n", ##__VA_ARGS__) |
| #define eagle_drv_dbg(fmt, ...) \ |
| pr_debug("DTS_EAGLE_DRIVER: " fmt "\n", ##__VA_ARGS__) |
| #define eagle_drv_err(fmt, ...) \ |
| pr_err("DTS_EAGLE_DRIVER: " fmt "\n", ##__VA_ARGS__) |
| #define eagle_precache_dbg(fmt, ...) \ |
| pr_debug("DTS_EAGLE_DRIVER_SENDCACHE_PRE: " fmt "\n", ##__VA_ARGS__) |
| #define eagle_precache_err(fmt, ...) \ |
| pr_err("DTS_EAGLE_DRIVER_SENDCACHE_PRE: " fmt "\n", ##__VA_ARGS__) |
| #define eagle_postcache_dbg(fmt, ...) \ |
| pr_debug("DTS_EAGLE_DRIVER_SENDCACHE_POST: " fmt "\n", ##__VA_ARGS__) |
| #define eagle_postcache_err(fmt, ...) \ |
| pr_err("DTS_EAGLE_DRIVER_SENDCACHE_POST: " fmt "\n", ##__VA_ARGS__) |
| #define eagle_ioctl_dbg(fmt, ...) \ |
| pr_debug("DTS_EAGLE_DRIVER_IOCTL: " fmt "\n", ##__VA_ARGS__) |
| #define eagle_ioctl_err(fmt, ...) \ |
| pr_err("DTS_EAGLE_DRIVER_IOCTL: " fmt "\n", ##__VA_ARGS__) |
| #define eagle_asm_dbg(fmt, ...) \ |
| pr_debug("DTS_EAGLE_DRIVER_ASM: " fmt "\n", ##__VA_ARGS__) |
| #define eagle_asm_err(fmt, ...) \ |
| pr_err("DTS_EAGLE_DRIVER_ASM: " fmt "\n", ##__VA_ARGS__) |
| #define eagle_adm_dbg(fmt, ...) \ |
| pr_debug("DTS_EAGLE_DRIVER_ADM: " fmt "\n", ##__VA_ARGS__) |
| #define eagle_adm_err(fmt, ...) \ |
| pr_err("DTS_EAGLE_DRIVER_ADM: " fmt "\n", ##__VA_ARGS__) |
| #define eagle_enable_dbg(fmt, ...) \ |
| pr_debug("DTS_EAGLE_ENABLE: " fmt "\n", ##__VA_ARGS__) |
| #define eagle_enable_err(fmt, ...) \ |
| pr_err("DTS_EAGLE_ENABLE: " fmt "\n", ##__VA_ARGS__) |
| #define eagle_ioctl_info(fmt, ...) \ |
| pr_err("DTS_EAGLE_IOCTL: " fmt "\n", ##__VA_ARGS__) |
| |
| enum { |
| AUDIO_DEVICE_OUT_EARPIECE = 0, |
| AUDIO_DEVICE_OUT_SPEAKER, |
| AUDIO_DEVICE_OUT_WIRED_HEADSET, |
| AUDIO_DEVICE_OUT_WIRED_HEADPHONE, |
| AUDIO_DEVICE_OUT_BLUETOOTH_SCO, |
| AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, |
| AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT, |
| AUDIO_DEVICE_OUT_BLUETOOTH_A2DP, |
| AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES, |
| AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER, |
| AUDIO_DEVICE_OUT_AUX_DIGITAL, |
| AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET, |
| AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET, |
| AUDIO_DEVICE_OUT_USB_ACCESSORY, |
| AUDIO_DEVICE_OUT_USB_DEVICE, |
| AUDIO_DEVICE_OUT_REMOTE_SUBMIX, |
| AUDIO_DEVICE_OUT_ANC_HEADSET, |
| AUDIO_DEVICE_OUT_ANC_HEADPHONE, |
| AUDIO_DEVICE_OUT_PROXY, |
| AUDIO_DEVICE_OUT_FM, |
| AUDIO_DEVICE_OUT_FM_TX, |
| |
| AUDIO_DEVICE_OUT_COUNT |
| }; |
| |
| #define AUDIO_DEVICE_COMBO 0x400000 /* bit 23 */ |
| |
| enum { /* cache block */ |
| CB_0 = 0, |
| CB_1, |
| CB_2, |
| CB_3, |
| CB_4, |
| CB_5, |
| CB_6, |
| CB_7, |
| |
| CB_COUNT |
| }; |
| |
| enum { /* cache block description */ |
| CBD_DEV_MASK = 0, |
| CBD_OFFSG, |
| CBD_CMD0, |
| CBD_SZ0, |
| CBD_OFFS1, |
| CBD_CMD1, |
| CBD_SZ1, |
| CBD_OFFS2, |
| CBD_CMD2, |
| CBD_SZ2, |
| CBD_OFFS3, |
| CBD_CMD3, |
| CBD_SZ3, |
| |
| CBD_COUNT, |
| }; |
| |
| static s32 _fx_logN(s32 x) |
| { |
| s32 t, y = 0xa65af; |
| |
| if (x < 0x00008000) { |
| x <<= 16; y -= 0xb1721; } |
| if (x < 0x00800000) { |
| x <<= 8; y -= 0x58b91; } |
| if (x < 0x08000000) { |
| x <<= 4; y -= 0x2c5c8; } |
| if (x < 0x20000000) { |
| x <<= 2; y -= 0x162e4; } |
| if (x < 0x40000000) { |
| x <<= 1; y -= 0x0b172; } |
| t = x + (x >> 1); |
| if ((t & 0x80000000) == 0) { |
| x = t; y -= 0x067cd; } |
| t = x + (x >> 2); |
| if ((t & 0x80000000) == 0) { |
| x = t; y -= 0x03920; } |
| t = x + (x >> 3); |
| if ((t & 0x80000000) == 0) { |
| x = t; y -= 0x01e27; } |
| t = x + (x >> 4); |
| if ((t & 0x80000000) == 0) { |
| x = t; y -= 0x00f85; } |
| t = x + (x >> 5); |
| if ((t & 0x80000000) == 0) { |
| x = t; y -= 0x007e1; } |
| t = x + (x >> 6); |
| if ((t & 0x80000000) == 0) { |
| x = t; y -= 0x003f8; } |
| t = x + (x >> 7); |
| if ((t & 0x80000000) == 0) { |
| x = t; y -= 0x001fe; } |
| x = 0x80000000 - x; |
| y -= x >> 15; |
| return y; |
| } |
| |
| static inline void *_getd(struct dts_eagle_param_desc *depd) |
| { |
| return (void *)(((char *)depd) + sizeof(struct dts_eagle_param_desc)); |
| } |
| |
| static int _ref_cnt; |
| /* dts eagle parameter cache */ |
| static char *_depc; |
| static u32 _depc_size; |
| static s32 _c_bl[CB_COUNT][CBD_COUNT]; |
| static u32 _device_primary; |
| static u32 _device_all; |
| /* ION states */ |
| static struct ion_client *_ion_client; |
| static struct ion_handle *_ion_handle; |
| static struct param_outband _po; |
| static struct audio_client *_ac_NT; |
| static struct ion_client *_ion_client_NT; |
| static struct ion_handle *_ion_handle_NT; |
| static struct param_outband _po_NT; |
| |
| #define SEC_BLOB_MAX_CNT 10 |
| #define SEC_BLOB_MAX_SIZE 0x4004 /*extra 4 for size*/ |
| static char *_sec_blob[SEC_BLOB_MAX_CNT]; |
| |
| /* multi-copp support */ |
| static int _cidx[AFE_MAX_PORTS] = {-1}; |
| |
| /* volume controls */ |
| #define VOL_CMD_CNT_MAX 10 |
| static u32 _vol_cmd_cnt; |
| static s32 **_vol_cmds; |
| struct vol_cmds_d { |
| s32 d[4]; |
| }; |
| static struct vol_cmds_d *_vol_cmds_d; |
| static const s32 _log10_10_inv_x20 = 0x0008af84; |
| |
| /* hpx master control */ |
| static u32 _is_hpx_enabled; |
| |
| static void _volume_cmds_free(void) |
| { |
| int i; |
| |
| for (i = 0; i < _vol_cmd_cnt; i++) |
| kfree(_vol_cmds[i]); |
| _vol_cmd_cnt = 0; |
| kfree(_vol_cmds); |
| kfree(_vol_cmds_d); |
| _vol_cmds = NULL; |
| _vol_cmds_d = NULL; |
| } |
| |
| static s32 _volume_cmds_alloc1(s32 size) |
| { |
| _volume_cmds_free(); |
| _vol_cmd_cnt = size; |
| _vol_cmds = kzalloc(_vol_cmd_cnt * sizeof(int *), GFP_KERNEL); |
| if (_vol_cmds) { |
| _vol_cmds_d = kzalloc(_vol_cmd_cnt * sizeof(struct vol_cmds_d), |
| GFP_KERNEL); |
| } |
| if (_vol_cmds_d) |
| return 0; |
| _volume_cmds_free(); |
| return -ENOMEM; |
| } |
| |
| /* assumes size is equal or less than 0xFFF */ |
| static s32 _volume_cmds_alloc2(s32 idx, s32 size) |
| { |
| kfree(_vol_cmds[idx]); |
| _vol_cmds[idx] = kzalloc(size, GFP_KERNEL); |
| if (_vol_cmds[idx]) |
| return 0; |
| _vol_cmds_d[idx].d[0] = 0; |
| return -ENOMEM; |
| } |
| |
| static void _init_cb_descs(void) |
| { |
| int i; |
| |
| for (i = 0; i < CB_COUNT; i++) { |
| _c_bl[i][CBD_DEV_MASK] = 0; |
| _c_bl[i][CBD_OFFSG] = _c_bl[i][CBD_OFFS1] = |
| _c_bl[i][CBD_OFFS2] = _c_bl[i][CBD_OFFS3] = |
| 0xFFFFFFFF; |
| _c_bl[i][CBD_CMD0] = _c_bl[i][CBD_SZ0] = |
| _c_bl[i][CBD_CMD1] = _c_bl[i][CBD_SZ1] = |
| _c_bl[i][CBD_CMD2] = _c_bl[i][CBD_SZ2] = |
| _c_bl[i][CBD_CMD3] = _c_bl[i][CBD_SZ3] = 0; |
| } |
| } |
| |
| static u32 _get_dev_mask_for_pid(int pid) |
| { |
| switch (pid) { |
| case SLIMBUS_0_RX: |
| return (1 << AUDIO_DEVICE_OUT_EARPIECE) | |
| (1 << AUDIO_DEVICE_OUT_SPEAKER) | |
| (1 << AUDIO_DEVICE_OUT_WIRED_HEADSET) | |
| (1 << AUDIO_DEVICE_OUT_WIRED_HEADPHONE) | |
| (1 << AUDIO_DEVICE_OUT_ANC_HEADSET) | |
| (1 << AUDIO_DEVICE_OUT_ANC_HEADPHONE); |
| /* fallthrough */ |
| case INT_BT_SCO_RX: |
| return (1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO) | |
| (1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET) | |
| (1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT); |
| /* fallthrough */ |
| case RT_PROXY_PORT_001_RX: |
| return (1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP) | |
| (1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES) | |
| (1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER) | |
| (1 << AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET) | |
| (1 << AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET) | |
| (1 << AUDIO_DEVICE_OUT_USB_ACCESSORY) | |
| (1 << AUDIO_DEVICE_OUT_USB_DEVICE) | |
| (1 << AUDIO_DEVICE_OUT_PROXY); |
| /* fallthrough */ |
| case HDMI_RX: |
| return 1 << AUDIO_DEVICE_OUT_AUX_DIGITAL; |
| case INT_FM_RX: |
| return 1 << AUDIO_DEVICE_OUT_FM; |
| case INT_FM_TX: |
| return 1 << AUDIO_DEVICE_OUT_FM_TX; |
| default: |
| return 0; |
| } |
| } |
| |
| static int _get_pid_from_dev(u32 device) |
| { |
| if (device & (1 << AUDIO_DEVICE_OUT_EARPIECE) || |
| device & (1 << AUDIO_DEVICE_OUT_SPEAKER) || |
| device & (1 << AUDIO_DEVICE_OUT_WIRED_HEADSET) || |
| device & (1 << AUDIO_DEVICE_OUT_WIRED_HEADPHONE) || |
| device & (1 << AUDIO_DEVICE_OUT_ANC_HEADSET) || |
| device & (1 << AUDIO_DEVICE_OUT_ANC_HEADPHONE)) { |
| return SLIMBUS_0_RX; |
| } else if (device & (1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO) || |
| device & (1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET) || |
| device & (1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT)) { |
| return INT_BT_SCO_RX; |
| } else if (device & (1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP) || |
| device & (1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES) || |
| device & (1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER) || |
| device & (1 << AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET) || |
| device & (1 << AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET) || |
| device & (1 << AUDIO_DEVICE_OUT_USB_ACCESSORY) || |
| device & (1 << AUDIO_DEVICE_OUT_USB_DEVICE) || |
| device & (1 << AUDIO_DEVICE_OUT_PROXY)) { |
| return RT_PROXY_PORT_001_RX; |
| } else if (device & (1 << AUDIO_DEVICE_OUT_AUX_DIGITAL)) { |
| return HDMI_RX; |
| } else if (device & (1 << AUDIO_DEVICE_OUT_FM)) { |
| return INT_FM_RX; |
| } else if (device & (1 << AUDIO_DEVICE_OUT_FM_TX)) { |
| return INT_FM_TX; |
| } |
| return 0; |
| } |
| |
| static s32 _get_cb_for_dev(int device) |
| { |
| s32 i; |
| |
| if (device & AUDIO_DEVICE_COMBO) { |
| for (i = 0; i < CB_COUNT; i++) { |
| if ((_c_bl[i][CBD_DEV_MASK] & device) == device) |
| return i; |
| } |
| } else { |
| for (i = 0; i < CB_COUNT; i++) { |
| if ((_c_bl[i][CBD_DEV_MASK] & device) && |
| !(_c_bl[i][CBD_DEV_MASK] & AUDIO_DEVICE_COMBO)) |
| return i; |
| } |
| } |
| eagle_drv_err("%s: device %i not found", __func__, device); |
| return -EINVAL; |
| } |
| |
| static int _is_port_open_and_eagle(int pid) |
| { |
| if (msm_routing_check_backend_enabled(pid)) |
| return 1; |
| return 1; |
| } |
| |
| static int _isNTDevice(u32 device) |
| { |
| if (device & |
| ((1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO) | |
| (1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET) | |
| (1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT) | |
| (1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP) | |
| (1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES) | |
| (1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER) | |
| (1 << AUDIO_DEVICE_OUT_AUX_DIGITAL))) |
| return 1; |
| return 0; |
| } |
| |
| static void _reg_ion_mem(void) |
| { |
| int rc; |
| |
| rc = msm_audio_ion_alloc("DTS_EAGLE", &_ion_client, &_ion_handle, |
| ION_MEM_SIZE, &_po.paddr, &_po.size, &_po.kvaddr); |
| if (rc) |
| eagle_drv_err("%s: msm audio ion alloc failed with %i", |
| __func__, rc); |
| } |
| |
| static void _unreg_ion_mem(void) |
| { |
| int rc; |
| |
| rc = msm_audio_ion_free(_ion_client, _ion_handle); |
| if (rc) |
| eagle_drv_err("%s: msm audio ion alloc failed with %i", |
| __func__, rc); |
| } |
| |
| static void _reg_ion_mem_NT(void) |
| { |
| int rc; |
| |
| eagle_drv_dbg("%s: NT ion mem", __func__); |
| rc = msm_audio_ion_alloc("DTS_EAGLE", &_ion_client_NT, |
| &_ion_handle_NT, ION_MEM_SIZE, |
| &_po_NT.paddr, &_po_NT.size, &_po_NT.kvaddr); |
| if (rc) { |
| eagle_drv_err("%s: msm audio ion alloc failed", __func__); |
| return; |
| } |
| rc = q6asm_memory_map(_ac_NT, _po_NT.paddr, |
| IN, _po_NT.size, 1); |
| if (rc < 0) { |
| eagle_drv_err("%s: memory map failed", __func__); |
| msm_audio_ion_free(_ion_client_NT, _ion_handle_NT); |
| _ion_client_NT = NULL; |
| _ion_handle_NT = NULL; |
| } |
| } |
| |
| static void _unreg_ion_mem_NT(void) |
| { |
| int rc; |
| |
| rc = q6asm_memory_unmap(_ac_NT, _po_NT.paddr, IN); |
| if (rc < 0) |
| eagle_drv_err("%s: mem unmap failed", __func__); |
| rc = msm_audio_ion_free(_ion_client_NT, _ion_handle_NT); |
| if (rc < 0) |
| eagle_drv_err("%s: mem free failed", __func__); |
| |
| _ion_client_NT = NULL; |
| _ion_handle_NT = NULL; |
| } |
| |
| static struct audio_client *_getNTDeviceAC(void) |
| { |
| return _ac_NT; |
| } |
| |
| static void _set_audioclient(struct audio_client *ac) |
| { |
| _ac_NT = ac; |
| _reg_ion_mem_NT(); |
| } |
| |
| static void _clear_audioclient(void) |
| { |
| _unreg_ion_mem_NT(); |
| _ac_NT = NULL; |
| } |
| |
| |
| static int _sendcache_pre(struct audio_client *ac) |
| { |
| uint32_t offset, size; |
| int32_t cidx, cmd, err = 0; |
| |
| cidx = _get_cb_for_dev(_device_primary); |
| if (cidx < 0) { |
| eagle_precache_err("%s: no cache for primary device %i found", |
| __func__, _device_primary); |
| return -EINVAL; |
| } |
| offset = _c_bl[cidx][CBD_OFFSG]; |
| cmd = _c_bl[cidx][CBD_CMD0]; |
| size = _c_bl[cidx][CBD_SZ0]; |
| /* check for integer overflow */ |
| if (offset > (UINT_MAX - size)) |
| err = -EINVAL; |
| if ((_depc_size == 0) || !_depc || (size == 0) || |
| cmd == 0 || ((offset + size) > _depc_size) || (err != 0)) { |
| eagle_precache_err("%s: primary device %i cache index %i general error - cache size = %u, cache ptr = %pK, offset = %u, size = %u, cmd = %i", |
| __func__, _device_primary, cidx, _depc_size, _depc, |
| offset, size, cmd); |
| return -EINVAL; |
| } |
| |
| if ((offset < (UINT_MAX - 124)) && ((offset + 124) < _depc_size)) |
| eagle_precache_dbg("%s: first 6 integers %i %i %i %i %i %i (30th %i)", |
| __func__, *((int *)&_depc[offset]), |
| *((int *)&_depc[offset+4]), |
| *((int *)&_depc[offset+8]), |
| *((int *)&_depc[offset+12]), |
| *((int *)&_depc[offset+16]), |
| *((int *)&_depc[offset+20]), |
| *((int *)&_depc[offset+120])); |
| eagle_precache_dbg("%s: sending full data block to port, with cache index = %d device mask 0x%X, param = 0x%X, offset = %u, and size = %u", |
| __func__, cidx, _c_bl[cidx][CBD_DEV_MASK], cmd, offset, size); |
| |
| if (q6asm_dts_eagle_set(ac, cmd, size, (void *)&_depc[offset], |
| NULL, MPRE)) |
| eagle_precache_err("%s: q6asm_dts_eagle_set failed with id = %d and size = %u", |
| __func__, cmd, size); |
| else |
| eagle_precache_dbg("%s: q6asm_dts_eagle_set succeeded with id = %d and size = %u", |
| __func__, cmd, size); |
| return 0; |
| } |
| |
| static int _sendcache_post(int port_id, int copp_idx, int topology) |
| { |
| int cidx = -1, cmd, mask, index, err = 0; |
| uint32_t offset, size; |
| |
| if (port_id == -1) { |
| cidx = _get_cb_for_dev(_device_primary); |
| if (cidx < 0) { |
| eagle_postcache_err("%s: no cache for primary device %i found. Port id was 0x%X", |
| __func__, _device_primary, port_id); |
| return -EINVAL; |
| } |
| goto NT_MODE_GOTO; |
| } |
| |
| index = adm_validate_and_get_port_index(port_id); |
| if (index < 0) { |
| eagle_postcache_err("%s: Invalid port idx %d port_id %#x", |
| __func__, index, port_id); |
| return -EINVAL; |
| } |
| eagle_postcache_dbg("%s: valid port idx %d for port_id %#x set to %i", |
| __func__, index, port_id, copp_idx); |
| _cidx[index] = copp_idx; |
| |
| mask = _get_dev_mask_for_pid(port_id); |
| if (mask & _device_primary) { |
| cidx = _get_cb_for_dev(_device_primary); |
| if (cidx < 0) { |
| eagle_postcache_err("%s: no cache for primary device %i found. Port id was 0x%X", |
| __func__, _device_primary, port_id); |
| return -EINVAL; |
| } |
| } else if (mask & _device_all) { |
| cidx = _get_cb_for_dev(_device_all); |
| if (cidx < 0) { |
| eagle_postcache_err("%s: no cache for combo device %i found. Port id was 0x%X", |
| __func__, _device_all, port_id); |
| return -EINVAL; |
| } |
| } else { |
| eagle_postcache_err("%s: port id 0x%X not for primary or combo device %i", |
| __func__, port_id, _device_primary); |
| return -EINVAL; |
| } |
| |
| NT_MODE_GOTO: |
| offset = _c_bl[cidx][CBD_OFFSG] + _c_bl[cidx][CBD_OFFS2]; |
| cmd = _c_bl[cidx][CBD_CMD2]; |
| size = _c_bl[cidx][CBD_SZ2]; |
| |
| /* check for integer overflow */ |
| if (offset > (UINT_MAX - size)) |
| err = -EINVAL; |
| if ((_depc_size == 0) || !_depc || (err != 0) || (size == 0) || |
| (cmd == 0) || (offset + size) > _depc_size) { |
| eagle_postcache_err("%s: primary device %i cache index %i port_id 0x%X general error - cache size = %u, cache ptr = %pK, offset = %u, size = %u, cmd = %i", |
| __func__, _device_primary, cidx, port_id, |
| _depc_size, _depc, offset, size, cmd); |
| return -EINVAL; |
| } |
| |
| if ((offset < (UINT_MAX - 24)) && ((offset + 24) < _depc_size)) |
| eagle_postcache_dbg("%s: first 6 integers %i %i %i %i %i %i", |
| __func__, *((int *)&_depc[offset]), |
| *((int *)&_depc[offset+4]), |
| *((int *)&_depc[offset+8]), |
| *((int *)&_depc[offset+12]), |
| *((int *)&_depc[offset+16]), |
| *((int *)&_depc[offset+20])); |
| eagle_postcache_dbg("%s: sending full data block to port, with cache index = %d device mask 0x%X, port_id = 0x%X, param = 0x%X, offset = %u, and size = %u", |
| __func__, cidx, _c_bl[cidx][CBD_DEV_MASK], port_id, cmd, |
| offset, size); |
| |
| if (_ac_NT) { |
| eagle_postcache_dbg("%s: NT Route detected", __func__); |
| if (q6asm_dts_eagle_set(_getNTDeviceAC(), cmd, size, |
| (void *)&_depc[offset], |
| &_po_NT, MPST)) |
| eagle_postcache_err("%s: q6asm_dts_eagle_set failed with id = 0x%X and size = %u", |
| __func__, cmd, size); |
| } else if (adm_dts_eagle_set(port_id, copp_idx, cmd, |
| (void *)&_depc[offset], size) < 0) |
| eagle_postcache_err("%s: adm_dts_eagle_set failed with id = 0x%X and size = %u", |
| __func__, cmd, size); |
| else |
| eagle_postcache_dbg("%s: adm_dts_eagle_set succeeded with id = 0x%X and size = %u", |
| __func__, cmd, size); |
| return 0; |
| } |
| |
| static int _enable_post_get_control(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| ucontrol->value.integer.value[0] = _is_hpx_enabled; |
| return 0; |
| } |
| |
| static int _enable_post_put_control(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| int idx = 0, be_index = 0, port_id, topology; |
| int flag = ucontrol->value.integer.value[0]; |
| struct msm_pcm_routing_bdai_data msm_bedai; |
| |
| eagle_drv_dbg("%s: flag %d", __func__, flag); |
| |
| _is_hpx_enabled = flag ? true : false; |
| msm_pcm_routing_acquire_lock(); |
| /* send cache postmix params when hpx is set On */ |
| for (be_index = 0; be_index < MSM_BACKEND_DAI_MAX; be_index++) { |
| msm_pcm_routing_get_bedai_info(be_index, &msm_bedai); |
| port_id = msm_bedai.port_id; |
| if (!(((port_id == SLIMBUS_0_RX) || |
| (port_id == RT_PROXY_PORT_001_RX)) && |
| msm_bedai.active)) |
| continue; |
| for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) { |
| topology = adm_get_topology_for_port_copp_idx( |
| port_id, idx); |
| if (topology == |
| ADM_CMD_COPP_OPEN_TOPOLOGY_ID_DTS_HPX) { |
| msm_dts_eagle_enable_adm(port_id, idx, |
| _is_hpx_enabled); |
| } |
| } |
| } |
| msm_pcm_routing_release_lock(); |
| return 0; |
| } |
| |
| static const struct snd_kcontrol_new _hpx_enabled_controls[] = { |
| SOC_SINGLE_EXT("Set HPX OnOff", SND_SOC_NOPM, 0, 1, 0, |
| _enable_post_get_control, _enable_post_put_control) |
| }; |
| |
| /** |
| * msm_dts_ion_memmap() - helper function to map ION memory |
| * @po_: Out of band memory structure used as memory. |
| * |
| * Assign already allocated ION memory for mapping it to dsp. |
| * |
| * Return: No return value. |
| */ |
| void msm_dts_ion_memmap(struct param_outband *po_) |
| { |
| po_->size = ION_MEM_SIZE; |
| po_->kvaddr = _po.kvaddr; |
| po_->paddr = _po.paddr; |
| } |
| |
| /** |
| * msm_dts_eagle_enable_asm() - Enable/disable dts module |
| * @ac: Enable/disable module in ASM session associated with this audio client. |
| * @enable: Enable/disable the dts module. |
| * @module: module id. |
| * |
| * Enable/disable specified dts module id in asm. |
| * |
| * Return: Return failure if any. |
| */ |
| int msm_dts_eagle_enable_asm(struct audio_client *ac, u32 enable, int module) |
| { |
| int ret = 0; |
| |
| eagle_enable_dbg("%s: enable = %i on module %i", |
| __func__, enable, module); |
| _is_hpx_enabled = enable; |
| ret = q6asm_dts_eagle_set(ac, AUDPROC_PARAM_ID_ENABLE, |
| sizeof(enable), &enable, |
| NULL, module); |
| if (_is_hpx_enabled) { |
| if (module == MPRE) |
| _sendcache_pre(ac); |
| else if (module == MPST) |
| _sendcache_post(-1, 0, 0); |
| } |
| return ret; |
| } |
| |
| /** |
| * msm_dts_eagle_enable_adm() - Enable/disable dts module in adm |
| * @port_id: Send enable/disable param to this port id. |
| * @copp_idx: Send enable/disable param to the relevant copp. |
| * @enable: Enable/disable the dts module. |
| * |
| * Enable/disable dts module in adm. |
| * |
| * Return: Return failure if any. |
| */ |
| int msm_dts_eagle_enable_adm(int port_id, int copp_idx, u32 enable) |
| { |
| int ret = 0; |
| |
| eagle_enable_dbg("%s: enable = %i", __func__, enable); |
| _is_hpx_enabled = enable; |
| ret = adm_dts_eagle_set(port_id, copp_idx, AUDPROC_PARAM_ID_ENABLE, |
| (char *)&enable, sizeof(enable)); |
| if (_is_hpx_enabled) |
| _sendcache_post(port_id, copp_idx, MPST); |
| return ret; |
| } |
| |
| /** |
| * msm_dts_eagle_add_controls() - Add mixer control to Enable/Disable DTS HPX |
| * @platform: Add mixer controls to this platform. |
| * |
| * Add mixer control to Enable/Disable DTS HPX module in ADM. |
| * |
| * Return: No return value. |
| */ |
| void msm_dts_eagle_add_controls(struct snd_soc_platform *platform) |
| { |
| snd_soc_add_platform_controls(platform, _hpx_enabled_controls, |
| ARRAY_SIZE(_hpx_enabled_controls)); |
| } |
| |
| /** |
| * msm_dts_eagle_set_stream_gain() - Set stream gain to DTS Premix module |
| * @ac: Set stream gain to ASM session associated with this audio client. |
| * @lgain: Left gain value. |
| * @rgain: Right gain value. |
| * |
| * Set stream gain to DTS Premix module in ASM. |
| * |
| * Return: failure or success. |
| */ |
| int msm_dts_eagle_set_stream_gain(struct audio_client *ac, int lgain, int rgain) |
| { |
| u32 i, val; |
| s32 idx, err = 0; |
| |
| eagle_vol_dbg("%s: - entry: vol_cmd_cnt = %u, lgain = %i, rgain = %i", |
| __func__, _vol_cmd_cnt, lgain, rgain); |
| |
| if (_depc_size == 0) { |
| eagle_vol_dbg("%s: driver cache not initialized", __func__); |
| return -EINVAL; |
| } |
| |
| for (i = 0; i < _vol_cmd_cnt; i++) { |
| if (_vol_cmds_d[i].d[0] & 0x8000) { |
| idx = (sizeof(struct dts_eagle_param_desc)/sizeof(int)) |
| + (_vol_cmds_d[i].d[0] & 0x3FF); |
| val = _fx_logN(((s32)(lgain+rgain)) << 2); |
| val = ((long long)val * _log10_10_inv_x20) >> 16; |
| _vol_cmds[i][idx] = (s32)clamp((int)(((long long)val * |
| _vol_cmds_d[i].d[1]) >> 16), |
| _vol_cmds_d[i].d[2], |
| _vol_cmds_d[i].d[3]); |
| eagle_vol_dbg("%s: loop %u cmd desc found %i, idx = %i. volume info: lgain = %i, rgain = %i, volume = %i (scale %i, min %i, max %i)", |
| __func__, i, _vol_cmds_d[i].d[0], idx, lgain, |
| rgain, _vol_cmds[i][idx], _vol_cmds_d[i].d[1], |
| _vol_cmds_d[i].d[2], _vol_cmds_d[i].d[3]); |
| } |
| idx = _get_cb_for_dev(_device_primary); |
| if (idx < 0) { |
| eagle_vol_err("%s: no cache for primary device %i found", |
| __func__, _device_primary); |
| return -EINVAL; |
| } |
| val = _c_bl[idx][CBD_OFFSG] + _vol_cmds[i][2]; |
| /* check for integer overflow */ |
| if (val > (UINT_MAX - _vol_cmds[i][1])) |
| err = -EINVAL; |
| if ((err != 0) || ((val + _vol_cmds[i][1]) > _depc_size)) { |
| eagle_vol_err("%s: volume size (%u) + offset (%i) out of bounds %i", |
| __func__, val, _vol_cmds[i][1], _depc_size); |
| return -EINVAL; |
| } |
| memcpy((void *)&_depc[val], &_vol_cmds[i][4], _vol_cmds[i][1]); |
| if (q6asm_dts_eagle_set(ac, _vol_cmds[i][0], |
| _vol_cmds[i][1], (void *)&_depc[val], NULL, MPRE)) |
| eagle_vol_err("%s: loop %u - volume set failed with id 0x%X, size %i, offset %i, cmd_desc %i, scale %i, min %i, max %i, data(...) %i", |
| __func__, i, _vol_cmds[i][0], _vol_cmds[i][1], |
| _vol_cmds[i][2], _vol_cmds_d[i].d[0], |
| _vol_cmds_d[i].d[1], _vol_cmds_d[i].d[2], |
| _vol_cmds_d[i].d[3], _vol_cmds[i][4]); |
| else |
| eagle_vol_dbg("%s: loop %u - volume set succeeded with id 0x%X, size %i, offset %i, cmd_desc %i, scale %i, min %i, max %i, data(...) %i", |
| __func__, i, _vol_cmds[i][0], _vol_cmds[i][1], |
| _vol_cmds[i][2], _vol_cmds_d[i].d[0], |
| _vol_cmds_d[i].d[1], _vol_cmds_d[i].d[2], |
| _vol_cmds_d[i].d[3], _vol_cmds[i][4]); |
| } |
| return 0; |
| } |
| |
| /** |
| * msm_dts_eagle_handle_asm() - Set or Get params from ASM |
| * @depd: DTS Eagle Params structure. |
| * @buf: Buffer to get queried param value. |
| * @for_pre: For premix module or postmix module. |
| * @get: Getting param from DSP or setting param. |
| * @ac: Set/Get from ASM session associated with this audio client. |
| * @po: Out of band memory to set or get postmix params. |
| * |
| * Set or Get params from modules in ASM session. |
| * |
| * Return: Return failure if any. |
| */ |
| int msm_dts_eagle_handle_asm(struct dts_eagle_param_desc *depd, char *buf, |
| bool for_pre, bool get, struct audio_client *ac, |
| struct param_outband *po) |
| { |
| struct dts_eagle_param_desc depd_ = {0}; |
| s32 ret = 0, isALSA = 0, err = 0, i, mod = for_pre ? MPRE : MPST; |
| u32 offset; |
| |
| eagle_asm_dbg("%s: set/get asm", __func__); |
| |
| /* special handling for ALSA route, to accommodate 64 bit platforms */ |
| if (depd == NULL) { |
| long *arg_ = (long *)buf; |
| |
| depd = &depd_; |
| depd->id = (u32)*arg_++; |
| depd->size = (u32)*arg_++; |
| depd->offset = (s32)*arg_++; |
| depd->device = (u32)*arg_++; |
| buf = (char *)arg_; |
| isALSA = 1; |
| } |
| |
| if (depd->size & 1) { |
| eagle_asm_err("%s: parameter size %u is not a multiple of 2", |
| __func__, depd->size); |
| return -EINVAL; |
| } |
| |
| if (get) { |
| void *buf_, *buf_m = NULL; |
| |
| eagle_asm_dbg("%s: get requested", __func__); |
| if (depd->offset == -1) { |
| eagle_asm_dbg("%s: get from dsp requested", __func__); |
| if (depd->size > 0 && depd->size <= DEPC_MAX_SIZE) { |
| buf_ = buf_m = vzalloc(depd->size); |
| } else { |
| eagle_asm_err("%s: get size %u invalid", |
| __func__, depd->size); |
| return -EINVAL; |
| } |
| if (!buf_m) { |
| eagle_asm_err("%s: out of memory", __func__); |
| return -ENOMEM; |
| } else if (q6asm_dts_eagle_get(ac, depd->id, |
| depd->size, buf_m, |
| po, mod) < 0) { |
| eagle_asm_err("%s: asm get failed", __func__); |
| ret = -EFAULT; |
| goto DTS_EAGLE_IOCTL_GET_PARAM_PRE_EXIT; |
| } |
| eagle_asm_dbg("%s: get result: param id 0x%x value %d size %u", |
| __func__, depd->id, *(int *)buf_m, depd->size); |
| } else { |
| s32 tgt = _get_cb_for_dev(depd->device); |
| |
| if (tgt < 0) { |
| eagle_asm_err("%s: no cache for device %u found", |
| __func__, depd->device); |
| return -EINVAL; |
| } |
| offset = _c_bl[tgt][CBD_OFFSG] + depd->offset; |
| /* check for integer overflow */ |
| if (offset > (UINT_MAX - depd->size)) |
| err = -EINVAL; |
| if ((err != 0) || (offset + depd->size) > _depc_size) { |
| eagle_asm_err("%s: invalid size %u and/or offset %u", |
| __func__, depd->size, offset); |
| return -EINVAL; |
| } |
| buf_ = (u32 *)&_depc[offset]; |
| } |
| if (isALSA) { |
| if (depd->size == 2) { |
| *(long *)buf = (long)*(__u16 *)buf_; |
| eagle_asm_dbg("%s: asm out 16 bit value %li", |
| __func__, *(long *)buf); |
| } else { |
| s32 *pbuf = (s32 *)buf_; |
| long *bufl = (long *)buf; |
| |
| for (i = 0; i < (depd->size >> 2); i++) { |
| *bufl++ = (long)*pbuf++; |
| eagle_asm_dbg("%s: asm out value %li", |
| __func__, *(bufl-1)); |
| } |
| } |
| } else { |
| memcpy(buf, buf_, depd->size); |
| } |
| DTS_EAGLE_IOCTL_GET_PARAM_PRE_EXIT: |
| vfree(buf_m); |
| return (int)ret; |
| } else { |
| s32 tgt = _get_cb_for_dev(depd->device); |
| |
| if (tgt < 0) { |
| eagle_asm_err("%s: no cache for device %u found", |
| __func__, depd->device); |
| return -EINVAL; |
| } |
| offset = _c_bl[tgt][CBD_OFFSG] + depd->offset; |
| /* check for integer overflow */ |
| if (offset > (UINT_MAX - depd->size)) |
| err = -EINVAL; |
| if ((err != 0) || ((offset + depd->size) > _depc_size)) { |
| eagle_asm_err("%s: invalid size %u and/or offset %u for parameter (cache is size %u)", |
| __func__, depd->size, offset, _depc_size); |
| return -EINVAL; |
| } |
| if (isALSA) { |
| if (depd->size == 2) { |
| *(__u16 *)&_depc[offset] = (__u16)*(long *)buf; |
| eagle_asm_dbg("%s: asm in 16 bit value %li", |
| __func__, *(long *)buf); |
| } else { |
| s32 *pbuf = (s32 *)&_depc[offset]; |
| long *bufl = (long *)buf; |
| |
| for (i = 0; i < (depd->size >> 2); i++) { |
| *pbuf++ = (s32)*bufl++; |
| eagle_asm_dbg("%s: asm in value %i", |
| __func__, *(pbuf-1)); |
| } |
| } |
| } else { |
| memcpy(&_depc[offset], buf, depd->size); |
| } |
| eagle_asm_dbg("%s: param info: param = 0x%X, size = %u, offset = %i, device = %u, cache block %i, global offset = %u, first bytes as integer = %i", |
| __func__, depd->id, depd->size, depd->offset, |
| depd->device, |
| tgt, offset, *(int *)&_depc[offset]); |
| if (q6asm_dts_eagle_set(ac, depd->id, depd->size, |
| (void *)&_depc[offset], po, mod)) |
| eagle_asm_err("%s: q6asm_dts_eagle_set failed with id = 0x%X, size = %u, offset = %d", |
| __func__, depd->id, depd->size, depd->offset); |
| else |
| eagle_asm_dbg("%s: q6asm_dts_eagle_set succeeded with id = 0x%X, size = %u, offset = %d", |
| __func__, depd->id, depd->size, depd->offset); |
| } |
| |
| return (int)ret; |
| } |
| |
| /** |
| * msm_dts_eagle_handle_adm() - Set or Get params from ADM |
| * @depd: DTS Eagle Params structure used to set or get. |
| * @buf: Buffer to get queried param value in NT mode. |
| * @for_pre: For premix module or postmix module. |
| * @get: Getting param from DSP or setting param. |
| * |
| * Set or Get params from modules in ADM session. |
| * |
| * Return: Return failure if any. |
| */ |
| int msm_dts_eagle_handle_adm(struct dts_eagle_param_desc *depd, char *buf, |
| bool for_pre, bool get) |
| { |
| u32 pid = _get_pid_from_dev(depd->device), cidx; |
| s32 ret = 0; |
| |
| eagle_adm_dbg("%s: set/get adm", __func__); |
| |
| if (_isNTDevice(depd->device)) { |
| eagle_adm_dbg("%s: NT Route detected", __func__); |
| ret = msm_dts_eagle_handle_asm(depd, buf, for_pre, get, |
| _getNTDeviceAC(), &_po_NT); |
| if (ret < 0) |
| eagle_adm_err("%s: NT Route set failed with id = 0x%X, size = %u, offset = %i, device = %u", |
| __func__, depd->id, depd->size, depd->offset, |
| depd->device); |
| } else if (get) { |
| cidx = adm_validate_and_get_port_index(pid); |
| eagle_adm_dbg("%s: get from qdsp requested (port id 0x%X)", |
| __func__, pid); |
| if (adm_dts_eagle_get(pid, _cidx[cidx], depd->id, |
| buf, depd->size) < 0) { |
| eagle_adm_err("%s: get from qdsp via adm with port id 0x%X failed", |
| __func__, pid); |
| return -EFAULT; |
| } |
| } else if (_is_port_open_and_eagle(pid)) { |
| cidx = adm_validate_and_get_port_index(pid); |
| eagle_adm_dbg("%s: adm_dts_eagle_set called with id = 0x%X, size = %u, offset = %i, device = %u, port id = %u, copp index = %u", |
| __func__, depd->id, depd->size, depd->offset, |
| depd->device, pid, cidx); |
| ret = adm_dts_eagle_set(pid, _cidx[cidx], depd->id, |
| (void *)buf, depd->size); |
| if (ret < 0) |
| eagle_adm_err("%s: adm_dts_eagle_set failed", __func__); |
| else |
| eagle_adm_dbg("%s: adm_dts_eagle_set succeeded", |
| __func__); |
| } else { |
| ret = -EINVAL; |
| eagle_adm_dbg("%s: port id 0x%X not active or not Eagle", |
| __func__, pid); |
| } |
| return (int)ret; |
| } |
| |
| /** |
| * msm_dts_eagle_ioctl() - ioctl handler function |
| * @cmd: cmd to handle. |
| * @arg: argument to the cmd. |
| * |
| * Handle DTS Eagle ioctl cmds. |
| * |
| * Return: Return failure if any. |
| */ |
| int msm_dts_eagle_ioctl(unsigned int cmd, unsigned long arg) |
| { |
| s32 ret = 0; |
| |
| switch (cmd) { |
| case DTS_EAGLE_IOCTL_GET_CACHE_SIZE: { |
| eagle_ioctl_info("%s: called with control 0x%X (get param cache size)", |
| __func__, cmd); |
| if (copy_to_user((void *)arg, &_depc_size, |
| sizeof(_depc_size))) { |
| eagle_ioctl_err("%s: error writing size", __func__); |
| return -EFAULT; |
| } |
| break; |
| } |
| case DTS_EAGLE_IOCTL_SET_CACHE_SIZE: { |
| u32 size = 0; |
| |
| eagle_ioctl_info("%s: called with control 0x%X (allocate param cache)", |
| __func__, cmd); |
| if (copy_from_user((void *)&size, (void *)arg, sizeof(size))) { |
| eagle_ioctl_err("%s: error copying size (src:%pK, tgt:%pK, size:%zu)", |
| __func__, (void *)arg, &size, sizeof(size)); |
| return -EFAULT; |
| } else if (size > DEPC_MAX_SIZE) { |
| eagle_ioctl_err("%s: cache size %u not allowed (min 0, max %u)", |
| __func__, size, DEPC_MAX_SIZE); |
| return -EINVAL; |
| } |
| if (_depc) { |
| eagle_ioctl_dbg("%s: previous param cache of size %u freed", |
| __func__, _depc_size); |
| _depc_size = 0; |
| vfree(_depc); |
| _depc = NULL; |
| } |
| if (size) |
| _depc = vzalloc(size); |
| else |
| eagle_ioctl_dbg("%s: %u bytes requested for param cache, nothing allocated", |
| __func__, size); |
| if (_depc) { |
| eagle_ioctl_dbg("%s: %u bytes allocated for param cache", |
| __func__, size); |
| _depc_size = size; |
| } else { |
| eagle_ioctl_err("%s: error allocating param cache (vzalloc failed on %u bytes)", |
| __func__, size); |
| _depc_size = 0; |
| return -ENOMEM; |
| } |
| break; |
| } |
| case DTS_EAGLE_IOCTL_GET_PARAM: { |
| struct dts_eagle_param_desc depd; |
| s32 for_pre = 0, get_from_core = 0, err = 0; |
| u32 offset; |
| void *buf, *buf_m = NULL; |
| |
| eagle_ioctl_info("%s: control 0x%X (get param)", |
| __func__, cmd); |
| if (copy_from_user((void *)&depd, (void *)arg, sizeof(depd))) { |
| eagle_ioctl_err("%s: error copying dts_eagle_param_desc (src:%pK, tgt:%pK, size:%zu)", |
| __func__, (void *)arg, &depd, sizeof(depd)); |
| return -EFAULT; |
| } |
| if (depd.device & DTS_EAGLE_FLAG_IOCTL_PRE) { |
| eagle_ioctl_dbg("%s: using for premix", __func__); |
| for_pre = 1; |
| } |
| if (depd.device & DTS_EAGLE_FLAG_IOCTL_GETFROMCORE) { |
| eagle_ioctl_dbg("%s: 'get from core' requested", |
| __func__); |
| get_from_core = 1; |
| depd.offset = -1; |
| } |
| depd.device &= DTS_EAGLE_FLAG_IOCTL_MASK; |
| if (depd.offset == -1) { |
| if (depd.size > 0 && depd.size <= DEPC_MAX_SIZE) { |
| buf = buf_m = vzalloc(depd.size); |
| } else { |
| eagle_ioctl_err("%s: get size %u invalid", |
| __func__, depd.size); |
| return -EINVAL; |
| } |
| if (!buf_m) { |
| eagle_ioctl_err("%s: out of memory", __func__); |
| return -ENOMEM; |
| } |
| if (get_from_core) |
| ret = core_dts_eagle_get(depd.id, depd.size, |
| buf); |
| else |
| ret = msm_dts_eagle_handle_adm(&depd, buf, |
| for_pre, true); |
| } else { |
| s32 cb = _get_cb_for_dev(depd.device); |
| |
| if (cb < 0) { |
| eagle_ioctl_err("%s: no cache for device %u found", |
| __func__, depd.device); |
| return -EINVAL; |
| } |
| offset = _c_bl[cb][CBD_OFFSG] + depd.offset; |
| /* check for integer overflow */ |
| if (offset > (UINT_MAX - depd.size)) |
| err = -EINVAL; |
| if ((err != 0) || |
| ((offset + depd.size) > _depc_size)) { |
| eagle_ioctl_err("%s: invalid size %u and/or offset %u", |
| __func__, depd.size, offset); |
| return -EINVAL; |
| } |
| buf = (void *)&_depc[offset]; |
| } |
| if (ret < 0) |
| eagle_ioctl_err("%s: error %i getting data", __func__, |
| ret); |
| else if (copy_to_user((void *)(((char *)arg)+sizeof(depd)), |
| buf, depd.size)) { |
| eagle_ioctl_err("%s: error copying get data", __func__); |
| ret = -EFAULT; |
| } |
| vfree(buf_m); |
| break; |
| } |
| case DTS_EAGLE_IOCTL_SET_PARAM: { |
| struct dts_eagle_param_desc depd; |
| s32 just_set_cache = 0, for_pre = 0, err = 0; |
| u32 offset; |
| s32 tgt; |
| |
| eagle_ioctl_info("%s: control 0x%X (set param)", |
| __func__, cmd); |
| if (copy_from_user((void *)&depd, (void *)arg, sizeof(depd))) { |
| eagle_ioctl_err("%s: error copying dts_eagle_param_desc (src:%pK, tgt:%pK, size:%zu)", |
| __func__, (void *)arg, &depd, sizeof(depd)); |
| return -EFAULT; |
| } |
| if (depd.device & DTS_EAGLE_FLAG_IOCTL_PRE) { |
| eagle_ioctl_dbg("%s: using for premix", __func__); |
| for_pre = 1; |
| } |
| if (depd.device & DTS_EAGLE_FLAG_IOCTL_JUSTSETCACHE) { |
| eagle_ioctl_dbg("%s: 'just set cache' requested", |
| __func__); |
| just_set_cache = 1; |
| } |
| depd.device &= DTS_EAGLE_FLAG_IOCTL_MASK; |
| tgt = _get_cb_for_dev(depd.device); |
| if (tgt < 0) { |
| eagle_ioctl_err("%s: no cache for device %u found", |
| __func__, depd.device); |
| return -EINVAL; |
| } |
| offset = _c_bl[tgt][CBD_OFFSG] + depd.offset; |
| /* check for integer overflow */ |
| if (offset > (UINT_MAX - depd.size)) |
| err = -EINVAL; |
| if ((err != 0) || ((offset + depd.size) > _depc_size)) { |
| eagle_ioctl_err("%s: invalid size %u and/or offset %u for parameter (target cache block %i with offset %i, global cache is size %u)", |
| __func__, depd.size, offset, tgt, |
| _c_bl[tgt][CBD_OFFSG], _depc_size); |
| return -EINVAL; |
| } |
| if (copy_from_user((void *)&_depc[offset], |
| (void *)(((char *)arg)+sizeof(depd)), |
| depd.size)) { |
| eagle_ioctl_err("%s: error copying param to cache (src:%pK, tgt:%pK, size:%u)", |
| __func__, ((char *)arg)+sizeof(depd), |
| &_depc[offset], depd.size); |
| return -EFAULT; |
| } |
| eagle_ioctl_dbg("%s: param info: param = 0x%X, size = %u, offset = %i, device = %u, cache block %i, global offset = %u, first bytes as integer = %i", |
| __func__, depd.id, depd.size, depd.offset, |
| depd.device, tgt, offset, *(int *)&_depc[offset]); |
| if (!just_set_cache) { |
| ret = msm_dts_eagle_handle_adm(&depd, &_depc[offset], |
| for_pre, false); |
| } |
| break; |
| } |
| case DTS_EAGLE_IOCTL_SET_CACHE_BLOCK: { |
| u32 b_[CBD_COUNT+1], *b = &b_[1], cb; |
| |
| eagle_ioctl_info("%s: with control 0x%X (set param cache block)", |
| __func__, cmd); |
| if (copy_from_user((void *)b_, (void *)arg, sizeof(b_))) { |
| eagle_ioctl_err("%s: error copying cache block data (src:%pK, tgt:%pK, size:%zu)", |
| __func__, (void *)arg, b_, sizeof(b_)); |
| return -EFAULT; |
| } |
| cb = b_[0]; |
| if (cb >= CB_COUNT) { |
| eagle_ioctl_err("%s: cache block %u out of range (max %u)", |
| __func__, cb, CB_COUNT-1); |
| return -EINVAL; |
| } |
| eagle_ioctl_dbg("%s: cache block %i set: devices 0x%X, global offset %i, offsets 1:%u 2:%u 3:%u, cmds/sizes 0:0x%X %u 1:0x%X %u 2:0x%X %u 3:0x%X %u", |
| __func__, cb, _c_bl[cb][CBD_DEV_MASK], _c_bl[cb][CBD_OFFSG], |
| _c_bl[cb][CBD_OFFS1], _c_bl[cb][CBD_OFFS2], |
| _c_bl[cb][CBD_OFFS3], _c_bl[cb][CBD_CMD0], _c_bl[cb][CBD_SZ0], |
| _c_bl[cb][CBD_CMD1], _c_bl[cb][CBD_SZ1], _c_bl[cb][CBD_CMD2], |
| _c_bl[cb][CBD_SZ2], _c_bl[cb][CBD_CMD3], _c_bl[cb][CBD_SZ3]); |
| if ((b[CBD_OFFSG]+b[CBD_OFFS1]+b[CBD_SZ1]) > _depc_size || |
| (b[CBD_OFFSG]+b[CBD_OFFS2]+b[CBD_SZ2]) > _depc_size || |
| (b[CBD_OFFSG]+b[CBD_OFFS3]+b[CBD_SZ3]) > _depc_size) { |
| eagle_ioctl_err("%s: cache block bounds out of range", |
| __func__); |
| return -EINVAL; |
| } |
| memcpy(_c_bl[cb], b, sizeof(_c_bl[cb])); |
| break; |
| } |
| case DTS_EAGLE_IOCTL_SET_ACTIVE_DEVICE: { |
| u32 data[2]; |
| |
| eagle_ioctl_dbg("%s: with control 0x%X (set active device)", |
| __func__, cmd); |
| if (copy_from_user((void *)data, (void *)arg, sizeof(data))) { |
| eagle_ioctl_err("%s: error copying active device data (src:%pK, tgt:%pK, size:%zu)", |
| __func__, (void *)arg, data, sizeof(data)); |
| return -EFAULT; |
| } |
| if (data[1] != 0) { |
| _device_primary = data[0]; |
| eagle_ioctl_dbg("%s: primary device %i", __func__, |
| data[0]); |
| } else { |
| _device_all = data[0]; |
| eagle_ioctl_dbg("%s: all devices 0x%X", __func__, |
| data[0]); |
| } |
| break; |
| } |
| case DTS_EAGLE_IOCTL_GET_LICENSE: { |
| u32 target = 0, size = 0; |
| s32 size_only; |
| |
| eagle_ioctl_dbg("%s: with control 0x%X (get license)", |
| __func__, cmd); |
| if (copy_from_user((void *)&target, (void *)arg, |
| sizeof(target))) { |
| eagle_ioctl_err("%s: error reading license index. (src:%pK, tgt:%pK, size:%zu)", |
| __func__, (void *)arg, &target, sizeof(target)); |
| return -EFAULT; |
| } |
| size_only = target & (1<<31) ? 1 : 0; |
| target &= 0x7FFFFFFF; |
| if (target >= SEC_BLOB_MAX_CNT) { |
| eagle_ioctl_err("%s: license index %u out of bounds (max index is %i)", |
| __func__, target, SEC_BLOB_MAX_CNT); |
| return -EINVAL; |
| } |
| if (_sec_blob[target] == NULL) { |
| eagle_ioctl_err("%s: license index %u never initialized", |
| __func__, target); |
| return -EINVAL; |
| } |
| size = ((u32 *)_sec_blob[target])[0]; |
| if ((size == 0) || (size > SEC_BLOB_MAX_SIZE)) { |
| eagle_ioctl_err("%s: license size %u for index %u invalid (min size is 1, max size is %u)", |
| __func__, size, target, SEC_BLOB_MAX_SIZE); |
| return -EINVAL; |
| } |
| if (size_only) { |
| eagle_ioctl_dbg("%s: reporting size of license data only", |
| __func__); |
| if (copy_to_user((void *)(((char *)arg)+sizeof(target)), |
| (void *)&size, sizeof(size))) { |
| eagle_ioctl_err("%s: error copying license size", |
| __func__); |
| return -EFAULT; |
| } |
| } else if (copy_to_user((void *)(((char *)arg)+sizeof(target)), |
| (void *)&(((s32 *)_sec_blob[target])[1]), size)) { |
| eagle_ioctl_err("%s: error copying license data", |
| __func__); |
| return -EFAULT; |
| } else { |
| eagle_ioctl_info("%s: license file %u bytes long from license index %u returned to user", |
| __func__, size, target); |
| } |
| break; |
| } |
| case DTS_EAGLE_IOCTL_SET_LICENSE: { |
| u32 target[2] = {0, 0}; |
| |
| eagle_ioctl_dbg("%s: control 0x%X (set license)", __func__, |
| cmd); |
| if (copy_from_user((void *)target, (void *)arg, |
| sizeof(target))) { |
| eagle_ioctl_err("%s: error reading license index (src:%pK, tgt:%pK, size:%zu)", |
| __func__, (void *)arg, target, sizeof(target)); |
| return -EFAULT; |
| } |
| if (target[0] >= SEC_BLOB_MAX_CNT) { |
| eagle_ioctl_err("%s: license index %u out of bounds (max index is %u)", |
| __func__, target[0], SEC_BLOB_MAX_CNT-1); |
| return -EINVAL; |
| } |
| if (target[1] == 0) { |
| eagle_ioctl_dbg("%s: request to free license index %u", |
| __func__, target[0]); |
| kfree(_sec_blob[target[0]]); |
| _sec_blob[target[0]] = NULL; |
| break; |
| } |
| if ((target[1] == 0) || (target[1] >= SEC_BLOB_MAX_SIZE)) { |
| eagle_ioctl_err("%s: license size %u for index %u invalid (min size is 1, max size is %u)", |
| __func__, target[1], target[0], |
| SEC_BLOB_MAX_SIZE); |
| return -EINVAL; |
| } |
| if (_sec_blob[target[0]] != NULL) { |
| if (((u32 *)_sec_blob[target[0]])[1] != target[1]) { |
| eagle_ioctl_dbg("%s: request new size for already allocated license index %u", |
| __func__, target[0]); |
| kfree(_sec_blob[target[0]]); |
| _sec_blob[target[0]] = NULL; |
| } |
| } |
| eagle_ioctl_dbg("%s: allocating %u bytes for license index %u", |
| __func__, target[1], target[0]); |
| _sec_blob[target[0]] = kzalloc(target[1] + 4, GFP_KERNEL); |
| if (!_sec_blob[target[0]]) { |
| eagle_ioctl_err("%s: error allocating license index %u (kzalloc failed on %u bytes)", |
| __func__, target[0], target[1]); |
| return -ENOMEM; |
| } |
| ((u32 *)_sec_blob[target[0]])[0] = target[1]; |
| if (copy_from_user( |
| (void *)&(((u32 *)_sec_blob[target[0]])[1]), |
| (void *)(((char *)arg)+sizeof(target)), |
| target[1])) { |
| eagle_ioctl_err("%s: error copying license to index %u, size %u (src:%pK, tgt:%pK, size:%u)", |
| __func__, target[0], target[1], |
| ((char *)arg)+sizeof(target), |
| &(((u32 *)_sec_blob[target[0]])[1]), |
| target[1]); |
| return -EFAULT; |
| } |
| eagle_ioctl_info("%s: license file %u bytes long copied to index license index %u", |
| __func__, target[1], target[0]); |
| break; |
| } |
| case DTS_EAGLE_IOCTL_SEND_LICENSE: { |
| u32 target = 0; |
| |
| eagle_ioctl_dbg("%s: control 0x%X (send license)", __func__, |
| cmd); |
| if (copy_from_user((void *)&target, (void *)arg, |
| sizeof(target))) { |
| eagle_ioctl_err("%s: error reading license index (src:%pK, tgt:%pK, size:%zu)", |
| __func__, (void *)arg, &target, sizeof(target)); |
| return -EFAULT; |
| } |
| if (target >= SEC_BLOB_MAX_CNT) { |
| eagle_ioctl_err("%s: license index %u out of bounds (max index is %i)", |
| __func__, target, SEC_BLOB_MAX_CNT-1); |
| return -EINVAL; |
| } |
| if (!_sec_blob[target] || |
| ((u32 *)_sec_blob[target])[0] == 0) { |
| eagle_ioctl_err("%s: license index %u is invalid", |
| __func__, target); |
| return -EINVAL; |
| } |
| if (core_dts_eagle_set(((s32 *)_sec_blob[target])[0], |
| (char *)&((s32 *)_sec_blob[target])[1]) < 0) |
| eagle_ioctl_err("%s: core_dts_eagle_set failed with id = %u", |
| __func__, target); |
| else |
| eagle_ioctl_info("%s: core_dts_eagle_set succeeded with id = %u", |
| __func__, target); |
| break; |
| } |
| case DTS_EAGLE_IOCTL_SET_VOLUME_COMMANDS: { |
| s32 spec = 0; |
| |
| eagle_ioctl_info("%s: control 0x%X (set volume commands)", |
| __func__, cmd); |
| if (copy_from_user((void *)&spec, (void *)arg, |
| sizeof(spec))) { |
| eagle_ioctl_err("%s: error reading volume command specifier (src:%pK, tgt:%pK, size:%zu)", |
| __func__, (void *)arg, &spec, sizeof(spec)); |
| return -EFAULT; |
| } |
| if (spec & 0x80000000) { |
| u32 idx = (spec & 0x0000F000) >> 12; |
| s32 size = spec & 0x00000FFF; |
| |
| eagle_ioctl_dbg("%s: setting volume command %i size: %i", |
| __func__, idx, size); |
| if (idx >= _vol_cmd_cnt) { |
| eagle_ioctl_err("%s: volume command index %u out of bounds (only %u allocated)", |
| __func__, idx, _vol_cmd_cnt); |
| return -EINVAL; |
| } |
| if (_volume_cmds_alloc2(idx, size) < 0) { |
| eagle_ioctl_err("%s: error allocating memory for volume controls", |
| __func__); |
| return -ENOMEM; |
| } |
| if (copy_from_user((void *)&_vol_cmds_d[idx], |
| (void *)(((char *)arg) + sizeof(int)), |
| sizeof(struct vol_cmds_d))) { |
| eagle_ioctl_err("%s: error reading volume command descriptor (src:%pK, tgt:%pK, size:%zu)", |
| __func__, ((char *)arg) + sizeof(int), |
| &_vol_cmds_d[idx], |
| sizeof(struct vol_cmds_d)); |
| return -EFAULT; |
| } |
| eagle_ioctl_dbg("%s: setting volume command %i spec (size %zu): %i %i %i %i", |
| __func__, idx, sizeof(struct vol_cmds_d), |
| _vol_cmds_d[idx].d[0], _vol_cmds_d[idx].d[1], |
| _vol_cmds_d[idx].d[2], _vol_cmds_d[idx].d[3]); |
| if (copy_from_user((void *)_vol_cmds[idx], |
| (void *)(((char *)arg) + (sizeof(int) + |
| sizeof(struct vol_cmds_d))), size)) { |
| eagle_ioctl_err("%s: error reading volume command string (src:%pK, tgt:%pK, size:%i)", |
| __func__, ((char *)arg) + (sizeof(int) + |
| sizeof(struct vol_cmds_d)), |
| _vol_cmds[idx], size); |
| return -EFAULT; |
| } |
| } else { |
| eagle_ioctl_dbg("%s: setting volume command size", |
| __func__); |
| if (spec < 0 || spec > VOL_CMD_CNT_MAX) { |
| eagle_ioctl_err("%s: volume command count %i out of bounds (min 0, max %i)", |
| __func__, spec, VOL_CMD_CNT_MAX); |
| return -EINVAL; |
| } else if (spec == 0) { |
| eagle_ioctl_dbg("%s: request to free volume commands", |
| __func__); |
| _volume_cmds_free(); |
| break; |
| } |
| eagle_ioctl_dbg("%s: setting volume command size requested = %i", |
| __func__, spec); |
| if (_volume_cmds_alloc1(spec) < 0) { |
| eagle_ioctl_err("%s: error allocating memory for volume controls", |
| __func__); |
| return -ENOMEM; |
| } |
| } |
| break; |
| } |
| default: { |
| eagle_ioctl_err("%s: control 0x%X (invalid control)", |
| __func__, cmd); |
| ret = -EINVAL; |
| } |
| } |
| return (int)ret; |
| } |
| |
| /** |
| * msm_dts_eagle_compat_ioctl() - To handle 32bit to 64bit ioctl compatibility |
| * @cmd: cmd to handle. |
| * @arg: argument to the cmd. |
| * |
| * Handle DTS Eagle ioctl cmds from 32bit userspace. |
| * |
| * Return: Return failure if any. |
| */ |
| #ifdef CONFIG_COMPAT |
| int msm_dts_eagle_compat_ioctl(unsigned int cmd, unsigned long arg) |
| { |
| switch (cmd) { |
| case DTS_EAGLE_IOCTL_GET_CACHE_SIZE32: |
| cmd = DTS_EAGLE_IOCTL_GET_CACHE_SIZE; |
| break; |
| case DTS_EAGLE_IOCTL_SET_CACHE_SIZE32: |
| cmd = DTS_EAGLE_IOCTL_SET_CACHE_SIZE; |
| break; |
| case DTS_EAGLE_IOCTL_GET_PARAM32: |
| cmd = DTS_EAGLE_IOCTL_GET_PARAM; |
| break; |
| case DTS_EAGLE_IOCTL_SET_PARAM32: |
| cmd = DTS_EAGLE_IOCTL_SET_PARAM; |
| break; |
| case DTS_EAGLE_IOCTL_SET_CACHE_BLOCK32: |
| cmd = DTS_EAGLE_IOCTL_SET_CACHE_BLOCK; |
| break; |
| case DTS_EAGLE_IOCTL_SET_ACTIVE_DEVICE32: |
| cmd = DTS_EAGLE_IOCTL_SET_ACTIVE_DEVICE; |
| break; |
| case DTS_EAGLE_IOCTL_GET_LICENSE32: |
| cmd = DTS_EAGLE_IOCTL_GET_LICENSE; |
| break; |
| case DTS_EAGLE_IOCTL_SET_LICENSE32: |
| cmd = DTS_EAGLE_IOCTL_SET_LICENSE; |
| break; |
| case DTS_EAGLE_IOCTL_SEND_LICENSE32: |
| cmd = DTS_EAGLE_IOCTL_SEND_LICENSE; |
| break; |
| case DTS_EAGLE_IOCTL_SET_VOLUME_COMMANDS32: |
| cmd = DTS_EAGLE_IOCTL_SET_VOLUME_COMMANDS; |
| break; |
| default: |
| break; |
| } |
| return msm_dts_eagle_ioctl(cmd, arg); |
| } |
| #endif |
| /** |
| * msm_dts_eagle_init_pre() - Initialize DTS premix module |
| * @ac: Initialize premix module in the ASM session. |
| * |
| * Initialize DTS premix module on provided ASM session |
| * |
| * Return: Return failure if any. |
| */ |
| int msm_dts_eagle_init_pre(struct audio_client *ac) |
| { |
| return msm_dts_eagle_enable_asm(ac, _is_hpx_enabled, |
| AUDPROC_MODULE_ID_DTS_HPX_PREMIX); |
| } |
| |
| /** |
| * msm_dts_eagle_deinit_pre() - Deinitialize DTS premix module |
| * @ac: Deinitialize premix module in the ASM session. |
| * |
| * Deinitialize DTS premix module on provided ASM session |
| * |
| * Return: Currently does nothing so 0. |
| */ |
| int msm_dts_eagle_deinit_pre(struct audio_client *ac) |
| { |
| return 0; |
| } |
| |
| /** |
| * msm_dts_eagle_init_post() - Initialize DTS postmix module |
| * @port_id: Port id for the ADM session. |
| * @copp_idx: Copp idx for the ADM session. |
| * |
| * Initialize DTS postmix module on ADM session |
| * |
| * Return: Return failure if any. |
| */ |
| int msm_dts_eagle_init_post(int port_id, int copp_idx) |
| { |
| return msm_dts_eagle_enable_adm(port_id, copp_idx, _is_hpx_enabled); |
| } |
| |
| /** |
| * msm_dts_eagle_deinit_post() - Deinitialize DTS postmix module |
| * @port_id: Port id for the ADM session. |
| * @topology: Topology in use. |
| * |
| * Deinitialize DTS postmix module on ADM session |
| * |
| * Return: Currently does nothing so 0. |
| */ |
| int msm_dts_eagle_deinit_post(int port_id, int topology) |
| { |
| return 0; |
| } |
| |
| /** |
| * msm_dts_eagle_init_master_module() - Initialize both DTS modules |
| * @ac: Initialize modules in the ASM session. |
| * |
| * Initialize DTS modules on ASM session |
| * |
| * Return: Success. |
| */ |
| int msm_dts_eagle_init_master_module(struct audio_client *ac) |
| { |
| _set_audioclient(ac); |
| msm_dts_eagle_enable_asm(ac, _is_hpx_enabled, |
| AUDPROC_MODULE_ID_DTS_HPX_PREMIX); |
| msm_dts_eagle_enable_asm(ac, _is_hpx_enabled, |
| AUDPROC_MODULE_ID_DTS_HPX_POSTMIX); |
| return 0; |
| } |
| |
| /** |
| * msm_dts_eagle_deinit_master_module() - Deinitialize both DTS modules |
| * @ac: Deinitialize modules in the ASM session. |
| * |
| * Deinitialize DTS modules on ASM session |
| * |
| * Return: Success. |
| */ |
| int msm_dts_eagle_deinit_master_module(struct audio_client *ac) |
| { |
| msm_dts_eagle_deinit_pre(ac); |
| msm_dts_eagle_deinit_post(-1, 0); |
| _clear_audioclient(); |
| return 0; |
| } |
| |
| /** |
| * msm_dts_eagle_is_hpx_on() - Check if HPX effects are On |
| * |
| * Check if HPX effects are On |
| * |
| * Return: On/Off. |
| */ |
| int msm_dts_eagle_is_hpx_on(void) |
| { |
| return _is_hpx_enabled; |
| } |
| |
| /** |
| * msm_dts_eagle_pcm_new() - Create hwdep node |
| * @runtime: snd_soc_pcm_runtime structure. |
| * |
| * Create hwdep node |
| * |
| * Return: Success. |
| */ |
| int msm_dts_eagle_pcm_new(struct snd_soc_pcm_runtime *runtime) |
| { |
| if (!_ref_cnt++) { |
| _init_cb_descs(); |
| _reg_ion_mem(); |
| } |
| return 0; |
| } |
| |
| /** |
| * msm_dts_eagle_pcm_free() - remove hwdep node |
| * @runtime: snd_soc_pcm_runtime structure. |
| * |
| * Remove hwdep node |
| * |
| * Return: void. |
| */ |
| void msm_dts_eagle_pcm_free(struct snd_pcm *pcm) |
| { |
| if (!--_ref_cnt) |
| _unreg_ion_mem(); |
| vfree(_depc); |
| } |
| |
| MODULE_DESCRIPTION("DTS EAGLE platform driver"); |
| MODULE_LICENSE("GPL v2"); |