msm: vidc: Add support to process HW Error
Add support to process Watchdog timeout and sys error
with SSR framework and reporting event bck to userspace.
Change-Id: Ie12a15044dd01cb0ceab82d4f29d612db2955b8d
Signed-off-by: Praneeth Paladugu <ppaladug@codeaurora.org>
diff --git a/drivers/media/video/msm_vidc/Makefile b/drivers/media/video/msm_vidc/Makefile
index 2a1f40f..9b3af9d 100644
--- a/drivers/media/video/msm_vidc/Makefile
+++ b/drivers/media/video/msm_vidc/Makefile
@@ -5,5 +5,6 @@
msm_venc.o \
msm_smem.o \
msm_vidc_debug.o \
+ msm_vidc_ssr.o \
vidc_hal.o \
vidc_hal_interrupt_handler.o \
diff --git a/drivers/media/video/msm_vidc/msm_v4l2_vidc.c b/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
index a608e1ab..e1a0994 100644
--- a/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
+++ b/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
@@ -29,6 +29,7 @@
#include "msm_vidc_debug.h"
#include "vidc_hal_api.h"
#include "msm_smem.h"
+#include "msm_vidc_ssr.h"
#define BASE_DEVICE_NUMBER 32
#define SHARED_QSIZE 0x1000000
@@ -584,7 +585,7 @@
buffer_info.m.planes[0].reserved[1],
buffer_info.m.planes[0].length);
rc = msm_vidc_release_buf(v4l2_inst->vidc_inst,
- &buffer_info);
+ &buffer_info);
if (rc)
dprintk(VIDC_ERR,
"Failed Release buffer: %d, %d, %d\n",
@@ -1243,6 +1244,9 @@
core->debugfs_root = msm_vidc_debugfs_init_core(
core, vidc_driver->debugfs_root);
pdev->dev.platform_data = core;
+ rc = msm_vidc_ssr_init(core);
+ if (rc < 0)
+ dprintk(VIDC_ERR, "msm_vidc : Sub Systrem Restart failed\n");
return rc;
err_cores_exceeded:
@@ -1271,6 +1275,7 @@
vidc_hal_delete_device(core->device);
video_unregister_device(&core->vdev[MSM_VIDC_ENCODER].vdev);
video_unregister_device(&core->vdev[MSM_VIDC_DECODER].vdev);
+ rc = msm_vidc_ssr_uninit(core);
v4l2_device_unregister(&core->v4l2_dev);
if (core->resources.ocmem.handle)
ocmem_notifier_unregister(core->resources.ocmem.handle,
diff --git a/drivers/media/video/msm_vidc/msm_vdec.c b/drivers/media/video/msm_vidc/msm_vdec.c
index 49b28ae..b615352 100644
--- a/drivers/media/video/msm_vidc/msm_vdec.c
+++ b/drivers/media/video/msm_vidc/msm_vdec.c
@@ -374,7 +374,14 @@
int rc = 0;
int i;
struct vidc_buffer_addr_info buffer_info;
-
+ struct msm_vidc_core *core = inst->core;
+ if (inst->state == MSM_VIDC_CORE_INVALID ||
+ core->state == VIDC_CORE_INVALID) {
+ dprintk(VIDC_ERR,
+ "Core %p in bad state, ignoring release output buf\n",
+ core);
+ goto exit;
+ }
switch (b->type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
break;
@@ -392,7 +399,7 @@
buffer_info.extradata_addr =
inst->extradata_handle->device_addr;
rc = vidc_hal_session_release_buffers(
- (void *)inst->session, &buffer_info);
+ (void *)inst->session, &buffer_info);
if (rc)
dprintk(VIDC_ERR,
"vidc_hal_session_release_buffers failed\n");
@@ -402,6 +409,7 @@
dprintk(VIDC_ERR, "Buffer type not recognized: %d\n", b->type);
break;
}
+exit:
return rc;
}
@@ -842,6 +850,8 @@
int msm_vdec_cmd(struct msm_vidc_inst *inst, struct v4l2_decoder_cmd *dec)
{
int rc = 0;
+ struct v4l2_event dqevent = {0};
+ struct msm_vidc_core *core = inst->core;
switch (dec->cmd) {
case V4L2_DEC_QCOM_CMD_FLUSH:
rc = msm_comm_flush(inst, dec->flags);
@@ -853,6 +863,15 @@
rc = msm_comm_release_persist_buffers(inst);
if (rc)
pr_err("Failed to release persist buffers: %d\n", rc);
+ if (inst->state == MSM_VIDC_CORE_INVALID ||
+ core->state == VIDC_CORE_INVALID) {
+ dprintk(VIDC_ERR,
+ "Core %p in bad state, Sending CLOSE event\n",
+ core);
+ dqevent.type = V4L2_EVENT_MSM_VIDC_CLOSE_DONE;
+ v4l2_event_queue_fh(&inst->event_handler, &dqevent);
+ goto exit;
+ }
rc = msm_comm_try_state(inst, MSM_VIDC_CLOSE_DONE);
break;
default:
diff --git a/drivers/media/video/msm_vidc/msm_venc.c b/drivers/media/video/msm_vidc/msm_venc.c
index 4573018..0ea2a60 100644
--- a/drivers/media/video/msm_vidc/msm_venc.c
+++ b/drivers/media/video/msm_vidc/msm_venc.c
@@ -1260,11 +1260,20 @@
int msm_venc_cmd(struct msm_vidc_inst *inst, struct v4l2_encoder_cmd *enc)
{
int rc = 0;
+ struct v4l2_event dqevent = {0};
+ struct msm_vidc_core *core;
+ core = inst->core;
switch (enc->cmd) {
case V4L2_ENC_QCOM_CMD_FLUSH:
rc = msm_comm_flush(inst, enc->flags);
break;
case V4L2_ENC_CMD_STOP:
+ if (inst->state == MSM_VIDC_CORE_INVALID ||
+ core->state == VIDC_CORE_INVALID) {
+ dqevent.type = V4L2_EVENT_MSM_VIDC_CLOSE_DONE;
+ v4l2_event_queue_fh(&inst->event_handler, &dqevent);
+ return rc;
+ }
rc = msm_comm_try_state(inst, MSM_VIDC_CLOSE_DONE);
break;
}
diff --git a/drivers/media/video/msm_vidc/msm_vidc.c b/drivers/media/video/msm_vidc/msm_vidc.c
index 8ff7714..886113f 100644
--- a/drivers/media/video/msm_vidc/msm_vidc.c
+++ b/drivers/media/video/msm_vidc/msm_vidc.c
@@ -538,7 +538,9 @@
list_del(&inst->list);
}
mutex_unlock(&core->sync_lock);
- rc = msm_comm_try_state(inst, MSM_VIDC_CORE_UNINIT);
+ if (inst->state != MSM_VIDC_CORE_INVALID &&
+ core->state != VIDC_CORE_INVALID)
+ rc = msm_comm_try_state(inst, MSM_VIDC_CORE_UNINIT);
if (rc)
dprintk(VIDC_ERR,
"Failed to move video instance to uninit state\n");
diff --git a/drivers/media/video/msm_vidc/msm_vidc_common.c b/drivers/media/video/msm_vidc/msm_vidc_common.c
index 4ff28d62..ed234b9 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_common.c
+++ b/drivers/media/video/msm_vidc/msm_vidc_common.c
@@ -23,6 +23,7 @@
#include "vidc_hal_api.h"
#include "msm_smem.h"
#include "msm_vidc_debug.h"
+#include "msm_vidc_ssr.h"
#define HW_RESPONSE_TIMEOUT (5 * 60 * 1000)
@@ -312,7 +313,7 @@
complete(&core->completions[SYS_MSG_INDEX(cmd)]);
}
-static inline void change_inst_state(struct msm_vidc_inst *inst,
+void change_inst_state(struct msm_vidc_inst *inst,
enum instance_state state)
{
unsigned long flags;
@@ -508,6 +509,13 @@
}
}
+static void handle_sys_watchdog_timeout(enum command_response cmd, void *data)
+{
+ subsystem_restart("msm_vidc");
+ dprintk(VIDC_ERR,
+ "msm_vidc: Sub System Restart initiated\n");
+}
+
static void handle_session_close(enum command_response cmd, void *data)
{
@@ -743,6 +751,9 @@
case SESSION_GET_SEQ_HDR_DONE:
handle_seq_hdr_done(cmd, data);
break;
+ case SYS_WATCHDOG_TIMEOUT:
+ handle_sys_watchdog_timeout(cmd, data);
+ break;
default:
dprintk(VIDC_ERR, "response unhandled\n");
break;
@@ -854,7 +865,7 @@
return rc;
}
-static void msm_comm_unload_fw(struct msm_vidc_core *core)
+void msm_comm_unload_fw(struct msm_vidc_core *core)
{
if (!core) {
dprintk(VIDC_ERR, "Invalid paramter: %p\n", core);
@@ -901,7 +912,7 @@
return rc;
}
-static int msm_comm_unset_ocmem(struct msm_vidc_core *core)
+int msm_comm_unset_ocmem(struct msm_vidc_core *core)
{
struct vidc_resource_hdr rhdr;
int rc = 0;
@@ -967,7 +978,7 @@
return rc;
}
-static int msm_comm_free_ocmem(struct msm_vidc_core *core)
+int msm_comm_free_ocmem(struct msm_vidc_core *core)
{
int rc = 0;
if (core->resources.ocmem.buf) {
@@ -1343,30 +1354,43 @@
{
int rc = 0;
int flipped_state;
+ struct msm_vidc_core *core;
if (!inst) {
dprintk(VIDC_ERR,
- "Invalid instance pointer = %p\n", inst);
+ "Invalid instance pointer = %p\n", inst);
return -EINVAL;
}
dprintk(VIDC_DBG,
- "Trying to move inst: %p from: 0x%x to 0x%x\n",
- inst, inst->state, state);
+ "Trying to move inst: %p from: 0x%x to 0x%x\n",
+ inst, inst->state, state);
+ core = inst->core;
+ if (!core) {
+ dprintk(VIDC_ERR,
+ "Invalid core pointer = %p\n", inst);
+ return -EINVAL;
+ }
mutex_lock(&inst->sync_lock);
+ if (inst->state == MSM_VIDC_CORE_INVALID ||
+ core->state == VIDC_CORE_INVALID) {
+ dprintk(VIDC_ERR,
+ "Core is in bad state can't change the state");
+ goto exit;
+ }
flipped_state = inst->state;
if (flipped_state < MSM_VIDC_STOP
- && state > MSM_VIDC_STOP) {
+ && state > MSM_VIDC_STOP) {
flipped_state = MSM_VIDC_STOP + (MSM_VIDC_STOP - flipped_state);
flipped_state &= 0xFFFE;
flipped_state = flipped_state - 1;
} else if (flipped_state > MSM_VIDC_STOP
- && state < MSM_VIDC_STOP) {
+ && state < MSM_VIDC_STOP) {
flipped_state = MSM_VIDC_STOP -
- (flipped_state - MSM_VIDC_STOP + 1);
+ (flipped_state - MSM_VIDC_STOP + 1);
flipped_state &= 0xFFFE;
flipped_state = flipped_state - 1;
}
dprintk(VIDC_DBG,
- "flipped_state = 0x%x\n", flipped_state);
+ "flipped_state = 0x%x\n", flipped_state);
switch (flipped_state) {
case MSM_VIDC_CORE_UNINIT_DONE:
case MSM_VIDC_CORE_INIT:
@@ -1421,14 +1445,14 @@
if (rc || state <= inst->state)
break;
dprintk(VIDC_DBG,
- "Moving to release resources done state\n");
+ "Moving to release resources done state\n");
case MSM_VIDC_CLOSE:
rc = msm_comm_session_close(flipped_state, inst);
if (rc || state <= inst->state)
break;
case MSM_VIDC_CLOSE_DONE:
rc = wait_for_state(inst, flipped_state, MSM_VIDC_CLOSE_DONE,
- SESSION_END_DONE);
+ SESSION_END_DONE);
if (rc || state <= inst->state)
break;
case MSM_VIDC_CORE_UNINIT:
@@ -1441,11 +1465,12 @@
rc = -EINVAL;
break;
}
+exit:
mutex_unlock(&inst->sync_lock);
if (rc)
dprintk(VIDC_ERR,
- "Failed to move from state: %d to %d\n",
- inst->state, state);
+ "Failed to move from state: %d to %d\n",
+ inst->state, state);
return rc;
}
@@ -1456,13 +1481,25 @@
struct msm_vidc_inst *inst;
struct vb2_buf_entry *entry;
struct vidc_frame_data frame_data;
+ struct msm_vidc_core *core;
q = vb->vb2_queue;
inst = q->drv_priv;
-
if (!inst || !vb) {
dprintk(VIDC_ERR, "Invalid input: %p, %p\n", inst, vb);
return -EINVAL;
}
+ core = inst->core;
+ if (!core) {
+ dprintk(VIDC_ERR,
+ "Invalid input: %p, %p, %p\n", inst, core, vb);
+ return -EINVAL;
+ }
+
+ if (inst->state == MSM_VIDC_CORE_INVALID ||
+ core->state == VIDC_CORE_INVALID) {
+ dprintk(VIDC_ERR, "Core is in bad state. Can't Queue\n");
+ return -EINVAL;
+ }
if (inst->state != MSM_VIDC_START_DONE) {
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
if (!entry) {
@@ -1580,7 +1617,6 @@
mutex_unlock(&inst->sync_lock);
return rc;
}
-
int msm_comm_release_scratch_buffers(struct msm_vidc_inst *inst)
{
struct msm_smem *handle;
@@ -1589,6 +1625,18 @@
struct vidc_buffer_addr_info buffer_info;
int rc = 0;
unsigned long flags;
+ struct msm_vidc_core *core;
+ if (!inst) {
+ dprintk(VIDC_ERR,
+ "Invalid instance pointer = %p\n", inst);
+ return -EINVAL;
+ }
+ core = inst->core;
+ if (!core) {
+ dprintk(VIDC_ERR,
+ "Invalid core pointer = %p\n", core);
+ return -EINVAL;
+ }
spin_lock_irqsave(&inst->lock, flags);
if (!list_empty(&inst->internalbufs)) {
list_for_each_safe(ptr, next, &inst->internalbufs) {
@@ -1599,13 +1647,17 @@
buffer_info.buffer_type = HAL_BUFFER_INTERNAL_SCRATCH;
buffer_info.num_buffers = 1;
buffer_info.align_device_addr = handle->device_addr;
- rc = vidc_hal_session_release_buffers(
- (void *) inst->session, &buffer_info);
- if (rc)
- dprintk(VIDC_WARN,
- "Failed to release scratch buffer: 0x%x, %d",
- buffer_info.align_device_addr,
- buffer_info.buffer_size);
+ if (inst->state != MSM_VIDC_CORE_INVALID &&
+ core->state != VIDC_CORE_INVALID) {
+ rc = vidc_hal_session_release_buffers(
+ (void *) inst->session,
+ &buffer_info);
+ if (rc)
+ dprintk(VIDC_WARN,
+ "Rel scrtch buf fail:0x%x, %d",
+ buffer_info.align_device_addr,
+ buffer_info.buffer_size);
+ }
list_del(&buf->list);
spin_unlock_irqrestore(&inst->lock, flags);
msm_smem_free(inst->mem_client, buf->handle);
@@ -1625,23 +1677,39 @@
struct vidc_buffer_addr_info buffer_info;
int rc = 0;
unsigned long flags;
+ struct msm_vidc_core *core;
+ if (!inst) {
+ dprintk(VIDC_ERR,
+ "Invalid instance pointer = %p\n", inst);
+ return -EINVAL;
+ }
+ core = inst->core;
+ if (!core) {
+ dprintk(VIDC_ERR,
+ "Invalid core pointer = %p\n", core);
+ return -EINVAL;
+ }
spin_lock_irqsave(&inst->lock, flags);
if (!list_empty(&inst->persistbufs)) {
list_for_each_safe(ptr, next, &inst->persistbufs) {
buf = list_entry(ptr, struct internal_buf,
- list);
+ list);
handle = buf->handle;
buffer_info.buffer_size = handle->size;
buffer_info.buffer_type = HAL_BUFFER_INTERNAL_PERSIST;
buffer_info.num_buffers = 1;
buffer_info.align_device_addr = handle->device_addr;
- rc = vidc_hal_session_release_buffers(
- (void *) inst->session, &buffer_info);
- if (rc)
- dprintk(VIDC_WARN,
- "Failed to release persist buffer 0x%x, %d\n",
- buffer_info.align_device_addr,
- buffer_info.buffer_size);
+ if (inst->state != MSM_VIDC_CORE_INVALID &&
+ core->state != VIDC_CORE_INVALID) {
+ rc = vidc_hal_session_release_buffers(
+ (void *) inst->session,
+ &buffer_info);
+ if (rc)
+ dprintk(VIDC_WARN,
+ "Rel prst buf fail:0x%x, %d",
+ buffer_info.align_device_addr,
+ buffer_info.buffer_size);
+ }
list_del(&buf->list);
spin_unlock_irqrestore(&inst->lock, flags);
msm_smem_free(inst->mem_client, buf->handle);
@@ -1779,6 +1847,50 @@
return rc;
}
+static void msm_comm_flush_in_invalid_state(struct msm_vidc_inst *inst)
+{
+ struct v4l2_event dqevent = {0};
+ struct list_head *ptr, *next;
+ struct vb2_buffer *vb;
+ if (!list_empty(&inst->bufq[CAPTURE_PORT].
+ vb2_bufq.queued_list)) {
+ list_for_each_safe(ptr, next,
+ &inst->bufq[CAPTURE_PORT].
+ vb2_bufq.queued_list) {
+ vb = container_of(ptr,
+ struct vb2_buffer,
+ queued_entry);
+ if (vb) {
+ vb->v4l2_planes[0].bytesused = 0;
+ mutex_lock(&inst->bufq[CAPTURE_PORT].lock);
+ vb2_buffer_done(vb,
+ VB2_BUF_STATE_DONE);
+ mutex_unlock(&inst->bufq[CAPTURE_PORT].lock);
+ }
+ }
+ }
+ if (!list_empty(&inst->bufq[OUTPUT_PORT].
+ vb2_bufq.queued_list)) {
+ list_for_each_safe(ptr, next,
+ &inst->bufq[OUTPUT_PORT].
+ vb2_bufq.queued_list) {
+ vb = container_of(ptr,
+ struct vb2_buffer,
+ queued_entry);
+ if (vb) {
+ vb->v4l2_planes[0].bytesused = 0;
+ mutex_lock(&inst->bufq[OUTPUT_PORT].lock);
+ vb2_buffer_done(vb,
+ VB2_BUF_STATE_DONE);
+ mutex_unlock(&inst->bufq[OUTPUT_PORT].lock);
+ }
+ }
+ }
+ dqevent.type = V4L2_EVENT_MSM_VIDC_FLUSH_DONE;
+ dqevent.id = 0;
+ v4l2_event_queue_fh(&inst->event_handler, &dqevent);
+ return;
+}
int msm_comm_flush(struct msm_vidc_inst *inst, u32 flags)
{
int rc = 0;
@@ -1787,12 +1899,34 @@
struct list_head *ptr, *next;
struct vb2_buf_entry *temp;
struct mutex *lock;
+ struct msm_vidc_core *core;
+ if (!inst) {
+ dprintk(VIDC_ERR,
+ "Invalid instance pointer = %p\n", inst);
+ return -EINVAL;
+ }
+ core = inst->core;
+ if (!core) {
+ dprintk(VIDC_ERR,
+ "Invalid core pointer = %p\n", core);
+ return -EINVAL;
+ }
+
ip_flush = flags & V4L2_QCOM_CMD_FLUSH_OUTPUT;
op_flush = flags & V4L2_QCOM_CMD_FLUSH_CAPTURE;
+
if (ip_flush && !op_flush) {
dprintk(VIDC_WARN, "Input only flush not supported\n");
return 0;
}
+ if (inst->state == MSM_VIDC_CORE_INVALID ||
+ core->state == VIDC_CORE_INVALID) {
+ dprintk(VIDC_ERR,
+ "Core %p and inst %p are in bad state\n",
+ core, inst);
+ msm_comm_flush_in_invalid_state(inst);
+ }
+
mutex_lock(&inst->sync_lock);
if (inst->in_reconfig && !ip_flush && op_flush) {
if (!list_empty(&inst->pendingq)) {
diff --git a/drivers/media/video/msm_vidc/msm_vidc_common.h b/drivers/media/video/msm_vidc/msm_vidc_common.h
index 0708724..20b4bc2 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_common.h
+++ b/drivers/media/video/msm_vidc/msm_vidc_common.h
@@ -34,6 +34,11 @@
int msm_comm_flush(struct msm_vidc_inst *inst, u32 flags);
int msm_comm_release_scratch_buffers(struct msm_vidc_inst *inst);
int msm_comm_release_persist_buffers(struct msm_vidc_inst *inst);
+void msm_comm_unload_fw(struct msm_vidc_core *core);
+void change_inst_state(struct msm_vidc_inst *inst,
+ enum instance_state state);
+int msm_comm_unset_ocmem(struct msm_vidc_core *core);
+int msm_comm_free_ocmem(struct msm_vidc_core *core);
#define IS_PRIV_CTRL(idx) (\
(V4L2_CTRL_ID2CLASS(idx) == V4L2_CTRL_CLASS_MPEG) && \
V4L2_CTRL_DRIVER_PRIV(idx))
diff --git a/drivers/media/video/msm_vidc/msm_vidc_internal.h b/drivers/media/video/msm_vidc/msm_vidc_internal.h
index 9806d771..0f6740f 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_internal.h
+++ b/drivers/media/video/msm_vidc/msm_vidc_internal.h
@@ -84,6 +84,7 @@
VIDC_CORE_UNINIT = 0,
VIDC_CORE_INIT,
VIDC_CORE_INIT_DONE,
+ VIDC_CORE_INVALID
};
/*Donot change the enum values unless
@@ -105,6 +106,7 @@
MSM_VIDC_CLOSE,
MSM_VIDC_CLOSE_DONE,
MSM_VIDC_CORE_UNINIT,
+ MSM_VIDC_CORE_INVALID
};
struct buf_info {
@@ -224,6 +226,13 @@
int counter;
};
+struct msm_vidc_ssr_info {
+ struct subsys_device *msm_vidc_dev;
+ struct subsys_desc *msm_vidc_subsys_desc;
+ void *msm_vidc_ramdump_dev;
+ bool ssr_in_progress;
+};
+
struct msm_vidc_core {
struct list_head list;
struct mutex sync_lock;
@@ -241,6 +250,7 @@
enum vidc_core_state state;
struct msm_vidc_resources resources;
struct completion completions[SYS_MSG_END - SYS_MSG_START + 1];
+ struct msm_vidc_ssr_info ssr_info;
};
struct msm_vidc_inst {
diff --git a/drivers/media/video/msm_vidc/msm_vidc_ssr.c b/drivers/media/video/msm_vidc/msm_vidc_ssr.c
new file mode 100644
index 0000000..e8a6745
--- /dev/null
+++ b/drivers/media/video/msm_vidc/msm_vidc_ssr.c
@@ -0,0 +1,174 @@
+/* Copyright (c) 2012, 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 <msm_vidc_ssr.h>
+
+static struct msm_vidc_core *get_vidc_core_from_dev(struct device *dev)
+{
+ struct video_device *vdev;
+ struct msm_video_device *videodev;
+ struct msm_vidc_core *core;
+ vdev = container_of(dev, struct video_device, dev);
+ videodev = container_of(vdev, struct msm_video_device, vdev);
+ core = container_of(videodev, struct msm_vidc_core,
+ vdev[MSM_VIDC_DECODER]);
+ return core;
+}
+int msm_vidc_shutdown(const struct subsys_desc *subsys)
+{
+ struct msm_vidc_inst *inst;
+ struct msm_vidc_core *core = NULL;
+ struct v4l2_event dqevent;
+ struct device *dev;
+ unsigned long flags;
+ int rc = 0;
+ if (!subsys) {
+ dprintk(VIDC_ERR, "Invalid subsys: %p\n", subsys);
+ rc = -EINVAL;
+ goto exit;
+ }
+ dev = subsys->dev;
+ if (dev)
+ core = get_vidc_core_from_dev(dev);
+ if (!core) {
+ dprintk(VIDC_ERR, "Invalid core: %p\n", core);
+ rc = -EINVAL;
+ goto exit;
+ }
+ core->ssr_info.ssr_in_progress = true;
+ spin_lock_irqsave(&core->lock, flags);
+ core->state = VIDC_CORE_INVALID;
+ spin_unlock_irqrestore(&core->lock, flags);
+ dqevent.type = V4L2_EVENT_MSM_VIDC_SYS_ERROR;
+ dqevent.id = 0;
+ list_for_each_entry(inst, &core->instances, list) {
+ if (inst) {
+ v4l2_event_queue_fh(&inst->event_handler, &dqevent);
+ spin_lock_irqsave(&inst->lock, flags);
+ inst->state = MSM_VIDC_CORE_INVALID;
+ spin_unlock_irqrestore(&inst->lock, flags);
+ }
+ }
+exit:
+ return rc;
+}
+int msm_vidc_ramdump(int enable, const struct subsys_desc *subsys)
+{
+ struct ramdump_segment memory_segments[] = {{0x0f500000, 0xFF000} };
+ struct msm_vidc_core *core = NULL;
+ void *dump_addr = NULL;
+ int rc = 0;
+ struct device *dev;
+ if (!subsys) {
+ dprintk(VIDC_ERR, "Invalid subsys: %p\n", subsys);
+ rc = -EINVAL;
+ goto exit;
+ }
+ dev = subsys->dev;
+ if (dev)
+ core = get_vidc_core_from_dev(dev);
+ if (!core) {
+ dprintk(VIDC_ERR, "Invalid core: %p\n", core);
+ rc = -EINVAL;
+ goto exit;
+ }
+ if (enable) {
+ rc = do_ramdump(core->ssr_info.msm_vidc_ramdump_dev,
+ memory_segments,
+ ARRAY_SIZE(memory_segments));
+ if (rc < 0)
+ dprintk(VIDC_DBG, "Failed : FW image memory dump\n");
+ dump_addr = kzalloc(core->resources.ocmem.buf->len, GFP_KERNEL);
+ if (dump_addr)
+ rc = ocmem_dump(OCMEM_VIDEO, core->resources.ocmem.buf,
+ (unsigned long)dump_addr);
+ if (rc < 0) {
+ dprintk(VIDC_DBG, "Failed : OCMEM copy\n");
+ } else {
+ memory_segments[0].address = (unsigned long)dump_addr;
+ memory_segments[0].size =
+ (unsigned long)core->resources.ocmem.buf->len;
+ rc = do_ramdump(core->ssr_info.msm_vidc_ramdump_dev,
+ memory_segments,
+ ARRAY_SIZE(memory_segments));
+ if (rc < 0)
+ dprintk(VIDC_DBG, "Failed : OCMEM dump\n");
+ }
+ kfree(dump_addr);
+ }
+exit:
+ return rc;
+}
+int msm_vidc_powerup(const struct subsys_desc *subsys)
+{
+ unsigned long flags;
+ struct msm_vidc_core *core = NULL;
+ int rc = 0;
+ struct device *dev;
+ if (!subsys) {
+ dprintk(VIDC_ERR, "Invalid subsys: %p\n", subsys);
+ rc = -EINVAL;
+ goto exit;
+ }
+ dev = subsys->dev;
+ if (dev)
+ core = get_vidc_core_from_dev(dev);
+ if (!core) {
+ dprintk(VIDC_ERR, "Invalid core: %p\n", core);
+ rc = -EINVAL;
+ goto exit;
+ }
+ msm_comm_free_ocmem(core);
+ vidc_hal_core_release(core->device);
+ spin_lock_irqsave(&core->lock, flags);
+ core->state = VIDC_CORE_UNINIT;
+ spin_unlock_irqrestore(&core->lock, flags);
+ msm_comm_unload_fw(core);
+exit:
+ return rc;
+}
+void msm_vidc_crash_shutdown(const struct subsys_desc *subsys)
+{
+ dprintk(VIDC_DBG, "Nothing implemented in crash shutdown\n");
+}
+static struct subsys_desc msm_vidc_subsystem = {
+ .name = "msm_vidc",
+ .dev = NULL,
+ .shutdown = msm_vidc_shutdown,
+ .powerup = msm_vidc_powerup,
+ .ramdump = msm_vidc_ramdump,
+ .crash_shutdown = msm_vidc_crash_shutdown
+};
+int msm_vidc_ssr_init(struct msm_vidc_core *core)
+{
+ int rc = 0;
+ msm_vidc_subsystem.dev = &core->vdev[MSM_VIDC_DECODER].vdev.dev;
+ core->ssr_info.msm_vidc_dev = subsys_register(&msm_vidc_subsystem);
+ if (IS_ERR_OR_NULL(core->ssr_info.msm_vidc_dev)) {
+ dprintk(VIDC_ERR, "msm_vidc Sub System registration failed\n");
+ rc = -ENODEV;
+ }
+ core->ssr_info.msm_vidc_ramdump_dev = create_ramdump_device("msm_vidc");
+ if (!core->ssr_info.msm_vidc_ramdump_dev) {
+ dprintk(VIDC_ERR, "Unable to create msm_vidc ramdump device\n");
+ rc = -ENODEV;
+ }
+ core->ssr_info.ssr_in_progress = false;
+ return rc;
+}
+
+int msm_vidc_ssr_uninit(struct msm_vidc_core *core)
+{
+ subsys_unregister(core->ssr_info.msm_vidc_dev);
+ destroy_ramdump_device(core->ssr_info.msm_vidc_ramdump_dev);
+ return 0;
+}
diff --git a/drivers/media/video/msm_vidc/msm_vidc_ssr.h b/drivers/media/video/msm_vidc/msm_vidc_ssr.h
new file mode 100644
index 0000000..90f7380
--- /dev/null
+++ b/drivers/media/video/msm_vidc/msm_vidc_ssr.h
@@ -0,0 +1,28 @@
+/* Copyright (c) 2012, 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 __MSM_VIDC_SSR__
+#define __MSM_VIDC_SSR__
+
+#include <../ramdump.h>
+#include <mach/subsystem_restart.h>
+#include <mach/subsystem_notif.h>
+#include <media/msm_vidc.h>
+#include "msm_vidc_internal.h"
+#include "msm_vidc_common.h"
+#include "msm_vidc_debug.h"
+#include "vidc_hal_api.h"
+int msm_vidc_ssr_init(struct msm_vidc_core *core);
+int msm_vidc_ssr_uninit(struct msm_vidc_core *core);
+
+#endif
diff --git a/drivers/media/video/msm_vidc/vidc_hal_api.h b/drivers/media/video/msm_vidc/vidc_hal_api.h
index 879418d..9def3e3 100644
--- a/drivers/media/video/msm_vidc/vidc_hal_api.h
+++ b/drivers/media/video/msm_vidc/vidc_hal_api.h
@@ -812,6 +812,7 @@
PC_PREP_DONE,
SYS_IDLE,
SYS_DEBUG,
+ SYS_WATCHDOG_TIMEOUT,
/* SESSION COMMANDS_DONE */
SESSION_LOAD_RESOURCE_DONE,
SESSION_INIT_DONE,
diff --git a/drivers/media/video/msm_vidc/vidc_hal_interrupt_handler.c b/drivers/media/video/msm_vidc/vidc_hal_interrupt_handler.c
index 043ed73..7eb0ae1 100644
--- a/drivers/media/video/msm_vidc/vidc_hal_interrupt_handler.c
+++ b/drivers/media/video/msm_vidc/vidc_hal_interrupt_handler.c
@@ -13,7 +13,9 @@
#include <linux/slab.h>
#include <linux/list.h>
+#include <linux/interrupt.h>
#include "vidc_hal.h"
+#include "vidc_hal_io.h"
#include "msm_vidc_debug.h"
static enum vidc_status vidc_map_hal_err_status(int hfi_err)
@@ -136,7 +138,14 @@
cmd_done.data = &event_notify;
device->callback(VIDC_EVENT_CHANGE, &cmd_done);
}
-
+static void hal_process_sys_watchdog_timeout(struct hal_device *device)
+{
+ struct msm_vidc_cb_cmd_done cmd_done;
+ disable_irq_nosync(device->hal_data->irq);
+ memset(&cmd_done, 0, sizeof(struct msm_vidc_cb_cmd_done));
+ cmd_done.device_id = device->device_id;
+ device->callback(SYS_WATCHDOG_TIMEOUT, &cmd_done);
+}
static void hal_process_event_notify(struct hal_device *device,
struct hfi_msg_event_notify_packet *pkt)
{
@@ -151,6 +160,7 @@
switch (pkt->event_id) {
case HFI_EVENT_SYS_ERROR:
dprintk(VIDC_INFO, "HFI_EVENT_SYS_ERROR");
+ hal_process_sys_watchdog_timeout(device);
break;
case HFI_EVENT_SESSION_ERROR:
dprintk(VIDC_INFO, "HFI_EVENT_SESSION_ERROR");
@@ -167,7 +177,6 @@
break;
}
}
-
static void hal_process_sys_init_done(struct hal_device *device,
struct hfi_msg_sys_init_done_packet *pkt)
{
@@ -764,6 +773,11 @@
}
dprintk(VIDC_INFO, "Received: 0x%x in ", msg_hdr->packet);
+ if ((device->intr_status & VIDC_WRAPPER_INTR_CLEAR_A2HWD_BMSK)) {
+ dprintk(VIDC_ERR, "Received: Watchdog timeout %s", __func__);
+ hal_process_sys_watchdog_timeout(device);
+ }
+
switch (msg_hdr->packet) {
case HFI_MSG_EVENT_NOTIFY:
hal_process_event_notify(device,
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index 07beb50..cc390d0 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -2366,6 +2366,7 @@
#define V4L2_EVENT_MSM_VIDC_PORT_SETTINGS_CHANGED_INSUFFICIENT \
(V4L2_EVENT_MSM_VIDC_START + 3)
#define V4L2_EVENT_MSM_VIDC_CLOSE_DONE (V4L2_EVENT_MSM_VIDC_START + 4)
+#define V4L2_EVENT_MSM_VIDC_SYS_ERROR (V4L2_EVENT_MSM_VIDC_START + 5)
/* Payload for V4L2_EVENT_VSYNC */