Merge "wcnss: send GPIO strength parameter to firmware"
diff --git a/arch/arm/mach-msm/msm_rtb.c b/arch/arm/mach-msm/msm_rtb.c
index 28b2195..6a71e36 100644
--- a/arch/arm/mach-msm/msm_rtb.c
+++ b/arch/arm/mach-msm/msm_rtb.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -23,7 +23,7 @@
#include <linux/string.h>
#include <linux/atomic.h>
#include <linux/of.h>
-#include <asm/io.h>
+#include <linux/io.h>
#include <asm-generic/sizes.h>
#include <mach/memory.h>
#include <mach/msm_rtb.h>
@@ -38,18 +38,20 @@
/* Write
* 1) 3 bytes sentinel
* 2) 1 bytes of log type
- * 3) 4 bytes of where the caller came from
+ * 3) 8 bytes of where the caller came from
* 4) 4 bytes index
- * 4) 4 bytes extra data from the caller
+ * 4) 8 bytes extra data from the caller
+ * 5) 8 bytes for timestamp
*
- * Total = 16 bytes.
+ * Total = 32 bytes.
*/
struct msm_rtb_layout {
unsigned char sentinel[3];
unsigned char log_type;
- void *caller;
- unsigned long idx;
- void *data;
+ uint32_t idx;
+ uint64_t caller;
+ uint64_t data;
+ uint64_t timestamp;
} __attribute__ ((__packed__));
@@ -70,7 +72,7 @@
static atomic_t msm_rtb_idx;
#endif
-struct msm_rtb_state msm_rtb = {
+static struct msm_rtb_state msm_rtb = {
.filter = 1 << LOGK_LOGBUF,
.enabled = 1,
};
@@ -109,24 +111,29 @@
start->log_type = (char)log_type;
}
-static void msm_rtb_write_caller(void *caller, struct msm_rtb_layout *start)
+static void msm_rtb_write_caller(uint64_t caller, struct msm_rtb_layout *start)
{
start->caller = caller;
}
-static void msm_rtb_write_idx(unsigned long idx,
+static void msm_rtb_write_idx(uint32_t idx,
struct msm_rtb_layout *start)
{
start->idx = idx;
}
-static void msm_rtb_write_data(void *data, struct msm_rtb_layout *start)
+static void msm_rtb_write_data(uint64_t data, struct msm_rtb_layout *start)
{
start->data = data;
}
-static void uncached_logk_pc_idx(enum logk_event_type log_type, void *caller,
- void *data, int idx)
+static void msm_rtb_write_timestamp(struct msm_rtb_layout *start)
+{
+ start->timestamp = sched_clock();
+}
+
+static void uncached_logk_pc_idx(enum logk_event_type log_type, uint64_t caller,
+ uint64_t data, int idx)
{
struct msm_rtb_layout *start;
@@ -137,6 +144,7 @@
msm_rtb_write_caller(caller, start);
msm_rtb_write_idx(idx, start);
msm_rtb_write_data(data, start);
+ msm_rtb_write_timestamp(start);
mb();
return;
@@ -145,13 +153,10 @@
static void uncached_logk_timestamp(int idx)
{
unsigned long long timestamp;
- void *timestamp_upper, *timestamp_lower;
timestamp = sched_clock();
- timestamp_lower = (void *)lower_32_bits(timestamp);
- timestamp_upper = (void *)upper_32_bits(timestamp);
-
- uncached_logk_pc_idx(LOGK_TIMESTAMP|LOGTYPE_NOPC, timestamp_lower,
- timestamp_upper, idx);
+ uncached_logk_pc_idx(LOGK_TIMESTAMP|LOGTYPE_NOPC,
+ (uint64_t)lower_32_bits(timestamp),
+ (uint64_t)upper_32_bits(timestamp), idx);
}
#if defined(CONFIG_MSM_RTB_SEPARATE_CPUS)
@@ -213,7 +218,8 @@
i = msm_rtb_get_idx();
- uncached_logk_pc_idx(log_type, caller, data, i);
+ uncached_logk_pc_idx(log_type, (uint64_t)((unsigned long) caller),
+ (uint64_t)((unsigned long) data), i);
return 1;
}
@@ -225,7 +231,7 @@
}
EXPORT_SYMBOL(uncached_logk);
-int msm_rtb_probe(struct platform_device *pdev)
+static int msm_rtb_probe(struct platform_device *pdev)
{
struct msm_rtb_platform_data *d = pdev->dev.platform_data;
#if defined(CONFIG_MSM_RTB_SEPARATE_CPUS)
@@ -297,7 +303,6 @@
{.compatible = RTB_COMPAT_STR},
{},
};
-EXPORT_COMPAT(RTB_COMPAT_STR);
static struct platform_driver msm_rtb_driver = {
.driver = {
diff --git a/drivers/gpu/msm/adreno_a3xx_snapshot.c b/drivers/gpu/msm/adreno_a3xx_snapshot.c
index 9f5765d..c8f0101 100644
--- a/drivers/gpu/msm/adreno_a3xx_snapshot.c
+++ b/drivers/gpu/msm/adreno_a3xx_snapshot.c
@@ -463,14 +463,23 @@
size = (adreno_is_a330(adreno_dev) ||
adreno_is_a305b(adreno_dev)) ? 0x2E : 0x14;
- snapshot = kgsl_snapshot_indexed_registers(device, snapshot,
- remain, REG_CP_STATE_DEBUG_INDEX,
- REG_CP_STATE_DEBUG_DATA, 0x0, size);
+ /* Skip indexed register dump for these chipsets 8974, 8x26, 8x10 */
+ if (adreno_is_a330(adreno_dev) ||
+ adreno_is_a330v2(adreno_dev) ||
+ adreno_is_a305b(adreno_dev) ||
+ adreno_is_a305c(adreno_dev) ) {
+ KGSL_DRV_ERR(device,
+ "Skipping indexed register dump\n");
+ } else {
+ snapshot = kgsl_snapshot_indexed_registers(device, snapshot,
+ remain, REG_CP_STATE_DEBUG_INDEX,
+ REG_CP_STATE_DEBUG_DATA, 0x0, size);
- /* CP_ME indexed registers */
- snapshot = kgsl_snapshot_indexed_registers(device, snapshot,
- remain, REG_CP_ME_CNTL, REG_CP_ME_STATUS,
- 64, 44);
+ /* CP_ME indexed registers */
+ snapshot = kgsl_snapshot_indexed_registers(device, snapshot,
+ remain, REG_CP_ME_CNTL, REG_CP_ME_STATUS,
+ 64, 44);
+ }
/* VPC memory */
snapshot = kgsl_snapshot_add_section(device,
@@ -482,10 +491,19 @@
KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain,
a3xx_snapshot_cp_meq, NULL);
- /* Shader working/shadow memory */
- snapshot = kgsl_snapshot_add_section(device,
+ /* Skip shader memory dump for these chipsets: 8974, 8x26, 8x10 */
+ if (adreno_is_a330(adreno_dev) ||
+ adreno_is_a330v2(adreno_dev) ||
+ adreno_is_a305b(adreno_dev) ||
+ adreno_is_a305c(adreno_dev) ) {
+ KGSL_DRV_ERR(device,
+ "Skipping shader memory dump\n");
+ } else {
+ /* Shader working/shadow memory */
+ snapshot = kgsl_snapshot_add_section(device,
KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain,
a3xx_snapshot_shader_memory, NULL);
+ }
/* CP PFP and PM4 */
diff --git a/drivers/gpu/msm/adreno_dispatch.c b/drivers/gpu/msm/adreno_dispatch.c
index bcab791..d79a169 100644
--- a/drivers/gpu/msm/adreno_dispatch.c
+++ b/drivers/gpu/msm/adreno_dispatch.c
@@ -221,13 +221,17 @@
struct adreno_context *drawctxt, struct kgsl_cmdbatch *cmdbatch)
{
unsigned int prev;
+ struct kgsl_device *device;
spin_lock(&drawctxt->lock);
if (kgsl_context_detached(&drawctxt->base) ||
drawctxt->state == ADRENO_CONTEXT_STATE_INVALID) {
spin_unlock(&drawctxt->lock);
+ device = cmdbatch->device;
/* get rid of this cmdbatch since the context is bad */
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
kgsl_cmdbatch_destroy(cmdbatch);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
return -EINVAL;
}
@@ -414,7 +418,10 @@
*/
if (cmdbatch->flags & KGSL_CONTEXT_SYNC) {
+ struct kgsl_device *device = cmdbatch->device;
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
kgsl_cmdbatch_destroy(cmdbatch);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
continue;
}
@@ -931,11 +938,8 @@
drawctxt->state == ADRENO_CONTEXT_STATE_INVALID) {
replay[i] = NULL;
- kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
kgsl_cancel_events_timestamp(device, cmd->context,
cmd->timestamp);
- kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
-
kgsl_cmdbatch_destroy(cmd);
}
}
@@ -1084,7 +1088,8 @@
kgsl_device_snapshot(device, 1);
}
- kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
+
/* Allocate memory to store the inflight commands */
replay = kzalloc(sizeof(*replay) * dispatcher->inflight, GFP_KERNEL);
@@ -1092,6 +1097,7 @@
if (replay == NULL) {
unsigned int ptr = dispatcher->head;
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
/* Recovery failed - mark everybody guilty */
mark_guilty_context(device, 0);
@@ -1111,6 +1117,7 @@
*/
count = 0;
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
goto replay;
}
@@ -1168,7 +1175,9 @@
cmdbatch->context->id, cmdbatch->timestamp);
mark_guilty_context(device, cmdbatch->context->id);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
adreno_drawctxt_invalidate(device, cmdbatch->context);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
}
/*
@@ -1277,7 +1286,9 @@
mark_guilty_context(device, cmdbatch->context->id);
/* Invalidate the context */
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
adreno_drawctxt_invalidate(device, cmdbatch->context);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
replay:
@@ -1296,8 +1307,10 @@
/* If adreno_reset() fails then what hope do we have for the future? */
BUG_ON(ret);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
/* Remove any pending command batches that have been invalidated */
remove_invalidated_cmdbatches(device, replay, count);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
/* Replay the pending command buffers */
for (i = 0; i < count; i++) {
@@ -1339,9 +1352,11 @@
/* Mark this context as guilty (failed recovery) */
mark_guilty_context(device, replay[i]->context->id);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
adreno_drawctxt_invalidate(device, replay[i]->context);
remove_invalidated_cmdbatches(device, &replay[i],
count - i);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
}
}
@@ -1449,8 +1464,10 @@
dispatcher->head = CMDQUEUE_NEXT(dispatcher->head,
ADRENO_DISPATCH_CMDQUEUE_SIZE);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
/* Destroy the retired command batch */
kgsl_cmdbatch_destroy(cmdbatch);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
/* Update the expire time for the next command batch */
@@ -1695,16 +1712,19 @@
void adreno_dispatcher_close(struct adreno_device *adreno_dev)
{
struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher;
+ struct kgsl_device *device = &adreno_dev->dev;
mutex_lock(&dispatcher->mutex);
del_timer_sync(&dispatcher->timer);
del_timer_sync(&dispatcher->fault_timer);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
while (dispatcher->head != dispatcher->tail) {
kgsl_cmdbatch_destroy(dispatcher->cmdqueue[dispatcher->head]);
dispatcher->head = (dispatcher->head + 1)
% ADRENO_DISPATCH_CMDQUEUE_SIZE;
}
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
mutex_unlock(&dispatcher->mutex);
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index 9adede8..a5b25a0 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -1807,16 +1807,13 @@
list_for_each_entry_safe(event, tmp, &cancel_synclist, node) {
if (event->type == KGSL_CMD_SYNCPOINT_TYPE_TIMESTAMP) {
- struct kgsl_device *device = cmdbatch->device;
/*
* Timestamp events are guaranteed to signal
* when canceled
*/
- kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
- kgsl_cancel_event(device, event->context,
+ kgsl_cancel_event(cmdbatch->device, event->context,
event->timestamp, kgsl_cmdbatch_sync_func,
event);
- kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
} else if (event->type == KGSL_CMD_SYNCPOINT_TYPE_FENCE) {
/* Put events that are successfully canceled */
if (kgsl_sync_fence_async_cancel(event->handle))
@@ -2314,8 +2311,11 @@
* -EPROTO is a "success" error - it just tells the user that the
* context had previously faulted
*/
- if (result && result != -EPROTO)
+ if (result && result != -EPROTO) {
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
kgsl_cmdbatch_destroy(cmdbatch);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
+ }
done:
kgsl_context_put(context);
@@ -2366,8 +2366,11 @@
* -EPROTO is a "success" error - it just tells the user that the
* context had previously faulted
*/
- if (result && result != -EPROTO)
+ if (result && result != -EPROTO) {
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
kgsl_cmdbatch_destroy(cmdbatch);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
+ }
done:
kgsl_context_put(context);
diff --git a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c
index 270fb38..e5d82fd 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c
@@ -211,13 +211,23 @@
pr_err("%s failed line %d\n", __func__, __LINE__);
return -EINVAL;
}
- /* assume total size within the max queue */
+
+ reg_addr = i2c_cmd->reg_addr;
while (cmd_size) {
- CDBG("%s cmd_size %d addr 0x%x data 0x%x", __func__,
+ CDBG("%s cmd_size %d addr 0x%x data 0x%x\n", __func__,
cmd_size, i2c_cmd->reg_addr, i2c_cmd->reg_data);
delay = i2c_cmd->delay;
data[i++] = CCI_I2C_WRITE_CMD;
- reg_addr = i2c_cmd->reg_addr;
+
+ /* in case of multiple command
+ * MSM_CCI_I2C_WRITE : address is not continuous, so update
+ * address for a new packet.
+ * MSM_CCI_I2C_WRITE_SEQ : address is continuous, need to keep
+ * the incremented address for a
+ * new packet */
+ if (c_ctrl->cmd == MSM_CCI_I2C_WRITE)
+ reg_addr = i2c_cmd->reg_addr;
+
/* either byte or word addr */
if (i2c_msg->addr_type == MSM_CAMERA_I2C_BYTE_ADDR)
data[i++] = reg_addr;
@@ -226,21 +236,25 @@
data[i++] = reg_addr & 0x00FF;
}
/* max of 10 data bytes */
- if (i2c_msg->data_type == MSM_CAMERA_I2C_BYTE_DATA) {
- data[i++] = i2c_cmd->reg_data;
- reg_addr++;
- } else {
- if ((i + 1) <= 10) {
- data[i++] = (i2c_cmd->reg_data &
- 0xFF00) >> 8; /* MSB */
- data[i++] = i2c_cmd->reg_data &
- 0x00FF; /* LSB */
- reg_addr += 2;
- } else
- break;
- }
- i2c_cmd++;
- --cmd_size;
+ do {
+ if (i2c_msg->data_type == MSM_CAMERA_I2C_BYTE_DATA) {
+ data[i++] = i2c_cmd->reg_data;
+ reg_addr++;
+ } else {
+ if ((i + 1) <= 10) {
+ data[i++] = (i2c_cmd->reg_data &
+ 0xFF00) >> 8; /* MSB */
+ data[i++] = i2c_cmd->reg_data &
+ 0x00FF; /* LSB */
+ reg_addr += 2;
+ } else
+ break;
+ }
+ i2c_cmd++;
+ --cmd_size;
+ } while ((c_ctrl->cmd == MSM_CCI_I2C_WRITE_SEQ) &&
+ (cmd_size > 0) && (i <= 10));
+
data[0] |= ((i-1) << 4);
len = ((i-1)/4) + 1;
rc = msm_cci_validate_queue(cci_dev, len, master, queue);
@@ -789,6 +803,7 @@
rc = msm_cci_i2c_read_bytes(sd, cci_ctrl);
break;
case MSM_CCI_I2C_WRITE:
+ case MSM_CCI_I2C_WRITE_SEQ:
rc = msm_cci_i2c_write(sd, cci_ctrl);
break;
case MSM_CCI_GPIO_WRITE:
diff --git a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.h b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.h
index 44c134e..283bd28 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.h
+++ b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.h
@@ -53,6 +53,7 @@
MSM_CCI_SET_SYNC_CID,
MSM_CCI_I2C_READ,
MSM_CCI_I2C_WRITE,
+ MSM_CCI_I2C_WRITE_SEQ,
MSM_CCI_GPIO_WRITE,
};
diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_cci_i2c.c b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_cci_i2c.c
index 8d9274b..6af0cea 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_cci_i2c.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_cci_i2c.c
@@ -158,7 +158,7 @@
reg_conf_tbl[i].reg_data = data[i];
reg_conf_tbl[i].delay = 0;
}
- cci_ctrl.cmd = MSM_CCI_I2C_WRITE;
+ cci_ctrl.cmd = MSM_CCI_I2C_WRITE_SEQ;
cci_ctrl.cci_info = client->cci_client;
cci_ctrl.cfg.cci_i2c_write_cfg.reg_setting = reg_conf_tbl;
cci_ctrl.cfg.cci_i2c_write_cfg.data_type = MSM_CAMERA_I2C_BYTE_DATA;
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index 276a6dc..399cea9 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -1115,8 +1115,8 @@
bool found_dead_app = false;
if (!memcmp(data->client.app_name, "keymaste", strlen("keymaste"))) {
- pr_warn("Do not unload keymaster app from tz\n");
- return 0;
+ pr_debug("Do not unload keymaster app from tz\n");
+ goto unload_exit;
}
if (data->client.app_id > 0) {
@@ -1211,6 +1211,7 @@
spin_unlock_irqrestore(&qseecom.registered_app_list_lock,
flags1);
}
+unload_exit:
qseecom_unmap_ion_allocated_memory(data);
data->released = true;
return ret;
diff --git a/drivers/nfc/nfc-nci.c b/drivers/nfc/nfc-nci.c
index 9ba0a87..e51da7f 100644
--- a/drivers/nfc/nfc-nci.c
+++ b/drivers/nfc/nfc-nci.c
@@ -762,6 +762,60 @@
}
/*
+ * Inside nfc_ioctl_nfcc_efuse
+ *
+ * @brief nfc_ioctl_nfcc_efuse
+ *
+ */
+int nfc_ioctl_nfcc_efuse(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ int r = 0;
+ unsigned short slave_addr = 0xE;
+ unsigned short curr_addr;
+ unsigned char efuse_addr = EFUSE_REG;
+ unsigned char efuse_value = 0xFF;
+
+ struct qca199x_dev *qca199x_dev = filp->private_data;
+
+ curr_addr = qca199x_dev->client->addr;
+ qca199x_dev->client->addr = slave_addr;
+
+ r = nfc_i2c_write(qca199x_dev->client,
+ &efuse_addr, 1);
+ if (r < 0) {
+ /* Restore original NFCC slave I2C address */
+ qca199x_dev->client->addr = curr_addr;
+ dev_err(&qca199x_dev->client->dev,
+ "ERROR_WRITE_FAIL : i2c write fail\n");
+ return -EIO;
+ }
+
+ /*
+ * NFCC chip needs to be at least
+ * 10usec high before make it low
+ */
+ usleep_range(10, 15);
+
+ r = i2c_master_recv(qca199x_dev->client, &efuse_value,
+ sizeof(efuse_value));
+ if (r < 0) {
+ /* Restore original NFCC slave I2C address */
+ qca199x_dev->client->addr = curr_addr;
+ dev_err(&qca199x_dev->client->dev,
+ "ERROR_I2C_RCV_FAIL : i2c recv fail\n");
+ return -EIO;
+ }
+
+ dev_dbg(&qca199x_dev->client->dev, "%s : EFUSE_VALUE %02x\n",
+ __func__, efuse_value);
+
+ /* Restore original NFCC slave I2C address */
+ qca199x_dev->client->addr = curr_addr;
+ return efuse_value;
+}
+
+/*
* Inside nfc_ioctl_nfcc_version
*
* @brief nfc_ioctl_nfcc_version
@@ -934,7 +988,7 @@
static long nfc_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg)
{
int r = 0;
-
+ struct qca199x_dev *qca199x_dev = pfile->private_data;
switch (cmd) {
case NFC_SET_PWR:
@@ -956,6 +1010,14 @@
case NFCC_INITIAL_CORE_RESET_NTF:
r = nfc_ioctl_core_reset_ntf(pfile, cmd, arg);
break;
+ case NFC_GET_EFUSE:
+ r = nfc_ioctl_nfcc_efuse(pfile, cmd, arg);
+ if (r < 0) {
+ r = 0xFF;
+ dev_err(&qca199x_dev->client->dev,
+ "nfc_ioctl : FAILED TO READ EFUSE TYPE\n");
+ }
+ break;
default:
r = -ENOIOCTLCMD;
}
diff --git a/drivers/nfc/nfc-nci.h b/drivers/nfc/nfc-nci.h
index 8186861..398fa3f 100644
--- a/drivers/nfc/nfc-nci.h
+++ b/drivers/nfc/nfc-nci.h
@@ -60,6 +60,7 @@
#define SET_RX_BLOCK _IOW(0xE9, 0x04, unsigned int)
#define SET_EMULATOR_TEST_POINT _IOW(0xE9, 0x05, unsigned int)
#define NFCC_VERSION _IOW(0xE9, 0x08, unsigned int)
+#define NFC_GET_EFUSE _IOW(0xE9, 0x09, unsigned int)
#define NFCC_INITIAL_CORE_RESET_NTF _IOW(0xE9, 0x10, unsigned int)
#define NFC_MAX_I2C_TRANSFER (0x0400)
diff --git a/include/sound/Kbuild b/include/sound/Kbuild
index aeccfed..bb8b17b 100644
--- a/include/sound/Kbuild
+++ b/include/sound/Kbuild
@@ -14,3 +14,4 @@
header-y += lsm_params.h
header-y += voice_params.h
header-y += voice_svc.h
+header-y += msmcal-hwdep.h
diff --git a/include/sound/asound.h b/include/sound/asound.h
index 7bf01b6..244bb30 100644
--- a/include/sound/asound.h
+++ b/include/sound/asound.h
@@ -95,9 +95,10 @@
SNDRV_HWDEP_IFACE_SB_RC, /* SB Extigy/Audigy2NX remote control */
SNDRV_HWDEP_IFACE_HDA, /* HD-audio */
SNDRV_HWDEP_IFACE_USB_STREAM, /* direct access to usb stream */
+ SNDRV_HWDEP_IFACE_AUDIO_CODEC, /* codec Audio Control */
/* Don't forget to change the following: */
- SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_USB_STREAM
+ SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_AUDIO_CODEC
};
struct snd_hwdep_info {
diff --git a/include/sound/msmcal-hwdep.h b/include/sound/msmcal-hwdep.h
new file mode 100644
index 0000000..324b497
--- /dev/null
+++ b/include/sound/msmcal-hwdep.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef _CALIB_HWDEP_H
+#define _CALIB_HWDEP_H
+
+#define WCD9XXX_CODEC_HWDEP_NODE 1000
+enum wcd_cal_type {
+ WCD9XXX_MIN_CAL,
+ WCD9XXX_ANC_CAL = WCD9XXX_MIN_CAL,
+ WCD9XXX_MAD_CAL,
+ WCD9XXX_MBHC_CAL,
+ WCD9XXX_MAX_CAL,
+};
+
+struct wcdcal_ioctl_buffer {
+ __u32 size;
+ __u8 __user *buffer;
+ enum wcd_cal_type cal_type;
+};
+
+#define SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE \
+ _IOW('U', 0x1, struct wcdcal_ioctl_buffer)
+
+#endif /*_CALIB_HWDEP_H*/
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index a3ed23d..b42599e 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -51,8 +51,8 @@
snd-soc-wcd9304-objs := wcd9304.o wcd9304-tables.o
snd-soc-wcd9310-objs := wcd9310.o wcd9310-tables.o
snd-soc-cs8427-objs := cs8427.o
-snd-soc-wcd9320-objs := wcd9xxx-resmgr.o wcd9320.o wcd9320-tables.o wcd9xxx-mbhc.o wcd9xxx-common.o
-snd-soc-wcd9306-objs := wcd9306.o wcd9306-tables.o wcd9xxx-common.o
+snd-soc-wcd9320-objs := wcd9xxx-resmgr.o wcd9320.o wcd9320-tables.o wcd9xxx-mbhc.o wcd9xxx-common.o wcdcal-hwdep.o
+snd-soc-wcd9306-objs := wcd9306.o wcd9306-tables.o wcd9xxx-common.o wcdcal-hwdep.o
snd-soc-msm8x10-wcd-objs := msm8x10-wcd.o msm8x10-wcd-tables.o wcd9xxx-common.o
snd-soc-wl1273-objs := wl1273.o
snd-soc-wm1250-ev1-objs := wm1250-ev1.o
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;
diff --git a/sound/soc/codecs/wcdcal-hwdep.c b/sound/soc/codecs/wcdcal-hwdep.c
new file mode 100644
index 0000000..1132a3c
--- /dev/null
+++ b/sound/soc/codecs/wcdcal-hwdep.c
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/ioctl.h>
+#include <linux/bitops.h>
+#include <sound/hwdep.h>
+#include <sound/msmcal-hwdep.h>
+#include <sound/soc.h>
+#include "wcdcal-hwdep.h"
+
+const int cal_size_info[WCD9XXX_MAX_CAL] = {
+ [WCD9XXX_ANC_CAL] = 4096,
+ [WCD9XXX_MBHC_CAL] = 4096,
+ [WCD9XXX_MAD_CAL] = 4096,
+};
+
+const char *cal_name_info[WCD9XXX_MAX_CAL] = {
+ [WCD9XXX_ANC_CAL] = "anc",
+ [WCD9XXX_MBHC_CAL] = "mbhc",
+ [WCD9XXX_MAD_CAL] = "mad",
+};
+
+struct firmware_cal *wcdcal_get_fw_cal(struct fw_info *fw_data,
+ enum wcd_cal_type type)
+{
+ if (!fw_data) {
+ pr_err("%s: fw_data is NULL\n", __func__);
+ return NULL;
+ }
+ if (type >= WCD9XXX_MAX_CAL ||
+ type < WCD9XXX_MIN_CAL) {
+ pr_err("%s: wrong cal type sent %d\n", __func__, type);
+ return NULL;
+ }
+ mutex_lock(&fw_data->lock);
+ if (!test_bit(WCDCAL_RECIEVED,
+ &fw_data->wcdcal_state[type])) {
+ pr_err("%s: cal not sent by userspace %d\n",
+ __func__, type);
+ mutex_unlock(&fw_data->lock);
+ return NULL;
+ }
+ mutex_unlock(&fw_data->lock);
+ return fw_data->fw[type];
+}
+EXPORT_SYMBOL(wcdcal_get_fw_cal);
+
+static int wcdcal_hwdep_ioctl_shared(struct snd_hwdep *hw,
+ struct wcdcal_ioctl_buffer fw_user)
+{
+ struct fw_info *fw_data = hw->private_data;
+ struct firmware_cal **fw = fw_data->fw;
+ void *data;
+
+ if (!test_bit(fw_user.cal_type, fw_data->cal_bit)) {
+ pr_err("%s: codec didn't set this %d!!\n",
+ __func__, fw_user.cal_type);
+ return -EFAULT;
+ }
+ if (fw_user.cal_type >= WCD9XXX_MAX_CAL ||
+ fw_user.cal_type < WCD9XXX_MIN_CAL) {
+ pr_err("%s: wrong cal type sent %d\n",
+ __func__, fw_user.cal_type);
+ return -EFAULT;
+ }
+ if (fw_user.size > cal_size_info[fw_user.cal_type] ||
+ fw_user.size <= 0) {
+ pr_err("%s: incorrect firmware size %d for %s\n",
+ __func__, fw_user.size,
+ cal_name_info[fw_user.cal_type]);
+ return -EFAULT;
+ }
+ data = fw[fw_user.cal_type]->data;
+ memcpy(data, fw_user.buffer, fw_user.size);
+ fw[fw_user.cal_type]->size = fw_user.size;
+ mutex_lock(&fw_data->lock);
+ set_bit(WCDCAL_RECIEVED, &fw_data->wcdcal_state[fw_user.cal_type]);
+ mutex_unlock(&fw_data->lock);
+ return 0;
+}
+
+#ifdef CONFIG_COMPAT
+struct wcdcal_ioctl_buffer32 {
+ u32 size;
+ compat_uptr_t buffer;
+ enum wcd_cal_type cal_type;
+};
+
+enum {
+ SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE32 =
+ _IOW('U', 0x1, struct wcdcal_ioctl_buffer32),
+};
+
+static int wcdcal_hwdep_ioctl_compat(struct snd_hwdep *hw, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct wcdcal_ioctl_buffer __user *argp = (void __user *)arg;
+ struct wcdcal_ioctl_buffer32 fw_user32;
+ struct wcdcal_ioctl_buffer fw_user_compat;
+
+ if (cmd != SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE32) {
+ pr_err("%s: wrong ioctl command sent %u!\n", __func__, cmd);
+ return -ENOIOCTLCMD;
+ }
+ if (copy_from_user(&fw_user32, argp, sizeof(fw_user32))) {
+ pr_err("%s: failed to copy\n", __func__);
+ return -EFAULT;
+ }
+ fw_user_compat.size = fw_user32.size;
+ fw_user_compat.buffer = compat_ptr(fw_user32.buffer);
+ fw_user_compat.cal_type = fw_user32.cal_type;
+ return wcdcal_hwdep_ioctl_shared(hw, fw_user_compat);
+}
+#else
+#define wcdcal_hwdep_ioctl_compat NULL
+#endif
+
+static int wcdcal_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct wcdcal_ioctl_buffer __user *argp = (void __user *)arg;
+ struct wcdcal_ioctl_buffer fw_user;
+
+ if (cmd != SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE) {
+ pr_err("%s: wrong ioctl command sent %d!\n", __func__, cmd);
+ return -ENOIOCTLCMD;
+ }
+ if (copy_from_user(&fw_user, argp, sizeof(fw_user))) {
+ pr_err("%s: failed to copy\n", __func__);
+ return -EFAULT;
+ }
+ return wcdcal_hwdep_ioctl_shared(hw, fw_user);
+}
+
+static int wcdcal_hwdep_release(struct snd_hwdep *hw, struct file *file)
+{
+ struct fw_info *fw_data = hw->private_data;
+ mutex_lock(&fw_data->lock);
+ /* clear all the calibrations */
+ memset(fw_data->wcdcal_state, 0,
+ sizeof(fw_data->wcdcal_state));
+ mutex_unlock(&fw_data->lock);
+ return 0;
+}
+
+int wcd_cal_create_hwdep(void *data, int node, struct snd_soc_codec *codec)
+{
+ char hwname[40];
+ struct snd_hwdep *hwdep;
+ struct firmware_cal **fw;
+ struct fw_info *fw_data = data;
+ int err, cal_bit;
+
+ if (!fw_data || !codec) {
+ pr_err("%s: wrong arguments passed\n", __func__);
+ return -EINVAL;
+ }
+
+ fw = fw_data->fw;
+ snprintf(hwname, strlen("Codec %s"), "Codec %s", codec->name);
+ err = snd_hwdep_new(codec->card->snd_card, hwname, node, &hwdep);
+ if (err < 0) {
+ dev_err(codec->dev, "%s: new hwdep failed %d\n",
+ __func__, err);
+ return err;
+ }
+ snprintf(hwdep->name, strlen("Codec %s"), "Codec %s", codec->name);
+ hwdep->iface = SNDRV_HWDEP_IFACE_AUDIO_CODEC;
+ hwdep->private_data = fw_data;
+ hwdep->ops.ioctl_compat = wcdcal_hwdep_ioctl_compat;
+ hwdep->ops.ioctl = wcdcal_hwdep_ioctl;
+ hwdep->ops.release = wcdcal_hwdep_release;
+ mutex_init(&fw_data->lock);
+
+ for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) {
+ set_bit(WCDCAL_UNINITIALISED,
+ &fw_data->wcdcal_state[cal_bit]);
+ fw[cal_bit] = kzalloc(sizeof *(fw[cal_bit]), GFP_KERNEL);
+ if (!fw[cal_bit]) {
+ dev_err(codec->dev, "%s: no memory for %s cal\n",
+ __func__, cal_name_info[cal_bit]);
+ goto end;
+ }
+ }
+ for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) {
+ fw[cal_bit]->data = kzalloc(cal_size_info[cal_bit],
+ GFP_KERNEL);
+ if (!fw[cal_bit]->data) {
+ dev_err(codec->dev, "%s: no memory for %s cal data\n",
+ __func__, cal_name_info[cal_bit]);
+ goto exit;
+ }
+ set_bit(WCDCAL_INITIALISED,
+ &fw_data->wcdcal_state[cal_bit]);
+ }
+ return 0;
+exit:
+ for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) {
+ kfree(fw[cal_bit]->data);
+ fw[cal_bit]->data = NULL;
+ }
+end:
+ for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) {
+ kfree(fw[cal_bit]);
+ fw[cal_bit] = NULL;
+ }
+ return -ENOMEM;
+}
+EXPORT_SYMBOL(wcd_cal_create_hwdep);
diff --git a/sound/soc/codecs/wcdcal-hwdep.h b/sound/soc/codecs/wcdcal-hwdep.h
new file mode 100644
index 0000000..632e2f1
--- /dev/null
+++ b/sound/soc/codecs/wcdcal-hwdep.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __WCD9XXX_HWDEP_H__
+#define __WCD9XXX_HWDEP_H__
+#include <sound/msmcal-hwdep.h>
+
+enum wcd_cal_states {
+ WCDCAL_UNINITIALISED,
+ WCDCAL_INITIALISED,
+ WCDCAL_RECIEVED
+};
+
+struct fw_info {
+ struct firmware_cal *fw[WCD9XXX_MAX_CAL];
+ DECLARE_BITMAP(cal_bit, WCD9XXX_MAX_CAL);
+ /* for calibration tracking */
+ unsigned long wcdcal_state[WCD9XXX_MAX_CAL];
+ struct mutex lock;
+};
+
+struct firmware_cal {
+ u8 *data;
+ size_t size;
+};
+
+struct snd_soc_codec;
+int wcd_cal_create_hwdep(void *fw, int node, struct snd_soc_codec *codec);
+struct firmware_cal *wcdcal_get_fw_cal(struct fw_info *fw_data,
+ enum wcd_cal_type type);
+#endif /* __WCD9XXX_HWDEP_H__ */
diff --git a/sound/soc/msm/Kconfig b/sound/soc/msm/Kconfig
index 86dd840..fbcd953 100644
--- a/sound/soc/msm/Kconfig
+++ b/sound/soc/msm/Kconfig
@@ -183,6 +183,7 @@
select SND_DYNAMIC_MINORS
select AUDIO_OCMEM
select DOLBY_DAP
+ select SND_HWDEP
help
To add support for SoC audio on MSM8974.
This will enable sound soc drivers which
diff --git a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c
index 4ca5764..ae5c42f 100644
--- a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c
@@ -304,6 +304,7 @@
uint32_t sample_rate = 0;
int bytes_available, stream_id;
uint32_t stream_index;
+ unsigned long flags;
pr_debug("%s opcode =%08x\n", __func__, opcode);
switch (opcode) {
@@ -476,12 +477,17 @@
case RESET_EVENTS:
pr_err("%s: Received reset events CB, move to error state",
__func__);
- spin_lock(&prtd->lock);
+ spin_lock_irqsave(&prtd->lock, flags);
snd_compr_fragment_elapsed(cstream);
prtd->copied_total = prtd->bytes_received;
atomic_set(&prtd->error, 1);
wake_up(&prtd->drain_wait);
- spin_unlock(&prtd->lock);
+ if (atomic_read(&prtd->eos)) {
+ pr_debug("%s:unblock eos wait queues", __func__);
+ wake_up(&prtd->eos_wait);
+ atomic_set(&prtd->eos, 0);
+ }
+ spin_unlock_irqrestore(&prtd->lock, flags);
break;
default:
pr_debug("%s: Not Supported Event opcode[0x%x]\n",
@@ -1249,7 +1255,9 @@
/* Wait indefinitely for DRAIN. Flush can also signal this*/
rc = wait_event_interruptible(prtd->eos_wait,
- (prtd->cmd_ack || prtd->cmd_interrupt));
+ (prtd->cmd_ack ||
+ prtd->cmd_interrupt ||
+ atomic_read(&prtd->error)));
if (rc < 0)
pr_err("%s: EOS wait failed\n", __func__);
@@ -1260,6 +1268,11 @@
if (prtd->cmd_interrupt)
rc = -EINTR;
+ if (atomic_read(&prtd->error)) {
+ pr_err("%s: Got RESET EVENTS notification, return\n", __func__);
+ rc = -ENETRESET;
+ }
+
/*FIXME : what if a flush comes while PC is here */
if (rc == 0) {
/*