Загрузка данных
const struct mxc_isi_format_info *
mxc_isi_format_by_fourcc(u32 fourcc, enum mxc_isi_video_type type)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(mxc_isi_formats); i++) {
const struct mxc_isi_format_info *fmt = &mxc_isi_formats[i];
if (fmt->fourcc == fourcc && fmt->type & type)
return fmt;
}
return NULL;
}
const struct mxc_isi_format_info *
mxc_isi_format_enum(unsigned int index, enum mxc_isi_video_type type)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(mxc_isi_formats); i++) {
const struct mxc_isi_format_info *fmt = &mxc_isi_formats[i];
if (!(fmt->type & type))
continue;
if (!index)
return fmt;
index--;
}
return NULL;
}
const struct mxc_isi_format_info *
mxc_isi_format_try(struct mxc_isi_pipe *pipe, struct v4l2_pix_format_mplane *pix,
enum mxc_isi_video_type type)
{
const struct mxc_isi_format_info *fmt;
unsigned int max_width;
unsigned int i;
max_width = (!pipe->bypass && pipe->id == pipe->isi->pdata->num_channels - 1)
? MXC_ISI_MAX_WIDTH_UNCHAINED
: MXC_ISI_MAX_WIDTH_CHAINED;
fmt = mxc_isi_format_by_fourcc(pix->pixelformat, type);
if (!fmt)
fmt = &mxc_isi_formats[0];
pix->width = clamp(pix->width, MXC_ISI_MIN_WIDTH, max_width);
pix->height = clamp(pix->height, MXC_ISI_MIN_HEIGHT, MXC_ISI_MAX_HEIGHT);
pix->pixelformat = fmt->fourcc;
pix->field = V4L2_FIELD_NONE;
if (pix->colorspace == V4L2_COLORSPACE_DEFAULT) {
pix->colorspace = MXC_ISI_DEF_COLOR_SPACE;
pix->ycbcr_enc = MXC_ISI_DEF_YCBCR_ENC;
pix->quantization = MXC_ISI_DEF_QUANTIZATION;
pix->xfer_func = MXC_ISI_DEF_XFER_FUNC;
}
if (pix->ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT)
pix->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(pix->colorspace);
if (pix->quantization == V4L2_QUANTIZATION_DEFAULT) {
bool is_rgb = fmt->encoding == MXC_ISI_ENC_RGB;
pix->quantization =
V4L2_MAP_QUANTIZATION_DEFAULT(is_rgb, pix->colorspace,
pix->ycbcr_enc);
}
if (pix->xfer_func == V4L2_XFER_FUNC_DEFAULT)
pix->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(pix->colorspace);
pix->num_planes = fmt->mem_planes;
for (i = 0; i < fmt->color_planes; ++i) {
struct v4l2_plane_pix_format *plane = &pix->plane_fmt[i];
unsigned int bpl;
/* The pitch must be identical for all planes. */
if (i == 0)
bpl = clamp(plane->bytesperline,
pix->width * fmt->depth[0] / 8,
65535U);
else
bpl = pix->plane_fmt[0].bytesperline;
plane->bytesperline = bpl;
plane->sizeimage = plane->bytesperline * pix->height;
if (i >= 1)
plane->sizeimage /= fmt->vsub;
}
/*
* For single-planar pixel formats with multiple color planes,
* concatenate the size of all planes and clear all planes but the
* first one.
*/
if (fmt->color_planes != fmt->mem_planes) {
for (i = 1; i < fmt->color_planes; ++i) {
struct v4l2_plane_pix_format *plane = &pix->plane_fmt[i];
pix->plane_fmt[0].sizeimage += plane->sizeimage;
plane->bytesperline = 0;
plane->sizeimage = 0;
}
}
return fmt;
}
/* -----------------------------------------------------------------------------
* videobuf2 queue operations
*/
static void mxc_isi_video_frame_write_done(struct mxc_isi_pipe *pipe,
u32 status)
{
struct mxc_isi_video *video = &pipe->video;
const struct mxc_isi_plat_data *pdata = pipe->isi->pdata;
struct device *dev = pipe->isi->dev;
struct mxc_isi_buffer *next_buf;
struct mxc_isi_buffer *buf;
enum mxc_isi_buf_id buf_id;
spin_lock(&video->buf_lock);
/* Check which buffer has just completed. */
buf_id = pdata->buf_active_reverse
? (status & CHNL_STS_BUF1_ACTIVE ? MXC_ISI_BUF2 : MXC_ISI_BUF1)
: (status & CHNL_STS_BUF1_ACTIVE ? MXC_ISI_BUF1 : MXC_ISI_BUF2);
buf = list_first_entry_or_null(&video->out_active,
struct mxc_isi_buffer, list);
/* Safety check, this should really never happen. */
if (!buf) {
dev_warn(dev, "trying to access empty active list\n");
goto done;
}
/*
* If the buffer that has completed doesn't match the buffer on the
* front of the active list, it means we have lost one frame end
* interrupt (or possibly a large odd number of interrupts, although
* quite unlikely).
*
* For instance, if IRQ1 is lost and we handle IRQ2, both B1 and B2
* have been completed, but B3 hasn't been programmed, BUF2 still
* addresses B1 and the ISI is now writing in B1 instead of B3. We
* can't complete B2 as that would result in out-of-order completion.
*
* The only option is to ignore this interrupt and try again. When IRQ3
* will be handled, we will complete B1 and be in sync again.
*/
if (buf->id != buf_id) {
dev_dbg(dev, "buffer ID mismatch (expected %u, got %u), skipping\n",
buf->id, buf_id);
/*
* Increment the frame count by two to account for the missed
* and the ignored interrupts.
*/
video->frame_count += 2;
goto done;
}
/* Pick the next buffer and queue it to the hardware. */
next_buf = list_first_entry_or_null(&video->out_pending,
struct mxc_isi_buffer, list);
if (!next_buf) {
next_buf = list_first_entry_or_null(&video->out_discard,
struct mxc_isi_buffer, list);
/* Safety check, this should never happen. */
if (!next_buf) {
dev_warn(dev, "trying to access empty discard list\n");
goto done;
}
}
mxc_isi_channel_set_outbuf(pipe, next_buf->dma_addrs, buf_id);
mxc_isi_channel_set_max_size(pipe, &video->pix, pdata->buf_max_size);
next_buf->id = buf_id;
/*
* Check if we have raced with the end of frame interrupt. If so, we
* can't tell if the ISI has recorded the new address, or is still
* using the previous buffer. We must assume the latter as that is the
* worst case.
*
* For instance, if we are handling IRQ1 and now detect the FRM
* interrupt, assume B2 has completed and the ISI has switched to BUF2
* using B1 just before we programmed B3. Unlike in the previous race
* condition, B3 has been programmed and will be written to the next
* time the ISI switches to BUF2. We can however handle this exactly as
* the first race condition, as we'll program B3 (still at the head of
* the pending list) when handling IRQ3.
*/
status = mxc_isi_channel_irq_status(pipe, false);
if (status & CHNL_STS_FRM_STRD) {
dev_dbg(dev, "raced with frame end interrupt\n");
video->frame_count += 2;
goto done;
}
/*
* The next buffer has been queued successfully, move it to the active
* list, and complete the current buffer.
*/
list_move_tail(&next_buf->list, &video->out_active);
if (!buf->discard) {
list_del_init(&buf->list);
buf->v4l2_buf.sequence = video->frame_count;
buf->v4l2_buf.vb2_buf.timestamp = ktime_get_ns();
vb2_buffer_done(&buf->v4l2_buf.vb2_buf, VB2_BUF_STATE_DONE);
} else {
list_move_tail(&buf->list, &video->out_discard);
}
video->frame_count++;
done:
spin_unlock(&video->buf_lock);
}