From aec5601a93b8940df440a1aa86b11e49b5f4120b Mon Sep 17 00:00:00 2001 From: Allon Huang Date: Wed, 8 Apr 2020 10:57:24 +0800 Subject: [PATCH] drivers: media: platform: rockchip: cif: fix panic when stream on log: [ 79.156494] Unable to handle kernel NULL pointer dereference at virtual address 00000200 [ 79.168633] pgd = df940b55 [ 79.171344] [00000200] *pgd=00000000 [ 79.174933] Internal error: Oops: 5 [#1] PREEMPT SMP ARM [ 79.180249] Modules linked in: [ 79.183321] CPU: 1 PID: 1574 Comm: ImguThread Not tainted 4.19.111 #155 [ 79.189933] Hardware name: Generic DT based system [ 79.194734] PC is at __list_add_valid+0x24/0x84 [ 79.199272] LR is at rkcif_buf_queue+0x78/0x164 [ 79.203805] pc : [] lr : [] psr: 60000093 [ 79.210073] sp : c6127d80 ip : ca0bc7e4 fp : c13e5ee4 [ 79.215295] r10: 60000013 r9 : ca0bc800 r8 : cb5e5b58 [ 79.220525] r7 : 00000200 r6 : cb5e5a00 r5 : ca0bc7e4 r4 : ca0bc320 [ 79.227048] r3 : ca0bc800 r2 : 00000200 r1 : 00000200 r0 : cb5e5b58 [ 79.233573] Flags: nZCv IRQs off FIQs on Mode SVC_32 ISA ARM Segment none [ 79.240797] Control: 10c5387d Table: 66f4c06a DAC: 00000051 Signed-off-by: Allon Huang Change-Id: Ifb3f307971faa9d80dc5a996a41327d11fac4d1b --- drivers/media/platform/rockchip/cif/capture.c | 211 +++++++++++------- 1 file changed, 126 insertions(+), 85 deletions(-) diff --git a/drivers/media/platform/rockchip/cif/capture.c b/drivers/media/platform/rockchip/cif/capture.c index 74187c3b71c3..1e176446e409 100644 --- a/drivers/media/platform/rockchip/cif/capture.c +++ b/drivers/media/platform/rockchip/cif/capture.c @@ -514,51 +514,69 @@ static void rkcif_assign_new_buffer_oneframe(struct rkcif_stream *stream, void __iomem *base = dev->base_addr; u32 frm_addr_y, frm_addr_uv; - /* Set up an empty buffer for the next frame */ spin_lock(&stream->vbq_lock); - if (!list_empty(&stream->buf_head)) { - if (stream->frame_phase == CIF_CSI_FRAME0_READY || - stat == RKCIF_YUV_ADDR_STATE_INIT) { - stream->curr_buf = list_first_entry(&stream->buf_head, - struct rkcif_buffer, queue); - list_del(&stream->curr_buf->queue); - buffer = stream->curr_buf; - } - if (stream->frame_phase == CIF_CSI_FRAME1_READY || - stat == RKCIF_YUV_ADDR_STATE_INIT) { - stream->next_buf = list_first_entry(&stream->buf_head, - struct rkcif_buffer, queue); - list_del(&stream->next_buf->queue); - buffer = stream->next_buf; - } - } else { - if (stream->frame_phase == CIF_CSI_FRAME0_READY) - stream->curr_buf = NULL; - if (stream->frame_phase == CIF_CSI_FRAME1_READY) - stream->next_buf = NULL; - buffer = NULL; - } - spin_unlock(&stream->vbq_lock); - if (stat == RKCIF_YUV_ADDR_STATE_INIT) { - if (stream->curr_buf && stream->next_buf) { + if (!stream->curr_buf) { + if (!list_empty(&stream->buf_head)) { + stream->curr_buf = list_first_entry(&stream->buf_head, + struct rkcif_buffer, + queue); + list_del(&stream->curr_buf->queue); + } + } + + if (stream->curr_buf) { write_cif_reg(base, CIF_FRM0_ADDR_Y, stream->curr_buf->buff_addr[RKCIF_PLANE_Y]); write_cif_reg(base, CIF_FRM0_ADDR_UV, stream->curr_buf->buff_addr[RKCIF_PLANE_CBCR]); + } else { + write_cif_reg(base, CIF_FRM0_ADDR_Y, + dummy_buf->dma_addr); + write_cif_reg(base, CIF_FRM0_ADDR_UV, + dummy_buf->dma_addr); + } + + if (!stream->next_buf) { + if (!list_empty(&stream->buf_head)) { + stream->next_buf = list_first_entry(&stream->buf_head, + struct rkcif_buffer, queue); + list_del(&stream->next_buf->queue); + } + } + + if (stream->next_buf) { write_cif_reg(base, CIF_FRM1_ADDR_Y, stream->next_buf->buff_addr[RKCIF_PLANE_Y]); write_cif_reg(base, CIF_FRM1_ADDR_UV, stream->next_buf->buff_addr[RKCIF_PLANE_CBCR]); } else { - v4l2_dbg(1, rkcif_debug, &dev->v4l2_dev, "Drop to dummy buf\n"); - write_cif_reg(base, CIF_FRM0_ADDR_Y, dummy_buf->dma_addr); - write_cif_reg(base, CIF_FRM0_ADDR_UV, dummy_buf->dma_addr); write_cif_reg(base, CIF_FRM1_ADDR_Y, dummy_buf->dma_addr); write_cif_reg(base, CIF_FRM1_ADDR_UV, dummy_buf->dma_addr); } + } else if (stat == RKCIF_YUV_ADDR_STATE_UPDATE) { + if (!list_empty(&stream->buf_head)) { + if (stream->frame_phase == CIF_CSI_FRAME0_READY) { + stream->curr_buf = list_first_entry(&stream->buf_head, + struct rkcif_buffer, queue); + list_del(&stream->curr_buf->queue); + buffer = stream->curr_buf; + } + + if (stream->frame_phase == CIF_CSI_FRAME1_READY ) { + stream->next_buf = list_first_entry(&stream->buf_head, + struct rkcif_buffer, queue); + list_del(&stream->next_buf->queue); + buffer = stream->next_buf; + } + } else { + if (stream->frame_phase == CIF_CSI_FRAME0_READY) + stream->curr_buf = NULL; + if (stream->frame_phase == CIF_CSI_FRAME1_READY) + stream->next_buf = NULL; + buffer = NULL; + } - } else { if (stream->frame_phase == CIF_CSI_FRAME0_READY) { frm_addr_y = CIF_FRM0_ADDR_Y; frm_addr_uv = CIF_FRM0_ADDR_UV; @@ -581,6 +599,7 @@ static void rkcif_assign_new_buffer_oneframe(struct rkcif_stream *stream, "frame Drop to dummy buf\n"); } } + spin_unlock(&stream->vbq_lock); } static void rkcif_assign_new_buffer_pingpong(struct rkcif_stream *stream, @@ -592,31 +611,6 @@ static void rkcif_assign_new_buffer_pingpong(struct rkcif_stream *stream, void __iomem *base = dev->base_addr; u32 frm_addr_y, frm_addr_uv; - /* Set up an empty buffer for the next frame */ - spin_lock(&stream->vbq_lock); - if (!list_empty(&stream->buf_head)) { - if (stream->frame_phase == CIF_CSI_FRAME0_READY || init) { - stream->curr_buf = list_first_entry(&stream->buf_head, - struct rkcif_buffer, queue); - list_del(&stream->curr_buf->queue); - buffer = stream->curr_buf; - } - if (stream->frame_phase == CIF_CSI_FRAME1_READY || init) { - stream->next_buf = list_first_entry(&stream->buf_head, - struct rkcif_buffer, queue); - list_del(&stream->next_buf->queue); - buffer = stream->next_buf; - } - } else { - if (stream->frame_phase & CIF_CSI_FRAME0_READY) - stream->curr_buf = NULL; - if (stream->frame_phase & CIF_CSI_FRAME1_READY) - stream->next_buf = NULL; - - buffer = NULL; - } - spin_unlock(&stream->vbq_lock); - if (init) { u32 frm0_addr_y, frm0_addr_uv; u32 frm1_addr_y, frm1_addr_uv; @@ -633,49 +627,94 @@ static void rkcif_assign_new_buffer_pingpong(struct rkcif_stream *stream, frm1_addr_uv = CIF_FRM1_ADDR_UV; } - if (stream->curr_buf && stream->next_buf) { + spin_lock(&stream->vbq_lock); + if (!stream->curr_buf) { + if (!list_empty(&stream->buf_head)) { + stream->curr_buf = list_first_entry(&stream->buf_head, + struct rkcif_buffer, + queue); + list_del(&stream->curr_buf->queue); + } + } + + if (stream->curr_buf) { write_cif_reg(base, frm0_addr_y, stream->curr_buf->buff_addr[RKCIF_PLANE_Y]); write_cif_reg(base, frm0_addr_uv, stream->curr_buf->buff_addr[RKCIF_PLANE_CBCR]); + } else { + write_cif_reg(base, frm0_addr_y, dummy_buf->dma_addr); + write_cif_reg(base, frm0_addr_uv, dummy_buf->dma_addr); + } + + if (!stream->next_buf) { + if (!list_empty(&stream->buf_head)) { + stream->next_buf = list_first_entry(&stream->buf_head, + struct rkcif_buffer, queue); + list_del(&stream->next_buf->queue); + } + } + + if (stream->next_buf) { write_cif_reg(base, frm1_addr_y, stream->next_buf->buff_addr[RKCIF_PLANE_Y]); write_cif_reg(base, frm1_addr_uv, stream->next_buf->buff_addr[RKCIF_PLANE_CBCR]); } else { - v4l2_dbg(1, rkcif_debug, &dev->v4l2_dev, "Drop to dummy buf\n"); - write_cif_reg(base, frm0_addr_y, dummy_buf->dma_addr); - write_cif_reg(base, frm0_addr_uv, dummy_buf->dma_addr); write_cif_reg(base, frm1_addr_y, dummy_buf->dma_addr); write_cif_reg(base, frm1_addr_uv, dummy_buf->dma_addr); } - - return; - } - - if (dev->active_sensor->mbus.type == V4L2_MBUS_CSI2) { - frm_addr_y = stream->frame_phase & CIF_CSI_FRAME1_READY ? - (CIF_CSI_FRM1_ADDR_Y_ID0 + 0x20 * csi_ch) : - (CIF_CSI_FRM0_ADDR_Y_ID0 + 0x20 * csi_ch); - frm_addr_uv = stream->frame_phase & CIF_CSI_FRAME1_READY ? - (CIF_CSI_FRM1_ADDR_UV_ID0 + 0x20 * csi_ch) : - (CIF_CSI_FRM0_ADDR_UV_ID0 + 0x20 * csi_ch); + spin_unlock(&stream->vbq_lock); } else { - frm_addr_y = stream->frame_phase & CIF_CSI_FRAME1_READY ? - CIF_FRM1_ADDR_Y : CIF_FRM0_ADDR_Y; - frm_addr_uv = stream->frame_phase & CIF_CSI_FRAME1_READY ? - CIF_FRM1_ADDR_UV : CIF_FRM0_ADDR_UV; - } + if (dev->active_sensor->mbus.type == V4L2_MBUS_CSI2) { + frm_addr_y = stream->frame_phase & CIF_CSI_FRAME1_READY ? + (CIF_CSI_FRM1_ADDR_Y_ID0 + 0x20 * csi_ch) : + (CIF_CSI_FRM0_ADDR_Y_ID0 + 0x20 * csi_ch); + frm_addr_uv = stream->frame_phase & CIF_CSI_FRAME1_READY ? + (CIF_CSI_FRM1_ADDR_UV_ID0 + 0x20 * csi_ch) : + (CIF_CSI_FRM0_ADDR_UV_ID0 + 0x20 * csi_ch); + } else { + frm_addr_y = stream->frame_phase & CIF_CSI_FRAME1_READY ? + CIF_FRM1_ADDR_Y : CIF_FRM0_ADDR_Y; + frm_addr_uv = stream->frame_phase & CIF_CSI_FRAME1_READY ? + CIF_FRM1_ADDR_UV : CIF_FRM0_ADDR_UV; + } - if (buffer) { - write_cif_reg(base, frm_addr_y, - buffer->buff_addr[RKCIF_PLANE_Y]); - write_cif_reg(base, frm_addr_uv, - buffer->buff_addr[RKCIF_PLANE_CBCR]); - } else { - v4l2_dbg(1, rkcif_debug, &dev->v4l2_dev, "Drop to dummy buf\n"); - write_cif_reg(base, frm_addr_y, dummy_buf->dma_addr); - write_cif_reg(base, frm_addr_uv, dummy_buf->dma_addr); + spin_lock(&stream->vbq_lock); + if (!list_empty(&stream->buf_head)) { + if (stream->frame_phase == CIF_CSI_FRAME0_READY) { + stream->curr_buf = list_first_entry(&stream->buf_head, + struct rkcif_buffer, queue); + list_del(&stream->curr_buf->queue); + buffer = stream->curr_buf; + } + + if (stream->frame_phase == CIF_CSI_FRAME1_READY) { + stream->next_buf = list_first_entry(&stream->buf_head, + struct rkcif_buffer, queue); + list_del(&stream->next_buf->queue); + buffer = stream->next_buf; + } + } else { + if (stream->frame_phase == CIF_CSI_FRAME0_READY) + stream->curr_buf = NULL; + if (stream->frame_phase == CIF_CSI_FRAME1_READY) + stream->next_buf = NULL; + buffer = NULL; + } + spin_unlock(&stream->vbq_lock); + + if (buffer) { + write_cif_reg(base, frm_addr_y, + buffer->buff_addr[RKCIF_PLANE_Y]); + write_cif_reg(base, frm_addr_uv, + buffer->buff_addr[RKCIF_PLANE_CBCR]); + } else { + write_cif_reg(base, frm_addr_y, dummy_buf->dma_addr); + write_cif_reg(base, frm_addr_uv, dummy_buf->dma_addr); + v4l2_dbg(1, rkcif_debug, &dev->v4l2_dev, + "frame Drop to dummy buf\n"); + } } } @@ -1389,9 +1428,9 @@ static int rkcif_start_streaming(struct vb2_queue *queue, unsigned int count) } else { ret = rkcif_stream_start(stream); } + if (ret < 0) goto runtime_put; - /* start sub-devices */ ret = dev->pipe.set_stream(&dev->pipe, true); if (ret < 0) @@ -1605,6 +1644,7 @@ static int rkcif_fh_open(struct file *filp) } else { rkcif_soft_reset(cifdev, true); } + return v4l2_fh_open(filp); } @@ -1617,6 +1657,7 @@ static int rkcif_fh_release(struct file *filp) int ret = 0; ret = vb2_fop_release(filp); + mutex_lock(&cifdev->stream_lock); if (!atomic_dec_return(&cifdev->fh_cnt)) rkcif_soft_reset(cifdev, true);