/* sound/soc/samsung/vts/vts.c * * ALSA SoC - Samsung VTS driver * * Copyright (c) 2016 Samsung Electronics Co. Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ //#define DEBUG #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "vts.h" #include "vts_log.h" #include "vts_dump.h" #undef EMULATOR #ifdef EMULATOR static void __iomem *pmu_alive; #define set_mask_value(id, mask, value) do {id = ((id & ~mask) | (value & mask));} while(0); static void update_mask_value(volatile void __iomem *sfr, unsigned int mask, unsigned int value) { unsigned int sfr_value = readl(sfr); set_mask_value(sfr_value, mask, value); writel(sfr_value, sfr); } #endif #ifdef CONFIG_SOC_EXYNOS8895 #define PAD_RETENTION_VTS_OPTION (0x3148) #define VTS_CPU_CONFIGURATION (0x24E0) #define VTS_CPU_LOCAL_PWR_CFG (0x00000001) #define VTS_CPU_STATUS (0x24E4) #define VTS_CPU_STATUS_STATUS_MASK (0x00000001) #define VTS_CPU_STANDBY VTS_CPU_STATUS #define VTS_CPU_STANDBY_STANDBYWFI_MASK (0x10000000) #define VTS_CPU_OPTION (0x24E8) #define VTS_CPU_OPTION_USE_STANDBYWFI_MASK (0x00010000) #define VTS_CPU_RESET_OPTION VTS_CPU_OPTION #define VTS_CPU_RESET_OPTION_ENABLE_CPU_MASK (0x00008000) #elif defined(CONFIG_SOC_EXYNOS9810) #define PAD_RETENTION_VTS_OPTION (0x4AD8) #define VTS_CPU_CONFIGURATION (0x4AC4) #define VTS_CPU_LOCAL_PWR_CFG (0x00000001) #define VTS_CPU_STATUS (0x4AC8) #define VTS_CPU_STATUS_STATUS_MASK (0x00000001) #define VTS_CPU_STANDBY (0x3814) #define VTS_CPU_STANDBY_STANDBYWFI_MASK (0x10000000) #define VTS_CPU_OPTION (0x3818) #define VTS_CPU_OPTION_USE_STANDBYWFI_MASK (0x00010000) #define VTS_CPU_RESET_OPTION (0x4ACC) #define VTS_CPU_RESET_OPTION_ENABLE_CPU_MASK (0x10000000) #endif #define LIMIT_IN_JIFFIES (msecs_to_jiffies(1000)) #define DMIC_CLK_RATE (768000) #define VTS_TRIGGERED_TIMEOUT_MS (5000) /* For only external static functions */ static struct vts_data *p_vts_data; static int vts_start_ipc_transaction_atomic(struct device *dev, struct vts_data *data, int msg, u32 (*values)[3], int sync) { long result = 0; u32 ack_value = 0; volatile enum ipc_state *state = &data->ipc_state_ap; dev_info(dev, "%s:++ msg:%d, values: 0x%08x, 0x%08x, 0x%08x\n", __func__, msg, (*values)[0], (*values)[1], (*values)[2]); if (pm_runtime_suspended(dev)) { dev_warn(dev, "%s: VTS IP is in suspended state, IPC cann't be processed \n", __func__); return -EINVAL; } if (!data->vts_ready) { dev_warn(dev, "%s: VTS Firmware Not running\n", __func__); return -EINVAL; } spin_lock(&data->ipc_spinlock); *state = SEND_MSG; mailbox_write_shared_register(data->pdev_mailbox, *values, 0, 3); mailbox_generate_interrupt(data->pdev_mailbox, msg); data->running_ipc = msg; if (sync) { int i; for (i = 1000; i && (*state != SEND_MSG_OK) && (*state != SEND_MSG_FAIL) && (ack_value != (0x1 << msg)); i--) { mailbox_read_shared_register(data->pdev_mailbox, &ack_value, 3, 1); dev_dbg(dev, "%s ACK-value: 0x%08x\n", __func__, ack_value); udelay(50); } if (!i) { dev_warn(dev, "Transaction timeout!! Ack_value:0x%x\n", ack_value); } if (*state == SEND_MSG_OK || ack_value == (0x1 << msg)) { dev_dbg(dev, "Transaction success Ack_value:0x%x\n", ack_value); if (ack_value == (0x1 << VTS_IRQ_AP_TEST_COMMAND) && ((*values)[0] == VTS_ENABLE_DEBUGLOG || (*values)[0] == VTS_ENABLE_AUDIODUMP || (*values)[0] == VTS_ENABLE_LOGDUMP)) { u32 ackvalues[3] = {0, 0, 0}; mailbox_read_shared_register(data->pdev_mailbox, ackvalues, 0, 2); dev_info(dev, "%s: offset: 0x%x size:0x%x\n", __func__, ackvalues[0], ackvalues[1]); if ((*values)[0] == VTS_ENABLE_DEBUGLOG) { /* Register debug log buffer */ vts_register_log_buffer(dev, ackvalues[0], ackvalues[1]); dev_dbg(dev, "%s: Log buffer\n", __func__); } else { u32 dumpmode = ((*values)[0] == VTS_ENABLE_LOGDUMP ? VTS_LOG_DUMP : VTS_AUDIO_DUMP); /* register dump offset & size */ vts_dump_addr_register(dev, ackvalues[0], ackvalues[1], dumpmode); dev_dbg(dev, "%s: Dump buffer\n", __func__); } } } else { dev_err(dev, "Transaction failed\n"); } result = (*state == SEND_MSG_OK || ack_value) ? 0 : -EIO; } /* Clear running IPC & ACK value */ ack_value = 0x0; mailbox_write_shared_register(data->pdev_mailbox, &ack_value, 3, 1); data->running_ipc = 0; *state = IDLE; spin_unlock(&data->ipc_spinlock); dev_info(dev, "%s:-- msg:%d \n", __func__, msg); return (int)result; } int vts_start_ipc_transaction(struct device *dev, struct vts_data *data, int msg, u32 (*values)[3], int atomic, int sync) { return vts_start_ipc_transaction_atomic(dev, data, msg, values, sync); } static int vts_ipc_ack(struct vts_data *data, u32 result) { if (!data->vts_ready) return 0; pr_debug("%s(%p, %u)\n", __func__, data, result); mailbox_write_shared_register(data->pdev_mailbox, &result, 0, 1); mailbox_generate_interrupt(data->pdev_mailbox, VTS_IRQ_AP_IPC_RECEIVED); return 0; } int vts_send_ipc_ack(struct vts_data *data, u32 result) { return vts_ipc_ack(data, result); } static void vts_cpu_power(bool on) { pr_info("%s(%d)\n", __func__, on); #ifndef EMULATOR exynos_pmu_update(VTS_CPU_CONFIGURATION, VTS_CPU_LOCAL_PWR_CFG, on ? VTS_CPU_LOCAL_PWR_CFG : 0); #else update_mask_value(pmu_alive + VTS_CPU_CONFIGURATION, VTS_CPU_LOCAL_PWR_CFG, on ? VTS_CPU_LOCAL_PWR_CFG : 0); #endif if (!on) { #ifndef EMULATOR exynos_pmu_update(VTS_CPU_OPTION, VTS_CPU_OPTION_USE_STANDBYWFI_MASK, VTS_CPU_OPTION_USE_STANDBYWFI_MASK); #else update_mask_value(pmu_alive + VTS_CPU_OPTION, VTS_CPU_OPTION_USE_STANDBYWFI_MASK, VTS_CPU_OPTION_USE_STANDBYWFI_MASK); #endif } } static int vts_cpu_enable(bool enable) { unsigned int mask = VTS_CPU_RESET_OPTION_ENABLE_CPU_MASK; unsigned int val = (enable ? mask : 0); unsigned int status = 0; unsigned long after; pr_info("%s(%d)\n", __func__, enable); #ifndef EMULATOR exynos_pmu_update(VTS_CPU_RESET_OPTION, mask, val); #else update_mask_value(pmu_alive + VTS_CPU_RESET_OPTION, mask, val); #endif if (enable) { after = jiffies + LIMIT_IN_JIFFIES; do { schedule(); #ifndef EMULATOR exynos_pmu_read(VTS_CPU_STATUS, &status); #else status = readl(pmu_alive + VTS_CPU_STATUS); #endif } while (((status & VTS_CPU_STATUS_STATUS_MASK) != VTS_CPU_STATUS_STATUS_MASK) && time_is_after_eq_jiffies(after)); if (time_is_before_jiffies(after)) { pr_err("vts cpu enable timeout\n"); return -ETIME; } } return 0; } static void vts_reset_cpu(void) { #ifndef EMULATOR vts_cpu_enable(false); vts_cpu_power(false); vts_cpu_power(true); vts_cpu_enable(true); #endif } static int vts_download_firmware(struct platform_device *pdev) { struct vts_data *data = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; dev_info(dev, "%s\n", __func__); if (!data->firmware) { dev_err(dev, "firmware is not loaded\n"); return -EAGAIN; } memcpy(data->sram_base, data->firmware->data, data->firmware->size); dev_info(dev, "firmware is downloaded to %p (size=%zu)\n", data->sram_base, data->firmware->size); return 0; } static int vts_wait_for_fw_ready(struct device *dev) { struct vts_data *data = dev_get_drvdata(dev); int result; result = wait_event_timeout(data->ipc_wait_queue, data->vts_ready, msecs_to_jiffies(3000)); if (data->vts_ready) { result = 0; } else { dev_err(dev, "VTS Firmware is not ready\n"); result = -ETIME; } return result; } static void vts_pad_retention(bool retention) { if (!retention) { exynos_pmu_update(PAD_RETENTION_VTS_OPTION, 0x10000000, 0x10000000); } } static void vts_cfg_gpio(struct device *dev, const char *name) { struct vts_data *data = dev_get_drvdata(dev); struct pinctrl_state *pin_state; int ret; dev_info(dev, "%s(%s)\n", __func__, name); pin_state = pinctrl_lookup_state(data->pinctrl, name); if (IS_ERR(pin_state)) { dev_err(dev, "Couldn't find pinctrl %s\n", name); } else { ret = pinctrl_select_state(data->pinctrl, pin_state); if (ret < 0) dev_err(dev, "Unable to configure pinctrl %s\n", name); } } #ifdef CONFIG_SOC_EXYNOS9810 static u32 vts_set_baaw(void __iomem *sfr_base, u64 base, u32 size) { u32 aligned_size = round_up(size, SZ_4M); u64 aligned_base = round_down(base, aligned_size); writel(VTS_BAAW_BASE / SZ_4K, sfr_base + VTS_BAAW_SRC_START_ADDRESS); writel((VTS_BAAW_BASE + aligned_size) / SZ_4K, sfr_base + VTS_BAAW_SRC_END_ADDRESS); writel(aligned_base / SZ_4K, sfr_base + VTS_BAAW_REMAPPED_ADDRESS); writel(0x80000003, sfr_base + VTS_BAAW_INIT_DONE); return base - aligned_base + VTS_BAAW_BASE; } #endif static int vts_clk_set_rate(struct device *dev, unsigned long combination) { struct vts_data *data = dev_get_drvdata(dev); unsigned long dmic_rate, dmic_sync, dmic_if; int result; dev_info(dev, "%s(%lu)\n", __func__, combination); switch (combination) { case 2: dmic_rate = 384000; dmic_sync = 384000; dmic_if = 768000; break; case 0: dmic_rate = 512000; dmic_sync = 512000; dmic_if = 1024000; break; case 1: dmic_rate = 768000; dmic_sync = 768000; dmic_if = 1536000; break; case 3: dmic_rate = 4096000; dmic_sync = 2048000; dmic_if = 4096000; break; default: result = -EINVAL; goto out; } result = clk_set_rate(data->clk_dmic_if, dmic_if); if (result < 0) { dev_err(dev, "Failed to set rate of the clock %s\n", "dmic_if"); goto out; } dev_info(dev, "DMIC IF clock rate: %lu\n", clk_get_rate(data->clk_dmic_if)); result = clk_set_rate(data->clk_dmic_sync, dmic_sync); if (result < 0) { dev_err(dev, "Failed to set rate of the clock %s\n", "dmic_sync"); goto out; } dev_info(dev, "DMIC SYNC clock rate: %lu\n", clk_get_rate(data->clk_dmic_sync)); result = clk_set_rate(data->clk_dmic, dmic_rate); if (result < 0) { dev_err(dev, "Failed to set rate of the clock %s\n", "dmic"); goto out; } dev_info(dev, "DMIC clock rate: %lu\n", clk_get_rate(data->clk_dmic)); out: return result; } int vts_acquire_sram(struct platform_device *pdev, int vts) { #ifdef CONFIG_SOC_EXYNOS8895 struct vts_data *data = platform_get_drvdata(pdev); int previous; if (IS_ENABLED(CONFIG_SOC_EXYNOS8895)) { dev_info(&pdev->dev, "%s(%d)\n", __func__, vts); if (!vts) { while(pm_runtime_active(&pdev->dev)) { dev_warn(&pdev->dev, "%s Clear existing active states\n", __func__); pm_runtime_put_sync(&pdev->dev); } } previous = test_and_set_bit(0, &data->sram_acquired); if (previous) { dev_err(&pdev->dev, "vts sram acquisition failed\n"); return -EBUSY; } if (!vts) { pm_runtime_get_sync(&pdev->dev); data->voicecall_enabled = true; data->vts_state = VTS_STATE_VOICECALL; } writel((vts ? 0 : 1) << VTS_MEM_SEL_OFFSET, data->sfr_base + VTS_SHARED_MEM_CTRL); } #endif return 0; } EXPORT_SYMBOL(vts_acquire_sram); int vts_release_sram(struct platform_device *pdev, int vts) { #ifdef CONFIG_SOC_EXYNOS8895 struct vts_data *data = platform_get_drvdata(pdev); dev_info(&pdev->dev, "%s(%d)\n", __func__, vts); if (IS_ENABLED(CONFIG_SOC_EXYNOS8895)) { if (test_bit(0, &data->sram_acquired) && (data->voicecall_enabled || vts)) { writel(0 << VTS_MEM_SEL_OFFSET, data->sfr_base + VTS_SHARED_MEM_CTRL); clear_bit(0, &data->sram_acquired); if (!vts) { pm_runtime_put_sync(&pdev->dev); data->voicecall_enabled = false; } dev_info(&pdev->dev, "%s(%d) completed\n", __func__, vts); } else dev_warn(&pdev->dev, "%s(%d) already released\n", __func__, vts); } #endif return 0; } EXPORT_SYMBOL(vts_release_sram); int vts_clear_sram(struct platform_device *pdev) { struct vts_data *data = platform_get_drvdata(pdev); pr_info("%s\n", __func__); memset(data->sram_base, 0, data->sram_size); return 0; } EXPORT_SYMBOL(vts_clear_sram); volatile bool vts_is_on(void) { return p_vts_data && p_vts_data->enabled; } EXPORT_SYMBOL(vts_is_on); volatile bool vts_is_recognitionrunning(void) { return p_vts_data && p_vts_data->running; } EXPORT_SYMBOL(vts_is_recognitionrunning); static struct snd_soc_dai_driver vts_dai[] = { { .name = "vts-tri", .capture = { .stream_name = "VTS Trigger Capture", .channels_min = 1, .channels_max = 2, .rates = SNDRV_PCM_RATE_16000, .formats = SNDRV_PCM_FMTBIT_S16, .sig_bits = 16, }, }, { .name = "vts-rec", .capture = { .stream_name = "VTS Capture", .channels_min = 1, .channels_max = 2, .rates = SNDRV_PCM_RATE_16000, .formats = SNDRV_PCM_FMTBIT_S16, .sig_bits = 16, }, }, }; static const char *vts_hpf_sel_texts[] = {"120Hz", "40Hz"}; static SOC_ENUM_SINGLE_DECL(vts_hpf_sel, VTS_DMIC_CONTROL_DMIC_IF, VTS_DMIC_HPF_SEL_OFFSET, vts_hpf_sel_texts); static const char *vts_cps_sel_texts[] = {"normal", "absolute"}; static SOC_ENUM_SINGLE_DECL(vts_cps_sel, VTS_DMIC_CONTROL_DMIC_IF, VTS_DMIC_CPS_SEL_OFFSET, vts_cps_sel_texts); static const DECLARE_TLV_DB_SCALE(vts_gain_tlv_array, 0, 6, 0); static const char *vts_sys_sel_texts[] = {"512kHz", "768kHz", "384kHz", "2048kHz"}; static SOC_ENUM_SINGLE_DECL(vts_sys_sel, VTS_DMIC_CONTROL_DMIC_IF, VTS_DMIC_SYS_SEL_OFFSET, vts_sys_sel_texts); static int vts_sys_sel_put_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct device *dev = component->dev; unsigned int *item = ucontrol->value.enumerated.item; struct vts_data *data = p_vts_data; dev_dbg(dev, "%s(%u)\n", __func__, item[0]); data->syssel_rate = item[0]; return snd_soc_put_enum_double(kcontrol, ucontrol); } static const char *vts_polarity_clk_texts[] = {"rising edge of clock", "falling edge of clock"}; static SOC_ENUM_SINGLE_DECL(vts_polarity_clk, VTS_DMIC_CONTROL_DMIC_IF, VTS_DMIC_POLARITY_CLK_OFFSET, vts_polarity_clk_texts); static const char *vts_polarity_output_texts[] = {"right first", "left first"}; static SOC_ENUM_SINGLE_DECL(vts_polarity_output, VTS_DMIC_CONTROL_DMIC_IF, VTS_DMIC_POLARITY_OUTPUT_OFFSET, vts_polarity_output_texts); static const char *vts_polarity_input_texts[] = {"left PDM on CLK high", "left PDM on CLK low"}; static SOC_ENUM_SINGLE_DECL(vts_polarity_input, VTS_DMIC_CONTROL_DMIC_IF, VTS_DMIC_POLARITY_INPUT_OFFSET, vts_polarity_input_texts); static const char *vts_ovfw_ctrl_texts[] = {"limit", "reset"}; static SOC_ENUM_SINGLE_DECL(vts_ovfw_ctrl, VTS_DMIC_CONTROL_DMIC_IF, VTS_DMIC_OVFW_CTRL_OFFSET, vts_ovfw_ctrl_texts); static const char *vts_cic_sel_texts[] = {"Off", "On"}; static SOC_ENUM_SINGLE_DECL(vts_cic_sel, VTS_DMIC_CONTROL_DMIC_IF, VTS_DMIC_CIC_SEL_OFFSET, vts_cic_sel_texts); static const char * const vtsvcrecog_mode_text[] = { "OFF", "VOICE_TRIGGER_MODE_ON", "SOUND_DETECT_MODE_ON", "VT_ALWAYS_ON_MODE_ON", "GOOGLE_TRIGGER_MODE_ON", "SENSORY_TRIGGER_MODE_ON", "VOICE_TRIGGER_MODE_OFF", "SOUND_DETECT_MODE_OFF", "VT_ALWAYS_ON_MODE_OFF", "GOOGLE_TRIGGER_MODE_OFF", "SENSORY_TRIGGER_MODE_OFF" }; /* Keyphrases svoice: "Hi Galaxy", Google:"Okay Google" Sensory: "Hi Blue Genie" */ static const char * const vtsactive_phrase_text[] = { "SVOICE", "GOOGLE", "SENSORY" }; static const char * const vtsforce_reset_text[] = { "NONE", "RESET" }; static SOC_ENUM_SINGLE_EXT_DECL(vtsvcrecog_mode_enum, vtsvcrecog_mode_text); static SOC_ENUM_SINGLE_EXT_DECL(vtsactive_phrase_enum, vtsactive_phrase_text); static SOC_ENUM_SINGLE_EXT_DECL(vtsforce_reset_enum, vtsforce_reset_text); static int vts_start_recognization(struct device *dev, int start) { struct vts_data *data = dev_get_drvdata(dev); int active_trigger = data->active_trigger; int result; u32 values[3]; dev_info(dev, "%s for %s\n", __func__, vtsactive_phrase_text[active_trigger]); start = !!start; if (start) { dev_info(dev, "%s for %s G-loaded:%d s-loaded: %d\n", __func__, vtsactive_phrase_text[active_trigger], data->google_info.loaded, data->svoice_info.loaded); dev_info(dev, "%s exec_mode %d active_trig :%d\n", __func__, data->exec_mode, active_trigger); if (!(data->exec_mode & (0x1 << VTS_SOUND_DETECT_MODE))) { if (active_trigger == TRIGGER_SVOICE && data->svoice_info.loaded) { /* * load svoice model.bin @ offset 0x2A800 * file before starting recognition */ memcpy(data->sram_base + 0x2A800, data->svoice_info.data, data->svoice_info.actual_sz); dev_info(dev, "svoice.bin Binary uploaded size=%zu\n", data->svoice_info.actual_sz); } else if (active_trigger == TRIGGER_GOOGLE && data->google_info.loaded) { /* * load google model.bin @ offset 0x32B00 * file before starting recognition */ memcpy(data->sram_base + 0x32B00, data->google_info.data, data->google_info.actual_sz); dev_info(dev, "google.bin Binary uploaded size=%zu\n", data->google_info.actual_sz); } else { dev_err(dev, "%s Model Binary File not Loaded\n", __func__); return -EINVAL; } } result = vts_set_dmicctrl(data->pdev, ((active_trigger == TRIGGER_SVOICE) ? VTS_MICCONF_FOR_TRIGGER : VTS_MICCONF_FOR_GOOGLE), true); if (result < 0) { dev_err(dev, "%s: MIC control failed\n", __func__); return result; } if (data->exec_mode & (0x1 << VTS_SOUND_DETECT_MODE)) { int ctrl_dmicif; /* set VTS Gain for LPSD mode */ ctrl_dmicif = readl(data->dmic_base + VTS_DMIC_CONTROL_DMIC_IF); ctrl_dmicif &= ~(0x7 << VTS_DMIC_GAIN_OFFSET); writel((ctrl_dmicif | (data->lpsdgain << VTS_DMIC_GAIN_OFFSET)), data->dmic_base + VTS_DMIC_CONTROL_DMIC_IF); } /* Send Start recognition IPC command to VTS */ values[0] = 1 << active_trigger; values[1] = 0; values[2] = 0; result = vts_start_ipc_transaction(dev, data, VTS_IRQ_AP_START_RECOGNITION, &values, 0, 1); if (result < 0) { dev_err(dev, "vts ipc VTS_IRQ_AP_START_RECOGNITION failed: %d\n", result); return result; } data->vts_state = VTS_STATE_RECOG_STARTED; dev_info(dev, "%s start=%d, active_trigger=%d\n", __func__, start, active_trigger); } else if (!start) { values[0] = 1 << active_trigger; values[1] = 0; values[2] = 0; result = vts_start_ipc_transaction(dev, data, VTS_IRQ_AP_STOP_RECOGNITION, &values, 0, 1); if (result < 0) { dev_err(dev, "vts ipc VTS_IRQ_AP_STOP_RECOGNITION failed: %d\n", result); return result; } data->vts_state = VTS_STATE_RECOG_STOPPED; dev_info(dev, "%s start=%d, active_trigger=%d\n", __func__, start, active_trigger); result = vts_set_dmicctrl(data->pdev, ((active_trigger == TRIGGER_SVOICE) ? VTS_MICCONF_FOR_TRIGGER : VTS_MICCONF_FOR_GOOGLE), false); if (result < 0) { dev_err(dev, "%s: MIC control failed\n", __func__); return result; } } return 0; } static int get_vtsvoicerecognize_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct vts_data *data = p_vts_data; ucontrol->value.integer.value[0] = data->exec_mode; dev_dbg(component->dev, "GET VTS Execution mode: %d\n", data->exec_mode); return 0; } static int set_vtsvoicerecognize_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct vts_data *data = p_vts_data; struct device *dev = component->dev; u32 values[3]; int result = 0; int vcrecognize_mode = 0; int vcrecognize_start = 0; u32 keyword_type = 1; char env[100] = {0,}; char *envp[2] = {env, NULL}; int loopcnt = 10; pm_runtime_barrier(component->dev); while (data->voicecall_enabled) { dev_warn(dev, "%s voicecall (%d)\n", __func__, data->voicecall_enabled); if (loopcnt <= 0) { dev_warn(dev, "%s VTS SRAM is Used for CP call\n", __func__); keyword_type = -EBUSY; snprintf(env, sizeof(env), "VOICE_WAKEUP_WORD_ID=%x", keyword_type); kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp); return -EBUSY; } loopcnt--; usleep_range(10000, 10000); } dev_warn(dev, "%s voicecall (%d) (End)\n", __func__, data->voicecall_enabled); vcrecognize_mode = ucontrol->value.integer.value[0]; if (vcrecognize_mode < VTS_VOICE_TRIGGER_MODE || vcrecognize_mode >= VTS_MODE_COUNT) { dev_err(dev, "Invalid voice control mode =%d", vcrecognize_mode); return 0; } else { dev_info(dev, "%s Current: %d requested %s\n", __func__, data->exec_mode, vtsvcrecog_mode_text[vcrecognize_mode]); if ((vcrecognize_mode < VTS_VOICE_TRIGGER_MODE_OFF && data->exec_mode & (0x1 << vcrecognize_mode)) || (vcrecognize_mode >= VTS_VOICE_TRIGGER_MODE_OFF && !(data->exec_mode & (0x1 << (vcrecognize_mode - VTS_SENSORY_TRIGGER_MODE))))) { dev_err(dev, "Requested Recognition mode=%d already completed", vcrecognize_mode); return 0; } if (vcrecognize_mode <= VTS_SENSORY_TRIGGER_MODE) { pm_runtime_get_sync(dev); vts_clk_set_rate(dev, data->syssel_rate); vcrecognize_start = true; } else vcrecognize_start = false; if (!pm_runtime_active(dev)) { dev_warn(dev, "%s wrong state %d req: %d\n", __func__, data->exec_mode, vcrecognize_mode); return 0; } values[0] = vcrecognize_mode; values[1] = 0; values[2] = 0; result = vts_start_ipc_transaction(dev, data, VTS_IRQ_AP_SET_MODE, &values, 0, 1); if (result < 0) { dev_err(dev, "%s IPC transaction Failed\n", vtsvcrecog_mode_text[vcrecognize_mode]); goto err_ipcmode; } if (vcrecognize_start) data->exec_mode |= (0x1 << vcrecognize_mode); else data->exec_mode &= ~(0x1 << (vcrecognize_mode - VTS_SENSORY_TRIGGER_MODE)); /* Start/stop the request Voice recognization mode */ result = vts_start_recognization(dev, vcrecognize_start); if (result < 0) { dev_err(dev, "Start Recognization Failed: %d\n", result); goto err_ipcmode; } if (vcrecognize_start) data->voicerecog_start |= (0x1 << data->active_trigger); else data->voicerecog_start &= ~(0x1 << data->active_trigger); dev_info(dev, "%s Configured: [%d] %s started\n", __func__, data->exec_mode, vtsvcrecog_mode_text[vcrecognize_mode]); if (!vcrecognize_start && pm_runtime_active(dev)) { pm_runtime_put_sync(dev); } } return 0; err_ipcmode: if (pm_runtime_active(dev) && (vcrecognize_start || data->exec_mode & (0x1 << vcrecognize_mode) || data->voicerecog_start & (0x1 << data->active_trigger))) { pm_runtime_put_sync(dev); } if (!vcrecognize_start) { data->exec_mode &= ~(0x1 << (vcrecognize_mode - VTS_SENSORY_TRIGGER_MODE)); data->voicerecog_start &= ~(0x1 << data->active_trigger); } return result; } #ifdef CONFIG_SOC_EXYNOS8895 static int set_vtsexec_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct vts_data *data = p_vts_data; u32 values[3]; int result = 0; int vtsexecution_mode; u32 keyword_type = 1; char env[100] = {0,}; char *envp[2] = {env, NULL}; struct device *dev = &data->pdev->dev; int loopcnt = 10; pm_runtime_barrier(component->dev); while (data->voicecall_enabled) { dev_warn(component->dev, "%s voicecall (%d)\n", __func__, data->voicecall_enabled); if (loopcnt <= 0) { dev_warn(component->dev, "%s VTS SRAM is Used for CP call\n", __func__); keyword_type = -EBUSY; snprintf(env, sizeof(env), "VOICE_WAKEUP_WORD_ID=%x", keyword_type); kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp); return -EBUSY; } loopcnt--; usleep_range(10000, 10000); } dev_warn(component->dev, "%s voicecall (%d) (End)\n", __func__, data->voicecall_enabled); vtsexecution_mode = ucontrol->value.integer.value[0]; if (vtsexecution_mode >= VTS_MODE_COUNT) { dev_err(component->dev, "Invalid voice control mode =%d", vtsexecution_mode); return 0; } else { dev_info(component->dev, "%s Current: %d requested %s\n", __func__, data->exec_mode, vtsexec_mode_text[vtsexecution_mode]); if (data->exec_mode == VTS_OFF_MODE && vtsexecution_mode != VTS_OFF_MODE) { pm_runtime_get_sync(component->dev); vts_clk_set_rate(component->dev, data->syssel_rate); } if (pm_runtime_active(component->dev)) { values[0] = vtsexecution_mode; values[1] = 0; values[2] = 0; result = vts_start_ipc_transaction(component->dev, data, VTS_IRQ_AP_SET_MODE, &values, 0, 1); if (result < 0) { dev_err(component->dev, "%s SET_MODE IPC transaction Failed\n", vtsexec_mode_text[vtsexecution_mode]); if (data->exec_mode == VTS_OFF_MODE && vtsexecution_mode != VTS_OFF_MODE) pm_runtime_put_sync(component->dev); return result; } } if (vtsexecution_mode <= VTS_SENSORY_TRIGGER_MODE) data->exec_mode |= (0x1 << vtsexecution_mode); else data->exec_mode &= ~(0x1 << (vtsexecution_mode - VTS_SENSORY_TRIGGER_MODE)); dev_info(component->dev, "%s Configured: [%d] %s\n", __func__, data->exec_mode, vtsexec_mode_text[vtsexecution_mode]); if (data->exec_mode == VTS_OFF_MODE && pm_runtime_active(component->dev)) { pm_runtime_put_sync(component->dev); } } return 0; } #endif static int get_vtsactive_phrase(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct vts_data *data = p_vts_data; ucontrol->value.integer.value[0] = data->active_trigger; dev_dbg(component->dev, "GET VTS Active Phrase: %s \n", vtsactive_phrase_text[data->active_trigger]); return 0; } static int set_vtsactive_phrase(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct vts_data *data = p_vts_data; int vtsactive_phrase; vtsactive_phrase = ucontrol->value.integer.value[0]; if (vtsactive_phrase > 2) { dev_err(component->dev, "Invalid VTS Trigger Key phrase =%d", vtsactive_phrase); return 0; } else { data->active_trigger = vtsactive_phrase; dev_info(component->dev, "VTS Active phrase: %s \n", vtsactive_phrase_text[vtsactive_phrase]); } return 0; } static int get_voicetrigger_value(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct vts_data *data = p_vts_data; ucontrol->value.integer.value[0] = data->target_size; dev_info(component->dev, "GET Voice Trigger Value: %d \n", data->target_size); return 0; } static int set_voicetrigger_value(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct vts_data *data = p_vts_data; int active_trigger = data->active_trigger; u32 values[3]; int result = 0; int trig_ms; pm_runtime_barrier(component->dev); if (data->voicecall_enabled) { u32 keyword_type = 1; char env[100] = {0,}; char *envp[2] = {env, NULL}; struct device *dev = &data->pdev->dev; dev_warn(component->dev, "%s VTS SRAM is Used for CP call\n", __func__); keyword_type = -EBUSY; snprintf(env, sizeof(env), "VOICE_WAKEUP_WORD_ID=%x", keyword_type); kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp); return -EBUSY; } trig_ms = ucontrol->value.integer.value[0]; if (trig_ms > 2000 || trig_ms < 0) { dev_err(component->dev, "Invalid Voice Trigger Value = %d (valid range 0~2000ms)", trig_ms); return 0; } else { /* Configure VTS target size */ values[0] = trig_ms * 32; /* 1ms requires (16KHz,16bit,Mono) = 16samples * 2 bytes = 32 bytes*/ values[1] = 1 << active_trigger; values[2] = 0; result = vts_start_ipc_transaction(component->dev, data, VTS_IRQ_AP_TARGET_SIZE, &values, 0, 1); if (result < 0) { dev_err(component->dev, "Voice Trigger Value setting IPC Transaction Failed: %d\n", result); return result; } data->target_size = trig_ms; dev_info(component->dev, "SET Voice Trigger Value: %dms\n", data->target_size); } return 0; } static int get_vtsforce_reset(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct vts_data *data = p_vts_data; ucontrol->value.integer.value[0] = data->running; dev_dbg(component->dev, "GET VTS Force Reset: %s\n", (data->running ? "VTS Running" : "VTS Not Running")); return 0; } static int set_vtsforce_reset(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct device *dev = component->dev; struct vts_data *data = p_vts_data; dev_dbg(dev, "VTS RESET: %s\n", __func__); while (data->running && pm_runtime_active(dev)) { dev_warn(dev, "%s Clear active models\n", __func__); pm_runtime_put_sync(dev); } return 0; } static const struct snd_kcontrol_new vts_controls[] = { SOC_SINGLE("PERIOD DATA2REQ", VTS_DMIC_ENABLE_DMIC_IF, VTS_DMIC_PERIOD_DATA2REQ_OFFSET, 3, 0), SOC_SINGLE("HPF EN", VTS_DMIC_CONTROL_DMIC_IF, VTS_DMIC_HPF_EN_OFFSET, 1, 0), SOC_ENUM("HPF SEL", vts_hpf_sel), SOC_ENUM("CPS SEL", vts_cps_sel), SOC_SINGLE_TLV("GAIN", VTS_DMIC_CONTROL_DMIC_IF, VTS_DMIC_GAIN_OFFSET, 4, 0, vts_gain_tlv_array), SOC_ENUM_EXT("SYS SEL", vts_sys_sel, snd_soc_get_enum_double, vts_sys_sel_put_enum), SOC_ENUM("POLARITY CLK", vts_polarity_clk), SOC_ENUM("POLARITY OUTPUT", vts_polarity_output), SOC_ENUM("POLARITY INPUT", vts_polarity_input), SOC_ENUM("OVFW CTRL", vts_ovfw_ctrl), SOC_ENUM("CIC SEL", vts_cic_sel), SOC_ENUM_EXT("VoiceRecognization Mode", vtsvcrecog_mode_enum, get_vtsvoicerecognize_mode, set_vtsvoicerecognize_mode), SOC_ENUM_EXT("Active Keyphrase", vtsactive_phrase_enum, get_vtsactive_phrase, set_vtsactive_phrase), SOC_SINGLE_EXT("VoiceTrigger Value", SND_SOC_NOPM, 0, 2000, 0, get_voicetrigger_value, set_voicetrigger_value), SOC_ENUM_EXT("Force Reset", vtsforce_reset_enum, get_vtsforce_reset, set_vtsforce_reset), }; static const char *dmic_sel_texts[] = {"DPDM", "APDM"}; static SOC_ENUM_SINGLE_DECL(dmic_sel_enum, VTS_DMIC_CONTROL_DMIC_IF, VTS_DMIC_DMIC_SEL_OFFSET, dmic_sel_texts); static const struct snd_kcontrol_new dmic_sel_controls[] = { SOC_DAPM_ENUM("MUX", dmic_sel_enum), }; static const struct snd_kcontrol_new dmic_if_controls[] = { SOC_DAPM_SINGLE("RCH EN", VTS_DMIC_CONTROL_DMIC_IF, VTS_DMIC_RCH_EN_OFFSET, 1, 0), SOC_DAPM_SINGLE("LCH EN", VTS_DMIC_CONTROL_DMIC_IF, VTS_DMIC_LCH_EN_OFFSET, 1, 0), }; static const struct snd_soc_dapm_widget vts_dapm_widgets[] = { SND_SOC_DAPM_INPUT("PAD APDM"), SND_SOC_DAPM_INPUT("PAD DPDM"), SND_SOC_DAPM_MUX("DMIC SEL", SND_SOC_NOPM, 0, 0, dmic_sel_controls), SOC_MIXER_ARRAY("DMIC IF", SND_SOC_NOPM, 0, 0, dmic_if_controls), }; static const struct snd_soc_dapm_route vts_dapm_routes[] = { // sink, control, source {"DMIC SEL", "APDM", "PAD APDM"}, {"DMIC SEL", "DPDM", "PAD DPDM"}, {"DMIC IF", "RCH EN", "DMIC SEL"}, {"DMIC IF", "LCH EN", "DMIC SEL"}, {"VTS Capture", NULL, "DMIC IF"}, }; static int vts_component_probe(struct snd_soc_component *component) { struct device *dev = component->dev; struct vts_data *data = dev_get_drvdata(dev); dev_info(dev, "%s\n", __func__); data->cmpnt = component; vts_clk_set_rate(component->dev, 0); return 0; } static const struct snd_soc_component_driver vts_component = { .probe = vts_component_probe, .controls = vts_controls, .num_controls = ARRAY_SIZE(vts_controls), .dapm_widgets = vts_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(vts_dapm_widgets), .dapm_routes = vts_dapm_routes, .num_dapm_routes = ARRAY_SIZE(vts_dapm_routes), }; int vts_set_dmicctrl(struct platform_device *pdev, int micconf_type, bool enable) { struct device *dev = &pdev->dev; struct vts_data *data = platform_get_drvdata(pdev); int dmic_clkctrl = 0; int ctrl_dmicif = 0; int select_dmicclk = 0; dev_dbg(dev, "%s-- flag: %d mictype: %d micusagecnt: %d\n", __func__, enable, micconf_type, data->micclk_init_cnt); if (!data->vts_ready) { dev_warn(dev, "%s: VTS Firmware Not running\n", __func__); return -EINVAL; } if (enable) { if (!data->micclk_init_cnt) { ctrl_dmicif = readl(data->dmic_base + VTS_DMIC_CONTROL_DMIC_IF); if (ctrl_dmicif & (0x1 << VTS_DMIC_DMIC_SEL_OFFSET)) { vts_cfg_gpio(dev, "amic_default"); select_dmicclk = ((0x1 << VTS_ENABLE_CLK_GEN_OFFSET) | (0x1 << VTS_SEL_EXT_DMIC_CLK_OFFSET) | (0x1 << VTS_ENABLE_CLK_CLK_GEN_OFFSET)); writel(select_dmicclk, data->sfr_base + VTS_DMIC_CLK_CON); /* Set AMIC VTS Gain */ writel((ctrl_dmicif | (data->amicgain << VTS_DMIC_GAIN_OFFSET)), data->dmic_base + VTS_DMIC_CONTROL_DMIC_IF); } else { vts_cfg_gpio(dev, "dmic_default"); select_dmicclk = ((0x0 << VTS_ENABLE_CLK_GEN_OFFSET) | (0x0 << VTS_SEL_EXT_DMIC_CLK_OFFSET) | (0x0 << VTS_ENABLE_CLK_CLK_GEN_OFFSET)); writel(select_dmicclk, data->sfr_base + VTS_DMIC_CLK_CON); /* Set DMIC VTS Gain */ writel((ctrl_dmicif | (data->dmicgain << VTS_DMIC_GAIN_OFFSET)), data->dmic_base + VTS_DMIC_CONTROL_DMIC_IF); } dmic_clkctrl = readl(data->sfr_base + VTS_DMIC_CLK_CTRL); writel(dmic_clkctrl | (0x1 << VTS_CLK_ENABLE_OFFSET), data->sfr_base + VTS_DMIC_CLK_CTRL); dev_info(dev, "%s Micclk setting ENABLED\n", __func__); } /* check whether Mic is already configure or not based on VTS option type for MIC configuration book keeping */ if ((!(data->mic_ready & (0x1 << VTS_MICCONF_FOR_TRIGGER)) || !(data->mic_ready & (0x1 << VTS_MICCONF_FOR_GOOGLE))) && (micconf_type == VTS_MICCONF_FOR_TRIGGER || micconf_type == VTS_MICCONF_FOR_GOOGLE)) { data->micclk_init_cnt++; data->mic_ready |= (0x1 << micconf_type); dev_info(dev, "%s Micclk ENABLED for TRIGGER ++ %d\n", __func__, data->mic_ready); } else if (!(data->mic_ready & (0x1 << VTS_MICCONF_FOR_RECORD)) && micconf_type == VTS_MICCONF_FOR_RECORD) { data->micclk_init_cnt++; data->mic_ready |= (0x1 << micconf_type); dev_info(dev, "%s Micclk ENABLED for RECORD ++ %d\n", __func__, data->mic_ready); } } else { if (data->micclk_init_cnt) data->micclk_init_cnt--; if (!data->micclk_init_cnt) { vts_cfg_gpio(dev, "idle"); dmic_clkctrl = readl(data->sfr_base + VTS_DMIC_CLK_CTRL); writel(dmic_clkctrl & ~(0x1 << VTS_CLK_ENABLE_OFFSET), data->sfr_base + VTS_DMIC_CLK_CTRL); writel(0x0, data->sfr_base + VTS_DMIC_CLK_CON); /* reset VTS Gain to default */ writel((ctrl_dmicif & (~(0x7 << VTS_DMIC_GAIN_OFFSET))), data->dmic_base + VTS_DMIC_CONTROL_DMIC_IF); dev_info(dev, "%s Micclk setting DISABLED\n", __func__); } /* MIC configuration book keeping */ if (((data->mic_ready & (0x1 << VTS_MICCONF_FOR_TRIGGER)) || (data->mic_ready & (0x1 << VTS_MICCONF_FOR_GOOGLE))) && (micconf_type == VTS_MICCONF_FOR_TRIGGER || micconf_type == VTS_MICCONF_FOR_GOOGLE)) { data->mic_ready &= ~(0x1 << micconf_type); dev_info(dev, "%s Micclk DISABLED for TRIGGER -- %d\n", __func__, data->mic_ready); } else if ((data->mic_ready & (0x1 << VTS_MICCONF_FOR_RECORD)) && micconf_type == VTS_MICCONF_FOR_RECORD) { data->mic_ready &= ~(0x1 << micconf_type); dev_info(dev, "%s Micclk DISABLED for RECORD -- %d\n", __func__, data->mic_ready); } } return 0; } static irqreturn_t vts_error_handler(int irq, void *dev_id) { struct platform_device *pdev = dev_id; struct device *dev = &pdev->dev; struct vts_data *data = platform_get_drvdata(pdev); u32 error_code; mailbox_read_shared_register(data->pdev_mailbox, &error_code, 3, 1); vts_ipc_ack(data, 1); dev_err(dev, "Error occurred on VTS: 0x%x\n", (int)error_code); vts_reset_cpu(); return IRQ_HANDLED; } static irqreturn_t vts_boot_completed_handler(int irq, void *dev_id) { struct platform_device *pdev = dev_id; struct device *dev = &pdev->dev; struct vts_data *data = platform_get_drvdata(pdev); data->vts_ready = 1; vts_ipc_ack(data, 1); wake_up(&data->ipc_wait_queue); dev_info(dev, "VTS boot completed\n"); return IRQ_HANDLED; } static irqreturn_t vts_ipc_received_handler(int irq, void *dev_id) { struct platform_device *pdev = dev_id; struct device *dev = &pdev->dev; struct vts_data *data = platform_get_drvdata(pdev); u32 result; mailbox_read_shared_register(data->pdev_mailbox, &result, 3, 1); dev_info(dev, "VTS received IPC: 0x%x\n", result); switch (data->ipc_state_ap) { case SEND_MSG: if (result == (0x1 << data->running_ipc)) { dev_dbg(dev, "IPC transaction completed\n"); data->ipc_state_ap = SEND_MSG_OK; } else { dev_err(dev, "IPC transaction error\n"); data->ipc_state_ap = SEND_MSG_FAIL; } break; default: dev_warn(dev, "State fault: %d Ack_value:0x%x\n", data->ipc_state_ap, result); break; } return IRQ_HANDLED; } static irqreturn_t vts_voice_triggered_handler(int irq, void *dev_id) { struct platform_device *pdev = dev_id; struct device *dev = &pdev->dev; struct vts_data *data = platform_get_drvdata(pdev); u32 id, score, frame_count; u32 keyword_type = 1; char env[100] = {0,}; char *envp[2] = {env, NULL}; if (data->mic_ready & (0x1 << VTS_MICCONF_FOR_TRIGGER) || data->mic_ready & (0x1 << VTS_MICCONF_FOR_GOOGLE)) { mailbox_read_shared_register(data->pdev_mailbox, &id, 3, 1); vts_ipc_ack(data, 1); frame_count = (u32)(id & GENMASK(15, 0)); score = (u32)((id & GENMASK(27, 16)) >> 16); id >>= 28; dev_info(dev, "VTS triggered: id = %u, score = %u\n", id, score); dev_info(dev, "VTS triggered: frame_count = %u\n", frame_count); if (!(data->exec_mode & (0x1 << VTS_SOUND_DETECT_MODE))) { keyword_type = id; snprintf(env, sizeof(env), "VOICE_WAKEUP_WORD_ID=%x", keyword_type); } else if (data->exec_mode & (0x1 << VTS_SOUND_DETECT_MODE)) { snprintf(env, sizeof(env), "VOICE_WAKEUP_WORD_ID=LPSD"); } else { dev_warn(dev, "Unknown VTS Execution Mode!!\n"); } kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp); wake_lock_timeout(&data->wake_lock, VTS_TRIGGERED_TIMEOUT_MS); data->vts_state = VTS_STATE_RECOG_TRIGGERED; } return IRQ_HANDLED; } static irqreturn_t vts_trigger_period_elapsed_handler(int irq, void *dev_id) { struct platform_device *pdev = dev_id; struct device *dev = &pdev->dev; struct vts_data *data = platform_get_drvdata(pdev); struct vts_platform_data *platform_data = platform_get_drvdata(data->pdev_vtsdma[0]); u32 pointer; if (data->mic_ready & (0x1 << VTS_MICCONF_FOR_TRIGGER) || data->mic_ready & (0x1 << VTS_MICCONF_FOR_GOOGLE)) { mailbox_read_shared_register(data->pdev_mailbox, &pointer, 2, 1); dev_dbg(dev, "%s:[%s] Base: %08x pointer:%08x\n", __func__, (platform_data->id ? "VTS-RECORD" : "VTS-TRIGGER"), data->dma_area_vts, pointer); if (pointer) platform_data->pointer = (pointer - data->dma_area_vts); vts_ipc_ack(data, 1); snd_pcm_period_elapsed(platform_data->substream); } return IRQ_HANDLED; } static irqreturn_t vts_record_period_elapsed_handler(int irq, void *dev_id) { struct platform_device *pdev = dev_id; struct device *dev = &pdev->dev; struct vts_data *data = platform_get_drvdata(pdev); struct vts_platform_data *platform_data = platform_get_drvdata(data->pdev_vtsdma[1]); u32 pointer; if (data->mic_ready & (0x1 << VTS_MICCONF_FOR_RECORD)) { mailbox_read_shared_register(data->pdev_mailbox, &pointer, 1, 1); dev_dbg(dev, "%s:[%s] Base: %08x pointer:%08x\n", __func__, (platform_data->id ? "VTS-RECORD" : "VTS-TRIGGER"), (data->dma_area_vts + BUFFER_BYTES_MAX/2), pointer); if (pointer) platform_data->pointer = (pointer - (data->dma_area_vts + BUFFER_BYTES_MAX/2)); vts_ipc_ack(data, 1); snd_pcm_period_elapsed(platform_data->substream); } return IRQ_HANDLED; } static irqreturn_t vts_debuglog_bufzero_handler(int irq, void *dev_id) { struct platform_device *pdev = dev_id; struct device *dev = &pdev->dev; struct vts_data *data = platform_get_drvdata(pdev); dev_dbg(dev, "%s LogBuffer Index: %d\n", __func__, 0); /* schedule log dump */ vts_log_schedule_flush(dev, 0); vts_ipc_ack(data, 1); return IRQ_HANDLED; } static irqreturn_t vts_debuglog_bufone_handler(int irq, void *dev_id) { struct platform_device *pdev = dev_id; struct device *dev = &pdev->dev; struct vts_data *data = platform_get_drvdata(pdev); dev_dbg(dev, "%s LogBuffer Index: %d\n", __func__, 1); /* schedule log dump */ vts_log_schedule_flush(dev, 1); vts_ipc_ack(data, 1); return IRQ_HANDLED; } static irqreturn_t vts_audiodump_handler(int irq, void *dev_id) { struct platform_device *pdev = dev_id; struct device *dev = &pdev->dev; struct vts_data *data = platform_get_drvdata(pdev); dev_info(dev, "%s\n", __func__); if (data->vts_ready && data->audiodump_enabled) { u32 ackvalues[3] = {0, 0, 0}; mailbox_read_shared_register(data->pdev_mailbox, ackvalues, 0, 2); dev_info(dev, "%sDump offset: 0x%x size:0x%x\n", __func__, ackvalues[0], ackvalues[1]); /* register audio dump offset & size */ vts_dump_addr_register(dev, ackvalues[0], ackvalues[1], VTS_AUDIO_DUMP); /* schedule pcm dump */ vts_audiodump_schedule_flush(dev); /* vts_ipc_ack should be sent once dump is completed */ } else { vts_ipc_ack(data, 1); } return IRQ_HANDLED; } static irqreturn_t vts_logdump_handler(int irq, void *dev_id) { struct platform_device *pdev = dev_id; struct device *dev = &pdev->dev; struct vts_data *data = platform_get_drvdata(pdev); dev_info(dev, "%s\n", __func__); if (data->vts_ready && data->logdump_enabled) { /* schedule pcm dump */ vts_logdump_schedule_flush(dev); /* vts_ipc_ack should be sent once dump is completed */ } else { vts_ipc_ack(data, 1); } return IRQ_HANDLED; } void vts_register_dma(struct platform_device *pdev_vts, struct platform_device *pdev_vts_dma, unsigned int id) { struct vts_data *data = platform_get_drvdata(pdev_vts); if (id < ARRAY_SIZE(data->pdev_vtsdma)) { data->pdev_vtsdma[id] = pdev_vts_dma; if (id > data->vtsdma_count) { data->vtsdma_count = id + 1; } dev_info(&data->pdev->dev, "%s: VTS-DMA id(%u)Registered \n", __func__, id); } else { dev_err(&data->pdev->dev, "%s: invalid id(%u)\n", __func__, id); } } static int vts_suspend(struct device *dev) { struct vts_data *data = dev_get_drvdata(dev); u32 values[3] = {0,0,0}; int result = 0; if (data->vts_ready) { if (data->running && data->vts_state == VTS_STATE_RECOG_TRIGGERED) { result = vts_start_ipc_transaction(dev, data, VTS_IRQ_AP_RESTART_RECOGNITION, &values, 0, 1); if (result < 0) { dev_err(dev, "%s restarted trigger failed\n", __func__); goto error_ipc; } data->vts_state = VTS_STATE_RECOG_STARTED; } /* enable vts wakeup source interrupts */ enable_irq_wake(data->irq[VTS_IRQ_VTS_VOICE_TRIGGERED]); enable_irq_wake(data->irq[VTS_IRQ_VTS_ERROR]); if (data->audiodump_enabled) enable_irq_wake(data->irq[VTS_IRQ_VTS_AUDIO_DUMP]); if (data->logdump_enabled) enable_irq_wake(data->irq[VTS_IRQ_VTS_LOG_DUMP]); dev_info(dev, "%s: Enable VTS Wakeup source irqs\n", __func__); } error_ipc: return result; } static int vts_resume(struct device *dev) { struct vts_data *data = dev_get_drvdata(dev); if (data->vts_ready) { /* disable vts wakeup source interrupts */ disable_irq_wake(data->irq[VTS_IRQ_VTS_VOICE_TRIGGERED]); disable_irq_wake(data->irq[VTS_IRQ_VTS_ERROR]); if (data->audiodump_enabled) disable_irq_wake(data->irq[VTS_IRQ_VTS_AUDIO_DUMP]); if (data->logdump_enabled) disable_irq_wake(data->irq[VTS_IRQ_VTS_LOG_DUMP]); dev_info(dev, "%s: Disable VTS Wakeup source irqs\n", __func__); } return 0; } static void vts_irq_enable(struct platform_device *pdev, bool enable) { struct vts_data *data = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; int irqidx; dev_info(dev, "%s IRQ Enable: [%s]\n", __func__, (enable ? "TRUE" : "FALSE")); for (irqidx = 0; irqidx < VTS_IRQ_COUNT; irqidx++) { if (enable) enable_irq(data->irq[irqidx]); else disable_irq(data->irq[irqidx]); } } static void vts_save_register(struct vts_data *data) { regcache_cache_only(data->regmap_dmic, true); regcache_mark_dirty(data->regmap_dmic); } static void vts_restore_register(struct vts_data *data) { regcache_cache_only(data->regmap_dmic, false); regcache_sync(data->regmap_dmic); } void vts_dbg_print_gpr(struct device *dev, struct vts_data *data) { int i; char buf[10]; unsigned int version = data->vtsfw_version; buf[0] = (((version >> 24) & 0xFF) + '0'); buf[1] = '.'; buf[2] = (((version >> 16) & 0xFF) + '0'); buf[3] = '.'; buf[4] = (((version >> 8) & 0xFF) + '0'); buf[5] = '.'; buf[6] = ((version & 0xFF) + '0'); buf[7] = '\0'; dev_info(dev, "========================================\n"); dev_info(dev, "VTS CM4 register dump (%s)\n", buf); dev_info(dev, "----------------------------------------\n"); for (i = 0; i <= 14; i++) { dev_info(dev, "CM4_R%02d : %08x\n", i, readl(data->gpr_base + VTS_CM4_R(i))); } dev_info(dev, "CM4_PC : %08x\n", readl(data->gpr_base + VTS_CM4_PC)); dev_info(dev, "========================================\n"); } void vts_dbg_dump_log_gpr( struct device *dev, struct vts_data *data, unsigned int dbg_type, const char *reason) { struct vts_dbg_dump *p_dump = NULL; int i; dev_dbg(dev, "%s\n", __func__); if (!vts_is_on() || !data->running) { dev_info(dev, "%s is skipped due to no power\n", __func__); return; } if (dbg_type == RUNTIME_SUSPEND_DUMP) p_dump = (struct vts_dbg_dump *)data->dmab_log.area; else p_dump = (struct vts_dbg_dump *)(data->dmab_log.area + (LOG_BUFFER_BYTES_MAX/2)); /* Get VTS firmware log msgs */ memcpy_fromio(p_dump->sram, data->sramlog_baseaddr, SZ_2K); p_dump->time = sched_clock(); strncpy(p_dump->reason, reason, sizeof(p_dump->reason) - 1); for (i = 0; i <= 14; i++) p_dump->gpr[i] = readl(data->gpr_base + VTS_CM4_R(i)); p_dump->gpr[i++] = readl(data->gpr_base + VTS_CM4_PC); } static void exynos_vts_panic_handler(void) { static bool has_run; struct vts_data *data = p_vts_data; struct device *dev = data ? (data->pdev ? &data->pdev->dev : NULL) : NULL; dev_dbg(dev, "%s\n", __func__); if (vts_is_on() && dev) { if (has_run) { dev_info(dev, "already dumped\n"); return; } has_run = true; /* Dump VTS GPR register & Log messages */ vts_dbg_dump_log_gpr(dev, data, KERNEL_PANIC_DUMP, "panic"); } else { dev_info(dev, "%s: dump is skipped due to no power\n", __func__); } } static int vts_panic_handler(struct notifier_block *nb, unsigned long action, void *data) { exynos_vts_panic_handler(); return NOTIFY_OK; } static struct notifier_block vts_panic_notifier = { .notifier_call = vts_panic_handler, .next = NULL, .priority = 0 /* priority: INT_MAX >= x >= 0 */ }; static int vts_runtime_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct vts_data *data = dev_get_drvdata(dev); int i = 1000; unsigned int status = 0; u32 values[3] = {0,0,0}; int result = 0; dev_info(dev, "%s \n", __func__); vts_save_register(data); if (data->running) { if (data->audiodump_enabled) { values[0] = VTS_DISABLE_AUDIODUMP; values[1] = 0; values[2] = 0; result = vts_start_ipc_transaction(dev, data, VTS_IRQ_AP_TEST_COMMAND, &values, 0, 1); if (result < 0) dev_warn(dev, "Disable_AudioDump ipc failed\n"); /* reset audio dump offset & size */ vts_dump_addr_register(dev, 0, 0, VTS_AUDIO_DUMP); } if (data->logdump_enabled) { values[0] = VTS_DISABLE_LOGDUMP; values[1] = 0; values[2] = 0; result = vts_start_ipc_transaction(dev, data, VTS_IRQ_AP_TEST_COMMAND, &values, 0, 1); if (result < 0) dev_warn(dev, "Disable_LogDump ipc failed\n"); /* reset audio dump offset & size */ vts_dump_addr_register(dev, 0, 0, VTS_LOG_DUMP); } if (data->vtslog_enabled) { values[0] = VTS_DISABLE_DEBUGLOG; values[1] = 0; values[2] = 0; result = vts_start_ipc_transaction(dev, data, VTS_IRQ_AP_TEST_COMMAND, &values, 0, 1); if (result < 0) dev_warn(dev, "Disable_debuglog ipc transaction failed\n"); /* reset VTS SRAM debug log buffer */ vts_register_log_buffer(dev, 0, 0); } values[0] = 0; values[1] = 0; values[2] = 0; result = vts_start_ipc_transaction(dev, data, VTS_IRQ_AP_POWER_DOWN, &values, 0, 1); if (result < 0) { dev_warn(dev, "POWER_DOWN IPC transaction Failed\n"); } /* Dump VTS GPR register & Log messages */ vts_dbg_print_gpr(dev, data); vts_dbg_dump_log_gpr(dev, data, RUNTIME_SUSPEND_DUMP, "runtime_suspend"); /* wait for VTS STANDBYWFI in STATUS SFR */ do { exynos_pmu_read(VTS_CPU_STANDBY, &status); dev_dbg(dev, "%s Read VTS_CPU_STANDBY for STANDBYWFI status\n", __func__); } while (i-- && !(status & VTS_CPU_STANDBY_STANDBYWFI_MASK)); if (!i) { dev_warn(dev, "VTS IP entering WFI time out\n"); } if (data->irq_state) { vts_irq_enable(pdev, false); data->irq_state = false; } clk_disable(data->clk_dmic); vts_cpu_enable(false); vts_cpu_power(false); data->running = false; } data->enabled = false; data->exec_mode = VTS_OFF_MODE; data->active_trigger = TRIGGER_SVOICE; data->voicerecog_start = 0; data->target_size = 0; /* reset micbias setting count */ data->micclk_init_cnt = 0; data->mic_ready = 0; data->vts_ready = 0; data->vts_state = VTS_STATE_NONE; dev_info(dev, "%s Exit \n", __func__); return 0; } static int vts_runtime_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct vts_data *data = dev_get_drvdata(dev); u32 values[3]; int result; dev_info(dev, "%s \n", __func__); data->enabled = true; vts_restore_register(data); vts_cfg_gpio(dev, "dmic_default"); vts_cfg_gpio(dev, "idle"); vts_pad_retention(false); if (!data->irq_state) { vts_irq_enable(pdev, true); data->irq_state = true; } result = clk_enable(data->clk_dmic); if (result < 0) { dev_err(dev, "Failed to enable the clock\n"); goto error_clk; } dev_info(dev, "dmic clock rate:%lu\n", clk_get_rate(data->clk_dmic)); vts_cpu_power(true); result = vts_download_firmware(pdev); if (result < 0) { dev_err(dev, "Failed to download firmware\n"); goto error_firmware; } vts_cpu_enable(true); vts_wait_for_fw_ready(dev); /* Configure select sys clock rate */ vts_clk_set_rate(dev, data->syssel_rate); #if 1 data->dma_area_vts= vts_set_baaw(data->baaw_base, data->dmab.addr, BUFFER_BYTES_MAX); #endif values[0] = data->dma_area_vts; values[1] = 0x140; values[2] = 0x800; result = vts_start_ipc_transaction(dev, data, VTS_IRQ_AP_SET_DRAM_BUFFER, &values, 0, 1); if (result < 0) { dev_err(dev, "DRAM_BUFFER Setting IPC transaction Failed\n"); goto error_firmware; } values[0] = VTS_OFF_MODE; values[1] = 0; values[2] = 0; result = vts_start_ipc_transaction(dev, data, VTS_IRQ_AP_SET_MODE, &values, 0, 1); if (result < 0) { dev_err(dev, "SET_MODE to OFF IPC transaction Failed\n"); goto error_firmware; } data->exec_mode = VTS_OFF_MODE; values[0] = VTS_ENABLE_SRAM_LOG; values[1] = 0; values[2] = 0; result = vts_start_ipc_transaction(dev, data, VTS_IRQ_AP_TEST_COMMAND, &values, 0, 1); if (result < 0) { dev_err(dev, "Enable_SRAM_log ipc transaction failed\n"); goto error_firmware; } if (data->vtslog_enabled) { values[0] = VTS_ENABLE_DEBUGLOG; values[1] = 0; values[2] = 0; result = vts_start_ipc_transaction(dev, data, VTS_IRQ_AP_TEST_COMMAND, &values, 0, 1); if (result < 0) { dev_err(dev, "Enable_debuglog ipc transaction failed\n"); goto error_firmware; } } /* Enable Audio data Dump */ if (data->audiodump_enabled) { values[0] = VTS_ENABLE_AUDIODUMP; values[1] = (VTS_ADUIODUMP_AFTER_MINS * 60); values[2] = 0; result = vts_start_ipc_transaction(dev, data, VTS_IRQ_AP_TEST_COMMAND, &values, 0, 2); if (result < 0) { dev_err(dev, "Enable_AudioDump ipc failed\n"); goto error_firmware; } } /* Enable VTS FW log Dump */ if (data->logdump_enabled) { values[0] = VTS_ENABLE_LOGDUMP; values[1] = (VTS_LOGDUMP_AFTER_MINS * 60); values[2] = 0; result = vts_start_ipc_transaction(dev, data, VTS_IRQ_AP_TEST_COMMAND, &values, 0, 2); if (result < 0) { dev_err(dev, "Enable_LogDump ipc failed\n"); goto error_firmware; } } /* Enable CM4 GPR Dump */ writel(0x1, data->gpr_base); dev_dbg(dev, "%s DRAM-setting and VTS-Mode is completed \n", __func__); dev_info(dev, "%s Exit \n", __func__); data->running = true; data->vts_state = VTS_STATE_IDLE; return 0; error_firmware: vts_cpu_power(false); clk_disable(data->clk_dmic); error_clk: if (data->irq_state) { vts_irq_enable(pdev, false); data->irq_state = false; } data->running = false; return 0; } static const struct dev_pm_ops samsung_vts_pm = { SET_SYSTEM_SLEEP_PM_OPS(vts_suspend, vts_resume) SET_RUNTIME_PM_OPS(vts_runtime_suspend, vts_runtime_resume, NULL) }; static const struct of_device_id exynos_vts_of_match[] = { { .compatible = "samsung,vts", }, {}, }; MODULE_DEVICE_TABLE(of, exynos_vts_of_match); static ssize_t vts_google_model_read(struct file *file, struct kobject *kobj, struct bin_attribute *bin_attr, char *buffer, loff_t ppos, size_t count) { dev_info(kobj_to_dev(kobj), "%s\n", __func__); return 0; } static ssize_t vts_google_model_write(struct file *file, struct kobject *kobj, struct bin_attribute *bin_attr, char *buffer, loff_t ppos, size_t count) { struct device *dev = kobj_to_dev(kobj); struct vts_data *data = dev_get_drvdata(dev); char *to; ssize_t available; dev_info(dev, "%s\n", __func__); if (!data->google_info.data) { dev_info(dev, "%s Google binary Buffer not allocated\n", __func__); return -EINVAL; } to = data->google_info.data; available = data->google_info.max_sz; dev_dbg(dev, "%s available %zu Cur-pos %lld size-to-copy %zu\n", __func__, available, ppos, count); if (ppos < 0) { dev_info(dev, "%s Error wrong current position\n", __func__); return -EINVAL; } if (ppos >= available || !count) { dev_info(dev, "%s Error copysize[%lld] greater than available buffer\n", __func__, ppos); return 0; } if (count > available - ppos) count = available - ppos; memcpy(to + ppos, buffer, count); to = (to + ppos); data->google_info.actual_sz = ppos + count; dev_info(dev, "%s updated Size %zu\n", __func__, data->google_info.actual_sz); data->google_info.loaded = true; return count; } static ssize_t vts_svoice_model_read(struct file *file, struct kobject *kobj, struct bin_attribute *bin_attr, char *buffer, loff_t ppos, size_t count) { dev_info(kobj_to_dev(kobj), "%s\n", __func__); return 0; } static ssize_t vts_svoice_model_write(struct file *file, struct kobject *kobj, struct bin_attribute *bin_attr, char *buffer, loff_t ppos, size_t count) { struct device *dev = kobj_to_dev(kobj); struct vts_data *data = dev_get_drvdata(dev); char *to; ssize_t available; dev_info(dev, "%s\n", __func__); if (!data->svoice_info.data) { dev_info(dev, "%s Grammar binary Buffer not allocated\n", __func__); return -EINVAL; } to = data->svoice_info.data; available = data->svoice_info.max_sz; dev_dbg(dev, "%s available %zu Cur-pos %lld size-to-copy %zu\n", __func__, available, ppos, count); if (ppos < 0) { dev_info(dev, "%s Error wrong current position\n", __func__); return -EINVAL; } if (ppos >= available || !count) { dev_info(dev, "%s Error copysize[%lld] greater than available buffer\n", __func__, ppos); return 0; } if (count > available - ppos) count = available - ppos; memcpy(to + ppos, buffer, count); to = (to + ppos); data->svoice_info.actual_sz = ppos + count; dev_info(dev, "%s updated Size %zu\n", __func__, data->svoice_info.actual_sz); data->svoice_info.loaded = true; return count; } static const BIN_ATTR_RW(vts_google_model, VTS_MODEL_GOOGLE_BIN_MAXSZ); static const BIN_ATTR_RW(vts_svoice_model, VTS_MODEL_SVOICE_BIN_MAXSZ); static ssize_t vtsfw_version_show(struct device *dev, struct device_attribute *attr, char *buf) { struct vts_data *data = dev_get_drvdata(dev); unsigned int version = data->vtsfw_version; buf[0] = (((version >> 24) & 0xFF) + '0'); buf[1] = '.'; buf[2] = (((version >> 16) & 0xFF) + '0'); buf[3] = '.'; buf[4] = (((version >> 8) & 0xFF) + '0'); buf[5] = '.'; buf[6] = ((version & 0xFF) + '0'); buf[7] = '\n'; buf[8] = '\0'; return 9; } static ssize_t vtsdetectlib_version_show(struct device *dev, struct device_attribute *attr, char *buf) { struct vts_data *data = dev_get_drvdata(dev); unsigned int version = data->vtsdetectlib_version; buf[0] = (((version >> 24) & 0xFF) + '0'); buf[1] = '.'; buf[2] = (((version >> 16) & 0xFF) + '0'); buf[3] = '.'; buf[4] = ((version & 0xFF) + '0'); buf[5] = '\n'; buf[6] = '\0'; return 7; } static ssize_t vts_audiodump_show(struct device *dev, struct device_attribute *attr, char *buf) { struct vts_data *data = dev_get_drvdata(dev); return sprintf(buf, "%d\n", data->audiodump_enabled); } static ssize_t vts_audiodump_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct vts_data *data = dev_get_drvdata(dev); u32 val = 0; u32 values[3] = {0, 0, 0}; int result; int err = kstrtouint(buf, 0, &val); if (err < 0) return err; data->audiodump_enabled = (val ? true : false); if (data->vts_ready) { if (data->audiodump_enabled) { values[0] = VTS_ENABLE_AUDIODUMP; values[1] = (VTS_ADUIODUMP_AFTER_MINS * 60); } else { values[0] = VTS_DISABLE_AUDIODUMP; values[1] = 0; } values[2] = 0; result = vts_start_ipc_transaction(dev, data, VTS_IRQ_AP_TEST_COMMAND, &values, 0, 2); if (result < 0) { dev_err(dev, "AudioDump[%d] ipc failed\n", data->audiodump_enabled); } } dev_info(dev, "%s: Audio dump %sabled\n", __func__, (val ? "en" : "dis")); return count; } static ssize_t vts_logdump_show(struct device *dev, struct device_attribute *attr, char *buf) { struct vts_data *data = dev_get_drvdata(dev); return sprintf(buf, "%d\n", data->logdump_enabled); } static ssize_t vts_logdump_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct vts_data *data = dev_get_drvdata(dev); u32 val = 0; u32 values[3] = {0, 0, 0}; int result; int err = kstrtouint(buf, 0, &val); if (err < 0) return err; data->logdump_enabled = (val ? true : false); if (data->vts_ready) { if (data->logdump_enabled) { values[0] = VTS_ENABLE_LOGDUMP; values[1] = (VTS_LOGDUMP_AFTER_MINS * 60); } else { values[0] = VTS_DISABLE_LOGDUMP; values[1] = 0; } values[2] = 0; result = vts_start_ipc_transaction(dev, data, VTS_IRQ_AP_TEST_COMMAND, &values, 0, 2); if (result < 0) { dev_err(dev, "LogDump[%d] ipc failed\n", data->logdump_enabled); } } dev_info(dev, "%s: Log dump %sabled\n", __func__, (val ? "en" : "dis")); return count; } static DEVICE_ATTR_RO(vtsfw_version); static DEVICE_ATTR_RO(vtsdetectlib_version); static DEVICE_ATTR_RW(vts_audiodump); static DEVICE_ATTR_RW(vts_logdump); static void vts_complete_firmware_request(const struct firmware *fw, void *context) { struct platform_device *pdev = context; struct device *dev = &pdev->dev; struct vts_data *data = platform_get_drvdata(pdev); unsigned int *pversion = NULL; if (!fw) { dev_err(dev, "Failed to request firmware\n"); return; } data->firmware = fw; pversion = (unsigned int*) (fw->data + VTSFW_VERSION_OFFSET); data->vtsfw_version = *pversion; pversion = (unsigned int*) (fw->data + DETLIB_VERSION_OFFSET); data->vtsdetectlib_version = *pversion; dev_info(dev, "Firmware loaded at %p (%zu)\n", fw->data, fw->size); dev_info(dev, "Firmware version: 0x%x Detection library version: 0x%x\n", data->vtsfw_version, data->vtsdetectlib_version); } static void __iomem *samsung_vts_devm_request_and_map(struct platform_device *pdev, const char *name, size_t *size) { struct resource *res; void __iomem *result; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); if (IS_ERR_OR_NULL(res)) { dev_err(&pdev->dev, "Failed to get %s\n", name); return ERR_PTR(-EINVAL); } if (size) { *size = resource_size(res); } res = devm_request_mem_region(&pdev->dev, res->start, resource_size(res), name); if (IS_ERR_OR_NULL(res)) { dev_err(&pdev->dev, "Failed to request %s\n", name); return ERR_PTR(-EFAULT); } result = devm_ioremap(&pdev->dev, res->start, resource_size(res)); if (IS_ERR_OR_NULL(result)) { dev_err(&pdev->dev, "Failed to map %s\n", name); return ERR_PTR(-EFAULT); } dev_info(&pdev->dev, "%s: %s(%p) is mapped on %p with size of %zu", __func__, name, (void *)res->start, result, (size_t)resource_size(res)); return result; } static int samsung_vts_devm_request_threaded_irq( struct platform_device *pdev, const char *irq_name, unsigned int hw_irq, irq_handler_t thread_fn) { struct device *dev = &pdev->dev; struct vts_data *data = platform_get_drvdata(pdev); int result; data->irq[hw_irq] = platform_get_irq_byname(pdev, irq_name); if (data->irq[hw_irq] < 0) { dev_err(dev, "Failed to get irq %s: %d\n", irq_name, data->irq[hw_irq]); return data->irq[hw_irq]; } result = devm_request_threaded_irq(dev, data->irq[hw_irq], NULL, thread_fn, IRQF_TRIGGER_RISING | IRQF_ONESHOT, dev->init_name, pdev); if (result < 0) { dev_err(dev, "Unable to request irq %s: %d\n", irq_name, result); } return result; } static struct clk *devm_clk_get_and_prepare(struct device *dev, const char *name) { struct clk *clk; int result; clk = devm_clk_get(dev, name); if (IS_ERR(clk)) { dev_err(dev, "Failed to get clock %s\n", name); goto error; } result = clk_prepare(clk); if (result < 0) { dev_err(dev, "Failed to prepare clock %s\n", name); goto error; } error: return clk; } static const struct reg_default vts_dmic_reg_defaults[] = { {0x0000, 0x00030000}, {0x0004, 0x00000000}, }; static const struct regmap_config vts_component_regmap_config = { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, .max_register = VTS_DMIC_CONTROL_DMIC_IF, .reg_defaults = vts_dmic_reg_defaults, .num_reg_defaults = ARRAY_SIZE(vts_dmic_reg_defaults), .cache_type = REGCACHE_RBTREE, .fast_io = true, }; static int samsung_vts_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct vts_data *data; int result; int dmic_clkctrl = 0; dev_info(dev, "%s \n", __func__); data = devm_kzalloc(dev, sizeof(struct vts_data), GFP_KERNEL); if (!data) { dev_err(dev, "Failed to allocate memory\n"); result = -ENOMEM; goto error; } /* Model binary memory allocation */ data->google_info.max_sz = VTS_MODEL_GOOGLE_BIN_MAXSZ; data->google_info.actual_sz = 0; data->google_info.loaded = false; data->google_info.data = vmalloc(VTS_MODEL_GOOGLE_BIN_MAXSZ); if (!data->google_info.data) { dev_err(dev, "%s Failed to allocate Grammar Bin memory\n", __func__); result = -ENOMEM; goto error; } data->svoice_info.max_sz = VTS_MODEL_SVOICE_BIN_MAXSZ; data->svoice_info.actual_sz = 0; data->svoice_info.loaded = false; data->svoice_info.data = vmalloc(VTS_MODEL_SVOICE_BIN_MAXSZ); if (!data->svoice_info.data) { dev_err(dev, "%s Failed to allocate Net Bin memory\n", __func__); result = -ENOMEM; goto error; } /* initialize device structure members */ data->active_trigger = TRIGGER_SVOICE; /* initialize micbias setting count */ data->micclk_init_cnt = 0; data->mic_ready = 0; data->vts_state = VTS_STATE_NONE; platform_set_drvdata(pdev, data); data->pdev = pdev; p_vts_data = data; init_waitqueue_head(&data->ipc_wait_queue); spin_lock_init(&data->ipc_spinlock); mutex_init(&data->ipc_mutex); wake_lock_init(&data->wake_lock, WAKE_LOCK_SUSPEND, "vts"); data->pinctrl = devm_pinctrl_get(dev); if (IS_ERR(data->pinctrl)) { dev_err(dev, "Couldn't get pins (%li)\n", PTR_ERR(data->pinctrl)); return PTR_ERR(data->pinctrl); } data->sfr_base = samsung_vts_devm_request_and_map(pdev, "sfr", NULL); if (IS_ERR(data->sfr_base)) { result = PTR_ERR(data->sfr_base); goto error; } data->baaw_base = samsung_vts_devm_request_and_map(pdev, "baaw", NULL); if (IS_ERR(data->baaw_base)) { result = PTR_ERR(data->baaw_base); goto error; } data->sram_base = samsung_vts_devm_request_and_map(pdev, "sram", &data->sram_size); if (IS_ERR(data->sram_base)) { result = PTR_ERR(data->sram_base); goto error; } data->dmic_base = samsung_vts_devm_request_and_map(pdev, "dmic", NULL); if (IS_ERR(data->dmic_base)) { result = PTR_ERR(data->dmic_base); goto error; } data->gpr_base = samsung_vts_devm_request_and_map(pdev, "gpr", NULL); if (IS_ERR(data->gpr_base)) { result = PTR_ERR(data->gpr_base); goto error; } data->lpsdgain = 0; data->dmicgain = 0; data->amicgain = 0; /* read tunned VTS gain values */ of_property_read_u32(np, "lpsd-gain", &data->lpsdgain); of_property_read_u32(np, "dmic-gain", &data->dmicgain); of_property_read_u32(np, "amic-gain", &data->amicgain); dev_info(dev, "VTS Tunned Gain value LPSD: %d DMIC: %d AMIC: %d\n", data->lpsdgain, data->dmicgain, data->amicgain); data->dmab.area = dmam_alloc_coherent(dev, BUFFER_BYTES_MAX, &data->dmab.addr, GFP_KERNEL); if (data->dmab.area == NULL) { result = -ENOMEM; goto error; } data->dmab.bytes = BUFFER_BYTES_MAX/2; data->dmab.dev.dev = dev; data->dmab.dev.type = SNDRV_DMA_TYPE_DEV; data->dmab_rec.area = (data->dmab.area + BUFFER_BYTES_MAX/2); data->dmab_rec.addr = (data->dmab.addr + BUFFER_BYTES_MAX/2); data->dmab_rec.bytes = BUFFER_BYTES_MAX/2; data->dmab_rec.dev.dev = dev; data->dmab_rec.dev.type = SNDRV_DMA_TYPE_DEV; data->dmab_log.area = dmam_alloc_coherent(dev, LOG_BUFFER_BYTES_MAX, &data->dmab_log.addr, GFP_KERNEL); if (data->dmab_log.area == NULL) { result = -ENOMEM; goto error; } data->dmab.bytes = LOG_BUFFER_BYTES_MAX; data->dmab.dev.dev = dev; data->dmab.dev.type = SNDRV_DMA_TYPE_DEV; #ifdef CONFIG_SOC_EXYNOS8895 data->clk_rco = devm_clk_get_and_prepare(dev, "rco"); if (IS_ERR(data->clk_rco)) { result = PTR_ERR(data->clk_rco); goto error; } result = clk_enable(data->clk_rco); if (result < 0) { dev_err(dev, "Failed to enable the rco\n"); goto error; } #endif data->clk_dmic = devm_clk_get_and_prepare(dev, "dmic"); if (IS_ERR(data->clk_dmic)) { result = PTR_ERR(data->clk_dmic); goto error; } data->clk_dmic_if= devm_clk_get_and_prepare(dev, "dmic_if"); if (IS_ERR(data->clk_dmic_if)) { result = PTR_ERR(data->clk_dmic_if); goto error; } data->clk_dmic_sync = devm_clk_get_and_prepare(dev, "dmic_sync"); if (IS_ERR(data->clk_dmic_sync)) { result = PTR_ERR(data->clk_dmic_sync); goto error; } result = samsung_vts_devm_request_threaded_irq(pdev, "error", VTS_IRQ_VTS_ERROR, vts_error_handler); if (result < 0) { goto error; } result = samsung_vts_devm_request_threaded_irq(pdev, "boot_completed", VTS_IRQ_VTS_BOOT_COMPLETED, vts_boot_completed_handler); if (result < 0) { goto error; } result = samsung_vts_devm_request_threaded_irq(pdev, "ipc_received", VTS_IRQ_VTS_IPC_RECEIVED, vts_ipc_received_handler); if (result < 0) { goto error; } result = samsung_vts_devm_request_threaded_irq(pdev, "voice_triggered", VTS_IRQ_VTS_VOICE_TRIGGERED, vts_voice_triggered_handler); if (result < 0) { goto error; } result = samsung_vts_devm_request_threaded_irq(pdev, "trigger_period_elapsed", VTS_IRQ_VTS_PERIOD_ELAPSED, vts_trigger_period_elapsed_handler); if (result < 0) { goto error; } result = samsung_vts_devm_request_threaded_irq(pdev, "record_period_elapsed", VTS_IRQ_VTS_REC_PERIOD_ELAPSED, vts_record_period_elapsed_handler); if (result < 0) { goto error; } result = samsung_vts_devm_request_threaded_irq(pdev, "debuglog_bufzero", VTS_IRQ_VTS_DBGLOG_BUFZERO, vts_debuglog_bufzero_handler); if (result < 0) goto error; result = samsung_vts_devm_request_threaded_irq(pdev, "debuglog_bufone", VTS_IRQ_VTS_DBGLOG_BUFONE, vts_debuglog_bufone_handler); if (result < 0) goto error; result = samsung_vts_devm_request_threaded_irq(pdev, "audio_dump", VTS_IRQ_VTS_AUDIO_DUMP, vts_audiodump_handler); if (result < 0) goto error; result = samsung_vts_devm_request_threaded_irq(pdev, "log_dump", VTS_IRQ_VTS_LOG_DUMP, vts_logdump_handler); if (result < 0) goto error; data->irq_state = true; data->pdev_mailbox = of_find_device_by_node(of_parse_phandle(np, "mailbox", 0)); if (!data->pdev_mailbox) { dev_err(dev, "Failed to get mailbox\n"); result = -EPROBE_DEFER; goto error; } result = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, "vts.bin", dev, GFP_KERNEL, pdev, vts_complete_firmware_request); if (result < 0) { dev_err(dev, "Failed to request firmware\n"); goto error; } result = device_create_bin_file(dev, &bin_attr_vts_google_model); if (result < 0) { dev_err(dev, "Failed to create attribute %s\n", "vts_google_model"); goto error; } result = device_create_bin_file(dev, &bin_attr_vts_svoice_model); if (result < 0) { dev_err(dev, "Failed to create attribute %s\n", "vts_svoice_model"); goto error; } data->regmap_dmic = devm_regmap_init_mmio_clk(dev, NULL, data->dmic_base, &vts_component_regmap_config); result = snd_soc_register_component(dev, &vts_component, vts_dai, ARRAY_SIZE(vts_dai)); if (result < 0) { dev_err(dev, "Failed to register ASoC component\n"); goto error; } #ifdef EMULATOR pmu_alive = ioremap(0x16480000, 0x10000); #endif printk("come hear %d\n", __LINE__); pm_runtime_enable(dev); pm_runtime_get_sync(dev); vts_cfg_gpio(dev, "idle"); data->voicecall_enabled = false; data->voicerecog_start = 0; data->syssel_rate = 0; data->target_size = 0; data->vtsfw_version = 0x0; data->vtsdetectlib_version = 0x0; data->vtslog_enabled = 0; data->audiodump_enabled = false; data->logdump_enabled = false; /* set VTS BAAW config */ writel(0x40300, data->baaw_base); writel(0x40600, data->baaw_base + 0x4); writel(0x14100, data->baaw_base + 0x8); writel(0x80000003, data->baaw_base + 0xC); dmic_clkctrl = readl(data->sfr_base + VTS_DMIC_CLK_CTRL); writel(dmic_clkctrl & ~(0x1 << VTS_CLK_ENABLE_OFFSET), data->sfr_base + VTS_DMIC_CLK_CTRL); dev_dbg(dev, "DMIC_CLK_CTRL: Before 0x%x After 0x%x \n", dmic_clkctrl, readl(data->sfr_base + VTS_DMIC_CLK_CTRL)); result = device_create_file(dev, &dev_attr_vtsfw_version); if (result < 0) dev_warn(dev, "Failed to create file: %s\n", "vtsfw_version"); result = device_create_file(dev, &dev_attr_vtsdetectlib_version); if (result < 0) dev_warn(dev, "Failed to create file: %s\n", "vtsdetectlib_version"); result = device_create_file(dev, &dev_attr_vts_audiodump); if (result < 0) dev_warn(dev, "Failed to create file: %s\n", "vts_audiodump"); result = device_create_file(dev, &dev_attr_vts_logdump); if (result < 0) dev_warn(dev, "Failed to create file: %s\n", "vts_logdump"); data->sramlog_baseaddr = (char *)(data->sram_base + VTS_SRAMLOG_MSGS_OFFSET); atomic_notifier_chain_register(&panic_notifier_list, &vts_panic_notifier); /* initialize log buffer offset as non */ vts_register_log_buffer(dev, 0, 0); device_init_wakeup(dev, true); dev_info(dev, "Probed successfully\n"); error: return result; } static int samsung_vts_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct vts_data *data = platform_get_drvdata(pdev); pm_runtime_disable(dev); clk_unprepare(data->clk_dmic); #ifndef CONFIG_PM vts_runtime_suspend(dev); #endif release_firmware(data->firmware); if (data->google_info.data) vfree(data->google_info.data); if (data->svoice_info.data) vfree(data->svoice_info.data); snd_soc_unregister_component(dev); #ifdef EMULATOR iounmap(pmu_alive); #endif return 0; } static struct platform_driver samsung_vts_driver = { .probe = samsung_vts_probe, .remove = samsung_vts_remove, .driver = { .name = "samsung-vts", .owner = THIS_MODULE, .of_match_table = of_match_ptr(exynos_vts_of_match), .pm = &samsung_vts_pm, }, }; module_platform_driver(samsung_vts_driver); static int __init samsung_vts_late_initcall(void) { pr_info("%s\n", __func__); if (p_vts_data && p_vts_data->pdev) { pm_runtime_put_sync(&p_vts_data->pdev->dev); } else { pr_err("%s: p_vts_data or p_vts_data->pdev is null", __func__); } return 0; } late_initcall(samsung_vts_late_initcall); /* Module information */ MODULE_AUTHOR("Gyeongtaek Lee, "); MODULE_AUTHOR("Palli Satish Kumar Reddy, "); MODULE_DESCRIPTION("Samsung Voice Trigger System"); MODULE_ALIAS("platform:samsung-vts"); MODULE_LICENSE("GPL");