summaryrefslogtreecommitdiff
path: root/drivers/soc/qcom
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/soc/qcom')
-rw-r--r--drivers/soc/qcom/icnss_utils.c36
-rw-r--r--drivers/soc/qcom/memshare/msm_memshare.c20
-rw-r--r--drivers/soc/qcom/memshare/msm_memshare.h3
-rw-r--r--drivers/soc/qcom/msm_bus/msm_bus_rules.c9
-rw-r--r--drivers/soc/qcom/qbt1000.c21
-rw-r--r--drivers/soc/qcom/qpnp-haptic.c956
-rw-r--r--drivers/soc/qcom/rpm_master_stat.c10
-rw-r--r--drivers/soc/qcom/service-locator.c9
-rw-r--r--drivers/soc/qcom/service-notifier.c21
-rw-r--r--drivers/soc/qcom/smcinvoke.c11
-rw-r--r--drivers/soc/qcom/spcom.c149
-rw-r--r--drivers/soc/qcom/wcd-dsp-glink.c81
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;