Usually only the subdevs have exact knowledge about buffer size requirements for a compressed data transfer over the video bus. The .g_framesamples() subdev callback is used to retrieve an exact required maximum memory buffer size for a compressed data frame from a subdev. This allows to avoid allocating huge buffers in the host driver. To make sure the allocated buffers are big enough for a subdev configuration at the time of VIDIOC_STREAMON, the video pipeline validation has been extended with an additional check. Flag FMT_FLAGS_COMPRESSED indicates that the buffer size have to be determined by means of .s_framesamples() callback. Signed-off-by: Sylwester Nawrocki <s.nawrocki@xxxxxxxxxxx> Signed-off-by: Kyungmin Park <kyungmin.park@xxxxxxxxxxx> --- drivers/media/video/s5p-fimc/fimc-capture.c | 63 +++++++++++++++++++++++++-- drivers/media/video/s5p-fimc/fimc-core.c | 11 ++++- drivers/media/video/s5p-fimc/fimc-core.h | 9 +++- 3 files changed, 74 insertions(+), 9 deletions(-) diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index 2092bcb..08fb9c5 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -246,6 +246,10 @@ static unsigned int get_plane_size(struct fimc_frame *fr, unsigned int plane) { if (!fr || plane >= fr->fmt->memplanes) return 0; + + if (fr->fmt->flags & FMT_FLAGS_COMPRESSED) + return fr->payload[0]; + return fr->f_width * fr->f_height * fr->fmt->depth[plane] / 8; } @@ -730,6 +734,28 @@ static int fimc_cap_g_fmt_mplane(struct file *file, void *fh, return fimc_fill_format(&ctx->d_frame, f); } +/* + * For all compressed image formats supported by this driver one + * media bus sample corresponds to one byte in the output buffer. + */ +static int fimc_capture_get_bufsize(struct fimc_dev *fimc, + struct v4l2_mbus_framefmt *mf, + unsigned int *size) +{ + unsigned int buf_size = 0; + + int ret = v4l2_subdev_call(fimc->pipeline.sensor, video, + g_mbus_framesamples, mf, &buf_size); + if (ret) + return ret; + + if (buf_size > FIMC_MAX_JPEG_BUF_SIZE) + return -EINVAL; + + *size = max(min(*size, (u32)FIMC_MAX_JPEG_BUF_SIZE), buf_size); + return 0; +} + static int fimc_cap_try_fmt_mplane(struct file *file, void *fh, struct v4l2_format *f) { @@ -770,7 +796,11 @@ static int fimc_cap_try_fmt_mplane(struct file *file, void *fh, } fimc_adjust_mplane_format(ffmt, pix->width, pix->height, pix); - return 0; + + if (!(ffmt->flags & FMT_FLAGS_COMPRESSED)) + return 0; + + return fimc_capture_get_bufsize(fimc, &mf, &pix->plane_fmt[0].sizeimage); } static void fimc_capture_mark_jpeg_xfer(struct fimc_ctx *ctx, bool jpeg) @@ -827,9 +857,19 @@ static int fimc_capture_set_format(struct fimc_dev *fimc, struct v4l2_format *f) pix->height = mf->height; } fimc_adjust_mplane_format(ff->fmt, pix->width, pix->height, pix); - for (i = 0; i < ff->fmt->colplanes; i++) - ff->payload[i] = - (pix->width * pix->height * ff->fmt->depth[i]) / 8; + + if (ff->fmt->flags & FMT_FLAGS_COMPRESSED) { + ret = fimc_capture_get_bufsize(fimc, mf, + &pix->plane_fmt[0].sizeimage); + if (ret) + return ret; + ff->payload[0] = pix->plane_fmt[0].sizeimage; + } else { + for (i = 0; i < ff->fmt->colplanes; i++) { + ff->payload[i] = pix->width * pix->height * + ff->fmt->depth[i] / 8; + } + } set_frame_bounds(ff, pix->width, pix->height); /* Reset the composition rectangle if not yet configured */ @@ -938,6 +978,21 @@ static int fimc_pipeline_validate(struct fimc_dev *fimc) src_fmt.format.height != sink_fmt.format.height || src_fmt.format.code != sink_fmt.format.code) return -EPIPE; + + if (sd == fimc->pipeline.sensor) { + unsigned int buf_size = 0; + + if (sink_fmt.format.code != V4L2_MBUS_FMT_JPEG_1X8) + continue; + + ret = v4l2_subdev_call(sd, video, g_mbus_framesamples, + &src_fmt.format, &buf_size); + if (ret < 0 && ret != -ENOIOCTLCMD) + return -EPIPE; + + if (!ret && vid_cap->ctx->d_frame.payload[0] < buf_size) + return -EPIPE; + } } return 0; } diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index 1cc6e9e..574df1d 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c @@ -163,11 +163,10 @@ static struct fimc_fmt fimc_formats[] = { .name = "JPEG encoded data", .fourcc = V4L2_PIX_FMT_JPEG, .color = S5P_FIMC_JPEG, - .depth = { 8 }, .memplanes = 1, .colplanes = 1, .mbus_code = V4L2_MBUS_FMT_JPEG_1X8, - .flags = FMT_FLAGS_CAM, + .flags = FMT_FLAGS_CAM | FMT_FLAGS_COMPRESSED, }, }; @@ -900,6 +899,11 @@ int fimc_fill_format(struct fimc_frame *frame, struct v4l2_format *f) if (frame->fmt->colplanes == 1) /* packed formats */ bpl = (bpl * frame->fmt->depth[0]) / 8; pixm->plane_fmt[i].bytesperline = bpl; + + if (frame->fmt->flags & FMT_FLAGS_COMPRESSED) { + pixm->plane_fmt[i].sizeimage = frame->payload[i]; + continue; + } pixm->plane_fmt[i].sizeimage = (frame->o_width * frame->o_height * frame->fmt->depth[i]) / 8; } @@ -941,6 +945,9 @@ void fimc_adjust_mplane_format(struct fimc_fmt *fmt, u32 width, u32 height, pix->height = height; pix->width = width; + if (fmt->flags & FMT_FLAGS_COMPRESSED) + return; + for (i = 0; i < pix->num_planes; ++i) { u32 bpl = pix->plane_fmt[i].bytesperline; u32 *sizeimage = &pix->plane_fmt[i].sizeimage; diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h index c7f01c4..25b2a86 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.h +++ b/drivers/media/video/s5p-fimc/fimc-core.h @@ -17,6 +17,7 @@ #include <linux/types.h> #include <linux/videodev2.h> #include <linux/io.h> +#include <asm/sizes.h> #include <media/media-entity.h> #include <media/videobuf2-core.h> @@ -44,6 +45,7 @@ #define SCALER_MAX_VRATIO 64 #define DMA_MIN_SIZE 8 #define FIMC_CAMIF_MAX_HEIGHT 0x2000 +#define FIMC_MAX_JPEG_BUF_SIZE (10 * SZ_1M) /* indices to the clocks array */ enum { @@ -160,8 +162,9 @@ struct fimc_fmt { u16 colplanes; u8 depth[VIDEO_MAX_PLANES]; u16 flags; -#define FMT_FLAGS_CAM (1 << 0) -#define FMT_FLAGS_M2M (1 << 1) +#define FMT_FLAGS_CAM (1 << 0) +#define FMT_FLAGS_M2M (1 << 1) +#define FMT_FLAGS_COMPRESSED (1 << 2) }; /** @@ -279,7 +282,7 @@ struct fimc_frame { u32 offs_v; u32 width; u32 height; - unsigned long payload[VIDEO_MAX_PLANES]; + unsigned int payload[VIDEO_MAX_PLANES]; struct fimc_addr paddr; struct fimc_dma_offset dma_offset; struct fimc_fmt *fmt; -- 1.7.7.1 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html