diff options
Diffstat (limited to 'drivers/soc/qcom')
| -rw-r--r-- | drivers/soc/qcom/icnss_utils.c | 36 | ||||
| -rw-r--r-- | drivers/soc/qcom/memshare/msm_memshare.c | 20 | ||||
| -rw-r--r-- | drivers/soc/qcom/memshare/msm_memshare.h | 3 | ||||
| -rw-r--r-- | drivers/soc/qcom/msm_bus/msm_bus_rules.c | 9 | ||||
| -rw-r--r-- | drivers/soc/qcom/qbt1000.c | 21 | ||||
| -rw-r--r-- | drivers/soc/qcom/qpnp-haptic.c | 956 | ||||
| -rw-r--r-- | drivers/soc/qcom/rpm_master_stat.c | 10 | ||||
| -rw-r--r-- | drivers/soc/qcom/service-locator.c | 9 | ||||
| -rw-r--r-- | drivers/soc/qcom/service-notifier.c | 21 | ||||
| -rw-r--r-- | drivers/soc/qcom/smcinvoke.c | 11 | ||||
| -rw-r--r-- | drivers/soc/qcom/spcom.c | 149 | ||||
| -rw-r--r-- | drivers/soc/qcom/wcd-dsp-glink.c | 81 |
12 files changed, 970 insertions, 356 deletions
diff --git a/drivers/soc/qcom/icnss_utils.c b/drivers/soc/qcom/icnss_utils.c index 5e187d5df8b3..a7a0ffa2c18e 100644 --- a/drivers/soc/qcom/icnss_utils.c +++ b/drivers/soc/qcom/icnss_utils.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -16,7 +16,7 @@ #define ICNSS_MAX_CH_NUM 45 static DEFINE_MUTEX(unsafe_channel_list_lock); -static DEFINE_MUTEX(dfs_nol_info_lock); +static DEFINE_SPINLOCK(dfs_nol_info_lock); static struct icnss_unsafe_channel_list { u16 unsafe_ch_count; @@ -77,27 +77,24 @@ EXPORT_SYMBOL(icnss_get_wlan_unsafe_channel); int icnss_wlan_set_dfs_nol(const void *info, u16 info_len) { void *temp; + void *old_nol_info; struct icnss_dfs_nol_info *dfs_info; - mutex_lock(&dfs_nol_info_lock); - if (!info || !info_len) { - mutex_unlock(&dfs_nol_info_lock); + if (!info || !info_len) return -EINVAL; - } - temp = kmalloc(info_len, GFP_KERNEL); - if (!temp) { - mutex_unlock(&dfs_nol_info_lock); + temp = kmalloc(info_len, GFP_ATOMIC); + if (!temp) return -ENOMEM; - } memcpy(temp, info, info_len); + spin_lock_bh(&dfs_nol_info_lock); dfs_info = &dfs_nol_info; - kfree(dfs_info->dfs_nol_info); - + old_nol_info = dfs_info->dfs_nol_info; dfs_info->dfs_nol_info = temp; dfs_info->dfs_nol_info_len = info_len; - mutex_unlock(&dfs_nol_info_lock); + spin_unlock_bh(&dfs_nol_info_lock); + kfree(old_nol_info); return 0; } @@ -108,24 +105,21 @@ int icnss_wlan_get_dfs_nol(void *info, u16 info_len) int len; struct icnss_dfs_nol_info *dfs_info; - mutex_lock(&dfs_nol_info_lock); - if (!info || !info_len) { - mutex_unlock(&dfs_nol_info_lock); + if (!info || !info_len) return -EINVAL; - } - dfs_info = &dfs_nol_info; + spin_lock_bh(&dfs_nol_info_lock); + dfs_info = &dfs_nol_info; if (dfs_info->dfs_nol_info == NULL || dfs_info->dfs_nol_info_len == 0) { - mutex_unlock(&dfs_nol_info_lock); + spin_unlock_bh(&dfs_nol_info_lock); return -ENOENT; } len = min(info_len, dfs_info->dfs_nol_info_len); - memcpy(info, dfs_info->dfs_nol_info, len); - mutex_unlock(&dfs_nol_info_lock); + spin_unlock_bh(&dfs_nol_info_lock); return len; } diff --git a/drivers/soc/qcom/memshare/msm_memshare.c b/drivers/soc/qcom/memshare/msm_memshare.c index b8417513ca55..7406dba44320 100644 --- a/drivers/soc/qcom/memshare/msm_memshare.c +++ b/drivers/soc/qcom/memshare/msm_memshare.c @@ -498,6 +498,7 @@ static int handle_alloc_generic_req(void *req_h, void *req, void *conn_h) struct mem_alloc_generic_resp_msg_v01 *alloc_resp; int rc, resp = 0; int client_id; + uint32_t size = 0; alloc_req = (struct mem_alloc_generic_req_msg_v01 *)req; pr_debug("memshare: alloc request client id: %d proc _id: %d\n", @@ -523,12 +524,12 @@ static int handle_alloc_generic_req(void *req_h, void *req, void *conn_h) return -EINVAL; } - memblock[client_id].free_memory += 1; - pr_debug("memshare: In %s, free memory count for client id: %d = %d", - __func__, memblock[client_id].client_id, - memblock[client_id].free_memory); if (!memblock[client_id].alloted) { - rc = memshare_alloc(memsh_drv->dev, alloc_req->num_bytes, + if (alloc_req->client_id == 1 && alloc_req->num_bytes > 0) + size = alloc_req->num_bytes + MEMSHARE_GUARD_BYTES; + else + size = alloc_req->num_bytes; + rc = memshare_alloc(memsh_drv->dev, size, &memblock[client_id]); if (rc) { pr_err("In %s,Unable to allocate memory for requested client\n", @@ -536,11 +537,16 @@ static int handle_alloc_generic_req(void *req_h, void *req, void *conn_h) resp = 1; } if (!resp) { + memblock[client_id].free_memory += 1; memblock[client_id].alloted = 1; memblock[client_id].size = alloc_req->num_bytes; memblock[client_id].peripheral = alloc_req->proc_id; } } + pr_debug("memshare: In %s, free memory count for client id: %d = %d", + __func__, memblock[client_id].client_id, + memblock[client_id].free_memory); + memblock[client_id].sequence_id = alloc_req->sequence_id; fill_alloc_response(alloc_resp, client_id, &resp); @@ -963,8 +969,10 @@ static int memshare_child_probe(struct platform_device *pdev) * Memshare allocation for guaranteed clients */ if (memblock[num_clients].guarantee) { + if (client_id == 1 && size > 0) + size += MEMSHARE_GUARD_BYTES; rc = memshare_alloc(memsh_child->dev, - memblock[num_clients].size, + size, &memblock[num_clients]); if (rc) { pr_err("In %s, Unable to allocate memory for guaranteed clients, rc: %d\n", diff --git a/drivers/soc/qcom/memshare/msm_memshare.h b/drivers/soc/qcom/memshare/msm_memshare.h index 398907532977..c7123fb1314b 100644 --- a/drivers/soc/qcom/memshare/msm_memshare.h +++ b/drivers/soc/qcom/memshare/msm_memshare.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -24,6 +24,7 @@ #define GPS 0 #define CHECK 0 #define FREE 1 +#define MEMSHARE_GUARD_BYTES (4*1024) struct mem_blocks { /* Client Id information */ diff --git a/drivers/soc/qcom/msm_bus/msm_bus_rules.c b/drivers/soc/qcom/msm_bus/msm_bus_rules.c index 297ba9fc3c35..ea29e303bbde 100644 --- a/drivers/soc/qcom/msm_bus/msm_bus_rules.c +++ b/drivers/soc/qcom/msm_bus/msm_bus_rules.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -410,8 +410,10 @@ void print_all_rules(void) { struct rule_node_info *node_it = NULL; + mutex_lock(&msm_bus_rules_lock); list_for_each_entry(node_it, &node_list, link) print_rules(node_it); + mutex_unlock(&msm_bus_rules_lock); } void print_rules_buf(char *buf, int max_buf) @@ -421,6 +423,7 @@ void print_rules_buf(char *buf, int max_buf) int i; int cnt = 0; + mutex_lock(&msm_bus_rules_lock); list_for_each_entry(node_it, &node_list, link) { cnt += scnprintf(buf + cnt, max_buf - cnt, "\n Now printing rules for Node %d cur_rule %d\n", @@ -452,6 +455,7 @@ void print_rules_buf(char *buf, int max_buf) node_rule->rule_ops.mode); } } + mutex_unlock(&msm_bus_rules_lock); } static int copy_rule(struct bus_rule_type *src, struct rules_def *node_rule, @@ -721,11 +725,12 @@ bool msm_rule_are_rules_registered(void) { bool ret = false; + mutex_lock(&msm_bus_rules_lock); if (list_empty(&node_list)) ret = false; else ret = true; - + mutex_unlock(&msm_bus_rules_lock); return ret; } diff --git a/drivers/soc/qcom/qbt1000.c b/drivers/soc/qcom/qbt1000.c index 4ba92436bd06..6e7d34ac9163 100644 --- a/drivers/soc/qcom/qbt1000.c +++ b/drivers/soc/qcom/qbt1000.c @@ -377,6 +377,12 @@ static long qbt1000_ioctl(struct file *file, unsigned cmd, unsigned long arg) drvdata = file->private_data; + if (IS_ERR(priv_arg)) { + dev_err(drvdata->dev, "%s: invalid user space pointer %lu\n", + __func__, arg); + return -EINVAL; + } + mutex_lock(&drvdata->mutex); pr_debug("qbt1000_ioctl %d\n", cmd); @@ -401,6 +407,13 @@ static long qbt1000_ioctl(struct file *file, unsigned cmd, unsigned long arg) goto end; } + if (strcmp(app.name, FP_APP_NAME)) { + dev_err(drvdata->dev, "%s: Invalid app name\n", + __func__); + rc = -EINVAL; + goto end; + } + if (drvdata->app_handle) { dev_err(drvdata->dev, "%s: LOAD app already loaded, unloading first\n", __func__); @@ -414,6 +427,7 @@ static long qbt1000_ioctl(struct file *file, unsigned cmd, unsigned long arg) } pr_debug("app %s load before\n", app.name); + app.name[MAX_NAME_SIZE - 1] = '\0'; /* start the TZ app */ rc = qseecom_start_app( @@ -427,7 +441,8 @@ static long qbt1000_ioctl(struct file *file, unsigned cmd, unsigned long arg) pr_err("App %s failed to set bw\n", app.name); } } else { - pr_err("app %s failed to load\n", app.name); + dev_err(drvdata->dev, "%s: Fingerprint Trusted App failed to load\n", + __func__); goto end; } @@ -447,9 +462,7 @@ static long qbt1000_ioctl(struct file *file, unsigned cmd, unsigned long arg) pr_debug("app %s load after\n", app.name); - if (!strcmp(app.name, FP_APP_NAME)) - drvdata->fp_app_handle = drvdata->app_handle; - + drvdata->fp_app_handle = drvdata->app_handle; break; } case QBT1000_UNLOAD_APP: diff --git a/drivers/soc/qcom/qpnp-haptic.c b/drivers/soc/qcom/qpnp-haptic.c index 70cf11359e97..d86f8671705a 100644 --- a/drivers/soc/qcom/qpnp-haptic.c +++ b/drivers/soc/qcom/qpnp-haptic.c @@ -85,6 +85,7 @@ #define QPNP_HAP_PM660_RES_CAL_PERIOD_MAX 256 #define QPNP_HAP_WF_SOURCE_MASK GENMASK(5, 4) #define QPNP_HAP_WF_SOURCE_SHIFT 4 +#define QPNP_HAP_VMAX_OVD_BIT BIT(6) #define QPNP_HAP_VMAX_MASK GENMASK(5, 1) #define QPNP_HAP_VMAX_SHIFT 1 #define QPNP_HAP_VMAX_MIN_MV 116 @@ -117,6 +118,8 @@ #define QPNP_HAP_WAV_REP_MAX 128 #define QPNP_HAP_WAV_S_REP_MIN 1 #define QPNP_HAP_WAV_S_REP_MAX 8 +#define QPNP_HAP_WF_AMP_MASK GENMASK(5, 1) +#define QPNP_HAP_WF_OVD_BIT BIT(6) #define QPNP_HAP_BRAKE_PAT_MASK 0x3 #define QPNP_HAP_ILIM_MIN_MA 400 #define QPNP_HAP_ILIM_MAX_MA 800 @@ -135,21 +138,20 @@ #define QPNP_HAP_WAV_SINE 0 #define QPNP_HAP_WAV_SQUARE 1 #define QPNP_HAP_WAV_SAMP_LEN 8 -#define QPNP_HAP_WAV_SAMP_MAX 0x7E +#define QPNP_HAP_WAV_SAMP_MAX 0x3E #define QPNP_HAP_BRAKE_PAT_LEN 4 -#define QPNP_HAP_PLAY_EN 0x80 +#define QPNP_HAP_PLAY_EN_BIT BIT(7) #define QPNP_HAP_EN_BIT BIT(7) #define QPNP_HAP_BRAKE_MASK BIT(0) #define QPNP_HAP_AUTO_RES_MASK BIT(7) #define AUTO_RES_ENABLE BIT(7) #define AUTO_RES_ERR_BIT 0x10 #define SC_FOUND_BIT 0x08 -#define SC_MAX_DURATION 5 +#define SC_MAX_COUNT 5 #define QPNP_HAP_TIMEOUT_MS_MAX 15000 #define QPNP_HAP_STR_SIZE 20 #define QPNP_HAP_MAX_RETRIES 5 -#define QPNP_HAP_CYCLS 5 #define QPNP_TEST_TIMER_MS 5 #define QPNP_HAP_TIME_REQ_FOR_BACK_EMF_GEN 20000 @@ -262,6 +264,22 @@ struct qpnp_pwm_info { }; /* + * qpnp_hap_lra_ares_cfg - Haptic auto_resonance configuration + * @ lra_qwd_drive_duration - LRA QWD drive duration + * @ calibrate_at_eop - Calibrate at EOP + * @ lra_res_cal_period - LRA resonance calibration period + * @ auto_res_mode - auto resonace mode + * @ lra_high_z - high z option line + */ +struct qpnp_hap_lra_ares_cfg { + int lra_qwd_drive_duration; + int calibrate_at_eop; + enum qpnp_hap_high_z lra_high_z; + u16 lra_res_cal_period; + u8 auto_res_mode; +}; + +/* * qpnp_hap - Haptic data structure * @ spmi - spmi device * @ hap_timer - hrtimer @@ -270,6 +288,7 @@ struct qpnp_pwm_info { * @ work - worker * @ sc_work - worker to handle short circuit condition * @ pwm_info - pwm info + * @ ares_cfg - auto resonance configuration * @ lock - mutex lock * @ wf_lock - mutex lock for waveform * @ init_drive_period_code - the initial lra drive period code @@ -281,10 +300,7 @@ struct qpnp_pwm_info { percentage variation on the higher side. * @ drive_period_code_min_limit - calculated drive period code with percentage variation on the lower side - * @ lra_res_cal_period - LRA resonance calibration period * @ play_mode - play mode - * @ auto_res_mode - auto resonace mode - * @ lra_high_z - high z option line * @ timeout_ms - max timeout in ms * @ time_required_to_generate_back_emf_us - the time required for sufficient back-emf to be generated for auto resonance to be successful @@ -293,6 +309,7 @@ struct qpnp_pwm_info { * @ sc_deb_cycles - short circuit debounce cycles * @ int_pwm_freq_khz - internal pwm frequency in khz * @ wave_play_rate_us - play rate for waveform + * @ play_time_ms - play time set by the user * @ ext_pwm_freq_khz - external pwm frequency in khz * @ wave_rep_cnt - waveform repeat count * @ wave_s_rep_cnt - waveform sample repeat count @@ -305,14 +322,12 @@ struct qpnp_pwm_info { * @ wave_samp - array of wave samples * @ shadow_wave_samp - shadow array of wave samples * @ brake_pat - pattern for active breaking - * @ reg_play - play register - * @ lra_res_cal_period - period for resonance calibration - * @ sc_duration - counter to determine the duration of short circuit condition + * @ sc_count - counter to determine the duration of short circuit condition * @ lra_hw_auto_resonance - enable hardware auto resonance * @ state - current state of haptics + * @ module_en - haptics module enable status * @ wf_update - waveform update flag * @ pwm_cfg_state - pwm mode configuration state - * @ buffer_cfg_state - buffer mode configuration state * @ en_brake - brake state * @ sup_brake_pat - support custom brake pattern * @ correct_lra_drive_freq - correct LRA Drive Frequency @@ -320,6 +335,9 @@ struct qpnp_pwm_info { * @ clk_trim_error_code - MISC clock trim error code * @ perform_lra_auto_resonance_search - whether lra auto resonance search * algorithm should be performed or not. + * @ auto_mode - Auto mode selection + * @ override_auto_mode_config - Flag to override auto mode configuration with + * user specified values through sysfs. */ struct qpnp_hap { struct platform_device *pdev; @@ -333,14 +351,12 @@ struct qpnp_hap { struct hrtimer hap_test_timer; struct work_struct test_work; struct qpnp_pwm_info pwm_info; + struct qpnp_hap_lra_ares_cfg ares_cfg; struct mutex lock; struct mutex wf_lock; spinlock_t bus_lock; struct completion completion; enum qpnp_hap_mode play_mode; - enum qpnp_hap_high_z lra_high_z; - int lra_qwd_drive_duration; - int calibrate_at_eop; u32 misc_clk_trim_error_reg; u32 init_drive_period_code; u32 timeout_ms; @@ -350,6 +366,7 @@ struct qpnp_hap { u32 sc_deb_cycles; u32 int_pwm_freq_khz; u32 wave_play_rate_us; + u32 play_time_ms; u32 ext_pwm_freq_khz; u32 wave_rep_cnt; u32 wave_s_rep_cnt; @@ -360,7 +377,6 @@ struct qpnp_hap { u16 last_rate_cfg; u16 drive_period_code_max_limit; u16 drive_period_code_min_limit; - u16 lra_res_cal_period; u8 drive_period_code_max_limit_percent_variation; u8 drive_period_code_min_limit_percent_variation; u8 act_type; @@ -368,23 +384,24 @@ struct qpnp_hap { u8 wave_samp[QPNP_HAP_WAV_SAMP_LEN]; u8 shadow_wave_samp[QPNP_HAP_WAV_SAMP_LEN]; u8 brake_pat[QPNP_HAP_BRAKE_PAT_LEN]; - u8 reg_play; - u8 sc_duration; + u8 sc_count; u8 ext_pwm_dtest_line; u8 pmic_subtype; - u8 auto_res_mode; u8 clk_trim_error_code; bool lra_hw_auto_resonance; bool vcc_pon_enabled; bool state; + bool module_en; bool manage_pon_supply; bool wf_update; bool pwm_cfg_state; - bool buffer_cfg_state; bool en_brake; bool sup_brake_pat; bool correct_lra_drive_freq; bool perform_lra_auto_resonance_search; + bool auto_mode; + bool override_auto_mode_config; + bool play_irq_en; }; static struct qpnp_hap *ghap; @@ -502,36 +519,50 @@ static void qpnp_handle_sc_irq(struct work_struct *work) /* clear short circuit register */ if (val & SC_FOUND_BIT) { - hap->sc_duration++; + hap->sc_count++; val = QPNP_HAP_SC_CLR; qpnp_hap_write_reg(hap, QPNP_HAP_SC_CLR_REG(hap->base), val); } } +#define QPNP_HAP_CYCLES 4 static int qpnp_hap_mod_enable(struct qpnp_hap *hap, bool on) { + unsigned long wait_time_us; u8 val; int rc, i; + if (hap->module_en == on) + return 0; + if (!on) { - for (i = 0; i < QPNP_HAP_MAX_RETRIES; i++) { - /* wait for 4 cycles of play rate */ - unsigned long sleep_time = - QPNP_HAP_CYCLS * hap->wave_play_rate_us; + /* + * Wait for 4 cycles of play rate for play time is > 20 ms and + * wait for play time when play time is < 20 ms. This way, there + * will be an improvement in waiting time polling BUSY status. + */ + if (hap->play_time_ms <= 20) + wait_time_us = hap->play_time_ms * 1000; + else + wait_time_us = QPNP_HAP_CYCLES * hap->wave_play_rate_us; + for (i = 0; i < QPNP_HAP_MAX_RETRIES; i++) { rc = qpnp_hap_read_reg(hap, QPNP_HAP_STATUS(hap->base), &val); + if (rc < 0) + return rc; pr_debug("HAP_STATUS=0x%x\n", val); - /* wait for QPNP_HAP_CYCLS cycles of play rate */ + /* wait for play_rate cycles */ if (val & QPNP_HAP_STATUS_BUSY) { - usleep_range(sleep_time, sleep_time + 1); + usleep_range(wait_time_us, wait_time_us + 1); if (hap->play_mode == QPNP_HAP_DIRECT || hap->play_mode == QPNP_HAP_PWM) break; - } else + } else { break; + } } if (i >= QPNP_HAP_MAX_RETRIES) @@ -543,27 +574,18 @@ static int qpnp_hap_mod_enable(struct qpnp_hap *hap, bool on) if (rc < 0) return rc; + hap->module_en = on; return 0; } -static int qpnp_hap_play(struct qpnp_hap *hap, int on) +static int qpnp_hap_play(struct qpnp_hap *hap, bool on) { u8 val; int rc; - val = hap->reg_play; - if (on) - val |= QPNP_HAP_PLAY_EN; - else - val &= ~QPNP_HAP_PLAY_EN; - + val = on ? QPNP_HAP_PLAY_EN_BIT : 0; rc = qpnp_hap_write_reg(hap, QPNP_HAP_PLAY_REG(hap->base), val); - if (rc < 0) - return rc; - - hap->reg_play = val; - - return 0; + return rc; } /* sysfs show debug registers */ @@ -624,13 +646,13 @@ static irqreturn_t qpnp_hap_sc_irq(int irq, void *_hap) pr_debug("Short circuit detected\n"); - if (hap->sc_duration < SC_MAX_DURATION) { + if (hap->sc_count < SC_MAX_COUNT) { qpnp_hap_read_reg(hap, QPNP_HAP_STATUS(hap->base), &val); if (val & SC_FOUND_BIT) schedule_delayed_work(&hap->sc_work, QPNP_HAP_SC_IRQ_STATUS_DELAY); else - hap->sc_duration = 0; + hap->sc_count = 0; } else { /* Disable haptics module if the duration of short circuit * exceeds the maximum limit (5 secs). @@ -645,9 +667,11 @@ static irqreturn_t qpnp_hap_sc_irq(int irq, void *_hap) } /* configuration api for buffer mode */ -static int qpnp_hap_buffer_config(struct qpnp_hap *hap) +static int qpnp_hap_buffer_config(struct qpnp_hap *hap, u8 *wave_samp, + bool overdrive) { - u8 val = 0; + u8 buf[QPNP_HAP_WAV_SAMP_LEN], val; + u8 *ptr; int rc, i; /* Configure the WAVE_REPEAT register */ @@ -668,16 +692,27 @@ static int qpnp_hap_buffer_config(struct qpnp_hap *hap) if (rc) return rc; + /* Don't set override bit in waveform sample for PM660 */ + if (hap->pmic_subtype == PM660_SUBTYPE) + overdrive = false; + + if (wave_samp) + ptr = wave_samp; + else + ptr = hap->wave_samp; + /* Configure WAVE_SAMPLE1 to WAVE_SAMPLE8 register */ - for (i = 0, val = 0; i < QPNP_HAP_WAV_SAMP_LEN; i++) { - val = hap->wave_samp[i]; - rc = qpnp_hap_write_reg(hap, - QPNP_HAP_WAV_S_REG_BASE(hap->base) + i, val); - if (rc) - return rc; + for (i = 0; i < QPNP_HAP_WAV_SAMP_LEN; i++) { + buf[i] = ptr[i] & QPNP_HAP_WF_AMP_MASK; + if (buf[i]) + buf[i] |= (overdrive ? QPNP_HAP_WF_OVD_BIT : 0); } - hap->buffer_cfg_state = true; + rc = qpnp_hap_write_mult_reg(hap, QPNP_HAP_WAV_S_REG_BASE(hap->base), + buf, QPNP_HAP_WAV_SAMP_LEN); + if (rc) + return rc; + return 0; } @@ -736,10 +771,12 @@ static int qpnp_hap_pwm_config(struct qpnp_hap *hap) return 0; } -static int qpnp_hap_lra_auto_res_config(struct qpnp_hap *hap) +static int qpnp_hap_lra_auto_res_config(struct qpnp_hap *hap, + struct qpnp_hap_lra_ares_cfg *tmp_cfg) { + struct qpnp_hap_lra_ares_cfg *ares_cfg; int rc; - u8 val, mask; + u8 val = 0, mask = 0; /* disable auto resonance for ERM */ if (hap->act_type == QPNP_HAP_ERM) { @@ -751,59 +788,73 @@ static int qpnp_hap_lra_auto_res_config(struct qpnp_hap *hap) if (hap->lra_hw_auto_resonance) { rc = qpnp_hap_masked_write_reg(hap, - QPNP_HAP_PM660_HW_AUTO_RES_MODE_BIT, QPNP_HAP_AUTO_RES_CTRL(hap->base), + QPNP_HAP_PM660_HW_AUTO_RES_MODE_BIT, QPNP_HAP_PM660_HW_AUTO_RES_MODE_BIT); if (rc) return rc; } - if (hap->lra_res_cal_period < QPNP_HAP_RES_CAL_PERIOD_MIN) - hap->lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MIN; + if (tmp_cfg) + ares_cfg = tmp_cfg; + else + ares_cfg = &hap->ares_cfg; + + if (ares_cfg->lra_res_cal_period < QPNP_HAP_RES_CAL_PERIOD_MIN) + ares_cfg->lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MIN; if (hap->pmic_subtype == PM660_SUBTYPE) { - if (hap->lra_res_cal_period > + if (ares_cfg->lra_res_cal_period > QPNP_HAP_PM660_RES_CAL_PERIOD_MAX) - hap->lra_res_cal_period = + ares_cfg->lra_res_cal_period = QPNP_HAP_PM660_RES_CAL_PERIOD_MAX; - if (hap->auto_res_mode == QPNP_HAP_PM660_AUTO_RES_QWD) - hap->lra_res_cal_period = 0; + if (ares_cfg->auto_res_mode == QPNP_HAP_PM660_AUTO_RES_QWD) + ares_cfg->lra_res_cal_period = 0; + + if (ares_cfg->lra_res_cal_period) + val = ilog2(ares_cfg->lra_res_cal_period / + QPNP_HAP_RES_CAL_PERIOD_MIN) + 1; } else { - if (hap->lra_res_cal_period > QPNP_HAP_RES_CAL_PERIOD_MAX) - hap->lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MAX; - } + if (ares_cfg->lra_res_cal_period > QPNP_HAP_RES_CAL_PERIOD_MAX) + ares_cfg->lra_res_cal_period = + QPNP_HAP_RES_CAL_PERIOD_MAX; - val = mask = 0; - if (hap->lra_res_cal_period) - val = ilog2(hap->lra_res_cal_period / - QPNP_HAP_RES_CAL_PERIOD_MIN); + if (ares_cfg->lra_res_cal_period) + val = ilog2(ares_cfg->lra_res_cal_period / + QPNP_HAP_RES_CAL_PERIOD_MIN); + } if (hap->pmic_subtype == PM660_SUBTYPE) { - val |= hap->auto_res_mode << + val |= ares_cfg->auto_res_mode << QPNP_HAP_PM660_AUTO_RES_MODE_SHIFT; mask = QPNP_HAP_PM660_AUTO_RES_MODE_BIT; - val |= hap->lra_high_z << + val |= ares_cfg->lra_high_z << QPNP_HAP_PM660_CALIBRATE_DURATION_SHIFT; mask |= QPNP_HAP_PM660_CALIBRATE_DURATION_MASK; - if (hap->lra_qwd_drive_duration != -EINVAL) { - val |= hap->lra_qwd_drive_duration << + if (ares_cfg->lra_qwd_drive_duration != -EINVAL) { + val |= ares_cfg->lra_qwd_drive_duration << QPNP_HAP_PM660_QWD_DRIVE_DURATION_SHIFT; mask |= QPNP_HAP_PM660_QWD_DRIVE_DURATION_BIT; } - if (hap->calibrate_at_eop != -EINVAL) { - val |= hap->calibrate_at_eop << + if (ares_cfg->calibrate_at_eop != -EINVAL) { + val |= ares_cfg->calibrate_at_eop << QPNP_HAP_PM660_CALIBRATE_AT_EOP_SHIFT; mask |= QPNP_HAP_PM660_CALIBRATE_AT_EOP_BIT; } mask |= QPNP_HAP_PM660_LRA_RES_CAL_PER_MASK; } else { - val |= (hap->auto_res_mode << QPNP_HAP_AUTO_RES_MODE_SHIFT); - val |= (hap->lra_high_z << QPNP_HAP_LRA_HIGH_Z_SHIFT); + val |= (ares_cfg->auto_res_mode << + QPNP_HAP_AUTO_RES_MODE_SHIFT); + val |= (ares_cfg->lra_high_z << QPNP_HAP_LRA_HIGH_Z_SHIFT); mask = QPNP_HAP_AUTO_RES_MODE_MASK | QPNP_HAP_LRA_HIGH_Z_MASK | QPNP_HAP_LRA_RES_CAL_PER_MASK; } + pr_debug("mode: %d hi_z period: %d cal_period: %d\n", + ares_cfg->auto_res_mode, ares_cfg->lra_high_z, + ares_cfg->lra_res_cal_period); + rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_LRA_AUTO_RES_REG(hap->base), mask, val); return rc; @@ -822,19 +873,29 @@ static int qpnp_hap_play_mode_config(struct qpnp_hap *hap) } /* configuration api for max voltage */ -static int qpnp_hap_vmax_config(struct qpnp_hap *hap) +static int qpnp_hap_vmax_config(struct qpnp_hap *hap, int vmax_mv, + bool overdrive) { u8 val = 0; int rc; - if (hap->vmax_mv < QPNP_HAP_VMAX_MIN_MV) - hap->vmax_mv = QPNP_HAP_VMAX_MIN_MV; - else if (hap->vmax_mv > QPNP_HAP_VMAX_MAX_MV) - hap->vmax_mv = QPNP_HAP_VMAX_MAX_MV; + if (vmax_mv < 0) + return -EINVAL; - val = (hap->vmax_mv / QPNP_HAP_VMAX_MIN_MV) << QPNP_HAP_VMAX_SHIFT; + /* Allow setting override bit in VMAX_CFG only for PM660 */ + if (hap->pmic_subtype != PM660_SUBTYPE) + overdrive = false; + + if (vmax_mv < QPNP_HAP_VMAX_MIN_MV) + vmax_mv = QPNP_HAP_VMAX_MIN_MV; + else if (vmax_mv > QPNP_HAP_VMAX_MAX_MV) + vmax_mv = QPNP_HAP_VMAX_MAX_MV; + + val = (vmax_mv / QPNP_HAP_VMAX_MIN_MV) << QPNP_HAP_VMAX_SHIFT; + if (overdrive) + val |= QPNP_HAP_VMAX_OVD_BIT; rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_VMAX_REG(hap->base), - QPNP_HAP_VMAX_MASK, val); + QPNP_HAP_VMAX_MASK | QPNP_HAP_VMAX_OVD_BIT, val); return rc; } @@ -912,6 +973,41 @@ static int qpnp_hap_int_pwm_config(struct qpnp_hap *hap) return rc; } +static int qpnp_hap_brake_config(struct qpnp_hap *hap, u8 *brake_pat) +{ + int rc, i; + u32 temp; + u8 *pat_ptr, val; + + if (!hap->en_brake) + return 0; + + /* Configure BRAKE register */ + rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_EN_CTL2_REG(hap->base), + QPNP_HAP_BRAKE_MASK, (u8)hap->en_brake); + if (rc) + return rc; + + if (!brake_pat) + pat_ptr = hap->brake_pat; + else + pat_ptr = brake_pat; + + if (hap->sup_brake_pat) { + for (i = QPNP_HAP_BRAKE_PAT_LEN - 1, val = 0; i >= 0; i--) { + pat_ptr[i] &= QPNP_HAP_BRAKE_PAT_MASK; + temp = i << 1; + val |= pat_ptr[i] << temp; + } + rc = qpnp_hap_write_reg(hap, QPNP_HAP_BRAKE_REG(hap->base), + val); + if (rc) + return rc; + } + + return 0; +} + /* DT parsing api for buffer mode */ static int qpnp_hap_parse_buffer_dt(struct qpnp_hap *hap) { @@ -920,6 +1016,9 @@ static int qpnp_hap_parse_buffer_dt(struct qpnp_hap *hap) u32 temp; int rc, i; + if (hap->wave_rep_cnt > 0 || hap->wave_s_rep_cnt > 0) + return 0; + hap->wave_rep_cnt = QPNP_HAP_WAV_REP_MIN; rc = of_property_read_u32(pdev->dev.of_node, "qcom,wave-rep-cnt", &temp); @@ -1251,6 +1350,25 @@ static ssize_t qpnp_hap_wf_s_rep_store(struct device *dev, return count; } +static int parse_string(const char *in_buf, char *out_buf) +{ + int i; + + if (snprintf(out_buf, QPNP_HAP_STR_SIZE, "%s", in_buf) + > QPNP_HAP_STR_SIZE) + return -EINVAL; + + for (i = 0; i < strlen(out_buf); i++) { + if (out_buf[i] == ' ' || out_buf[i] == '\n' || + out_buf[i] == '\t') { + out_buf[i] = '\0'; + break; + } + } + + return 0; +} + /* sysfs store function for play mode*/ static ssize_t qpnp_hap_play_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -1259,17 +1377,12 @@ static ssize_t qpnp_hap_play_mode_store(struct device *dev, struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, timed_dev); char str[QPNP_HAP_STR_SIZE + 1]; - int rc = 0, temp, old_mode, i; + int rc = 0, temp, old_mode; - if (snprintf(str, QPNP_HAP_STR_SIZE, "%s", buf) > QPNP_HAP_STR_SIZE) - return -EINVAL; + rc = parse_string(buf, str); + if (rc < 0) + return rc; - for (i = 0; i < strlen(str); i++) { - if (str[i] == ' ' || str[i] == '\n' || str[i] == '\t') { - str[i] = '\0'; - break; - } - } if (strcmp(str, "buffer") == 0) temp = QPNP_HAP_BUFFER; else if (strcmp(str, "direct") == 0) @@ -1284,10 +1397,10 @@ static ssize_t qpnp_hap_play_mode_store(struct device *dev, if (temp == hap->play_mode) return count; - if (temp == QPNP_HAP_BUFFER && !hap->buffer_cfg_state) { + if (temp == QPNP_HAP_BUFFER) { rc = qpnp_hap_parse_buffer_dt(hap); if (!rc) - rc = qpnp_hap_buffer_config(hap); + rc = qpnp_hap_buffer_config(hap, NULL, false); } else if (temp == QPNP_HAP_PWM && !hap->pwm_cfg_state) { rc = qpnp_hap_parse_pwm_dt(hap); if (!rc) @@ -1436,6 +1549,245 @@ static ssize_t qpnp_hap_ramp_test_data_show(struct device *dev, } +static ssize_t qpnp_hap_auto_res_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct timed_output_dev *timed_dev = dev_get_drvdata(dev); + struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, + timed_dev); + char *str; + + if (hap->pmic_subtype == PM660_SUBTYPE) { + switch (hap->ares_cfg.auto_res_mode) { + case QPNP_HAP_PM660_AUTO_RES_ZXD: + str = "ZXD"; + break; + case QPNP_HAP_PM660_AUTO_RES_QWD: + str = "QWD"; + break; + default: + str = "None"; + break; + } + } else { + switch (hap->ares_cfg.auto_res_mode) { + case QPNP_HAP_AUTO_RES_NONE: + str = "None"; + break; + case QPNP_HAP_AUTO_RES_ZXD: + str = "ZXD"; + break; + case QPNP_HAP_AUTO_RES_QWD: + str = "QWD"; + break; + case QPNP_HAP_AUTO_RES_MAX_QWD: + str = "MAX_QWD"; + break; + case QPNP_HAP_AUTO_RES_ZXD_EOP: + str = "ZXD_EOP"; + break; + default: + str = "None"; + break; + } + } + + return snprintf(buf, PAGE_SIZE, "%s\n", str); +} + +static ssize_t qpnp_hap_auto_res_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct timed_output_dev *timed_dev = dev_get_drvdata(dev); + struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, + timed_dev); + char str[QPNP_HAP_STR_SIZE + 1]; + int rc = 0, temp; + + rc = parse_string(buf, str); + if (rc < 0) + return rc; + + if (hap->pmic_subtype == PM660_SUBTYPE) { + if (strcmp(str, "ZXD") == 0 || + strcmp(str, "zxd") == 0) + temp = QPNP_HAP_PM660_AUTO_RES_ZXD; + else if (strcmp(str, "QWD") == 0 || + strcmp(str, "qwd") == 0) + temp = QPNP_HAP_PM660_AUTO_RES_QWD; + else { + pr_err("Should be ZXD or QWD\n"); + return -EINVAL; + } + } else { + if (strcmp(str, "None") == 0) + temp = QPNP_HAP_AUTO_RES_NONE; + else if (strcmp(str, "ZXD") == 0 || + strcmp(str, "zxd") == 0) + temp = QPNP_HAP_AUTO_RES_ZXD; + else if (strcmp(str, "QWD") == 0 || + strcmp(str, "qwd") == 0) + temp = QPNP_HAP_AUTO_RES_QWD; + else if (strcmp(str, "ZXD_EOP") == 0 || + strcmp(str, "zxd_eop") == 0) + temp = QPNP_HAP_AUTO_RES_ZXD_EOP; + else if (strcmp(str, "MAX_QWD") == 0 || + strcmp(str, "max_qwd") == 0) + temp = QPNP_HAP_AUTO_RES_MAX_QWD; + else { + pr_err("Should be None or ZXD or QWD or ZXD_EOP or MAX_QWD\n"); + return -EINVAL; + } + } + + hap->ares_cfg.auto_res_mode = temp; + return count; +} + +static ssize_t qpnp_hap_hi_z_period_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct timed_output_dev *timed_dev = dev_get_drvdata(dev); + struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, + timed_dev); + char *str; + + switch (hap->ares_cfg.lra_high_z) { + case QPNP_HAP_LRA_HIGH_Z_NONE: + str = "high_z_none"; + break; + case QPNP_HAP_LRA_HIGH_Z_OPT1: + str = "high_z_opt1"; + break; + case QPNP_HAP_LRA_HIGH_Z_OPT2: + str = "high_z_opt2"; + break; + case QPNP_HAP_LRA_HIGH_Z_OPT3: + str = "high_z_opt3"; + break; + } + + return snprintf(buf, PAGE_SIZE, "%s\n", str); +} + +static ssize_t qpnp_hap_hi_z_period_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct timed_output_dev *timed_dev = dev_get_drvdata(dev); + struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, + timed_dev); + int data, rc; + + rc = kstrtoint(buf, 10, &data); + if (rc) + return rc; + + if (data < QPNP_HAP_LRA_HIGH_Z_NONE + || data > QPNP_HAP_LRA_HIGH_Z_OPT3) { + pr_err("Invalid high Z configuration\n"); + return -EINVAL; + } + + hap->ares_cfg.lra_high_z = data; + return count; +} + +static ssize_t qpnp_hap_calib_period_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct timed_output_dev *timed_dev = dev_get_drvdata(dev); + struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, + timed_dev); + + return snprintf(buf, PAGE_SIZE, "%d\n", + hap->ares_cfg.lra_res_cal_period); +} + +static ssize_t qpnp_hap_calib_period_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct timed_output_dev *timed_dev = dev_get_drvdata(dev); + struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, + timed_dev); + int data, rc; + + rc = kstrtoint(buf, 10, &data); + if (rc) + return rc; + + if (data < QPNP_HAP_RES_CAL_PERIOD_MIN) { + pr_err("Invalid auto resonance calibration period\n"); + return -EINVAL; + } + + if (hap->pmic_subtype == PM660_SUBTYPE) { + if (data > QPNP_HAP_PM660_RES_CAL_PERIOD_MAX) { + pr_err("Invalid auto resonance calibration period\n"); + return -EINVAL; + } + } else { + if (data > QPNP_HAP_RES_CAL_PERIOD_MAX) { + pr_err("Invalid auto resonance calibration period\n"); + return -EINVAL; + } + } + + hap->ares_cfg.lra_res_cal_period = data; + return count; +} + +static ssize_t qpnp_hap_override_auto_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct timed_output_dev *timed_dev = dev_get_drvdata(dev); + struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, + timed_dev); + + return snprintf(buf, PAGE_SIZE, "%d\n", hap->override_auto_mode_config); +} + +static ssize_t qpnp_hap_override_auto_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct timed_output_dev *timed_dev = dev_get_drvdata(dev); + struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, + timed_dev); + int data, rc; + + rc = kstrtoint(buf, 10, &data); + if (rc) + return rc; + + hap->override_auto_mode_config = data; + return count; +} + +static ssize_t qpnp_hap_vmax_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct timed_output_dev *timed_dev = dev_get_drvdata(dev); + struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, + timed_dev); + + return snprintf(buf, PAGE_SIZE, "%d\n", hap->vmax_mv); +} + +static ssize_t qpnp_hap_vmax_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct timed_output_dev *timed_dev = dev_get_drvdata(dev); + struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, + timed_dev); + int data, rc; + + rc = kstrtoint(buf, 10, &data); + if (rc) + return rc; + + hap->vmax_mv = data; + return count; +} + /* sysfs attributes */ static struct device_attribute qpnp_hap_attrs[] = { __ATTR(wf_s0, 0664, qpnp_hap_wf_s0_show, qpnp_hap_wf_s0_store), @@ -1457,6 +1809,16 @@ static struct device_attribute qpnp_hap_attrs[] = { qpnp_hap_ramp_test_data_store), __ATTR(min_max_test, 0664, qpnp_hap_min_max_test_data_show, qpnp_hap_min_max_test_data_store), + __ATTR(auto_res_mode, 0664, qpnp_hap_auto_res_mode_show, + qpnp_hap_auto_res_mode_store), + __ATTR(high_z_period, 0664, qpnp_hap_hi_z_period_show, + qpnp_hap_hi_z_period_store), + __ATTR(calib_period, 0664, qpnp_hap_calib_period_show, + qpnp_hap_calib_period_store), + __ATTR(override_auto_mode_config, 0664, + qpnp_hap_override_auto_mode_show, + qpnp_hap_override_auto_mode_store), + __ATTR(vmax_mv, 0664, qpnp_hap_vmax_show, qpnp_hap_vmax_store), }; static int calculate_lra_code(struct qpnp_hap *hap) @@ -1515,9 +1877,47 @@ static int calculate_lra_code(struct qpnp_hap *hap) static int qpnp_hap_auto_res_enable(struct qpnp_hap *hap, int enable) { int rc = 0; - u8 val; + u32 back_emf_delay_us = hap->time_required_to_generate_back_emf_us; + u8 val, auto_res_mode_qwd; + + if (hap->act_type != QPNP_HAP_LRA) + return 0; + + if (hap->pmic_subtype == PM660_SUBTYPE) + auto_res_mode_qwd = (hap->ares_cfg.auto_res_mode == + QPNP_HAP_PM660_AUTO_RES_QWD); + else + auto_res_mode_qwd = (hap->ares_cfg.auto_res_mode == + QPNP_HAP_AUTO_RES_QWD); + + /* + * Do not enable auto resonance if auto mode is enabled and auto + * resonance mode is QWD, meaning short pattern. + */ + if (hap->auto_mode && auto_res_mode_qwd && enable) { + pr_debug("auto_mode enabled, not enabling auto_res\n"); + return 0; + } + + if (!hap->correct_lra_drive_freq && !auto_res_mode_qwd) { + pr_debug("correct_lra_drive_freq: %d auto_res_mode_qwd: %d\n", + hap->correct_lra_drive_freq, auto_res_mode_qwd); + return 0; + } val = enable ? AUTO_RES_ENABLE : 0; + /* + * For auto resonance detection to work properly, sufficient back-emf + * has to be generated. In general, back-emf takes some time to build + * up. When the auto resonance mode is chosen as QWD, high-z will be + * applied for every LRA cycle and hence there won't be enough back-emf + * at the start-up. Hence, the motor needs to vibrate for few LRA cycles + * after the PLAY bit is asserted. Enable the auto resonance after + * 'time_required_to_generate_back_emf_us' is completed. + */ + if (enable) + usleep_range(back_emf_delay_us, back_emf_delay_us + 1); + if (hap->pmic_subtype == PM660_SUBTYPE) rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_AUTO_RES_CTRL(hap->base), @@ -1534,6 +1934,7 @@ static int qpnp_hap_auto_res_enable(struct qpnp_hap *hap, int enable) else hap->status_flags &= ~AUTO_RESONANCE_ENABLED; + pr_debug("auto_res %sabled\n", enable ? "en" : "dis"); return rc; } @@ -1617,65 +2018,57 @@ static enum hrtimer_restart detect_auto_res_error(struct hrtimer *timer) return HRTIMER_RESTART; } +static bool is_sw_lra_auto_resonance_control(struct qpnp_hap *hap) +{ + if (hap->act_type != QPNP_HAP_LRA) + return false; + + if (hap->lra_hw_auto_resonance) + return false; + + if (!hap->correct_lra_drive_freq) + return false; + + if (hap->auto_mode && hap->play_mode == QPNP_HAP_BUFFER) + return false; + + return true; +} + /* set api for haptics */ -static int qpnp_hap_set(struct qpnp_hap *hap, int on) +static int qpnp_hap_set(struct qpnp_hap *hap, bool on) { - u8 auto_res_mode_qwd; int rc = 0; unsigned long timeout_ns = POLL_TIME_AUTO_RES_ERR_NS; - u32 back_emf_delay_us = hap->time_required_to_generate_back_emf_us; if (hap->play_mode == QPNP_HAP_PWM) { - if (on) + if (on) { rc = pwm_enable(hap->pwm_info.pwm_dev); - else + if (rc < 0) + return rc; + } else { pwm_disable(hap->pwm_info.pwm_dev); + } } else if (hap->play_mode == QPNP_HAP_BUFFER || hap->play_mode == QPNP_HAP_DIRECT) { if (on) { - /* - * For auto resonance detection to work properly, - * sufficient back-emf has to be generated. In general, - * back-emf takes some time to build up. When the auto - * resonance mode is chosen as QWD, high-z will be - * applied for every LRA cycle and hence there won't be - * enough back-emf at the start-up. Hence, the motor - * needs to vibrate for few LRA cycles after the PLAY - * bit is asserted. So disable the auto resonance here - * and enable it after the sleep of - * 'time_required_to_generate_back_emf_us' is completed. - */ - if (hap->pmic_subtype == PM660_SUBTYPE) - auto_res_mode_qwd = (hap->auto_res_mode == - QPNP_HAP_PM660_AUTO_RES_QWD); - else - auto_res_mode_qwd = (hap->auto_res_mode == - QPNP_HAP_AUTO_RES_QWD); - - if ((hap->act_type == QPNP_HAP_LRA) && - (hap->correct_lra_drive_freq || - auto_res_mode_qwd)) - qpnp_hap_auto_res_enable(hap, 0); + rc = qpnp_hap_auto_res_enable(hap, 0); + if (rc < 0) + return rc; rc = qpnp_hap_mod_enable(hap, on); if (rc < 0) return rc; rc = qpnp_hap_play(hap, on); + if (rc < 0) + return rc; - if ((hap->act_type == QPNP_HAP_LRA) && - (hap->correct_lra_drive_freq || - auto_res_mode_qwd)) { - usleep_range(back_emf_delay_us, - (back_emf_delay_us + 1)); + rc = qpnp_hap_auto_res_enable(hap, 1); + if (rc < 0) + return rc; - rc = qpnp_hap_auto_res_enable(hap, 1); - if (rc < 0) - return rc; - } - if (hap->act_type == QPNP_HAP_LRA && - hap->correct_lra_drive_freq && - !hap->lra_hw_auto_resonance) { + if (is_sw_lra_auto_resonance_control(hap)) { /* * Start timer to poll Auto Resonance error bit */ @@ -1683,7 +2076,7 @@ static int qpnp_hap_set(struct qpnp_hap *hap, int on) hrtimer_cancel(&hap->auto_res_err_poll_timer); hrtimer_start(&hap->auto_res_err_poll_timer, ktime_set(0, timeout_ns), - HRTIMER_MODE_REL); + HRTIMER_MODE_REL); mutex_unlock(&hap->lock); } } else { @@ -1691,54 +2084,182 @@ static int qpnp_hap_set(struct qpnp_hap *hap, int on) if (rc < 0) return rc; - if (hap->act_type == QPNP_HAP_LRA && - hap->correct_lra_drive_freq && - (hap->status_flags & AUTO_RESONANCE_ENABLED) && - !hap->lra_hw_auto_resonance) { + if (is_sw_lra_auto_resonance_control(hap) && + (hap->status_flags & AUTO_RESONANCE_ENABLED)) update_lra_frequency(hap); - } rc = qpnp_hap_mod_enable(hap, on); - if (hap->act_type == QPNP_HAP_LRA && - hap->correct_lra_drive_freq && - !hap->lra_hw_auto_resonance) { + if (rc < 0) + return rc; + + if (is_sw_lra_auto_resonance_control(hap)) hrtimer_cancel(&hap->auto_res_err_poll_timer); - } } } return rc; } +static int qpnp_hap_auto_mode_config(struct qpnp_hap *hap, int time_ms) +{ + struct qpnp_hap_lra_ares_cfg ares_cfg; + enum qpnp_hap_mode old_play_mode; + u8 old_ares_mode; + u8 brake_pat[QPNP_HAP_BRAKE_PAT_LEN] = {0}; + u8 wave_samp[QPNP_HAP_WAV_SAMP_LEN] = {0}; + int rc, vmax_mv; + + /* For now, this is for LRA only */ + if (hap->act_type == QPNP_HAP_ERM) + return 0; + + old_ares_mode = hap->ares_cfg.auto_res_mode; + old_play_mode = hap->play_mode; + pr_debug("auto_mode, time_ms: %d\n", time_ms); + if (time_ms <= 20) { + wave_samp[0] = QPNP_HAP_WAV_SAMP_MAX; + wave_samp[1] = QPNP_HAP_WAV_SAMP_MAX; + if (time_ms > 15) + wave_samp[2] = QPNP_HAP_WAV_SAMP_MAX; + + /* short pattern */ + rc = qpnp_hap_parse_buffer_dt(hap); + if (!rc) + rc = qpnp_hap_buffer_config(hap, wave_samp, true); + if (rc < 0) { + pr_err("Error in configuring buffer mode %d\n", + rc); + return rc; + } + + ares_cfg.lra_high_z = QPNP_HAP_LRA_HIGH_Z_OPT1; + ares_cfg.lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MIN; + if (hap->pmic_subtype == PM660_SUBTYPE) { + ares_cfg.auto_res_mode = + QPNP_HAP_PM660_AUTO_RES_QWD; + ares_cfg.lra_qwd_drive_duration = 0; + ares_cfg.calibrate_at_eop = 0; + } else { + ares_cfg.auto_res_mode = QPNP_HAP_AUTO_RES_QWD; + ares_cfg.lra_qwd_drive_duration = -EINVAL; + ares_cfg.calibrate_at_eop = -EINVAL; + } + + vmax_mv = QPNP_HAP_VMAX_MAX_MV; + rc = qpnp_hap_vmax_config(hap, vmax_mv, true); + if (rc < 0) + return rc; + + rc = qpnp_hap_brake_config(hap, brake_pat); + if (rc < 0) + return rc; + + /* enable play_irq for buffer mode */ + if (hap->play_irq >= 0 && !hap->play_irq_en) { + enable_irq(hap->play_irq); + hap->play_irq_en = true; + } + + hap->play_mode = QPNP_HAP_BUFFER; + hap->wave_shape = QPNP_HAP_WAV_SQUARE; + } else { + /* long pattern */ + ares_cfg.lra_high_z = QPNP_HAP_LRA_HIGH_Z_OPT1; + if (hap->pmic_subtype == PM660_SUBTYPE) { + ares_cfg.auto_res_mode = + QPNP_HAP_PM660_AUTO_RES_ZXD; + ares_cfg.lra_res_cal_period = + QPNP_HAP_PM660_RES_CAL_PERIOD_MAX; + ares_cfg.lra_qwd_drive_duration = 0; + ares_cfg.calibrate_at_eop = 1; + } else { + ares_cfg.auto_res_mode = QPNP_HAP_AUTO_RES_ZXD_EOP; + ares_cfg.lra_res_cal_period = + QPNP_HAP_RES_CAL_PERIOD_MAX; + ares_cfg.lra_qwd_drive_duration = -EINVAL; + ares_cfg.calibrate_at_eop = -EINVAL; + } + + vmax_mv = hap->vmax_mv; + rc = qpnp_hap_vmax_config(hap, vmax_mv, false); + if (rc < 0) + return rc; + + brake_pat[0] = 0x3; + rc = qpnp_hap_brake_config(hap, brake_pat); + if (rc < 0) + return rc; + + /* enable play_irq for direct mode */ + if (hap->play_irq >= 0 && hap->play_irq_en) { + disable_irq(hap->play_irq); + hap->play_irq_en = false; + } + + hap->play_mode = QPNP_HAP_DIRECT; + hap->wave_shape = QPNP_HAP_WAV_SINE; + } + + if (hap->override_auto_mode_config) { + rc = qpnp_hap_lra_auto_res_config(hap, NULL); + } else { + hap->ares_cfg.auto_res_mode = ares_cfg.auto_res_mode; + rc = qpnp_hap_lra_auto_res_config(hap, &ares_cfg); + } + + if (rc < 0) { + hap->ares_cfg.auto_res_mode = old_ares_mode; + return rc; + } + + rc = qpnp_hap_play_mode_config(hap); + if (rc < 0) { + hap->play_mode = old_play_mode; + return rc; + } + + rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_CFG2_REG(hap->base), + QPNP_HAP_WAV_SHAPE_MASK, hap->wave_shape); + if (rc < 0) + return rc; + + return 0; +} + /* enable interface from timed output class */ -static void qpnp_hap_td_enable(struct timed_output_dev *dev, int value) +static void qpnp_hap_td_enable(struct timed_output_dev *dev, int time_ms) { struct qpnp_hap *hap = container_of(dev, struct qpnp_hap, timed_dev); + int rc; - mutex_lock(&hap->lock); + if (time_ms <= 0) + return; - if (hap->act_type == QPNP_HAP_LRA && - hap->correct_lra_drive_freq && - !hap->lra_hw_auto_resonance) + if (time_ms < 10) + time_ms = 10; + + mutex_lock(&hap->lock); + if (is_sw_lra_auto_resonance_control(hap)) hrtimer_cancel(&hap->auto_res_err_poll_timer); hrtimer_cancel(&hap->hap_timer); - if (value == 0) { - if (hap->state == 0) { + if (hap->auto_mode) { + rc = qpnp_hap_auto_mode_config(hap, time_ms); + if (rc < 0) { + pr_err("Unable to do auto mode config\n"); mutex_unlock(&hap->lock); return; } - hap->state = 0; - } else { - value = (value > hap->timeout_ms ? - hap->timeout_ms : value); - hap->state = 1; - hrtimer_start(&hap->hap_timer, - ktime_set(value / 1000, (value % 1000) * 1000000), - HRTIMER_MODE_REL); } + + time_ms = (time_ms > hap->timeout_ms ? hap->timeout_ms : time_ms); + hap->play_time_ms = time_ms; + hap->state = 1; + hrtimer_start(&hap->hap_timer, + ktime_set(time_ms / 1000, (time_ms % 1000) * 1000000), + HRTIMER_MODE_REL); mutex_unlock(&hap->lock); schedule_work(&hap->work); } @@ -1824,7 +2345,7 @@ static void qpnp_hap_worker(struct work_struct *work) /* Disable haptics module if the duration of short circuit * exceeds the maximum limit (5 secs). */ - if (hap->sc_duration == SC_MAX_DURATION) { + if (hap->sc_count >= SC_MAX_COUNT) { rc = qpnp_hap_write_reg(hap, QPNP_HAP_EN_CTL_REG(hap->base), val); } else { @@ -1890,7 +2411,7 @@ static int qpnp_haptic_suspend(struct device *dev) hrtimer_cancel(&hap->hap_timer); cancel_work_sync(&hap->work); /* turn-off haptic */ - qpnp_hap_set(hap, 0); + qpnp_hap_set(hap, false); return 0; } @@ -1902,8 +2423,7 @@ static SIMPLE_DEV_PM_OPS(qpnp_haptic_pm_ops, qpnp_haptic_suspend, NULL); static int qpnp_hap_config(struct qpnp_hap *hap) { u8 val = 0; - u32 temp; - int rc, i; + int rc; /* * This denotes the percentage error in rc clock multiplied by 10 @@ -1917,7 +2437,7 @@ static int qpnp_hap_config(struct qpnp_hap *hap) return rc; /* Configure auto resonance parameters */ - rc = qpnp_hap_lra_auto_res_config(hap); + rc = qpnp_hap_lra_auto_res_config(hap, NULL); if (rc) return rc; @@ -1927,7 +2447,7 @@ static int qpnp_hap_config(struct qpnp_hap *hap) return rc; /* Configure the VMAX register */ - rc = qpnp_hap_vmax_config(hap); + rc = qpnp_hap_vmax_config(hap, hap->vmax_mv, false); if (rc) return rc; @@ -2037,32 +2557,12 @@ static int qpnp_hap_config(struct qpnp_hap *hap) hap->drive_period_code_min_limit); } - /* Configure BRAKE register */ - rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_EN_CTL2_REG(hap->base), - QPNP_HAP_BRAKE_MASK, (u8)hap->en_brake); - if (rc) - return rc; - - if (hap->en_brake && hap->sup_brake_pat) { - for (i = QPNP_HAP_BRAKE_PAT_LEN - 1, val = 0; i >= 0; i--) { - hap->brake_pat[i] &= QPNP_HAP_BRAKE_PAT_MASK; - temp = i << 1; - val |= hap->brake_pat[i] << temp; - } - rc = qpnp_hap_write_reg(hap, QPNP_HAP_BRAKE_REG(hap->base), - val); - if (rc) - return rc; - } - - /* Cache play register */ - rc = qpnp_hap_read_reg(hap, QPNP_HAP_PLAY_REG(hap->base), &val); + rc = qpnp_hap_brake_config(hap, NULL); if (rc < 0) return rc; - hap->reg_play = val; if (hap->play_mode == QPNP_HAP_BUFFER) - rc = qpnp_hap_buffer_config(hap); + rc = qpnp_hap_buffer_config(hap, NULL, false); else if (hap->play_mode == QPNP_HAP_PWM) rc = qpnp_hap_pwm_config(hap); else if (hap->play_mode == QPNP_HAP_AUDIO) @@ -2083,8 +2583,10 @@ static int qpnp_hap_config(struct qpnp_hap *hap) } /* use play_irq only for buffer mode */ - if (hap->play_mode != QPNP_HAP_BUFFER) + if (hap->play_mode != QPNP_HAP_BUFFER) { disable_irq(hap->play_irq); + hap->play_irq_en = false; + } } /* setup short circuit irq */ @@ -2099,7 +2601,7 @@ static int qpnp_hap_config(struct qpnp_hap *hap) } } - hap->sc_duration = 0; + hap->sc_count = 0; return rc; } @@ -2173,30 +2675,31 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) "qcom,lra-auto-res-mode", &temp_str); if (!rc) { if (hap->pmic_subtype == PM660_SUBTYPE) { - hap->auto_res_mode = + hap->ares_cfg.auto_res_mode = QPNP_HAP_PM660_AUTO_RES_QWD; if (strcmp(temp_str, "zxd") == 0) - hap->auto_res_mode = + hap->ares_cfg.auto_res_mode = QPNP_HAP_PM660_AUTO_RES_ZXD; else if (strcmp(temp_str, "qwd") == 0) - hap->auto_res_mode = + hap->ares_cfg.auto_res_mode = QPNP_HAP_PM660_AUTO_RES_QWD; } else { - hap->auto_res_mode = QPNP_HAP_AUTO_RES_ZXD_EOP; + hap->ares_cfg.auto_res_mode = + QPNP_HAP_AUTO_RES_ZXD_EOP; if (strcmp(temp_str, "none") == 0) - hap->auto_res_mode = + hap->ares_cfg.auto_res_mode = QPNP_HAP_AUTO_RES_NONE; else if (strcmp(temp_str, "zxd") == 0) - hap->auto_res_mode = + hap->ares_cfg.auto_res_mode = QPNP_HAP_AUTO_RES_ZXD; else if (strcmp(temp_str, "qwd") == 0) - hap->auto_res_mode = + hap->ares_cfg.auto_res_mode = QPNP_HAP_AUTO_RES_QWD; else if (strcmp(temp_str, "max-qwd") == 0) - hap->auto_res_mode = + hap->ares_cfg.auto_res_mode = QPNP_HAP_AUTO_RES_MAX_QWD; else - hap->auto_res_mode = + hap->ares_cfg.auto_res_mode = QPNP_HAP_AUTO_RES_ZXD_EOP; } } else if (rc != -EINVAL) { @@ -2204,42 +2707,48 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) return rc; } - hap->lra_high_z = QPNP_HAP_LRA_HIGH_Z_OPT3; + hap->ares_cfg.lra_high_z = QPNP_HAP_LRA_HIGH_Z_OPT3; rc = of_property_read_string(pdev->dev.of_node, "qcom,lra-high-z", &temp_str); if (!rc) { if (strcmp(temp_str, "none") == 0) - hap->lra_high_z = QPNP_HAP_LRA_HIGH_Z_NONE; + hap->ares_cfg.lra_high_z = + QPNP_HAP_LRA_HIGH_Z_NONE; + else if (strcmp(temp_str, "opt1") == 0) + hap->ares_cfg.lra_high_z = + QPNP_HAP_LRA_HIGH_Z_OPT1; + else if (strcmp(temp_str, "opt2") == 0) + hap->ares_cfg.lra_high_z = + QPNP_HAP_LRA_HIGH_Z_OPT2; + else + hap->ares_cfg.lra_high_z = + QPNP_HAP_LRA_HIGH_Z_OPT3; + if (hap->pmic_subtype == PM660_SUBTYPE) { if (strcmp(temp_str, "opt0") == 0) - hap->lra_high_z = + hap->ares_cfg.lra_high_z = QPNP_HAP_LRA_HIGH_Z_NONE; } - else if (strcmp(temp_str, "opt1") == 0) - hap->lra_high_z = QPNP_HAP_LRA_HIGH_Z_OPT1; - else if (strcmp(temp_str, "opt2") == 0) - hap->lra_high_z = QPNP_HAP_LRA_HIGH_Z_OPT2; - else - hap->lra_high_z = QPNP_HAP_LRA_HIGH_Z_OPT3; } else if (rc != -EINVAL) { pr_err("Unable to read LRA high-z\n"); return rc; } - hap->lra_qwd_drive_duration = -EINVAL; + hap->ares_cfg.lra_qwd_drive_duration = -EINVAL; rc = of_property_read_u32(pdev->dev.of_node, "qcom,lra-qwd-drive-duration", - &hap->lra_qwd_drive_duration); + &hap->ares_cfg.lra_qwd_drive_duration); - hap->calibrate_at_eop = -EINVAL; + hap->ares_cfg.calibrate_at_eop = -EINVAL; rc = of_property_read_u32(pdev->dev.of_node, - "qcom,lra-calibrate-at-eop", &hap->calibrate_at_eop); + "qcom,lra-calibrate-at-eop", + &hap->ares_cfg.calibrate_at_eop); - hap->lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MAX; + hap->ares_cfg.lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MAX; rc = of_property_read_u32(pdev->dev.of_node, "qcom,lra-res-cal-period", &temp); if (!rc) { - hap->lra_res_cal_period = temp; + hap->ares_cfg.lra_res_cal_period = temp; } else if (rc != -EINVAL) { pr_err("Unable to read cal period\n"); return rc; @@ -2271,7 +2780,7 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) hap->drive_period_code_min_limit_percent_variation = (u8) temp; - if (hap->auto_res_mode == QPNP_HAP_AUTO_RES_QWD) { + if (hap->ares_cfg.auto_res_mode == QPNP_HAP_AUTO_RES_QWD) { hap->time_required_to_generate_back_emf_us = QPNP_HAP_TIME_REQ_FOR_BACK_EMF_GEN; rc = of_property_read_u32(pdev->dev.of_node, @@ -2409,6 +2918,8 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) if (of_find_property(pdev->dev.of_node, "vcc_pon-supply", NULL)) hap->manage_pon_supply = true; + hap->auto_mode = of_property_read_bool(pdev->dev.of_node, + "qcom,lra-auto-mode"); return 0; } @@ -2503,12 +3014,9 @@ static int qpnp_haptic_probe(struct platform_device *pdev) hap->timed_dev.get_time = qpnp_hap_get_time; hap->timed_dev.enable = qpnp_hap_td_enable; - if (hap->act_type == QPNP_HAP_LRA && hap->correct_lra_drive_freq && - !hap->lra_hw_auto_resonance) { - hrtimer_init(&hap->auto_res_err_poll_timer, CLOCK_MONOTONIC, - HRTIMER_MODE_REL); - hap->auto_res_err_poll_timer.function = detect_auto_res_error; - } + hrtimer_init(&hap->auto_res_err_poll_timer, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); + hap->auto_res_err_poll_timer.function = detect_auto_res_error; rc = timed_output_dev_register(&hap->timed_dev); if (rc < 0) { @@ -2546,9 +3054,7 @@ sysfs_fail: timed_output_dev_unregister(&hap->timed_dev); timed_output_fail: cancel_work_sync(&hap->work); - if (hap->act_type == QPNP_HAP_LRA && hap->correct_lra_drive_freq && - !hap->lra_hw_auto_resonance) - hrtimer_cancel(&hap->auto_res_err_poll_timer); + hrtimer_cancel(&hap->auto_res_err_poll_timer); hrtimer_cancel(&hap->hap_timer); mutex_destroy(&hap->lock); mutex_destroy(&hap->wf_lock); @@ -2566,9 +3072,7 @@ static int qpnp_haptic_remove(struct platform_device *pdev) &qpnp_hap_attrs[i].attr); cancel_work_sync(&hap->work); - if (hap->act_type == QPNP_HAP_LRA && hap->correct_lra_drive_freq && - !hap->lra_hw_auto_resonance) - hrtimer_cancel(&hap->auto_res_err_poll_timer); + hrtimer_cancel(&hap->auto_res_err_poll_timer); hrtimer_cancel(&hap->hap_timer); timed_output_dev_unregister(&hap->timed_dev); mutex_destroy(&hap->lock); diff --git a/drivers/soc/qcom/rpm_master_stat.c b/drivers/soc/qcom/rpm_master_stat.c index 14004a2b721e..7bf18ffe6ad2 100644 --- a/drivers/soc/qcom/rpm_master_stat.c +++ b/drivers/soc/qcom/rpm_master_stat.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -96,6 +96,7 @@ static int msm_rpm_master_copy_stats( int count, j = 0; char *buf; static DEFINE_MUTEX(msm_rpm_master_stats_mutex); + unsigned long active_cores; mutex_lock(&msm_rpm_master_stats_mutex); @@ -247,12 +248,11 @@ static int msm_rpm_master_copy_stats( record.active_cores); } - j = find_first_bit((unsigned long *)&record.active_cores, - BITS_PER_LONG); + active_cores = record.active_cores; + j = find_first_bit(&active_cores, BITS_PER_LONG); while (j < BITS_PER_LONG) { SNPRINTF(buf, count, "\t\tcore%d\n", j); - j = find_next_bit((unsigned long *)&record.active_cores, - BITS_PER_LONG, j + 1); + j = find_next_bit(&active_cores, BITS_PER_LONG, j + 1); } master_cnt++; diff --git a/drivers/soc/qcom/service-locator.c b/drivers/soc/qcom/service-locator.c index 97cd11201262..f19db5fe99b3 100644 --- a/drivers/soc/qcom/service-locator.c +++ b/drivers/soc/qcom/service-locator.c @@ -31,7 +31,6 @@ #define SERVREG_LOC_SERVICE_INSTANCE_ID 1 -#define QMI_RESP_BIT_SHIFT(x) (x << 16) #define QMI_SERVREG_LOC_SERVER_INITIAL_TIMEOUT 2000 #define QMI_SERVREG_LOC_SERVER_TIMEOUT 2000 #define INITIAL_TIMEOUT 100000 @@ -199,9 +198,9 @@ static int servreg_loc_send_msg(struct msg_desc *req_desc, } /* Check the response */ - if (QMI_RESP_BIT_SHIFT(resp->resp.result) != QMI_RESULT_SUCCESS_V01) { + if (resp->resp.result != QMI_RESULT_SUCCESS_V01) { pr_err("QMI request for client %s failed 0x%x\n", - pd->client_name, QMI_RESP_BIT_SHIFT(resp->resp.error)); + pd->client_name, resp->resp.error); return -EREMOTEIO; } return rc; @@ -220,7 +219,7 @@ static int service_locator_send_msg(struct pd_qmi_client_data *pd) return -EAGAIN; } - req = kmalloc(sizeof( + req = kzalloc(sizeof( struct qmi_servreg_loc_get_domain_list_req_msg_v01), GFP_KERNEL); if (!req) { @@ -228,7 +227,7 @@ static int service_locator_send_msg(struct pd_qmi_client_data *pd) rc = -ENOMEM; goto out; } - resp = kmalloc(sizeof( + resp = kzalloc(sizeof( struct qmi_servreg_loc_get_domain_list_resp_msg_v01), GFP_KERNEL); if (!resp) { diff --git a/drivers/soc/qcom/service-notifier.c b/drivers/soc/qcom/service-notifier.c index b5681a5c6817..221ae0c1fefb 100644 --- a/drivers/soc/qcom/service-notifier.c +++ b/drivers/soc/qcom/service-notifier.c @@ -30,7 +30,6 @@ #include <soc/qcom/service-notifier.h> #include "service-notifier-private.h" -#define QMI_RESP_BIT_SHIFT(x) (x << 16) #define SERVREG_NOTIF_NAME_LENGTH QMI_SERVREG_NOTIF_NAME_LENGTH_V01 #define SERVREG_NOTIF_SERVICE_ID SERVREG_NOTIF_SERVICE_ID_V01 #define SERVREG_NOTIF_SERVICE_VERS SERVREG_NOTIF_SERVICE_VERS_V01 @@ -225,9 +224,8 @@ static void send_ind_ack(struct work_struct *work) } /* Check the response */ - if (QMI_RESP_BIT_SHIFT(resp.resp.result) != QMI_RESULT_SUCCESS_V01) - pr_err("QMI request failed 0x%x\n", - QMI_RESP_BIT_SHIFT(resp.resp.error)); + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) + pr_err("QMI request failed 0x%x\n", resp.resp.error); pr_info("Indication ACKed for transid %d, service %s, instance %d!\n", data->ind_msg.transaction_id, data->ind_msg.service_path, data->instance_id); @@ -318,9 +316,8 @@ static int send_notif_listener_msg_req(struct service_notif_info *service_notif, } /* Check the response */ - if (QMI_RESP_BIT_SHIFT(resp.resp.result) != QMI_RESULT_SUCCESS_V01) { - pr_err("QMI request failed 0x%x\n", - QMI_RESP_BIT_SHIFT(resp.resp.error)); + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + pr_err("QMI request failed 0x%x\n", resp.resp.error); return -EREMOTEIO; } @@ -645,15 +642,15 @@ static int send_pd_restart_req(const char *service_path, } /* Check response if PDR is disabled */ - if (QMI_RESP_BIT_SHIFT(resp.resp.result) == QMI_ERR_DISABLED_V01) { - pr_err("PD restart is disabled 0x%x\n", - QMI_RESP_BIT_SHIFT(resp.resp.error)); + if (resp.resp.result == QMI_RESULT_FAILURE_V01 && + resp.resp.error == QMI_ERR_DISABLED_V01) { + pr_err("PD restart is disabled 0x%x\n", resp.resp.error); return -EOPNOTSUPP; } /* Check the response for other error case*/ - if (QMI_RESP_BIT_SHIFT(resp.resp.result) != QMI_RESULT_SUCCESS_V01) { + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { pr_err("QMI request for PD restart failed 0x%x\n", - QMI_RESP_BIT_SHIFT(resp.resp.error)); + resp.resp.error); return -EREMOTEIO; } diff --git a/drivers/soc/qcom/smcinvoke.c b/drivers/soc/qcom/smcinvoke.c index 99ae24735f05..f69ff47ae0f7 100644 --- a/drivers/soc/qcom/smcinvoke.c +++ b/drivers/soc/qcom/smcinvoke.c @@ -429,10 +429,17 @@ long smcinvoke_ioctl(struct file *filp, unsigned cmd, unsigned long arg) if (ret) goto out; - ret = marshal_out(in_msg, inmsg_size, &req, args_buf); + /* + * if invoke op results in an err, no need to marshal_out and + * copy args buf to user space + */ + if (!req.result) { + ret = marshal_out(in_msg, inmsg_size, &req, args_buf); - ret |= copy_to_user((void __user *)(uintptr_t)(req.args), + ret |= copy_to_user( + (void __user *)(uintptr_t)(req.args), args_buf, nr_args * req.argsize); + } ret |= copy_to_user((void __user *)arg, &req, sizeof(req)); if (ret) goto out; diff --git a/drivers/soc/qcom/spcom.c b/drivers/soc/qcom/spcom.c index 10eebb0316e8..c2af34926b37 100644 --- a/drivers/soc/qcom/spcom.c +++ b/drivers/soc/qcom/spcom.c @@ -220,9 +220,9 @@ struct spcom_channel { bool tx_abort; /* rx data info */ - int rx_buf_size; /* allocated rx buffer size */ + size_t rx_buf_size; /* allocated rx buffer size */ bool rx_buf_ready; - int actual_rx_size; /* actual data size received */ + size_t actual_rx_size; /* actual data size received */ const void *glink_rx_buf; /* ION lock/unlock support */ @@ -302,6 +302,10 @@ static inline bool spcom_is_channel_open(struct spcom_channel *ch) */ static inline bool spcom_is_channel_connected(struct spcom_channel *ch) { + /* Channel must be open before it gets connected */ + if (!spcom_is_channel_open(ch)) + return false; + return (ch->glink_state == GLINK_CONNECTED); } @@ -359,6 +363,11 @@ static void spcom_link_state_notif_cb(struct glink_link_state_cb_info *cb_info, struct spcom_channel *ch = NULL; const char *ch_name = "sp_kernel"; + if (!cb_info) { + pr_err("invalid NULL cb_info.\n"); + return; + } + if (!spcom_is_ready()) { pr_err("spcom is not ready.\n"); return; @@ -411,13 +420,17 @@ static void spcom_notify_rx(void *handle, struct spcom_channel *ch = (struct spcom_channel *) priv; if (!ch) { - pr_err("invalid ch parameter.\n"); + pr_err("invalid NULL channel param\n"); + return; + } + if (!buf) { + pr_err("invalid NULL buf param\n"); return; } - pr_debug("ch [%s] rx size [%d].\n", ch->name, (int) size); + pr_debug("ch [%s] rx size [%zu]\n", ch->name, size); - ch->actual_rx_size = (int) size; + ch->actual_rx_size = size; ch->glink_rx_buf = (void *) buf; complete_all(&ch->rx_done); @@ -436,7 +449,11 @@ static void spcom_notify_tx_done(void *handle, int *tx_buf = (int *) buf; if (!ch) { - pr_err("invalid ch parameter.\n"); + pr_err("invalid NULL channel param\n"); + return; + } + if (!buf) { + pr_err("invalid NULL buf param\n"); return; } @@ -460,6 +477,11 @@ static void spcom_notify_state(void *handle, const void *priv, unsigned event) int ret; struct spcom_channel *ch = (struct spcom_channel *) priv; + if (!ch) { + pr_err("invalid NULL channel param\n"); + return; + } + switch (event) { case GLINK_CONNECTED: pr_debug("GLINK_CONNECTED, ch name [%s].\n", ch->name); @@ -479,7 +501,7 @@ static void spcom_notify_state(void *handle, const void *priv, unsigned event) if (ret) { pr_err("glink_queue_rx_intent() err [%d]\n", ret); } else { - pr_debug("rx buf is ready, size [%d].\n", + pr_debug("rx buf is ready, size [%zu]\n", ch->rx_buf_size); ch->rx_buf_ready = true; } @@ -536,9 +558,7 @@ static void spcom_notify_state(void *handle, const void *priv, unsigned event) static bool spcom_notify_rx_intent_req(void *handle, const void *priv, size_t req_size) { - struct spcom_channel *ch = (struct spcom_channel *) priv; - - pr_err("Unexpected intent request for ch [%s].\n", ch->name); + pr_err("Unexpected intent request\n"); return false; } @@ -554,6 +574,11 @@ static void spcom_notify_rx_abort(void *handle, const void *priv, { struct spcom_channel *ch = (struct spcom_channel *) priv; + if (!ch) { + pr_err("invalid NULL channel param\n"); + return; + } + pr_debug("ch [%s] pending rx aborted.\n", ch->name); if (spcom_is_channel_open(ch) && (!ch->rx_abort)) { @@ -574,6 +599,11 @@ static void spcom_notify_tx_abort(void *handle, const void *priv, { struct spcom_channel *ch = (struct spcom_channel *) priv; + if (!ch) { + pr_err("invalid NULL channel param\n"); + return; + } + pr_debug("ch [%s] pending tx aborted.\n", ch->name); if (spcom_is_channel_connected(ch) && (!ch->tx_abort)) { @@ -792,6 +822,8 @@ static int spcom_close(struct spcom_channel *ch) * @size: buffer size * * ACK is expected within a very short time (few msec). + * + * Return: 0 on successful operation, negative value otherwise. */ static int spcom_tx(struct spcom_channel *ch, void *buf, @@ -856,13 +888,15 @@ exit_err: * @size: buffer size * * ACK is expected within a very short time (few msec). + * + * Return: size in bytes on success, negative value on failure. */ static int spcom_rx(struct spcom_channel *ch, void *buf, uint32_t size, uint32_t timeout_msec) { - int ret; + int ret = -1; unsigned long jiffies = msecs_to_jiffies(timeout_msec); long timeleft = 1; @@ -870,7 +904,7 @@ static int spcom_rx(struct spcom_channel *ch, /* check for already pending data */ if (ch->actual_rx_size) { - pr_debug("already pending data size [%d].\n", + pr_debug("already pending data size [%zu]\n", ch->actual_rx_size); goto copy_buf; } @@ -893,7 +927,7 @@ static int spcom_rx(struct spcom_channel *ch, mutex_unlock(&ch->lock); return -ERESTART; /* probably SSR */ } else if (ch->actual_rx_size) { - pr_debug("actual_rx_size is [%d].\n", ch->actual_rx_size); + pr_debug("actual_rx_size is [%zu]\n", ch->actual_rx_size); } else { pr_err("actual_rx_size is zero.\n"); goto exit_err; @@ -922,7 +956,7 @@ copy_buf: pr_err("glink_queue_rx_intent() failed, ret [%d]", ret); goto exit_err; } else { - pr_debug("queue rx_buf, size [%d].\n", ch->rx_buf_size); + pr_debug("queue rx_buf, size [%zu]\n", ch->rx_buf_size); } mutex_unlock(&ch->lock); @@ -942,6 +976,8 @@ exit_err: * Server needs the size of the next request to allocate a request buffer. * Initially used intent-request, however this complicated the remote side, * so both sides are not using glink_tx() with INTENT_REQ anymore. + * + * Return: size in bytes on success, negative value on failure. */ static int spcom_get_next_request_size(struct spcom_channel *ch) { @@ -953,7 +989,7 @@ static int spcom_get_next_request_size(struct spcom_channel *ch) /* check if already got it via callback */ if (ch->actual_rx_size) { - pr_debug("next-req-size already ready ch [%s] size [%d].\n", + pr_debug("next-req-size already ready ch [%s] size [%zu]\n", ch->name, ch->actual_rx_size); goto exit_ready; } @@ -968,7 +1004,7 @@ static int spcom_get_next_request_size(struct spcom_channel *ch) } if (ch->actual_rx_size <= 0) { - pr_err("invalid rx size [%d] ch [%s].\n", + pr_err("invalid rx size [%zu] ch [%s]\n", ch->actual_rx_size, ch->name); goto exit_error; } @@ -1101,16 +1137,21 @@ int spcom_unregister_client(struct spcom_client *client) } if (!client) { - pr_err("Invalid parameter.\n"); + pr_err("Invalid client parameter.\n"); return -EINVAL; } - ch = client->ch; - kfree(client); + ch = client->ch; + if (!ch) { + pr_err("Invalid channel.\n"); + return -EINVAL; + } spcom_close(ch); + kfree(client); + return 0; } EXPORT_SYMBOL(spcom_unregister_client); @@ -1127,6 +1168,8 @@ EXPORT_SYMBOL(spcom_unregister_client); * @timeout_msec: timeout waiting for response. * * The timeout depends on the specific request handling time at the remote side. + * + * Return: number of rx bytes on success, negative value on failure. */ int spcom_client_send_message_sync(struct spcom_client *client, void *req_ptr, @@ -1149,6 +1192,10 @@ int spcom_client_send_message_sync(struct spcom_client *client, } ch = client->ch; + if (!ch) { + pr_err("Invalid channel.\n"); + return -EINVAL; + } /* Check if remote side connect */ if (!spcom_is_channel_connected(ch)) { @@ -1183,6 +1230,7 @@ EXPORT_SYMBOL(spcom_client_send_message_sync); bool spcom_client_is_server_connected(struct spcom_client *client) { bool connected; + struct spcom_channel *ch; if (!spcom_is_ready()) { pr_err("spcom is not ready.\n"); @@ -1194,7 +1242,13 @@ bool spcom_client_is_server_connected(struct spcom_client *client) return false; } - connected = spcom_is_channel_connected(client->ch); + ch = client->ch; + if (!ch) { + pr_err("Invalid channel.\n"); + return -EINVAL; + } + + connected = spcom_is_channel_connected(ch); return connected; } @@ -1267,16 +1321,20 @@ int spcom_unregister_service(struct spcom_server *server) } if (!server) { - pr_err("Invalid parameter.\n"); + pr_err("Invalid server parameter.\n"); return -EINVAL; } ch = server->ch; - - kfree(server); + if (!ch) { + pr_err("Invalid channel parameter.\n"); + return -EINVAL; + } spcom_close(ch); + kfree(server); + return 0; } EXPORT_SYMBOL(spcom_unregister_service); @@ -1286,7 +1344,7 @@ EXPORT_SYMBOL(spcom_unregister_service); * * @server: server handle * - * Return: request size in bytes. + * Return: size in bytes on success, negative value on failure. */ int spcom_server_get_next_request_size(struct spcom_server *server) { @@ -1299,6 +1357,10 @@ int spcom_server_get_next_request_size(struct spcom_server *server) } ch = server->ch; + if (!ch) { + pr_err("Invalid channel.\n"); + return -EINVAL; + } /* Check if remote side connect */ if (!spcom_is_channel_connected(ch)) { @@ -1321,7 +1383,7 @@ EXPORT_SYMBOL(spcom_server_get_next_request_size); * @req_ptr: request buffer pointer * @req_size: max request size * - * Return: request size in bytes. + * Return: size in bytes on success, negative value on failure. */ int spcom_server_wait_for_request(struct spcom_server *server, void *req_ptr, @@ -1341,6 +1403,10 @@ int spcom_server_wait_for_request(struct spcom_server *server, } ch = server->ch; + if (!ch) { + pr_err("Invalid channel.\n"); + return -EINVAL; + } /* Check if remote side connect */ if (!spcom_is_channel_connected(ch)) { @@ -1379,6 +1445,10 @@ int spcom_server_send_response(struct spcom_server *server, } ch = server->ch; + if (!ch) { + pr_err("Invalid channel.\n"); + return -EINVAL; + } /* Check if remote side connect */ if (!spcom_is_channel_connected(ch)) { @@ -1845,18 +1915,6 @@ static int spcom_handle_unlock_ion_buf_command(struct spcom_channel *ch, } /** - * spcom_handle_fake_ssr_command() - Handle fake ssr command from user space. - */ -static int spcom_handle_fake_ssr_command(struct spcom_channel *ch, int arg) -{ - pr_debug("Start Fake glink SSR subsystem [%s].\n", spcom_edge); - glink_ssr(spcom_edge); - pr_debug("Fake glink SSR subsystem [%s] done.\n", spcom_edge); - - return 0; -} - -/** * spcom_handle_write() - Handle user space write commands. * * @buf: command buffer. @@ -1900,9 +1958,6 @@ static int spcom_handle_write(struct spcom_channel *ch, case SPCOM_CMD_UNLOCK_ION_BUF: ret = spcom_handle_unlock_ion_buf_command(ch, buf, buf_size); break; - case SPCOM_CMD_FSSR: - ret = spcom_handle_fake_ssr_command(ch, cmd->arg); - break; case SPCOM_CMD_CREATE_CHANNEL: ret = spcom_handle_create_channel_command(buf, buf_size); break; @@ -1921,7 +1976,7 @@ static int spcom_handle_write(struct spcom_channel *ch, * @buf: command buffer. * @size: command buffer size. * - * Return: size in bytes. + * Return: size in bytes on success, negative value on failure. */ static int spcom_handle_get_req_size(struct spcom_channel *ch, void *buf, @@ -1949,7 +2004,7 @@ static int spcom_handle_get_req_size(struct spcom_channel *ch, * @buf: command buffer. * @size: command buffer size. * - * Return: size in bytes. + * Return: size in bytes on success, negative value on failure. */ static int spcom_handle_read_req_resp(struct spcom_channel *ch, void *buf, @@ -2033,7 +2088,7 @@ exit_err: * A special size SPCOM_GET_NEXT_REQUEST_SIZE, which is bigger than the max * response/request tells the kernel that user space only need the size. * - * Return: size in bytes. + * Return: size in bytes on success, negative value on failure. */ static int spcom_handle_read(struct spcom_channel *ch, void *buf, @@ -2115,8 +2170,6 @@ static int spcom_device_open(struct inode *inode, struct file *filp) return -ENODEV; } - filp->private_data = ch; - ret = spcom_open(ch, OPEN_CHANNEL_TIMEOUT_MSEC); if (ret == -ETIMEDOUT) { pr_err("Connection timeout channel [%s].\n", name); @@ -2125,6 +2178,8 @@ static int spcom_device_open(struct inode *inode, struct file *filp) return ret; } + filp->private_data = ch; + pr_debug("finished.\n"); return 0; @@ -2209,8 +2264,8 @@ static ssize_t spcom_device_write(struct file *filp, ch = filp->private_data; if (!ch) { - pr_debug("invalid ch pointer.\n"); - /* Allow some special commands via /dev/spcom and /dev/sp_ssr */ + pr_err("invalid ch pointer, command not allowed.\n"); + return -EINVAL; } else { /* Check if remote side connect */ if (!spcom_is_channel_connected(ch)) { diff --git a/drivers/soc/qcom/wcd-dsp-glink.c b/drivers/soc/qcom/wcd-dsp-glink.c index f601e6646852..3c9d8efd3956 100644 --- a/drivers/soc/qcom/wcd-dsp-glink.c +++ b/drivers/soc/qcom/wcd-dsp-glink.c @@ -21,6 +21,7 @@ #include <linux/list.h> #include <linux/cdev.h> #include <linux/platform_device.h> +#include <linux/vmalloc.h> #include <soc/qcom/glink.h> #include "sound/wcd-dsp-glink.h" @@ -29,6 +30,10 @@ #define WDSP_MAX_READ_SIZE (4 * 1024) #define WDSP_MAX_NO_OF_INTENTS (20) #define WDSP_MAX_NO_OF_CHANNELS (10) +#define WDSP_WRITE_PKT_SIZE (sizeof(struct wdsp_write_pkt)) +#define WDSP_REG_PKT_SIZE (sizeof(struct wdsp_reg_pkt)) +#define WDSP_CMD_PKT_SIZE (sizeof(struct wdsp_cmd_pkt)) +#define WDSP_CH_CFG_SIZE (sizeof(struct wdsp_glink_ch_cfg)) #define MINOR_NUMBER_COUNT 1 #define WDSP_EDGE "wdsp" @@ -183,7 +188,7 @@ static void wdsp_glink_notify_tx_done(void *handle, const void *priv, return; } /* Free tx pkt */ - kfree(pkt_priv); + vfree(pkt_priv); } /* @@ -201,7 +206,7 @@ static void wdsp_glink_notify_tx_abort(void *handle, const void *priv, return; } /* Free tx pkt */ - kfree(pkt_priv); + vfree(pkt_priv); } /* @@ -519,9 +524,10 @@ static void wdsp_glink_link_state_cb(struct glink_link_state_cb_info *cb_info, * and register with glink * wpriv: Wdsp_glink private structure. * pkt: Glink registration packet contains glink channel information. + * pkt_size: Size of the pkt. */ static int wdsp_glink_ch_info_init(struct wdsp_glink_priv *wpriv, - struct wdsp_reg_pkt *pkt) + struct wdsp_reg_pkt *pkt, size_t pkt_size) { int ret = 0, i, j; struct glink_link_info link_info; @@ -530,6 +536,7 @@ static int wdsp_glink_ch_info_init(struct wdsp_glink_priv *wpriv, u8 no_of_channels; u8 *payload; u32 ch_size, ch_cfg_size; + size_t size = WDSP_WRITE_PKT_SIZE + WDSP_REG_PKT_SIZE; mutex_lock(&wpriv->glink_mutex); if (wpriv->ch) { @@ -542,9 +549,10 @@ static int wdsp_glink_ch_info_init(struct wdsp_glink_priv *wpriv, no_of_channels = pkt->no_of_channels; if (no_of_channels > WDSP_MAX_NO_OF_CHANNELS) { - dev_info(wpriv->dev, "%s: no_of_channels = %d are limited to %d\n", - __func__, no_of_channels, WDSP_MAX_NO_OF_CHANNELS); - no_of_channels = WDSP_MAX_NO_OF_CHANNELS; + dev_err(wpriv->dev, "%s: no_of_channels: %d but max allowed are %d\n", + __func__, no_of_channels, WDSP_MAX_NO_OF_CHANNELS); + ret = -EINVAL; + goto done; } ch = kcalloc(no_of_channels, sizeof(struct wdsp_glink_ch *), GFP_KERNEL); @@ -558,20 +566,34 @@ static int wdsp_glink_ch_info_init(struct wdsp_glink_priv *wpriv, for (i = 0; i < no_of_channels; i++) { ch_cfg = (struct wdsp_glink_ch_cfg *)payload; + size += WDSP_CH_CFG_SIZE; + if (size > pkt_size) { + dev_err(wpriv->dev, "%s: Invalid size = %zd, pkt_size = %zd\n", + __func__, size, pkt_size); + ret = -EINVAL; + goto err_ch_mem; + } if (ch_cfg->no_of_intents > WDSP_MAX_NO_OF_INTENTS) { dev_err(wpriv->dev, "%s: Invalid no_of_intents = %d\n", __func__, ch_cfg->no_of_intents); ret = -EINVAL; goto err_ch_mem; } + size += (sizeof(u32) * ch_cfg->no_of_intents); + if (size > pkt_size) { + dev_err(wpriv->dev, "%s: Invalid size = %zd, pkt_size = %zd\n", + __func__, size, pkt_size); + ret = -EINVAL; + goto err_ch_mem; + } ch_cfg_size = sizeof(struct wdsp_glink_ch_cfg) + (sizeof(u32) * ch_cfg->no_of_intents); ch_size = sizeof(struct wdsp_glink_ch) + (sizeof(u32) * ch_cfg->no_of_intents); - dev_dbg(wpriv->dev, "%s: channels = %d, ch_cfg_size %d", - __func__, no_of_channels, ch_cfg_size); + dev_dbg(wpriv->dev, "%s: channels: %d ch_cfg_size: %d, size: %zd, pkt_size: %zd", + __func__, no_of_channels, ch_cfg_size, size, pkt_size); ch[i] = kzalloc(ch_size, GFP_KERNEL); if (!ch[i]) { @@ -658,7 +680,7 @@ static void wdsp_glink_tx_buf_work(struct work_struct *work) * there won't be any tx_done notification to * free the buffer. */ - kfree(tx_buf); + vfree(tx_buf); } } else { mutex_unlock(&tx_buf->ch->mutex); @@ -668,7 +690,7 @@ static void wdsp_glink_tx_buf_work(struct work_struct *work) * Free tx_buf here as there won't be any tx_done * notification in this case also. */ - kfree(tx_buf); + vfree(tx_buf); } } @@ -761,6 +783,7 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, struct wdsp_cmd_pkt *cpkt; struct wdsp_glink_tx_buf *tx_buf; struct wdsp_glink_priv *wpriv; + size_t pkt_max_size; wpriv = (struct wdsp_glink_priv *)file->private_data; if (!wpriv) { @@ -769,7 +792,7 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, goto done; } - if ((count < sizeof(struct wdsp_write_pkt)) || + if ((count < WDSP_WRITE_PKT_SIZE) || (count > WDSP_MAX_WRITE_SIZE)) { dev_err(wpriv->dev, "%s: Invalid count = %zd\n", __func__, count); @@ -779,8 +802,8 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, dev_dbg(wpriv->dev, "%s: count = %zd\n", __func__, count); - tx_buf_size = WDSP_MAX_WRITE_SIZE + sizeof(struct wdsp_glink_tx_buf); - tx_buf = kzalloc(tx_buf_size, GFP_KERNEL); + tx_buf_size = count + sizeof(struct wdsp_glink_tx_buf); + tx_buf = vzalloc(tx_buf_size); if (!tx_buf) { ret = -ENOMEM; goto done; @@ -797,19 +820,20 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, wpkt = (struct wdsp_write_pkt *)tx_buf->buf; switch (wpkt->pkt_type) { case WDSP_REG_PKT: - if (count <= (sizeof(struct wdsp_write_pkt) + - sizeof(struct wdsp_reg_pkt))) { + if (count < (WDSP_WRITE_PKT_SIZE + WDSP_REG_PKT_SIZE + + WDSP_CH_CFG_SIZE)) { dev_err(wpriv->dev, "%s: Invalid reg pkt size = %zd\n", __func__, count); ret = -EINVAL; goto free_buf; } ret = wdsp_glink_ch_info_init(wpriv, - (struct wdsp_reg_pkt *)wpkt->payload); + (struct wdsp_reg_pkt *)wpkt->payload, + count); if (IS_ERR_VALUE(ret)) dev_err(wpriv->dev, "%s: glink register failed, ret = %d\n", __func__, ret); - kfree(tx_buf); + vfree(tx_buf); break; case WDSP_READY_PKT: ret = wait_event_timeout(wpriv->link_state_wait, @@ -823,11 +847,10 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, goto free_buf; } ret = 0; - kfree(tx_buf); + vfree(tx_buf); break; case WDSP_CMD_PKT: - if (count <= (sizeof(struct wdsp_write_pkt) + - sizeof(struct wdsp_cmd_pkt))) { + if (count <= (WDSP_WRITE_PKT_SIZE + WDSP_CMD_PKT_SIZE)) { dev_err(wpriv->dev, "%s: Invalid cmd pkt size = %zd\n", __func__, count); ret = -EINVAL; @@ -843,10 +866,18 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, goto free_buf; } mutex_unlock(&wpriv->glink_mutex); - cpkt = (struct wdsp_cmd_pkt *)wpkt->payload; - dev_dbg(wpriv->dev, "%s: requested ch_name: %s\n", __func__, - cpkt->ch_name); + pkt_max_size = sizeof(struct wdsp_write_pkt) + + sizeof(struct wdsp_cmd_pkt) + + cpkt->payload_size; + if (count < pkt_max_size) { + dev_err(wpriv->dev, "%s: Invalid cmd pkt count = %zd, pkt_size = %zd\n", + __func__, count, pkt_max_size); + ret = -EINVAL; + goto free_buf; + } + dev_dbg(wpriv->dev, "%s: requested ch_name: %s, pkt_size: %zd\n", + __func__, cpkt->ch_name, pkt_max_size); for (i = 0; i < wpriv->no_of_channels; i++) { if (wpriv->ch && wpriv->ch[i] && (!strcmp(cpkt->ch_name, @@ -881,13 +912,13 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, default: dev_err(wpriv->dev, "%s: Invalid packet type\n", __func__); ret = -EINVAL; - kfree(tx_buf); + vfree(tx_buf); break; } goto done; free_buf: - kfree(tx_buf); + vfree(tx_buf); done: return ret; |
