Merge "ASoC: wcd9320: use hwdep nodes to get calibration"
diff --git a/sound/soc/codecs/wcd9320.c b/sound/soc/codecs/wcd9320.c
index ce42a94..1dfd72b 100644
--- a/sound/soc/codecs/wcd9320.c
+++ b/sound/soc/codecs/wcd9320.c
@@ -19,7 +19,6 @@
#include <linux/ratelimit.h>
#include <linux/debugfs.h>
#include <linux/wait.h>
-#include <linux/bitops.h>
#include <linux/mfd/wcd9xxx/core.h>
#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
#include <linux/mfd/wcd9xxx/wcd9320_registers.h>
@@ -41,6 +40,8 @@
#include "wcd9320.h"
#include "wcd9xxx-resmgr.h"
#include "wcd9xxx-common.h"
+#include "wcdcal-hwdep.h"
+
#define TAIKO_MAD_SLIMBUS_TX_PORT 12
#define TAIKO_MAD_AUDIO_FIRMWARE_PATH "wcd9320/wcd9320_mad_audio.bin"
@@ -453,6 +454,8 @@
*/
struct list_head reg_save_restore;
struct pm_qos_request pm_qos_req;
+ /* cal info for codec */
+ struct fw_info *fw_data;
};
static const u32 comp_shift[] = {
@@ -2680,8 +2683,11 @@
int ret;
const struct firmware *fw;
struct mad_audio_cal *mad_cal;
+ struct firmware_cal *hwdep_cal = NULL;
+ const void *data;
const char *filename = TAIKO_MAD_AUDIO_FIRMWARE_PATH;
struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
+ size_t cal_size;
pr_debug("%s: enter\n", __func__);
/* wakeup for codec calibration access */
@@ -2690,24 +2696,46 @@
PM_QOS_DEFAULT_VALUE);
pm_qos_update_request(&taiko->pm_qos_req,
msm_cpuidle_get_deep_idle_latency());
- ret = request_firmware(&fw, filename, codec->dev);
- if (ret != 0) {
- pr_err("Failed to acquire MAD firwmare data %s: %d\n", filename,
- ret);
+ if (!taiko->fw_data) {
+ dev_err(codec->dev, "%s: invalid cal data\n",
+ __func__);
return -ENODEV;
}
-
- if (fw->size < sizeof(struct mad_audio_cal)) {
- pr_err("%s: incorrect firmware size %u\n", __func__, fw->size);
- release_firmware(fw);
- return -ENOMEM;
+ hwdep_cal = wcdcal_get_fw_cal(taiko->fw_data, WCD9XXX_MAD_CAL);
+ if (hwdep_cal) {
+ data = hwdep_cal->data;
+ cal_size = hwdep_cal->size;
+ dev_dbg(codec->dev, "%s: using hwdep calibration\n",
+ __func__);
+ } else {
+ ret = request_firmware(&fw, filename, codec->dev);
+ if (ret != 0) {
+ pr_err("Failed to acquire MAD firwmare data %s: %d\n",
+ filename, ret);
+ return -ENODEV;
+ }
+ if (!fw) {
+ dev_err(codec->dev, "failed to get mad fw");
+ return -ENODEV;
+ }
+ data = fw->data;
+ cal_size = fw->size;
+ dev_dbg(codec->dev, "%s: using request_firmware calibration\n",
+ __func__);
+ }
+ if (cal_size < sizeof(struct mad_audio_cal)) {
+ pr_err("%s: incorrect hwdep cal size %zu\n",
+ __func__, cal_size);
+ ret = -ENOMEM;
+ goto err;
}
- mad_cal = (struct mad_audio_cal *)(fw->data);
+ mad_cal = (struct mad_audio_cal *)(data);
if (!mad_cal) {
- pr_err("%s: Invalid calibration data\n", __func__);
- release_firmware(fw);
- return -EINVAL;
+ dev_err(codec->dev, "%s: Invalid calibration data\n",
+ __func__);
+ ret = -EINVAL;
+ goto err;
}
snd_soc_write(codec, TAIKO_A_CDC_MAD_MAIN_CTL_2,
@@ -2757,11 +2785,13 @@
snd_soc_write(codec, TAIKO_A_CDC_MAD_ULTR_CTL_6,
mad_cal->ultrasound_info.rms_threshold_msb);
- release_firmware(fw);
pr_debug("%s: leave ret %d\n", __func__, ret);
pm_qos_update_request(&taiko->pm_qos_req,
PM_QOS_DEFAULT_VALUE);
pm_qos_remove_request(&taiko->pm_qos_req);
+err:
+ if (!hwdep_cal)
+ release_firmware(fw);
return ret;
}
@@ -3318,15 +3348,18 @@
const char *filename;
const struct firmware *fw;
int i;
- int ret;
+ int ret =0;
int num_anc_slots;
struct wcd9xxx_anc_header *anc_head;
struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
+ struct firmware_cal *hwdep_cal = NULL;
u32 anc_writes_size = 0;
int anc_size_remaining;
u32 *anc_ptr;
u16 reg;
u8 mask, val, old_val;
+ size_t cal_size;
+ const void *data;
if (taiko->anc_func == 0)
@@ -3335,38 +3368,53 @@
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
filename = "wcd9320/wcd9320_anc.bin";
+ hwdep_cal = wcdcal_get_fw_cal(taiko->fw_data, WCD9XXX_ANC_CAL);
+ if (hwdep_cal) {
+ data = hwdep_cal->data;
+ cal_size = hwdep_cal->size;
+ dev_dbg(codec->dev, "%s: using hwdep calibration\n",
+ __func__);
+ } else {
+ ret = request_firmware(&fw, filename, codec->dev);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to acquire ANC data: %d\n",
+ ret);
+ return -ENODEV;
+ }
+ if (!fw) {
+ dev_err(codec->dev, "failed to get anc fw");
+ return -ENODEV;
+ }
+ data = fw->data;
+ cal_size = fw->size;
+ dev_dbg(codec->dev, "%s: using request_firmware calibration\n",
+ __func__);
- ret = request_firmware(&fw, filename, codec->dev);
- if (ret != 0) {
- dev_err(codec->dev, "Failed to acquire ANC data: %d\n",
- ret);
- return -ENODEV;
}
- if (fw->size < sizeof(struct wcd9xxx_anc_header)) {
+ if (cal_size < sizeof(struct wcd9xxx_anc_header)) {
dev_err(codec->dev, "Not enough data\n");
- release_firmware(fw);
- return -ENOMEM;
+ goto err;
}
/* First number is the number of register writes */
- anc_head = (struct wcd9xxx_anc_header *)(fw->data);
- anc_ptr = (u32 *)((u32)fw->data +
+ anc_head = (struct wcd9xxx_anc_header *)(data);
+ anc_ptr = (u32 *)(data +
sizeof(struct wcd9xxx_anc_header));
- anc_size_remaining = fw->size -
+ anc_size_remaining = cal_size -
sizeof(struct wcd9xxx_anc_header);
num_anc_slots = anc_head->num_anc_slots;
if (taiko->anc_slot >= num_anc_slots) {
dev_err(codec->dev, "Invalid ANC slot selected\n");
- release_firmware(fw);
- return -EINVAL;
+ ret = -EINVAL;
+ goto err;
}
for (i = 0; i < num_anc_slots; i++) {
if (anc_size_remaining < TAIKO_PACKED_REG_SIZE) {
dev_err(codec->dev, "Invalid register format\n");
- release_firmware(fw);
- return -EINVAL;
+ ret = -EINVAL;
+ goto err;
}
anc_writes_size = (u32)(*anc_ptr);
anc_size_remaining -= sizeof(u32);
@@ -3375,8 +3423,8 @@
if (anc_writes_size * TAIKO_PACKED_REG_SIZE
> anc_size_remaining) {
dev_err(codec->dev, "Invalid register format\n");
- release_firmware(fw);
- return -ENOMEM;
+ ret = -EINVAL;
+ goto err;
}
if (taiko->anc_slot == i)
@@ -3388,8 +3436,8 @@
}
if (i == num_anc_slots) {
dev_err(codec->dev, "Selected ANC slot not present\n");
- release_firmware(fw);
- return -ENOMEM;
+ ret = -EINVAL;
+ goto err;
}
for (i = 0; i < anc_writes_size; i++) {
TAIKO_CODEC_UNPACK_ENTRY(anc_ptr[i], reg,
@@ -3399,6 +3447,8 @@
(val & mask));
}
release_firmware(fw);
+ if (!hwdep_cal)
+ release_firmware(fw);
break;
case SND_SOC_DAPM_PRE_PMD:
msleep(40);
@@ -3411,6 +3461,11 @@
break;
}
return 0;
+err:
+ if (!hwdep_cal)
+ release_firmware(fw);
+ return ret;
+
}
static int taiko_hph_pa_event(struct snd_soc_dapm_widget *w,
@@ -6586,6 +6641,27 @@
wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_SLIMBUS, taiko);
}
+static
+struct firmware_cal *taiko_get_hwdep_fw_cal(struct snd_soc_codec *codec,
+ enum wcd_cal_type type)
+{
+ struct taiko_priv *taiko;
+ struct firmware_cal *hwdep_cal;
+
+ if (!codec) {
+ pr_err("%s: NULL codec pointer\n", __func__);
+ return NULL;
+ }
+ taiko = snd_soc_codec_get_drvdata(codec);
+ hwdep_cal = wcdcal_get_fw_cal(taiko->fw_data, type);
+ if (!hwdep_cal) {
+ dev_err(codec->dev, "%s: cal not sent by %d\n",
+ __func__, type);
+ return NULL;
+ }
+
+ return hwdep_cal;
+}
int taiko_hs_detect(struct snd_soc_codec *codec,
struct wcd9xxx_mbhc_config *mbhc_cfg)
@@ -6840,6 +6916,7 @@
.get_cdc_type = taiko_get_cdc_type,
.setup_zdet = taiko_setup_zdet,
.compute_impedance = taiko_compute_impedance,
+ .get_hwdep_fw_cal = taiko_get_hwdep_fw_cal,
};
static const struct wcd9xxx_mbhc_intr cdc_intr_ids = {
@@ -7093,7 +7170,7 @@
WCD9XXX_CDC_TYPE_TAIKO);
if (ret) {
pr_err("%s: wcd9xxx init failed %d\n", __func__, ret);
- goto err_init;
+ goto err_nomem_slimch;
}
taiko->clsh_d.buck_mv = taiko_codec_get_buck_mv(codec);
@@ -7105,7 +7182,20 @@
rco_clk_rate = TAIKO_MCLK_CLK_12P288MHZ;
else
rco_clk_rate = TAIKO_MCLK_CLK_9P6MHZ;
-
+ taiko->fw_data = kzalloc(sizeof(*(taiko->fw_data)), GFP_KERNEL);
+ if (!taiko->fw_data) {
+ dev_err(codec->dev, "Failed to allocate fw_data\n");
+ goto err_nomem_slimch;
+ }
+ set_bit(WCD9XXX_ANC_CAL, taiko->fw_data->cal_bit);
+ set_bit(WCD9XXX_MAD_CAL, taiko->fw_data->cal_bit);
+ set_bit(WCD9XXX_MBHC_CAL, taiko->fw_data->cal_bit);
+ ret = wcd_cal_create_hwdep(taiko->fw_data,
+ WCD9XXX_CODEC_HWDEP_NODE, codec);
+ if (ret < 0) {
+ dev_err(codec->dev, "%s hwdep failed %d\n", __func__, ret);
+ goto err_hwdep;
+ }
/* init and start mbhc */
ret = wcd9xxx_mbhc_init(&taiko->mbhc, &taiko->resmgr, codec,
taiko_enable_mbhc_micbias,
@@ -7113,7 +7203,7 @@
rco_clk_rate, true);
if (ret) {
pr_err("%s: mbhc init failed %d\n", __func__, ret);
- goto err_init;
+ goto err_hwdep;
}
taiko->codec = codec;
@@ -7137,7 +7227,7 @@
ret = taiko_handle_pdata(taiko);
if (IS_ERR_VALUE(ret)) {
pr_err("%s: bad pdata\n", __func__);
- goto err_pdata;
+ goto err_hwdep;
}
taiko->spkdrv_reg = taiko_codec_find_regulator(codec,
@@ -7155,7 +7245,7 @@
if (!ptr) {
pr_err("%s: no mem for slim chan ctl data\n", __func__);
ret = -ENOMEM;
- goto err_nomem_slimch;
+ goto err_hwdep;
}
if (taiko->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
@@ -7233,11 +7323,11 @@
err_irq:
taiko_cleanup_irqs(taiko);
-err_pdata:
- kfree(ptr);
+ kfree(ptr);
+err_hwdep:
+ kfree(taiko->fw_data);
err_nomem_slimch:
kfree(taiko);
-err_init:
return ret;
}
static int taiko_codec_remove(struct snd_soc_codec *codec)
@@ -7261,6 +7351,7 @@
taiko->spkdrv_reg = NULL;
+ kfree(taiko->fw_data);
kfree(taiko);
return 0;
}
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.c b/sound/soc/codecs/wcd9xxx-mbhc.c
index 09721e1..1475df2 100644
--- a/sound/soc/codecs/wcd9xxx-mbhc.c
+++ b/sound/soc/codecs/wcd9xxx-mbhc.c
@@ -35,6 +35,7 @@
#include <linux/kernel.h>
#include <linux/gpio.h>
#include <linux/input.h>
+#include "wcdcal-hwdep.h"
#include "wcd9320.h"
#include "wcd9306.h"
#include "wcd9xxx-mbhc.h"
@@ -74,7 +75,7 @@
#define OCP_ATTEMPT 1
#define FW_READ_ATTEMPTS 15
-#define FW_READ_TIMEOUT 2000000
+#define FW_READ_TIMEOUT 4000000
#define BUTTON_POLLING_SUPPORTED true
@@ -2806,35 +2807,39 @@
wcd9xxx_unlock_sleep(core_res);
}
-static bool wcd9xxx_mbhc_fw_validate(const struct firmware *fw)
+static bool wcd9xxx_mbhc_fw_validate(const void *data, size_t size)
{
u32 cfg_offset;
struct wcd9xxx_mbhc_imped_detect_cfg *imped_cfg;
struct wcd9xxx_mbhc_btn_detect_cfg *btn_cfg;
+ struct firmware_cal fw;
- if (fw->size < WCD9XXX_MBHC_CAL_MIN_SIZE)
+ fw.data = (void *)data;
+ fw.size = size;
+
+ if (fw.size < WCD9XXX_MBHC_CAL_MIN_SIZE)
return false;
/*
* Previous check guarantees that there is enough fw data up
* to num_btn
*/
- btn_cfg = WCD9XXX_MBHC_CAL_BTN_DET_PTR(fw->data);
- cfg_offset = (u32) ((void *) btn_cfg - (void *) fw->data);
- if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_BTN_SZ(btn_cfg)))
+ btn_cfg = WCD9XXX_MBHC_CAL_BTN_DET_PTR(fw.data);
+ cfg_offset = (u32) ((void *) btn_cfg - (void *) fw.data);
+ if (fw.size < (cfg_offset + WCD9XXX_MBHC_CAL_BTN_SZ(btn_cfg)))
return false;
/*
* Previous check guarantees that there is enough fw data up
* to start of impedance detection configuration
*/
- imped_cfg = WCD9XXX_MBHC_CAL_IMPED_DET_PTR(fw->data);
- cfg_offset = (u32) ((void *) imped_cfg - (void *) fw->data);
+ imped_cfg = WCD9XXX_MBHC_CAL_IMPED_DET_PTR(fw.data);
+ cfg_offset = (u32) ((void *) imped_cfg - (void *) fw.data);
- if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_IMPED_MIN_SZ))
+ if (fw.size < (cfg_offset + WCD9XXX_MBHC_CAL_IMPED_MIN_SZ))
return false;
- if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_IMPED_SZ(imped_cfg)))
+ if (fw.size < (cfg_offset + WCD9XXX_MBHC_CAL_IMPED_SZ(imped_cfg)))
return false;
return true;
@@ -4171,7 +4176,9 @@
struct wcd9xxx_mbhc *mbhc;
struct snd_soc_codec *codec;
const struct firmware *fw;
+ struct firmware_cal *fw_data = NULL;
int ret = -1, retry = 0;
+ bool use_default_cal = false;
dwork = to_delayed_work(work);
mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_firmware_dwork);
@@ -4179,29 +4186,62 @@
while (retry < FW_READ_ATTEMPTS) {
retry++;
- pr_info("%s:Attempt %d to request MBHC firmware\n",
- __func__, retry);
- ret = request_firmware(&fw, "wcd9320/wcd9320_mbhc.bin",
- codec->dev);
-
- if (ret != 0) {
+ pr_debug("%s:Attempt %d to request MBHC firmware\n",
+ __func__, retry);
+ if (mbhc->mbhc_cb->get_hwdep_fw_cal)
+ fw_data = mbhc->mbhc_cb->get_hwdep_fw_cal(codec,
+ WCD9XXX_MBHC_CAL);
+ if (!fw_data)
+ ret = request_firmware(&fw, "wcd9320/wcd9320_mbhc.bin",
+ codec->dev);
+ /*
+ * if request_firmware and hwdep cal both fail then
+ * retry for few times before bailing out
+ */
+ if ((ret != 0) && !fw_data) {
usleep_range(FW_READ_TIMEOUT, FW_READ_TIMEOUT);
} else {
pr_info("%s: MBHC Firmware read succesful\n", __func__);
break;
}
}
-
- if (ret != 0) {
+ if (!fw_data)
+ pr_debug("%s: using request_firmware\n", __func__);
+ else
+ pr_debug("%s: using hwdep cal\n", __func__);
+ if (ret != 0 && !fw_data) {
pr_err("%s: Cannot load MBHC firmware use default cal\n",
- __func__);
- } else if (wcd9xxx_mbhc_fw_validate(fw) == false) {
- pr_err("%s: Invalid MBHC cal data size use default cal\n",
- __func__);
- release_firmware(fw);
- } else {
- mbhc->mbhc_cfg->calibration = (void *)fw->data;
- mbhc->mbhc_fw = fw;
+ __func__);
+ use_default_cal = true;
+ }
+ if (!use_default_cal) {
+ const void *data;
+ size_t size;
+
+ if (fw_data) {
+ data = fw_data->data;
+ size = fw_data->size;
+ } else {
+ data = fw->data;
+ size = fw->size;
+ }
+ if (wcd9xxx_mbhc_fw_validate(data, size) == false) {
+ pr_err("%s: Invalid MBHC cal data size use default cal\n",
+ __func__);
+ if (!fw_data)
+ release_firmware(fw);
+ } else {
+ if (fw_data) {
+ mbhc->mbhc_cfg->calibration =
+ (void *)fw_data->data;
+ mbhc->mbhc_cal = fw_data;
+ } else {
+ mbhc->mbhc_cfg->calibration =
+ (void *)fw->data;
+ mbhc->mbhc_fw = fw;
+ }
+ }
+
}
(void) wcd9xxx_init_and_calibrate(mbhc);
@@ -4391,15 +4431,16 @@
mbhc->mbhc_cb->enable_clock_gate(mbhc->codec, true);
if (!mbhc->mbhc_cfg->read_fw_bin ||
- (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_fw)) {
+ (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_fw) ||
+ (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_cal)) {
rc = wcd9xxx_init_and_calibrate(mbhc);
} else {
- if (!mbhc->mbhc_fw)
+ if (!mbhc->mbhc_fw || !mbhc->mbhc_cal)
schedule_delayed_work(&mbhc->mbhc_firmware_dwork,
usecs_to_jiffies(FW_READ_TIMEOUT));
else
- pr_debug("%s: Skipping to read mbhc fw, 0x%p\n",
- __func__, mbhc->mbhc_fw);
+ pr_debug("%s: Skipping to read mbhc fw, 0x%p 0x%p\n",
+ __func__, mbhc->mbhc_fw, mbhc->mbhc_cal);
}
pr_debug("%s: leave %d\n", __func__, rc);
@@ -4409,10 +4450,12 @@
void wcd9xxx_mbhc_stop(struct wcd9xxx_mbhc *mbhc)
{
- if (mbhc->mbhc_fw) {
+ if (mbhc->mbhc_fw || mbhc->mbhc_cal) {
cancel_delayed_work_sync(&mbhc->mbhc_firmware_dwork);
- release_firmware(mbhc->mbhc_fw);
+ if (!mbhc->mbhc_cal)
+ release_firmware(mbhc->mbhc_fw);
mbhc->mbhc_fw = NULL;
+ mbhc->mbhc_cal = NULL;
}
}
EXPORT_SYMBOL(wcd9xxx_mbhc_stop);
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.h b/sound/soc/codecs/wcd9xxx-mbhc.h
index 91edaca..7eba649 100644
--- a/sound/soc/codecs/wcd9xxx-mbhc.h
+++ b/sound/soc/codecs/wcd9xxx-mbhc.h
@@ -13,6 +13,7 @@
#define __WCD9XXX_MBHC_H__
#include "wcd9xxx-resmgr.h"
+#include "wcdcal-hwdep.h"
#define WCD9XXX_CFILT_FAST_MODE 0x00
#define WCD9XXX_CFILT_SLOW_MODE 0x40
@@ -285,6 +286,9 @@
int (*enable_mb_source) (struct snd_soc_codec *, bool, bool);
void (*setup_int_rbias) (struct snd_soc_codec *, bool);
void (*pull_mb_to_vddio) (struct snd_soc_codec *, bool);
+ struct firmware_cal * (*get_hwdep_fw_cal) (struct snd_soc_codec *,
+ enum wcd_cal_type);
+
};
struct wcd9xxx_mbhc {
@@ -312,6 +316,7 @@
const struct firmware *mbhc_fw;
struct delayed_work mbhc_insert_dwork;
+ struct firmware_cal *mbhc_cal;
u8 current_plug;
struct work_struct correct_plug_swch;