| /* Copyright (c) 2010-2013, 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/slab.h> |
| #include <linux/fs.h> |
| #include <linux/module.h> |
| #include <linux/miscdevice.h> |
| #include <linux/mutex.h> |
| #include <linux/uaccess.h> |
| #include <linux/msm_ion.h> |
| #include <linux/mm.h> |
| #include <linux/msm_audio_ion.h> |
| #include "audio_acdb.h" |
| #include "q6voice.h" |
| |
| #include <sound/q6adm-v2.h> |
| #include <sound/q6afe-v2.h> |
| #include <sound/q6asm-v2.h> |
| #include <sound/q6lsm.h> |
| |
| |
| #define MAX_NETWORKS 15 |
| #define MAX_IOCTL_DATA (MAX_NETWORKS * 2) |
| #define MAX_COL_SIZE 324 |
| |
| #define ACDB_BLOCK_SIZE 4096 |
| #define NUM_VOCPROC_BLOCKS (6 * MAX_NETWORKS) |
| #define ACDB_TOTAL_VOICE_ALLOCATION (ACDB_BLOCK_SIZE * NUM_VOCPROC_BLOCKS) |
| |
| #define MAX_HW_DELAY_ENTRIES 25 |
| |
| struct acdb_data { |
| uint32_t usage_count; |
| |
| struct mutex acdb_mutex; |
| |
| /* ANC Cal */ |
| struct acdb_cal_block anc_cal; |
| |
| /* AANC Cal */ |
| struct acdb_cal_block aanc_cal; |
| |
| /* LSM Cal */ |
| struct acdb_cal_block lsm_cal; |
| |
| /* AudProc Cal */ |
| uint32_t asm_topology; |
| uint32_t adm_topology[MAX_AUDPROC_TYPES]; |
| struct acdb_cal_block audproc_cal[MAX_AUDPROC_TYPES]; |
| struct acdb_cal_block audstrm_cal[MAX_AUDPROC_TYPES]; |
| struct acdb_cal_block audvol_cal[MAX_AUDPROC_TYPES]; |
| |
| /* VocProc Cal */ |
| uint32_t voice_rx_topology; |
| uint32_t voice_tx_topology; |
| struct acdb_cal_block vocproc_cal; |
| struct acdb_cal_block vocstrm_cal; |
| struct acdb_cal_block vocvol_cal; |
| |
| /* Voice Column data */ |
| struct acdb_cal_block vocproc_col_cal[MAX_VOCPROC_TYPES]; |
| uint32_t *col_data[MAX_VOCPROC_TYPES]; |
| |
| /* VocProc dev cfg cal*/ |
| struct acdb_cal_block vocproc_dev_cal; |
| |
| /* Custom topology */ |
| struct acdb_cal_block adm_custom_topology; |
| struct acdb_cal_block asm_custom_topology; |
| uint32_t valid_adm_custom_top; |
| uint32_t valid_asm_custom_top; |
| |
| /* AFE cal */ |
| struct acdb_cal_block afe_cal[MAX_AUDPROC_TYPES]; |
| |
| /* Sidetone Cal */ |
| struct sidetone_cal sidetone_cal; |
| |
| /* Allocation information */ |
| struct ion_client *ion_client; |
| struct ion_handle *ion_handle; |
| uint32_t map_handle; |
| uint64_t paddr; |
| uint64_t kvaddr; |
| uint64_t mem_len; |
| |
| /* Speaker protection */ |
| struct msm_spk_prot_cfg spk_prot_cfg; |
| |
| /* Av sync delay info */ |
| struct hw_delay hw_delay_rx; |
| struct hw_delay hw_delay_tx; |
| }; |
| |
| static struct acdb_data acdb_data; |
| |
| uint32_t get_voice_rx_topology(void) |
| { |
| return acdb_data.voice_rx_topology; |
| } |
| |
| void store_voice_rx_topology(uint32_t topology) |
| { |
| acdb_data.voice_rx_topology = topology; |
| } |
| |
| uint32_t get_voice_tx_topology(void) |
| { |
| return acdb_data.voice_tx_topology; |
| } |
| |
| void store_voice_tx_topology(uint32_t topology) |
| { |
| acdb_data.voice_tx_topology = topology; |
| } |
| |
| uint32_t get_adm_rx_topology(void) |
| { |
| return acdb_data.adm_topology[RX_CAL]; |
| } |
| |
| void store_adm_rx_topology(uint32_t topology) |
| { |
| acdb_data.adm_topology[RX_CAL] = topology; |
| } |
| |
| uint32_t get_adm_tx_topology(void) |
| { |
| return acdb_data.adm_topology[TX_CAL]; |
| } |
| |
| void store_adm_tx_topology(uint32_t topology) |
| { |
| acdb_data.adm_topology[TX_CAL] = topology; |
| } |
| |
| uint32_t get_asm_topology(void) |
| { |
| return acdb_data.asm_topology; |
| } |
| |
| void store_asm_topology(uint32_t topology) |
| { |
| acdb_data.asm_topology = topology; |
| } |
| |
| void reset_custom_topology_flags(void) |
| { |
| mutex_lock(&acdb_data.acdb_mutex); |
| acdb_data.valid_adm_custom_top = 1; |
| acdb_data.valid_asm_custom_top = 1; |
| mutex_unlock(&acdb_data.acdb_mutex); |
| } |
| |
| int get_adm_custom_topology(struct acdb_cal_block *cal_block) |
| { |
| int result = 0; |
| pr_debug("%s\n", __func__); |
| |
| if (cal_block == NULL) { |
| pr_err("ACDB=> NULL pointer sent to %s\n", __func__); |
| result = -EINVAL; |
| goto done; |
| } |
| |
| mutex_lock(&acdb_data.acdb_mutex); |
| /* Only return allow one access after memory registered */ |
| if (acdb_data.valid_adm_custom_top == 0) { |
| cal_block->cal_size = 0; |
| goto unlock; |
| } |
| acdb_data.valid_adm_custom_top = 0; |
| |
| cal_block->cal_size = acdb_data.adm_custom_topology.cal_size; |
| cal_block->cal_paddr = acdb_data.adm_custom_topology.cal_paddr; |
| cal_block->cal_kvaddr = acdb_data.adm_custom_topology.cal_kvaddr; |
| unlock: |
| mutex_unlock(&acdb_data.acdb_mutex); |
| done: |
| return result; |
| } |
| |
| int store_adm_custom_topology(struct cal_block *cal_block) |
| { |
| int result = 0; |
| pr_debug("%s,\n", __func__); |
| |
| if (cal_block->cal_offset > acdb_data.mem_len) { |
| pr_err("%s: offset %d is > mem_len %llu\n", |
| __func__, cal_block->cal_offset, acdb_data.mem_len); |
| result = -EINVAL; |
| goto done; |
| } |
| |
| acdb_data.adm_custom_topology.cal_size = cal_block->cal_size; |
| acdb_data.adm_custom_topology.cal_paddr = |
| cal_block->cal_offset + acdb_data.paddr; |
| acdb_data.adm_custom_topology.cal_kvaddr = |
| cal_block->cal_offset + acdb_data.kvaddr; |
| done: |
| return result; |
| } |
| |
| int get_asm_custom_topology(struct acdb_cal_block *cal_block) |
| { |
| int result = 0; |
| pr_debug("%s,\n", __func__); |
| |
| if (cal_block == NULL) { |
| pr_err("ACDB=> NULL pointer sent to %s\n", __func__); |
| result = -EINVAL; |
| goto done; |
| } |
| |
| mutex_lock(&acdb_data.acdb_mutex); |
| /* Only return allow one access after memory registered */ |
| if (acdb_data.valid_asm_custom_top == 0) { |
| cal_block->cal_size = 0; |
| goto unlock; |
| } |
| acdb_data.valid_asm_custom_top = 0; |
| |
| cal_block->cal_size = acdb_data.asm_custom_topology.cal_size; |
| cal_block->cal_paddr = acdb_data.asm_custom_topology.cal_paddr; |
| cal_block->cal_kvaddr = acdb_data.asm_custom_topology.cal_kvaddr; |
| unlock: |
| mutex_unlock(&acdb_data.acdb_mutex); |
| done: |
| return result; |
| } |
| |
| int store_asm_custom_topology(struct cal_block *cal_block) |
| { |
| int result = 0; |
| pr_debug("%s,\n", __func__); |
| |
| if (cal_block->cal_offset > acdb_data.mem_len) { |
| pr_err("%s: offset %d is > mem_len %llu\n", |
| __func__, cal_block->cal_offset, acdb_data.mem_len); |
| result = -EINVAL; |
| goto done; |
| } |
| |
| acdb_data.asm_custom_topology.cal_size = cal_block->cal_size; |
| acdb_data.asm_custom_topology.cal_paddr = |
| cal_block->cal_offset + acdb_data.paddr; |
| acdb_data.asm_custom_topology.cal_kvaddr = |
| cal_block->cal_offset + acdb_data.kvaddr; |
| done: |
| return result; |
| } |
| |
| int get_voice_cal_allocation(struct acdb_cal_block *cal_block) |
| { |
| int result = 0; |
| pr_debug("%s,\n", __func__); |
| |
| if (cal_block == NULL) { |
| pr_err("ACDB=> NULL pointer sent to %s\n", __func__); |
| result = -EINVAL; |
| goto done; |
| } |
| |
| cal_block->cal_size = ACDB_TOTAL_VOICE_ALLOCATION; |
| cal_block->cal_paddr = acdb_data.vocproc_cal.cal_paddr; |
| cal_block->cal_kvaddr = acdb_data.vocproc_cal.cal_kvaddr; |
| done: |
| return result; |
| } |
| |
| int get_aanc_cal(struct acdb_cal_block *cal_block) |
| { |
| int result = 0; |
| pr_debug("%s,\n", __func__); |
| |
| if (cal_block == NULL) { |
| pr_err("ACDB=> NULL pointer sent to %s\n", __func__); |
| result = -EINVAL; |
| goto done; |
| } |
| |
| cal_block->cal_size = acdb_data.aanc_cal.cal_size; |
| cal_block->cal_paddr = acdb_data.aanc_cal.cal_paddr; |
| cal_block->cal_kvaddr = acdb_data.aanc_cal.cal_kvaddr; |
| done: |
| return result; |
| } |
| |
| int store_aanc_cal(struct cal_block *cal_block) |
| { |
| int result = 0; |
| pr_debug("%s,\n", __func__); |
| |
| if (cal_block->cal_offset > acdb_data.mem_len) { |
| pr_err("%s: offset %d is > mem_len %llu\n", |
| __func__, cal_block->cal_offset, acdb_data.mem_len); |
| result = -EINVAL; |
| goto done; |
| } |
| |
| acdb_data.aanc_cal.cal_size = cal_block->cal_size; |
| acdb_data.aanc_cal.cal_paddr = |
| cal_block->cal_offset + acdb_data.paddr; |
| acdb_data.aanc_cal.cal_kvaddr = |
| cal_block->cal_offset + acdb_data.kvaddr; |
| done: |
| return result; |
| } |
| |
| int get_lsm_cal(struct acdb_cal_block *cal_block) |
| { |
| int result = 0; |
| pr_debug("%s,\n", __func__); |
| |
| if (cal_block == NULL) { |
| pr_err("ACDB=> NULL pointer sent to %s\n", __func__); |
| result = -EINVAL; |
| goto done; |
| } |
| |
| cal_block->cal_size = acdb_data.lsm_cal.cal_size; |
| cal_block->cal_paddr = acdb_data.lsm_cal.cal_paddr; |
| cal_block->cal_kvaddr = acdb_data.lsm_cal.cal_kvaddr; |
| done: |
| return result; |
| } |
| |
| int store_lsm_cal(struct cal_block *cal_block) |
| { |
| int result = 0; |
| pr_debug("%s,\n", __func__); |
| |
| if (cal_block->cal_offset > acdb_data.mem_len) { |
| pr_err("%s: offset %d is > mem_len %llu\n", |
| __func__, cal_block->cal_offset, acdb_data.mem_len); |
| result = -EINVAL; |
| goto done; |
| } |
| |
| acdb_data.lsm_cal.cal_size = cal_block->cal_size; |
| acdb_data.lsm_cal.cal_paddr = |
| cal_block->cal_offset + acdb_data.paddr; |
| acdb_data.lsm_cal.cal_kvaddr = |
| cal_block->cal_offset + acdb_data.kvaddr; |
| done: |
| return result; |
| } |
| |
| int get_hw_delay(int32_t path, struct hw_delay_entry *entry) |
| { |
| int i, result = 0; |
| struct hw_delay *delay = NULL; |
| struct hw_delay_entry *info = NULL; |
| pr_debug("%s,\n", __func__); |
| |
| if (entry == NULL) { |
| pr_err("ACDB=> NULL pointer sent to %s\n", __func__); |
| result = -EINVAL; |
| goto ret; |
| } |
| if ((path >= MAX_AUDPROC_TYPES) || (path < 0)) { |
| pr_err("ACDB=> Bad path sent to %s, path: %d\n", |
| __func__, path); |
| result = -EINVAL; |
| goto ret; |
| } |
| mutex_lock(&acdb_data.acdb_mutex); |
| if (path == RX_CAL) |
| delay = &acdb_data.hw_delay_rx; |
| else if (path == TX_CAL) |
| delay = &acdb_data.hw_delay_tx; |
| |
| if ((delay == NULL) || ((delay != NULL) && delay->num_entries == 0)) { |
| pr_debug("ACDB=> %s Invalid delay/ delay entries\n", __func__); |
| result = -EINVAL; |
| goto done; |
| } |
| |
| info = (struct hw_delay_entry *)(delay->delay_info); |
| if (info == NULL) { |
| pr_err("ACDB=> %s Delay entries info is NULL\n", __func__); |
| result = -EFAULT; |
| goto done; |
| } |
| for (i = 0; i < delay->num_entries; i++) { |
| if (info[i].sample_rate == entry->sample_rate) { |
| entry->delay_usec = info[i].delay_usec; |
| break; |
| } |
| } |
| if (i == delay->num_entries) { |
| pr_err("ACDB=> %s: Unable to find delay for sample rate %d\n", |
| __func__, entry->sample_rate); |
| result = -EFAULT; |
| } |
| |
| done: |
| mutex_unlock(&acdb_data.acdb_mutex); |
| ret: |
| pr_debug("ACDB=> %s: Path = %d samplerate = %u usec = %u status %d\n", |
| __func__, path, entry->sample_rate, entry->delay_usec, result); |
| return result; |
| } |
| |
| int store_hw_delay(int32_t path, void *arg) |
| { |
| int result = 0; |
| struct hw_delay delay; |
| struct hw_delay *delay_dest = NULL; |
| pr_debug("%s,\n", __func__); |
| |
| if ((path >= MAX_AUDPROC_TYPES) || (path < 0) || (arg == NULL)) { |
| pr_err("ACDB=> Bad path/ pointer sent to %s, path: %d\n", |
| __func__, path); |
| result = -EINVAL; |
| goto done; |
| } |
| result = copy_from_user((void *)&delay, (void *)arg, |
| sizeof(struct hw_delay)); |
| if (result) { |
| pr_err("ACDB=> %s failed to copy hw delay: result=%d path=%d\n", |
| __func__, result, path); |
| result = -EFAULT; |
| goto done; |
| } |
| if ((delay.num_entries <= 0) || |
| (delay.num_entries > MAX_HW_DELAY_ENTRIES)) { |
| pr_debug("ACDB=> %s incorrect no of hw delay entries: %d\n", |
| __func__, delay.num_entries); |
| result = -EINVAL; |
| goto done; |
| } |
| if ((path >= MAX_AUDPROC_TYPES) || (path < 0)) { |
| pr_err("ACDB=> Bad path sent to %s, path: %d\n", |
| __func__, path); |
| result = -EINVAL; |
| goto done; |
| } |
| |
| pr_debug("ACDB=> %s : Path = %d num_entries = %d\n", |
| __func__, path, delay.num_entries); |
| |
| if (path == RX_CAL) |
| delay_dest = &acdb_data.hw_delay_rx; |
| else if (path == TX_CAL) |
| delay_dest = &acdb_data.hw_delay_tx; |
| |
| delay_dest->num_entries = delay.num_entries; |
| |
| result = copy_from_user(delay_dest->delay_info, |
| delay.delay_info, |
| (sizeof(struct hw_delay_entry)* |
| delay.num_entries)); |
| if (result) { |
| pr_err("ACDB=> %s failed to copy hw delay info res=%d path=%d", |
| __func__, result, path); |
| result = -EFAULT; |
| } |
| done: |
| return result; |
| } |
| |
| int get_anc_cal(struct acdb_cal_block *cal_block) |
| { |
| int result = 0; |
| pr_debug("%s,\n", __func__); |
| |
| if (cal_block == NULL) { |
| pr_err("ACDB=> NULL pointer sent to %s\n", __func__); |
| result = -EINVAL; |
| goto done; |
| } |
| |
| cal_block->cal_size = acdb_data.anc_cal.cal_size; |
| cal_block->cal_paddr = acdb_data.anc_cal.cal_paddr; |
| cal_block->cal_kvaddr = acdb_data.anc_cal.cal_kvaddr; |
| done: |
| return result; |
| } |
| |
| int store_anc_cal(struct cal_block *cal_block) |
| { |
| int result = 0; |
| pr_debug("%s,\n", __func__); |
| |
| if (cal_block->cal_offset > acdb_data.mem_len) { |
| pr_err("%s: offset %d is > mem_len %llu\n", |
| __func__, cal_block->cal_offset, acdb_data.mem_len); |
| result = -EINVAL; |
| goto done; |
| } |
| |
| acdb_data.anc_cal.cal_size = cal_block->cal_size; |
| acdb_data.anc_cal.cal_paddr = |
| cal_block->cal_offset + acdb_data.paddr; |
| acdb_data.anc_cal.cal_kvaddr = |
| cal_block->cal_offset + acdb_data.kvaddr; |
| done: |
| return result; |
| } |
| |
| int store_afe_cal(int32_t path, struct cal_block *cal_block) |
| { |
| int result = 0; |
| pr_debug("%s, path = %d\n", __func__, path); |
| |
| if (cal_block->cal_offset > acdb_data.mem_len) { |
| pr_err("%s: offset %d is > mem_len %llu\n", |
| __func__, cal_block->cal_offset, acdb_data.mem_len); |
| result = -EINVAL; |
| goto done; |
| } |
| if ((path >= MAX_AUDPROC_TYPES) || (path < 0)) { |
| pr_err("ACDB=> Bad path sent to %s, path: %d\n", |
| __func__, path); |
| result = -EINVAL; |
| goto done; |
| } |
| |
| acdb_data.afe_cal[path].cal_size = cal_block->cal_size; |
| acdb_data.afe_cal[path].cal_paddr = |
| cal_block->cal_offset + acdb_data.paddr; |
| acdb_data.afe_cal[path].cal_kvaddr = |
| cal_block->cal_offset + acdb_data.kvaddr; |
| done: |
| return result; |
| } |
| |
| int get_afe_cal(int32_t path, struct acdb_cal_block *cal_block) |
| { |
| int result = 0; |
| pr_debug("%s, path = %d\n", __func__, path); |
| |
| if (cal_block == NULL) { |
| pr_err("ACDB=> NULL pointer sent to %s\n", __func__); |
| result = -EINVAL; |
| goto done; |
| } |
| if ((path >= MAX_AUDPROC_TYPES) || (path < 0)) { |
| pr_err("ACDB=> Bad path sent to %s, path: %d\n", |
| __func__, path); |
| result = -EINVAL; |
| goto done; |
| } |
| |
| cal_block->cal_size = acdb_data.afe_cal[path].cal_size; |
| cal_block->cal_paddr = acdb_data.afe_cal[path].cal_paddr; |
| cal_block->cal_kvaddr = acdb_data.afe_cal[path].cal_kvaddr; |
| done: |
| return result; |
| } |
| |
| int store_audproc_cal(int32_t path, struct cal_block *cal_block) |
| { |
| int result = 0; |
| pr_debug("%s, path = %d\n", __func__, path); |
| |
| if (cal_block->cal_offset > acdb_data.mem_len) { |
| pr_err("%s: offset %d is > mem_len %llu\n", |
| __func__, cal_block->cal_offset, acdb_data.mem_len); |
| result = -EINVAL; |
| goto done; |
| } |
| if (path >= MAX_AUDPROC_TYPES) { |
| pr_err("ACDB=> Bad path sent to %s, path: %d\n", |
| __func__, path); |
| result = -EINVAL; |
| goto done; |
| } |
| |
| acdb_data.audproc_cal[path].cal_size = cal_block->cal_size; |
| acdb_data.audproc_cal[path].cal_paddr = |
| cal_block->cal_offset + acdb_data.paddr; |
| acdb_data.audproc_cal[path].cal_kvaddr = |
| cal_block->cal_offset + acdb_data.kvaddr; |
| done: |
| return result; |
| } |
| |
| int get_audproc_cal(int32_t path, struct acdb_cal_block *cal_block) |
| { |
| int result = 0; |
| pr_debug("%s, path = %d\n", __func__, path); |
| |
| if (cal_block == NULL) { |
| pr_err("ACDB=> NULL pointer sent to %s\n", __func__); |
| result = -EINVAL; |
| goto done; |
| } |
| if (path >= MAX_AUDPROC_TYPES) { |
| pr_err("ACDB=> Bad path sent to %s, path: %d\n", |
| __func__, path); |
| result = -EINVAL; |
| goto done; |
| } |
| |
| cal_block->cal_size = acdb_data.audproc_cal[path].cal_size; |
| cal_block->cal_paddr = acdb_data.audproc_cal[path].cal_paddr; |
| cal_block->cal_kvaddr = acdb_data.audproc_cal[path].cal_kvaddr; |
| done: |
| return result; |
| } |
| |
| int store_audstrm_cal(int32_t path, struct cal_block *cal_block) |
| { |
| int result = 0; |
| pr_debug("%s, path = %d\n", __func__, path); |
| |
| if (cal_block->cal_offset > acdb_data.mem_len) { |
| pr_err("%s: offset %d is > mem_len %llu\n", |
| __func__, cal_block->cal_offset, acdb_data.mem_len); |
| result = -EINVAL; |
| goto done; |
| } |
| if (path >= MAX_AUDPROC_TYPES) { |
| pr_err("ACDB=> Bad path sent to %s, path: %d\n", |
| __func__, path); |
| result = -EINVAL; |
| goto done; |
| } |
| |
| acdb_data.audstrm_cal[path].cal_size = cal_block->cal_size; |
| acdb_data.audstrm_cal[path].cal_paddr = |
| cal_block->cal_offset + acdb_data.paddr; |
| acdb_data.audstrm_cal[path].cal_kvaddr = |
| cal_block->cal_offset + acdb_data.kvaddr; |
| done: |
| return result; |
| } |
| |
| int get_audstrm_cal(int32_t path, struct acdb_cal_block *cal_block) |
| { |
| int result = 0; |
| pr_debug("%s, path = %d\n", __func__, path); |
| |
| if (cal_block == NULL) { |
| pr_err("ACDB=> NULL pointer sent to %s\n", __func__); |
| result = -EINVAL; |
| goto done; |
| } |
| if (path >= MAX_AUDPROC_TYPES) { |
| pr_err("ACDB=> Bad path sent to %s, path: %d\n", |
| __func__, path); |
| result = -EINVAL; |
| goto done; |
| } |
| |
| cal_block->cal_size = acdb_data.audstrm_cal[path].cal_size; |
| cal_block->cal_paddr = acdb_data.audstrm_cal[path].cal_paddr; |
| cal_block->cal_kvaddr = acdb_data.audstrm_cal[path].cal_kvaddr; |
| done: |
| return result; |
| } |
| |
| int store_audvol_cal(int32_t path, struct cal_block *cal_block) |
| { |
| int result = 0; |
| pr_debug("%s, path = %d\n", __func__, path); |
| |
| if (cal_block->cal_offset > acdb_data.mem_len) { |
| pr_err("%s: offset %d is > mem_len %llu\n", |
| __func__, cal_block->cal_offset, acdb_data.mem_len); |
| result = -EINVAL; |
| goto done; |
| } |
| if (path >= MAX_AUDPROC_TYPES) { |
| pr_err("ACDB=> Bad path sent to %s, path: %d\n", |
| __func__, path); |
| result = -EINVAL; |
| goto done; |
| } |
| |
| acdb_data.audvol_cal[path].cal_size = cal_block->cal_size; |
| acdb_data.audvol_cal[path].cal_paddr = |
| cal_block->cal_offset + acdb_data.paddr; |
| acdb_data.audvol_cal[path].cal_kvaddr = |
| cal_block->cal_offset + acdb_data.kvaddr; |
| done: |
| return result; |
| } |
| |
| int get_audvol_cal(int32_t path, struct acdb_cal_block *cal_block) |
| { |
| int result = 0; |
| pr_debug("%s, path = %d\n", __func__, path); |
| |
| if (cal_block == NULL) { |
| pr_err("ACDB=> NULL pointer sent to %s\n", __func__); |
| result = -EINVAL; |
| goto done; |
| } |
| if (path >= MAX_AUDPROC_TYPES || path < 0) { |
| pr_err("ACDB=> Bad path sent to %s, path: %d\n", |
| __func__, path); |
| result = -EINVAL; |
| goto done; |
| } |
| |
| cal_block->cal_size = acdb_data.audvol_cal[path].cal_size; |
| cal_block->cal_paddr = acdb_data.audvol_cal[path].cal_paddr; |
| cal_block->cal_kvaddr = acdb_data.audvol_cal[path].cal_kvaddr; |
| done: |
| return result; |
| } |
| |
| int store_voice_col_data(uint32_t vocproc_type, uint32_t cal_size, |
| uint32_t *cal_block) |
| { |
| int result = 0; |
| pr_debug("%s,\n", __func__); |
| |
| if (cal_block == NULL) { |
| pr_err("ACDB=> NULL pointer sent to %s\n", __func__); |
| result = -EINVAL; |
| goto done; |
| } |
| if (cal_size > MAX_COL_SIZE) { |
| pr_err("%s: col size is to big %d\n", __func__, cal_size); |
| result = -EINVAL; |
| goto done; |
| } |
| if (acdb_data.col_data[vocproc_type] == NULL) { |
| pr_err("%s: vocproc_type %d data not allocated!\n", |
| __func__, vocproc_type); |
| result = -EINVAL; |
| goto done; |
| } |
| if (copy_from_user(acdb_data.col_data[vocproc_type], |
| (void *)((uint8_t *)cal_block + sizeof(cal_size)), |
| cal_size)) { |
| pr_err("%s: fail to copy col size %d\n", __func__, cal_size); |
| result = -EINVAL; |
| goto done; |
| } |
| acdb_data.vocproc_col_cal[vocproc_type].cal_size = cal_size; |
| done: |
| return result; |
| } |
| |
| int get_voice_col_data(uint32_t vocproc_type, |
| struct acdb_cal_block *cal_block) |
| { |
| int result = 0; |
| pr_debug("%s,\n", __func__); |
| |
| if (cal_block == NULL) { |
| pr_err("ACDB=> NULL pointer sent to %s\n", __func__); |
| result = -EINVAL; |
| goto done; |
| } |
| if (acdb_data.col_data[vocproc_type] == NULL) { |
| pr_err("%s: vocproc_type %d data not allocated!\n", |
| __func__, vocproc_type); |
| result = -EINVAL; |
| goto done; |
| } |
| |
| cal_block->cal_size = acdb_data. |
| vocproc_col_cal[vocproc_type].cal_size; |
| cal_block->cal_paddr = acdb_data. |
| vocproc_col_cal[vocproc_type].cal_paddr; |
| cal_block->cal_kvaddr = acdb_data. |
| vocproc_col_cal[vocproc_type].cal_kvaddr; |
| done: |
| return result; |
| } |
| |
| int store_vocproc_dev_cfg_cal(struct cal_block *cal_block) |
| { |
| int result = 0; |
| pr_debug("%s,\n", __func__); |
| |
| |
| if (cal_block->cal_offset > |
| acdb_data.mem_len) { |
| pr_err("%s: offset %d is > mem_len %llu\n", |
| __func__, cal_block->cal_offset, acdb_data.mem_len); |
| acdb_data.vocproc_dev_cal.cal_size = 0; |
| result = -EINVAL; |
| goto done; |
| } |
| |
| acdb_data.vocproc_dev_cal.cal_size = cal_block->cal_size; |
| acdb_data.vocproc_dev_cal.cal_paddr = |
| cal_block->cal_offset + acdb_data.paddr; |
| acdb_data.vocproc_dev_cal.cal_kvaddr = |
| cal_block->cal_offset + acdb_data.kvaddr; |
| done: |
| return result; |
| } |
| |
| int get_vocproc_dev_cfg_cal(struct acdb_cal_block *cal_block) |
| { |
| int result = 0; |
| pr_debug("%s,\n", __func__); |
| |
| if (cal_block == NULL) { |
| pr_err("ACDB=> NULL pointer sent to %s\n", __func__); |
| result = -EINVAL; |
| goto done; |
| } |
| |
| cal_block->cal_size = acdb_data.vocproc_dev_cal.cal_size; |
| cal_block->cal_paddr = acdb_data.vocproc_dev_cal.cal_paddr; |
| cal_block->cal_kvaddr = acdb_data.vocproc_dev_cal.cal_kvaddr; |
| done: |
| return result; |
| } |
| |
| |
| |
| int store_vocproc_cal(struct cal_block *cal_block) |
| { |
| int result = 0; |
| pr_debug("%s,\n", __func__); |
| |
| if (cal_block->cal_offset > |
| acdb_data.mem_len) { |
| pr_err("%s: offset %d is > mem_len %llu\n", |
| __func__, cal_block->cal_offset, acdb_data.mem_len); |
| acdb_data.vocproc_cal.cal_size = 0; |
| result = -EINVAL; |
| goto done; |
| } |
| |
| acdb_data.vocproc_cal.cal_size = cal_block->cal_size; |
| acdb_data.vocproc_cal.cal_paddr = |
| cal_block->cal_offset + acdb_data.paddr; |
| acdb_data.vocproc_cal.cal_kvaddr = |
| cal_block->cal_offset + acdb_data.kvaddr; |
| done: |
| return result; |
| } |
| |
| int get_vocproc_cal(struct acdb_cal_block *cal_block) |
| { |
| int result = 0; |
| pr_debug("%s,\n", __func__); |
| |
| if (cal_block == NULL) { |
| pr_err("ACDB=> NULL pointer sent to %s\n", __func__); |
| result = -EINVAL; |
| goto done; |
| } |
| |
| cal_block->cal_size = acdb_data.vocproc_cal.cal_size; |
| cal_block->cal_paddr = acdb_data.vocproc_cal.cal_paddr; |
| cal_block->cal_kvaddr = acdb_data.vocproc_cal.cal_kvaddr; |
| done: |
| return result; |
| } |
| |
| int store_vocstrm_cal(struct cal_block *cal_block) |
| { |
| int result = 0; |
| pr_debug("%s,\n", __func__); |
| |
| if (cal_block->cal_offset > |
| acdb_data.mem_len) { |
| pr_err("%s: offset %d is > mem_len %llu\n", |
| __func__, cal_block->cal_offset, acdb_data.mem_len); |
| acdb_data.vocstrm_cal.cal_size = 0; |
| result = -EINVAL; |
| goto done; |
| } |
| |
| acdb_data.vocstrm_cal.cal_size = cal_block->cal_size; |
| acdb_data.vocstrm_cal.cal_paddr = |
| cal_block->cal_offset + acdb_data.paddr; |
| acdb_data.vocstrm_cal.cal_kvaddr = |
| cal_block->cal_offset + acdb_data.kvaddr; |
| done: |
| return result; |
| } |
| |
| int get_vocstrm_cal(struct acdb_cal_block *cal_block) |
| { |
| int result = 0; |
| pr_debug("%s,\n", __func__); |
| |
| if (cal_block == NULL) { |
| pr_err("ACDB=> NULL pointer sent to %s\n", __func__); |
| result = -EINVAL; |
| goto done; |
| } |
| |
| cal_block->cal_size = acdb_data.vocstrm_cal.cal_size; |
| cal_block->cal_paddr = acdb_data.vocstrm_cal.cal_paddr; |
| cal_block->cal_kvaddr = acdb_data.vocstrm_cal.cal_kvaddr; |
| done: |
| return result; |
| } |
| |
| int store_vocvol_cal(struct cal_block *cal_block) |
| { |
| int result = 0; |
| pr_debug("%s,\n", __func__); |
| |
| if (cal_block->cal_offset > acdb_data.mem_len) { |
| pr_err("%s: offset %d is > mem_len %llu\n", |
| __func__, cal_block->cal_offset, acdb_data.mem_len); |
| acdb_data.vocvol_cal.cal_size = 0; |
| result = -EINVAL; |
| goto done; |
| } |
| |
| acdb_data.vocvol_cal.cal_size = cal_block->cal_size; |
| acdb_data.vocvol_cal.cal_paddr = |
| cal_block->cal_offset + acdb_data.paddr; |
| acdb_data.vocvol_cal.cal_kvaddr = |
| cal_block->cal_offset + acdb_data.kvaddr; |
| done: |
| return result; |
| } |
| |
| int get_vocvol_cal(struct acdb_cal_block *cal_block) |
| { |
| int result = 0; |
| pr_debug("%s,\n", __func__); |
| |
| if (cal_block == NULL) { |
| pr_err("ACDB=> NULL pointer sent to %s\n", __func__); |
| result = -EINVAL; |
| goto done; |
| } |
| |
| cal_block->cal_size = acdb_data.vocvol_cal.cal_size; |
| cal_block->cal_paddr = acdb_data.vocvol_cal.cal_paddr; |
| cal_block->cal_kvaddr = acdb_data.vocvol_cal.cal_kvaddr; |
| done: |
| return result; |
| } |
| |
| void store_sidetone_cal(struct sidetone_cal *cal_data) |
| { |
| pr_debug("%s,\n", __func__); |
| |
| acdb_data.sidetone_cal.enable = cal_data->enable; |
| acdb_data.sidetone_cal.gain = cal_data->gain; |
| } |
| |
| int get_sidetone_cal(struct sidetone_cal *cal_data) |
| { |
| int result = 0; |
| pr_debug("%s,\n", __func__); |
| |
| if (cal_data == NULL) { |
| pr_err("ACDB=> NULL pointer sent to %s\n", __func__); |
| result = -EINVAL; |
| goto done; |
| } |
| |
| mutex_lock(&acdb_data.acdb_mutex); |
| cal_data->enable = acdb_data.sidetone_cal.enable; |
| cal_data->gain = acdb_data.sidetone_cal.gain; |
| mutex_unlock(&acdb_data.acdb_mutex); |
| done: |
| return result; |
| } |
| |
| int get_spk_protection_cfg(struct msm_spk_prot_cfg *prot_cfg) |
| { |
| int result = 0; |
| pr_debug("%s,\n", __func__); |
| |
| mutex_lock(&acdb_data.acdb_mutex); |
| if (prot_cfg) { |
| prot_cfg->mode = acdb_data.spk_prot_cfg.mode; |
| prot_cfg->r0 = acdb_data.spk_prot_cfg.r0; |
| prot_cfg->t0 = acdb_data.spk_prot_cfg.t0; |
| } else { |
| pr_err("%s prot_cfg is NULL\n", __func__); |
| result = -EINVAL; |
| } |
| mutex_unlock(&acdb_data.acdb_mutex); |
| |
| return result; |
| } |
| |
| static int get_spk_protection_status(struct msm_spk_prot_status *status) |
| { |
| int result = 0; |
| struct afe_spkr_prot_get_vi_calib calib_resp; |
| pr_debug("%s,\n", __func__); |
| |
| /*Call AFE function here to query the status*/ |
| if (status) { |
| status->status = -EINVAL; |
| if (!afe_spk_prot_get_calib_data(&calib_resp)) { |
| if (calib_resp.res_cfg.th_vi_ca_state == 1) |
| status->status = -EAGAIN; |
| else if (calib_resp.res_cfg.th_vi_ca_state == 2) { |
| status->status = 0; |
| status->r0 = calib_resp.res_cfg.r0_cali_q24; |
| } |
| } |
| } else { |
| pr_err("%s invalid params\n", __func__); |
| result = -EINVAL; |
| } |
| |
| return result; |
| } |
| |
| static int register_vocvol_table(void) |
| { |
| int result = 0; |
| pr_debug("%s\n", __func__); |
| |
| result = voc_register_vocproc_vol_table(); |
| if (result < 0) { |
| pr_err("%s: Register vocproc vol failed!\n", __func__); |
| goto done; |
| } |
| |
| done: |
| return result; |
| } |
| |
| static int deregister_vocvol_table(void) |
| { |
| int result = 0; |
| pr_debug("%s\n", __func__); |
| |
| result = voc_deregister_vocproc_vol_table(); |
| if (result < 0) { |
| pr_err("%s: Deregister vocproc vol failed!\n", __func__); |
| goto done; |
| } |
| |
| done: |
| return result; |
| } |
| |
| static int acdb_open(struct inode *inode, struct file *f) |
| { |
| s32 result = 0; |
| pr_debug("%s\n", __func__); |
| |
| mutex_lock(&acdb_data.acdb_mutex); |
| if (acdb_data.mem_len) |
| pr_debug("%s: ACDB opened but memory allocated, using existing allocation!\n", |
| __func__); |
| |
| acdb_data.valid_adm_custom_top = 1; |
| acdb_data.valid_asm_custom_top = 1; |
| acdb_data.usage_count++; |
| mutex_unlock(&acdb_data.acdb_mutex); |
| |
| return result; |
| } |
| |
| static void deallocate_hw_delay_entries(void) |
| { |
| kfree(acdb_data.hw_delay_rx.delay_info); |
| kfree(acdb_data.hw_delay_tx.delay_info); |
| |
| acdb_data.hw_delay_rx.delay_info = NULL; |
| acdb_data.hw_delay_tx.delay_info = NULL; |
| } |
| |
| static int allocate_hw_delay_entries(void) |
| { |
| int result = 0; |
| |
| /* Allocate memory for hw delay entries */ |
| acdb_data.hw_delay_rx.num_entries = 0; |
| acdb_data.hw_delay_tx.num_entries = 0; |
| acdb_data.hw_delay_rx.delay_info = |
| kmalloc(sizeof(struct hw_delay_entry)* |
| MAX_HW_DELAY_ENTRIES, |
| GFP_KERNEL); |
| if (acdb_data.hw_delay_rx.delay_info == NULL) { |
| pr_err("%s : Failed to allocate av sync delay entries rx\n", |
| __func__); |
| result = -ENOMEM; |
| goto done; |
| } |
| acdb_data.hw_delay_tx.delay_info = |
| kmalloc(sizeof(struct hw_delay_entry)* |
| MAX_HW_DELAY_ENTRIES, |
| GFP_KERNEL); |
| if (acdb_data.hw_delay_tx.delay_info == NULL) { |
| pr_err("%s : Failed to allocate av sync delay entries tx\n", |
| __func__); |
| deallocate_hw_delay_entries(); |
| result = -ENOMEM; |
| goto done; |
| } |
| done: |
| return result; |
| } |
| |
| static void deallocate_col_data(void) |
| { |
| int i; |
| |
| for (i = 0; i < MAX_VOCPROC_TYPES; i++) { |
| kfree(acdb_data.col_data[i]); |
| acdb_data.col_data[i] = NULL; |
| } |
| } |
| |
| static int allocate_col_data(void) |
| { |
| int result = 0; |
| int i; |
| |
| for (i = 0; i < MAX_VOCPROC_TYPES; i++) { |
| acdb_data.col_data[i] = kmalloc(MAX_COL_SIZE, GFP_KERNEL); |
| if (acdb_data.col_data[i] == NULL) { |
| pr_err("%s: kmalloc column data failed, type = %d\n", |
| __func__, i); |
| deallocate_col_data(); |
| result = -ENOMEM; |
| goto done; |
| } |
| acdb_data.vocproc_col_cal[i].cal_kvaddr = |
| (uint32_t)acdb_data.col_data[i]; |
| } |
| |
| done: |
| return result; |
| } |
| |
| static int unmap_cal_tables(void) |
| { |
| int result = 0; |
| int result2 = 0; |
| |
| result2 = adm_unmap_cal_blocks(); |
| if (result2 < 0) { |
| pr_err("%s: adm_unmap_cal_blocks failed, err = %d\n", |
| __func__, result2); |
| result = result2; |
| } |
| |
| result2 = afe_unmap_cal_blocks(); |
| if (result2 < 0) { |
| pr_err("%s: afe_unmap_cal_blocks failed, err = %d\n", |
| __func__, result2); |
| result = result2; |
| } |
| |
| result2 = q6lsm_unmap_cal_blocks(); |
| if (result2 < 0) { |
| pr_err("%s: lsm_unmap_cal_blocks failed, err = %d\n", |
| __func__, result2); |
| result = result2; |
| } |
| |
| result2 = q6asm_unmap_cal_blocks(); |
| if (result2 < 0) { |
| pr_err("%s: asm_unmap_cal_blocks failed, err = %d\n", |
| __func__, result2); |
| result = result2; |
| } |
| |
| result2 = voc_unmap_cal_blocks(); |
| if (result2 < 0) { |
| pr_err("%s: voice_unmap_cal_blocks failed, err = %d\n", |
| __func__, result2); |
| result = result2; |
| } |
| |
| return result; |
| } |
| |
| static int deregister_memory(void) |
| { |
| int result = 0; |
| pr_debug("%s\n", __func__); |
| |
| if (acdb_data.mem_len == 0) |
| goto done; |
| |
| pr_debug("Remove existing memory\n"); |
| acdb_data.mem_len = 0; |
| |
| /* unmap all cal data */ |
| result = unmap_cal_tables(); |
| if (result < 0) |
| pr_err("%s: unmap_cal_tables failed, err = %d\n", |
| __func__, result); |
| |
| msm_audio_ion_free(acdb_data.ion_client, acdb_data.ion_handle); |
| acdb_data.ion_client = NULL; |
| acdb_data.ion_handle = NULL; |
| |
| deallocate_col_data(); |
| deallocate_hw_delay_entries(); |
| done: |
| return result; |
| } |
| |
| static int register_memory(void) |
| { |
| int result; |
| ion_phys_addr_t paddr; |
| void *kvptr; |
| unsigned long kvaddr; |
| unsigned long mem_len; |
| pr_debug("%s\n", __func__); |
| |
| result = allocate_col_data(); |
| if (result) { |
| pr_err("%s: allocate_hw_delay_entries failed, rc = %d\n", |
| __func__, result); |
| goto err_done; |
| } |
| |
| result = allocate_hw_delay_entries(); |
| if (result) { |
| pr_err("%s: allocate_hw_delay_entries failed, rc = %d\n", |
| __func__, result); |
| goto err_col; |
| } |
| |
| result = msm_audio_ion_import("audio_acdb_client", |
| &acdb_data.ion_client, |
| &acdb_data.ion_handle, |
| acdb_data.map_handle, |
| NULL, 0, |
| &paddr, (size_t *)&mem_len, &kvptr); |
| if (result) { |
| pr_err("%s: audio ION alloc failed, rc = %d\n", |
| __func__, result); |
| goto err_hw_delay; |
| } |
| |
| kvaddr = (unsigned long)kvptr; |
| acdb_data.paddr = paddr; |
| acdb_data.kvaddr = kvaddr; |
| acdb_data.mem_len = mem_len; |
| |
| pr_debug("%s done! paddr = 0x%llx, kvaddr = 0x%llx, len = 0x%llx\n", |
| __func__, acdb_data.paddr, acdb_data.kvaddr, |
| acdb_data.mem_len); |
| |
| return result; |
| err_hw_delay: |
| deallocate_hw_delay_entries(); |
| err_col: |
| deallocate_col_data(); |
| err_done: |
| acdb_data.mem_len = 0; |
| return result; |
| } |
| static long acdb_ioctl(struct file *f, |
| unsigned int cmd, unsigned long arg) |
| { |
| int32_t result = 0; |
| int32_t size; |
| int32_t map_fd; |
| uint32_t topology; |
| uint32_t data[MAX_IOCTL_DATA]; |
| struct msm_spk_prot_status prot_status; |
| struct msm_spk_prot_status acdb_spk_status; |
| pr_debug("%s\n", __func__); |
| |
| mutex_lock(&acdb_data.acdb_mutex); |
| switch (cmd) { |
| case AUDIO_REGISTER_PMEM: |
| pr_debug("AUDIO_REGISTER_PMEM\n"); |
| result = deregister_memory(); |
| if (result < 0) |
| pr_err("%s: deregister_memory failed returned %d!\n", |
| __func__, result); |
| |
| if (copy_from_user(&map_fd, (void *)arg, sizeof(map_fd))) { |
| pr_err("%s: fail to copy memory handle!\n", __func__); |
| result = -EFAULT; |
| } else { |
| acdb_data.map_handle = map_fd; |
| result = register_memory(); |
| } |
| goto done; |
| case AUDIO_DEREGISTER_PMEM: |
| pr_debug("AUDIO_DEREGISTER_PMEM\n"); |
| result = deregister_memory(); |
| goto done; |
| case AUDIO_SET_VOICE_RX_TOPOLOGY: |
| if (copy_from_user(&topology, (void *)arg, |
| sizeof(topology))) { |
| pr_err("%s: fail to copy topology!\n", __func__); |
| result = -EFAULT; |
| } |
| store_voice_rx_topology(topology); |
| goto done; |
| case AUDIO_SET_VOICE_TX_TOPOLOGY: |
| if (copy_from_user(&topology, (void *)arg, |
| sizeof(topology))) { |
| pr_err("%s: fail to copy topology!\n", __func__); |
| result = -EFAULT; |
| } |
| store_voice_tx_topology(topology); |
| goto done; |
| case AUDIO_SET_ADM_RX_TOPOLOGY: |
| if (copy_from_user(&topology, (void *)arg, |
| sizeof(topology))) { |
| pr_err("%s: fail to copy topology!\n", __func__); |
| result = -EFAULT; |
| } |
| store_adm_rx_topology(topology); |
| goto done; |
| case AUDIO_SET_ADM_TX_TOPOLOGY: |
| if (copy_from_user(&topology, (void *)arg, |
| sizeof(topology))) { |
| pr_err("%s: fail to copy topology!\n", __func__); |
| result = -EFAULT; |
| } |
| store_adm_tx_topology(topology); |
| goto done; |
| case AUDIO_SET_ASM_TOPOLOGY: |
| if (copy_from_user(&topology, (void *)arg, |
| sizeof(topology))) { |
| pr_err("%s: fail to copy topology!\n", __func__); |
| result = -EFAULT; |
| } |
| store_asm_topology(topology); |
| goto done; |
| case AUDIO_SET_SPEAKER_PROT: |
| if (copy_from_user(&acdb_data.spk_prot_cfg, (void *)arg, |
| sizeof(acdb_data.spk_prot_cfg))) { |
| pr_err("%s fail to copy spk_prot_cfg\n", __func__); |
| result = -EFAULT; |
| } |
| goto done; |
| case AUDIO_GET_SPEAKER_PROT: |
| /*Indicates calibration was succesfull*/ |
| if (acdb_data.spk_prot_cfg.mode == MSM_SPKR_PROT_CALIBRATED) { |
| prot_status.r0 = acdb_data.spk_prot_cfg.r0; |
| prot_status.status = 0; |
| } else if (acdb_data.spk_prot_cfg.mode == |
| MSM_SPKR_PROT_CALIBRATION_IN_PROGRESS) { |
| /*Call AFE to query the status*/ |
| acdb_spk_status.status = -EINVAL; |
| acdb_spk_status.r0 = -1; |
| get_spk_protection_status(&acdb_spk_status); |
| prot_status.r0 = acdb_spk_status.r0; |
| prot_status.status = acdb_spk_status.status; |
| if (!acdb_spk_status.status) { |
| acdb_data.spk_prot_cfg.mode = |
| MSM_SPKR_PROT_CALIBRATED; |
| acdb_data.spk_prot_cfg.r0 = prot_status.r0; |
| } |
| } else { |
| /*Indicates calibration data is invalid*/ |
| prot_status.status = -EINVAL; |
| prot_status.r0 = -1; |
| } |
| if (copy_to_user((void *)arg, &prot_status, |
| sizeof(prot_status))) { |
| pr_err("%s: Failed to update prot_status\n", __func__); |
| } |
| goto done; |
| case AUDIO_REGISTER_VOCPROC_VOL_TABLE: |
| result = register_vocvol_table(); |
| goto done; |
| case AUDIO_DEREGISTER_VOCPROC_VOL_TABLE: |
| result = deregister_vocvol_table(); |
| goto done; |
| case AUDIO_SET_HW_DELAY_RX: |
| result = store_hw_delay(RX_CAL, (void *)arg); |
| goto done; |
| case AUDIO_SET_HW_DELAY_TX: |
| result = store_hw_delay(TX_CAL, (void *)arg); |
| goto done; |
| } |
| |
| if (copy_from_user(&size, (void *) arg, sizeof(size))) { |
| |
| result = -EFAULT; |
| goto done; |
| } |
| |
| if ((size <= 0) || (size > sizeof(data))) { |
| pr_err("%s: Invalid size sent to driver: %d\n", |
| __func__, size); |
| result = -EFAULT; |
| goto done; |
| } |
| |
| switch (cmd) { |
| case AUDIO_SET_VOCPROC_COL_CAL: |
| result = store_voice_col_data(VOCPROC_CAL, |
| size, (uint32_t *)arg); |
| goto done; |
| case AUDIO_SET_VOCSTRM_COL_CAL: |
| result = store_voice_col_data(VOCSTRM_CAL, |
| size, (uint32_t *)arg); |
| goto done; |
| case AUDIO_SET_VOCVOL_COL_CAL: |
| result = store_voice_col_data(VOCVOL_CAL, |
| size, (uint32_t *)arg); |
| goto done; |
| } |
| |
| if (copy_from_user(data, (void *)(arg + sizeof(size)), size)) { |
| |
| pr_err("%s: fail to copy table size %d\n", __func__, size); |
| result = -EFAULT; |
| goto done; |
| } |
| |
| if (data == NULL) { |
| pr_err("%s: NULL pointer sent to driver!\n", __func__); |
| result = -EFAULT; |
| goto done; |
| } |
| |
| if (size > sizeof(struct cal_block)) |
| pr_err("%s: More cal data for ioctl 0x%x then expected, size received: %d\n", |
| __func__, cmd, size); |
| |
| switch (cmd) { |
| case AUDIO_SET_AUDPROC_TX_CAL: |
| result = store_audproc_cal(TX_CAL, (struct cal_block *)data); |
| goto done; |
| case AUDIO_SET_AUDPROC_RX_CAL: |
| result = store_audproc_cal(RX_CAL, (struct cal_block *)data); |
| goto done; |
| case AUDIO_SET_AUDPROC_TX_STREAM_CAL: |
| result = store_audstrm_cal(TX_CAL, (struct cal_block *)data); |
| goto done; |
| case AUDIO_SET_AUDPROC_RX_STREAM_CAL: |
| result = store_audstrm_cal(RX_CAL, (struct cal_block *)data); |
| goto done; |
| case AUDIO_SET_AUDPROC_TX_VOL_CAL: |
| result = store_audvol_cal(TX_CAL, (struct cal_block *)data); |
| goto done; |
| case AUDIO_SET_AUDPROC_RX_VOL_CAL: |
| result = store_audvol_cal(RX_CAL, (struct cal_block *)data); |
| goto done; |
| case AUDIO_SET_AFE_TX_CAL: |
| result = store_afe_cal(TX_CAL, (struct cal_block *)data); |
| goto done; |
| case AUDIO_SET_AFE_RX_CAL: |
| result = store_afe_cal(RX_CAL, (struct cal_block *)data); |
| goto done; |
| case AUDIO_SET_VOCPROC_CAL: |
| result = store_vocproc_cal((struct cal_block *)data); |
| goto done; |
| case AUDIO_SET_VOCPROC_STREAM_CAL: |
| result = store_vocstrm_cal((struct cal_block *)data); |
| goto done; |
| case AUDIO_SET_VOCPROC_VOL_CAL: |
| result = store_vocvol_cal((struct cal_block *)data); |
| goto done; |
| case AUDIO_SET_VOCPROC_DEV_CFG_CAL: |
| result = store_vocproc_dev_cfg_cal((struct cal_block *)data); |
| goto done; |
| case AUDIO_SET_SIDETONE_CAL: |
| store_sidetone_cal((struct sidetone_cal *)data); |
| goto done; |
| case AUDIO_SET_ANC_CAL: |
| result = store_anc_cal((struct cal_block *)data); |
| goto done; |
| case AUDIO_SET_LSM_CAL: |
| result = store_lsm_cal((struct cal_block *)data); |
| goto done; |
| case AUDIO_SET_ADM_CUSTOM_TOPOLOGY: |
| result = store_adm_custom_topology((struct cal_block *)data); |
| goto done; |
| case AUDIO_SET_ASM_CUSTOM_TOPOLOGY: |
| result = store_asm_custom_topology((struct cal_block *)data); |
| goto done; |
| case AUDIO_SET_AANC_CAL: |
| result = store_aanc_cal((struct cal_block *)data); |
| goto done; |
| default: |
| pr_err("ACDB=> ACDB ioctl not found!\n"); |
| result = -EFAULT; |
| goto done; |
| } |
| |
| done: |
| mutex_unlock(&acdb_data.acdb_mutex); |
| return result; |
| } |
| |
| static int acdb_mmap(struct file *file, struct vm_area_struct *vma) |
| { |
| int result = 0; |
| size_t size = vma->vm_end - vma->vm_start; |
| |
| pr_debug("%s\n", __func__); |
| |
| mutex_lock(&acdb_data.acdb_mutex); |
| if (acdb_data.mem_len) { |
| if (size <= acdb_data.mem_len) { |
| vma->vm_page_prot = pgprot_noncached( |
| vma->vm_page_prot); |
| result = remap_pfn_range(vma, |
| vma->vm_start, |
| acdb_data.paddr >> PAGE_SHIFT, |
| size, |
| vma->vm_page_prot); |
| } else { |
| pr_err("%s: Not enough memory!\n", __func__); |
| result = -ENOMEM; |
| } |
| } else { |
| pr_err("%s: memory is not allocated, yet!\n", __func__); |
| result = -ENODEV; |
| } |
| mutex_unlock(&acdb_data.acdb_mutex); |
| |
| return result; |
| } |
| |
| static int acdb_release(struct inode *inode, struct file *f) |
| { |
| int result = 0; |
| pr_debug("%s\n", __func__); |
| |
| mutex_lock(&acdb_data.acdb_mutex); |
| acdb_data.usage_count--; |
| |
| pr_debug("%s: ref count %d!\n", __func__, acdb_data.usage_count); |
| |
| if (acdb_data.usage_count > 0) { |
| result = -EBUSY; |
| goto done; |
| } |
| |
| result = deregister_memory(); |
| done: |
| mutex_unlock(&acdb_data.acdb_mutex); |
| return result; |
| } |
| |
| static const struct file_operations acdb_fops = { |
| .owner = THIS_MODULE, |
| .open = acdb_open, |
| .release = acdb_release, |
| .unlocked_ioctl = acdb_ioctl, |
| .mmap = acdb_mmap, |
| }; |
| |
| struct miscdevice acdb_misc = { |
| .minor = MISC_DYNAMIC_MINOR, |
| .name = "msm_acdb", |
| .fops = &acdb_fops, |
| }; |
| |
| static int __init acdb_init(void) |
| { |
| memset(&acdb_data, 0, sizeof(acdb_data)); |
| /*Speaker protection disabled*/ |
| acdb_data.spk_prot_cfg.mode = MSM_SPKR_PROT_DISABLED; |
| mutex_init(&acdb_data.acdb_mutex); |
| acdb_data.usage_count = 0; |
| acdb_data.valid_adm_custom_top = 1; |
| acdb_data.valid_asm_custom_top = 1; |
| |
| return misc_register(&acdb_misc); |
| } |
| |
| static void __exit acdb_exit(void) |
| { |
| } |
| |
| module_init(acdb_init); |
| module_exit(acdb_exit); |
| |
| MODULE_DESCRIPTION("SoC QDSP6v2 Audio ACDB driver"); |
| MODULE_LICENSE("GPL v2"); |