/* Copyright (c) 2012-2014, 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 #include #include #include #include #include #include #include #include #include #include "msm-dts-srs-tm-config.h" #include "msm-pcm-routing-v2.h" static int srs_port_id[AFE_MAX_PORTS] = {-1}; static int srs_copp_idx[AFE_MAX_PORTS] = {-1}; static union srs_trumedia_params_u msm_srs_trumedia_params; static struct ion_client *ion_client; static struct ion_handle *ion_handle; static struct param_outband po; static atomic_t ref_cnt; #define ION_MEM_SIZE (8 * 1024) static int set_port_id(int port_id, int copp_idx) { int index = adm_validate_and_get_port_index(port_id); if (index < 0) { pr_err("%s: Invalid port idx %d port_id %#x\n", __func__, index, port_id); return -EINVAL; } srs_port_id[index] = port_id; srs_copp_idx[index] = copp_idx; return 0; } static void msm_dts_srs_tm_send_params(__s32 port_id, __u32 techs) { __s32 index = adm_validate_and_get_port_index(port_id); if (index < 0) { pr_err("%s: Invalid port idx %d port_id 0x%x\n", __func__, index, port_id); return; } if ((srs_copp_idx[index] < 0) || (srs_copp_idx[index] >= MAX_COPPS_PER_PORT)) { pr_debug("%s: send params called before copp open. so, caching\n", __func__); return; } pr_debug("SRS %s: called, port_id = %d, techs flags = %u\n", __func__, port_id, techs); /* force all if techs is set to 1 */ if (techs == 1) techs = 0xFFFFFFFF; if (techs & (1 << SRS_ID_WOWHD)) srs_trumedia_open(port_id, srs_copp_idx[index], SRS_ID_WOWHD, (void *)&msm_srs_trumedia_params.srs_params.wowhd); if (techs & (1 << SRS_ID_CSHP)) srs_trumedia_open(port_id, srs_copp_idx[index], SRS_ID_CSHP, (void *)&msm_srs_trumedia_params.srs_params.cshp); if (techs & (1 << SRS_ID_HPF)) srs_trumedia_open(port_id, srs_copp_idx[index], SRS_ID_HPF, (void *)&msm_srs_trumedia_params.srs_params.hpf); if (techs & (1 << SRS_ID_AEQ)) srs_trumedia_open(port_id, srs_copp_idx[index], SRS_ID_AEQ, (void *)&msm_srs_trumedia_params.srs_params.aeq); if (techs & (1 << SRS_ID_HL)) srs_trumedia_open(port_id, srs_copp_idx[index], SRS_ID_HL, (void *)&msm_srs_trumedia_params.srs_params.hl); if (techs & (1 << SRS_ID_GEQ)) srs_trumedia_open(port_id, srs_copp_idx[index], SRS_ID_GEQ, (void *)&msm_srs_trumedia_params.srs_params.geq); if (techs & (1 << SRS_ID_GLOBAL)) srs_trumedia_open(port_id, srs_copp_idx[index], SRS_ID_GLOBAL, (void *)&msm_srs_trumedia_params.srs_params.global); } static int msm_dts_srs_trumedia_control_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { ucontrol->value.integer.value[0] = 0; return 0; } static int msm_dts_srs_trumedia_control_set_(int port_id, struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { __u16 offset, value, max = sizeof(msm_srs_trumedia_params) >> 1; if (SRS_CMD_UPLOAD == (ucontrol->value.integer.value[0] & SRS_CMD_UPLOAD)) { __u32 techs = ucontrol->value.integer.value[0] & 0xFF; __s32 index = adm_validate_and_get_port_index(port_id); if (index < 0) { pr_err("%s: Invalid port idx %d port_id 0x%x\n", __func__, index, port_id); return -EINVAL; } pr_debug("SRS %s: send params request, flag = %u\n", __func__, techs); if (srs_port_id[index] >= 0 && techs) msm_dts_srs_tm_send_params(port_id, techs); return 0; } offset = (__u16)((ucontrol->value.integer.value[0] & SRS_PARAM_OFFSET_MASK) >> 16); value = (__u16)(ucontrol->value.integer.value[0] & SRS_PARAM_VALUE_MASK); if (offset < max) { msm_srs_trumedia_params.raw_params[offset] = value; pr_debug("SRS %s: index set... (max %d, requested %d, value 0x%X)\n", __func__, max, offset, value); } else { pr_err("SRS %s: index out of bounds! (max %d, requested %d)\n", __func__, max, offset); } return 0; } static int msm_dts_srs_trumedia_control_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { int ret, port_id; pr_debug("SRS control normal called\n"); msm_pcm_routing_acquire_lock(); port_id = SLIMBUS_0_RX; ret = msm_dts_srs_trumedia_control_set_(port_id, kcontrol, ucontrol); msm_pcm_routing_release_lock(); return ret; } static int msm_dts_srs_trumedia_control_i2s_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { int ret, port_id; pr_debug("SRS control I2S called\n"); msm_pcm_routing_acquire_lock(); port_id = PRIMARY_I2S_RX; ret = msm_dts_srs_trumedia_control_set_(port_id, kcontrol, ucontrol); msm_pcm_routing_release_lock(); return ret; } static int msm_dts_srs_trumedia_control_mi2s_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { int ret, port_id; pr_debug("SRS control MI2S called\n"); msm_pcm_routing_acquire_lock(); port_id = AFE_PORT_ID_PRIMARY_MI2S_RX; ret = msm_dts_srs_trumedia_control_set_(port_id, kcontrol, ucontrol); msm_pcm_routing_release_lock(); return ret; } static int msm_dts_srs_trumedia_control_hdmi_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { int ret, port_id; pr_debug("SRS control HDMI called\n"); msm_pcm_routing_acquire_lock(); port_id = HDMI_RX; ret = msm_dts_srs_trumedia_control_set_(port_id, kcontrol, ucontrol); msm_pcm_routing_release_lock(); return ret; } static const struct snd_kcontrol_new lpa_srs_trumedia_controls[] = { {.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "SRS TruMedia", .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE, .info = snd_soc_info_volsw, .get = msm_dts_srs_trumedia_control_get, .put = msm_dts_srs_trumedia_control_set, .private_value = ((unsigned long)&(struct soc_mixer_control) {.reg = SND_SOC_NOPM, .rreg = SND_SOC_NOPM, .shift = 0, .rshift = 0, .max = 0xFFFFFFFF, .platform_max = 0xFFFFFFFF, .invert = 0 }) } }; static const struct snd_kcontrol_new lpa_srs_trumedia_controls_hdmi[] = { {.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "SRS TruMedia HDMI", .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE, .info = snd_soc_info_volsw, .get = msm_dts_srs_trumedia_control_get, .put = msm_dts_srs_trumedia_control_hdmi_set, .private_value = ((unsigned long)&(struct soc_mixer_control) {.reg = SND_SOC_NOPM, .rreg = SND_SOC_NOPM, .shift = 0, .rshift = 0, .max = 0xFFFFFFFF, .platform_max = 0xFFFFFFFF, .invert = 0 }) } }; static const struct snd_kcontrol_new lpa_srs_trumedia_controls_i2s[] = { {.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "SRS TruMedia I2S", .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE, .info = snd_soc_info_volsw, .get = msm_dts_srs_trumedia_control_get, .put = msm_dts_srs_trumedia_control_i2s_set, .private_value = ((unsigned long)&(struct soc_mixer_control) {.reg = SND_SOC_NOPM, .rreg = SND_SOC_NOPM, .shift = 0, .rshift = 0, .max = 0xFFFFFFFF, .platform_max = 0xFFFFFFFF, .invert = 0 }) } }; static const struct snd_kcontrol_new lpa_srs_trumedia_controls_mi2s[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "SRS TruMedia MI2S", .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE, .info = snd_soc_info_volsw, .get = msm_dts_srs_trumedia_control_get, .put = msm_dts_srs_trumedia_control_mi2s_set, .private_value = ((unsigned long)&(struct soc_mixer_control) { .reg = SND_SOC_NOPM, .rreg = SND_SOC_NOPM, .shift = 0, .rshift = 0, .max = 0xFFFFFFFF, .platform_max = 0xFFFFFFFF, .invert = 0 }) } }; void msm_dts_srs_tm_add_controls(struct snd_soc_platform *platform) { snd_soc_add_platform_controls(platform, lpa_srs_trumedia_controls, ARRAY_SIZE(lpa_srs_trumedia_controls)); snd_soc_add_platform_controls(platform, lpa_srs_trumedia_controls_hdmi, ARRAY_SIZE(lpa_srs_trumedia_controls_hdmi)); snd_soc_add_platform_controls(platform, lpa_srs_trumedia_controls_i2s, ARRAY_SIZE(lpa_srs_trumedia_controls_i2s)); snd_soc_add_platform_controls(platform, lpa_srs_trumedia_controls_mi2s, ARRAY_SIZE(lpa_srs_trumedia_controls_mi2s)); } static int reg_ion_mem(void) { int rc; rc = msm_audio_ion_alloc("SRS_TRUMEDIA", &ion_client, &ion_handle, ION_MEM_SIZE, &po.paddr, (size_t *)&po.size, &po.kvaddr); if (rc != 0) pr_err("%s: failed to allocate memory.\n", __func__); pr_debug("%s: exited ion_client = %p, ion_handle = %p, phys_addr = %lu, length = %d, vaddr = %p, rc = 0x%x\n", __func__, ion_client, ion_handle, (long)po.paddr, (unsigned int)po.size, po.kvaddr, rc); return rc; } void msm_dts_srs_tm_ion_memmap(struct param_outband *po_) { if (po.kvaddr == NULL) { pr_debug("%s: callingreg_ion_mem()\n", __func__); reg_ion_mem(); } po_->size = ION_MEM_SIZE; po_->kvaddr = po.kvaddr; po_->paddr = po.paddr; } static void unreg_ion_mem(void) { msm_audio_ion_free(ion_client, ion_handle); po.kvaddr = NULL; po.paddr = 0; po.size = 0; } void msm_dts_srs_tm_deinit(int port_id) { set_port_id(port_id, -1); atomic_dec(&ref_cnt); if (po.kvaddr != NULL) { if (!atomic_read(&ref_cnt)) { pr_debug("%s: calling unreg_ion_mem()\n", __func__); unreg_ion_mem(); } } return; } void msm_dts_srs_tm_init(int port_id, int copp_idx) { int cur_ref_cnt = 0; if (set_port_id(port_id, copp_idx) < 0) { pr_err("%s: Invalid port_id: %d\n", __func__, port_id); return; } cur_ref_cnt = atomic_read(&ref_cnt); atomic_inc(&ref_cnt); if (!cur_ref_cnt && po.kvaddr == NULL) { pr_debug("%s: calling reg_ion_mem()\n", __func__); if (reg_ion_mem() != 0) { atomic_dec(&ref_cnt); po.kvaddr = NULL; return; } } msm_dts_srs_tm_send_params(port_id, 1); return; }