linux-stable/drivers/media/pci/cobalt/cobalt-irq.c
Hans Verkuil 0cd25448a1 media: cobalt: replace VB2_BUF_STATE_REQUEUEING by _ERROR
The cobalt driver is the only driver that uses VB2_BUF_STATE_REQUEUEING.
Replace it by VB2_BUF_STATE_ERROR so we can drop support for the
REQUEUEING state.

The requeueing state was used in the cobalt driver to optimize
buffer handling while waiting for a valid signal: by requeueing
buffers internally there was no need for userspace to handle and
requeue buffers with the ERROR flag set.

However, requeueing also makes the buffer handling unordered, which
is generally a bad idea. Requeueing also does not work with requests
and any future fence support.

Since it is really a minor optimization in the cobalt driver it is
best to just return the buffer in an ERROR state. With this change
support for requeueing can now be removed in vb2.

Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
2019-03-19 13:50:38 -04:00

247 lines
7.2 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* cobalt interrupt handling
*
* Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates.
* All rights reserved.
*/
#include <media/i2c/adv7604.h>
#include "cobalt-driver.h"
#include "cobalt-irq.h"
#include "cobalt-omnitek.h"
static void cobalt_dma_stream_queue_handler(struct cobalt_stream *s)
{
struct cobalt *cobalt = s->cobalt;
int rx = s->video_channel;
struct m00473_freewheel_regmap __iomem *fw =
COBALT_CVI_FREEWHEEL(s->cobalt, rx);
struct m00233_video_measure_regmap __iomem *vmr =
COBALT_CVI_VMR(s->cobalt, rx);
struct m00389_cvi_regmap __iomem *cvi =
COBALT_CVI(s->cobalt, rx);
struct m00479_clk_loss_detector_regmap __iomem *clkloss =
COBALT_CVI_CLK_LOSS(s->cobalt, rx);
struct cobalt_buffer *cb;
bool skip = false;
spin_lock(&s->irqlock);
if (list_empty(&s->bufs)) {
pr_err("no buffers!\n");
spin_unlock(&s->irqlock);
return;
}
/* Give the fresh filled up buffer to the user.
* Note that the interrupt is only sent if the DMA can continue
* with a new buffer, so it is always safe to return this buffer
* to userspace. */
cb = list_first_entry(&s->bufs, struct cobalt_buffer, list);
list_del(&cb->list);
spin_unlock(&s->irqlock);
if (s->is_audio || s->is_output)
goto done;
if (s->unstable_frame) {
uint32_t stat = ioread32(&vmr->irq_status);
iowrite32(stat, &vmr->irq_status);
if (!(ioread32(&vmr->status) &
M00233_STATUS_BITMAP_INIT_DONE_MSK)) {
cobalt_dbg(1, "!init_done\n");
if (s->enable_freewheel)
goto restart_fw;
goto done;
}
if (ioread32(&clkloss->status) &
M00479_STATUS_BITMAP_CLOCK_MISSING_MSK) {
iowrite32(0, &clkloss->ctrl);
iowrite32(M00479_CTRL_BITMAP_ENABLE_MSK, &clkloss->ctrl);
cobalt_dbg(1, "no clock\n");
if (s->enable_freewheel)
goto restart_fw;
goto done;
}
if ((stat & (M00233_IRQ_STATUS_BITMAP_VACTIVE_AREA_MSK |
M00233_IRQ_STATUS_BITMAP_HACTIVE_AREA_MSK)) ||
ioread32(&vmr->vactive_area) != s->timings.bt.height ||
ioread32(&vmr->hactive_area) != s->timings.bt.width) {
cobalt_dbg(1, "unstable\n");
if (s->enable_freewheel)
goto restart_fw;
goto done;
}
if (!s->enable_cvi) {
s->enable_cvi = true;
iowrite32(M00389_CONTROL_BITMAP_ENABLE_MSK, &cvi->control);
goto done;
}
if (!(ioread32(&cvi->status) & M00389_STATUS_BITMAP_LOCK_MSK)) {
cobalt_dbg(1, "cvi no lock\n");
if (s->enable_freewheel)
goto restart_fw;
goto done;
}
if (!s->enable_freewheel) {
cobalt_dbg(1, "stable\n");
s->enable_freewheel = true;
iowrite32(0, &fw->ctrl);
goto done;
}
cobalt_dbg(1, "enabled fw\n");
iowrite32(M00233_CONTROL_BITMAP_ENABLE_MEASURE_MSK |
M00233_CONTROL_BITMAP_ENABLE_INTERRUPT_MSK,
&vmr->control);
iowrite32(M00473_CTRL_BITMAP_ENABLE_MSK, &fw->ctrl);
s->enable_freewheel = false;
s->unstable_frame = false;
s->skip_first_frames = 2;
skip = true;
goto done;
}
if (ioread32(&fw->status) & M00473_STATUS_BITMAP_FREEWHEEL_MODE_MSK) {
restart_fw:
cobalt_dbg(1, "lost lock\n");
iowrite32(M00233_CONTROL_BITMAP_ENABLE_MEASURE_MSK,
&vmr->control);
iowrite32(M00473_CTRL_BITMAP_ENABLE_MSK |
M00473_CTRL_BITMAP_FORCE_FREEWHEEL_MODE_MSK,
&fw->ctrl);
iowrite32(0, &cvi->control);
s->unstable_frame = true;
s->enable_freewheel = false;
s->enable_cvi = false;
}
done:
if (s->skip_first_frames) {
skip = true;
s->skip_first_frames--;
}
cb->vb.vb2_buf.timestamp = ktime_get_ns();
/* TODO: the sequence number should be read from the FPGA so we
also know about dropped frames. */
cb->vb.sequence = s->sequence++;
vb2_buffer_done(&cb->vb.vb2_buf,
(skip || s->unstable_frame) ?
VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
}
irqreturn_t cobalt_irq_handler(int irq, void *dev_id)
{
struct cobalt *cobalt = (struct cobalt *)dev_id;
u32 dma_interrupt =
cobalt_read_bar0(cobalt, DMA_INTERRUPT_STATUS_REG) & 0xffff;
u32 mask = cobalt_read_bar1(cobalt, COBALT_SYS_STAT_MASK);
u32 edge = cobalt_read_bar1(cobalt, COBALT_SYS_STAT_EDGE);
int i;
/* Clear DMA interrupt */
cobalt_write_bar0(cobalt, DMA_INTERRUPT_STATUS_REG, dma_interrupt);
cobalt_write_bar1(cobalt, COBALT_SYS_STAT_MASK, mask & ~edge);
cobalt_write_bar1(cobalt, COBALT_SYS_STAT_EDGE, edge);
for (i = 0; i < COBALT_NUM_STREAMS; i++) {
struct cobalt_stream *s = &cobalt->streams[i];
unsigned dma_fifo_mask = s->dma_fifo_mask;
if (dma_interrupt & (1 << s->dma_channel)) {
cobalt->irq_dma[i]++;
/* Give fresh buffer to user and chain newly
* queued buffers */
cobalt_dma_stream_queue_handler(s);
if (!s->is_audio) {
edge &= ~dma_fifo_mask;
cobalt_write_bar1(cobalt, COBALT_SYS_STAT_MASK,
mask & ~edge);
}
}
if (s->is_audio)
continue;
if (edge & s->adv_irq_mask)
set_bit(COBALT_STREAM_FL_ADV_IRQ, &s->flags);
if ((edge & mask & dma_fifo_mask) && vb2_is_streaming(&s->q)) {
cobalt_info("full rx FIFO %d\n", i);
cobalt->irq_full_fifo++;
}
}
queue_work(cobalt->irq_work_queues, &cobalt->irq_work_queue);
if (edge & mask & (COBALT_SYSSTAT_VI0_INT1_MSK |
COBALT_SYSSTAT_VI1_INT1_MSK |
COBALT_SYSSTAT_VI2_INT1_MSK |
COBALT_SYSSTAT_VI3_INT1_MSK |
COBALT_SYSSTAT_VIHSMA_INT1_MSK |
COBALT_SYSSTAT_VOHSMA_INT1_MSK))
cobalt->irq_adv1++;
if (edge & mask & (COBALT_SYSSTAT_VI0_INT2_MSK |
COBALT_SYSSTAT_VI1_INT2_MSK |
COBALT_SYSSTAT_VI2_INT2_MSK |
COBALT_SYSSTAT_VI3_INT2_MSK |
COBALT_SYSSTAT_VIHSMA_INT2_MSK))
cobalt->irq_adv2++;
if (edge & mask & COBALT_SYSSTAT_VOHSMA_INT1_MSK)
cobalt->irq_advout++;
if (dma_interrupt)
cobalt->irq_dma_tot++;
if (!(edge & mask) && !dma_interrupt)
cobalt->irq_none++;
dma_interrupt = cobalt_read_bar0(cobalt, DMA_INTERRUPT_STATUS_REG);
return IRQ_HANDLED;
}
void cobalt_irq_work_handler(struct work_struct *work)
{
struct cobalt *cobalt =
container_of(work, struct cobalt, irq_work_queue);
int i;
for (i = 0; i < COBALT_NUM_NODES; i++) {
struct cobalt_stream *s = &cobalt->streams[i];
if (test_and_clear_bit(COBALT_STREAM_FL_ADV_IRQ, &s->flags)) {
u32 mask;
v4l2_subdev_call(cobalt->streams[i].sd, core,
interrupt_service_routine, 0, NULL);
mask = cobalt_read_bar1(cobalt, COBALT_SYS_STAT_MASK);
cobalt_write_bar1(cobalt, COBALT_SYS_STAT_MASK,
mask | s->adv_irq_mask);
}
}
}
void cobalt_irq_log_status(struct cobalt *cobalt)
{
u32 mask;
int i;
cobalt_info("irq: adv1=%u adv2=%u advout=%u none=%u full=%u\n",
cobalt->irq_adv1, cobalt->irq_adv2, cobalt->irq_advout,
cobalt->irq_none, cobalt->irq_full_fifo);
cobalt_info("irq: dma_tot=%u (", cobalt->irq_dma_tot);
for (i = 0; i < COBALT_NUM_STREAMS; i++)
pr_cont("%s%u", i ? "/" : "", cobalt->irq_dma[i]);
pr_cont(")\n");
cobalt->irq_dma_tot = cobalt->irq_adv1 = cobalt->irq_adv2 = 0;
cobalt->irq_advout = cobalt->irq_none = cobalt->irq_full_fifo = 0;
memset(cobalt->irq_dma, 0, sizeof(cobalt->irq_dma));
mask = cobalt_read_bar1(cobalt, COBALT_SYS_STAT_MASK);
cobalt_write_bar1(cobalt, COBALT_SYS_STAT_MASK,
mask |
COBALT_SYSSTAT_VI0_LOST_DATA_MSK |
COBALT_SYSSTAT_VI1_LOST_DATA_MSK |
COBALT_SYSSTAT_VI2_LOST_DATA_MSK |
COBALT_SYSSTAT_VI3_LOST_DATA_MSK |
COBALT_SYSSTAT_VIHSMA_LOST_DATA_MSK |
COBALT_SYSSTAT_VOHSMA_LOST_DATA_MSK |
COBALT_SYSSTAT_AUD_IN_LOST_DATA_MSK |
COBALT_SYSSTAT_AUD_OUT_LOST_DATA_MSK);
}