diff options
Diffstat (limited to 'drivers/media/test-drivers')
39 files changed, 1623 insertions, 760 deletions
diff --git a/drivers/media/test-drivers/vicodec/vicodec-core.c b/drivers/media/test-drivers/vicodec/vicodec-core.c index e13f5452b927..c45f5cf12ded 100644 --- a/drivers/media/test-drivers/vicodec/vicodec-core.c +++ b/drivers/media/test-drivers/vicodec/vicodec-core.c @@ -26,7 +26,7 @@ #include "codec-v4l2-fwht.h" MODULE_DESCRIPTION("Virtual codec device"); -MODULE_AUTHOR("Hans Verkuil <hans.verkuil@cisco.com>"); +MODULE_AUTHOR("Hans Verkuil <hansverk@cisco.com>"); MODULE_LICENSE("GPL v2"); static bool multiplanar; @@ -43,6 +43,8 @@ MODULE_PARM_DESC(debug, " activates debug info"); #define MIN_WIDTH 640U #define MAX_HEIGHT 2160U #define MIN_HEIGHT 360U +/* Recommended number of buffers for the stateful codecs */ +#define VICODEC_REC_BUFS 2 #define dprintk(dev, fmt, arg...) \ v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg) @@ -1215,8 +1217,7 @@ static int vicodec_encoder_cmd(struct file *file, void *fh, if (ret < 0) return ret; - if (!vb2_is_streaming(&ctx->fh.m2m_ctx->cap_q_ctx.q) || - !vb2_is_streaming(&ctx->fh.m2m_ctx->out_q_ctx.q)) + if (!vb2_is_streaming(&ctx->fh.m2m_ctx->out_q_ctx.q)) return 0; ret = v4l2_m2m_ioctl_encoder_cmd(file, fh, ec); @@ -1250,8 +1251,7 @@ static int vicodec_decoder_cmd(struct file *file, void *fh, if (ret < 0) return ret; - if (!vb2_is_streaming(&ctx->fh.m2m_ctx->cap_q_ctx.q) || - !vb2_is_streaming(&ctx->fh.m2m_ctx->out_q_ctx.q)) + if (!vb2_is_streaming(&ctx->fh.m2m_ctx->out_q_ctx.q)) return 0; ret = v4l2_m2m_ioctl_decoder_cmd(file, fh, dc); @@ -1345,6 +1345,7 @@ static const struct v4l2_ioctl_ops vicodec_ioctl_ops = { .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + .vidioc_remove_bufs = v4l2_m2m_ioctl_remove_bufs, .vidioc_streamon = v4l2_m2m_ioctl_streamon, .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, @@ -1689,8 +1690,6 @@ static const struct vb2_ops vicodec_qops = { .buf_request_complete = vicodec_buf_request_complete, .start_streaming = vicodec_start_streaming, .stop_streaming = vicodec_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; static int queue_init(void *priv, struct vb2_queue *src_vq, @@ -1708,12 +1707,14 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, src_vq->ops = &vicodec_qops; src_vq->mem_ops = &vb2_vmalloc_memops; src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; - if (ctx->is_enc) + if (ctx->is_enc) { src_vq->lock = &ctx->dev->stateful_enc.mutex; - else if (ctx->is_stateless) + src_vq->min_reqbufs_allocation = VICODEC_REC_BUFS; + } else if (ctx->is_stateless) { src_vq->lock = &ctx->dev->stateless_dec.mutex; - else + } else { src_vq->lock = &ctx->dev->stateful_dec.mutex; + } src_vq->supports_requests = ctx->is_stateless; src_vq->requires_requests = ctx->is_stateless; ret = vb2_queue_init(src_vq); @@ -1731,6 +1732,8 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->mem_ops = &vb2_vmalloc_memops; dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; dst_vq->lock = src_vq->lock; + if (!ctx->is_stateless && !ctx->is_enc) + dst_vq->min_reqbufs_allocation = VICODEC_REC_BUFS; return vb2_queue_init(dst_vq); } @@ -1855,11 +1858,16 @@ static int vicodec_open(struct file *file) 1, 31, 1, 20); v4l2_ctrl_new_std(hdl, &vicodec_ctrl_ops, V4L2_CID_FWHT_P_FRAME_QP, 1, 31, 1, 20); - if (ctx->is_enc) - v4l2_ctrl_new_std(hdl, &vicodec_ctrl_ops, - V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, 1, 1, 1, 1); + if (ctx->is_stateless) v4l2_ctrl_new_custom(hdl, &vicodec_ctrl_stateless_state, NULL); + else + v4l2_ctrl_new_std(hdl, &vicodec_ctrl_ops, ctx->is_enc ? + V4L2_CID_MIN_BUFFERS_FOR_OUTPUT : + V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, + VICODEC_REC_BUFS, VICODEC_REC_BUFS, 1, + VICODEC_REC_BUFS); + if (hdl->error) { rc = hdl->error; v4l2_ctrl_handler_free(hdl); @@ -2208,7 +2216,7 @@ static void vicodec_remove(struct platform_device *pdev) static struct platform_driver vicodec_pdrv = { .probe = vicodec_probe, - .remove_new = vicodec_remove, + .remove = vicodec_remove, .driver = { .name = VICODEC_NAME, }, diff --git a/drivers/media/test-drivers/vidtv/vidtv_bridge.c b/drivers/media/test-drivers/vidtv/vidtv_bridge.c index 613949df897d..438483c62fac 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_bridge.c +++ b/drivers/media/test-drivers/vidtv/vidtv_bridge.c @@ -191,10 +191,11 @@ static int vidtv_start_streaming(struct vidtv_dvb *dvb) mux_args.mux_buf_sz = mux_buf_sz; - dvb->streaming = true; dvb->mux = vidtv_mux_init(dvb->fe[0], dev, &mux_args); if (!dvb->mux) return -ENOMEM; + + dvb->streaming = true; vidtv_mux_start_thread(dvb->mux); dev_dbg_ratelimited(dev, "Started streaming\n"); @@ -205,6 +206,11 @@ static int vidtv_stop_streaming(struct vidtv_dvb *dvb) { struct device *dev = &dvb->pdev->dev; + if (!dvb->streaming) { + dev_warn_ratelimited(dev, "No streaming. Skipping.\n"); + return 0; + } + dvb->streaming = false; vidtv_mux_stop_thread(dvb->mux); vidtv_mux_destroy(dvb->mux); @@ -572,7 +578,7 @@ static struct platform_driver vidtv_bridge_driver = { .name = VIDTV_PDEV_NAME, }, .probe = vidtv_bridge_probe, - .remove_new = vidtv_bridge_remove, + .remove = vidtv_bridge_remove, }; static void __exit vidtv_bridge_exit(void) diff --git a/drivers/media/test-drivers/vidtv/vidtv_channel.c b/drivers/media/test-drivers/vidtv/vidtv_channel.c index 7838e6272712..f3023e91b3eb 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_channel.c +++ b/drivers/media/test-drivers/vidtv/vidtv_channel.c @@ -497,7 +497,7 @@ free_sdt: vidtv_psi_sdt_table_destroy(m->si.sdt); free_pat: vidtv_psi_pat_table_destroy(m->si.pat); - return 0; + return -EINVAL; } void vidtv_channel_si_destroy(struct vidtv_mux *m) diff --git a/drivers/media/test-drivers/vidtv/vidtv_demod.c b/drivers/media/test-drivers/vidtv/vidtv_demod.c index 7a0cd9601917..505f96fccbf3 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_demod.c +++ b/drivers/media/test-drivers/vidtv/vidtv_demod.c @@ -407,7 +407,7 @@ static const struct dvb_frontend_ops vidtv_demod_ops = { }; static const struct i2c_device_id vidtv_demod_i2c_id_table[] = { - {"dvb_vidtv_demod", 0}, + { "dvb_vidtv_demod" }, {} }; MODULE_DEVICE_TABLE(i2c, vidtv_demod_i2c_id_table); diff --git a/drivers/media/test-drivers/vidtv/vidtv_tuner.c b/drivers/media/test-drivers/vidtv/vidtv_tuner.c index a748737d47f3..4ba302d569d6 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_tuner.c +++ b/drivers/media/test-drivers/vidtv/vidtv_tuner.c @@ -385,7 +385,7 @@ static const struct dvb_tuner_ops vidtv_tuner_ops = { }; static const struct i2c_device_id vidtv_tuner_i2c_id_table[] = { - {"dvb_vidtv_tuner", 0}, + { "dvb_vidtv_tuner" }, {} }; MODULE_DEVICE_TABLE(i2c, vidtv_tuner_i2c_id_table); diff --git a/drivers/media/test-drivers/vim2m.c b/drivers/media/test-drivers/vim2m.c index 3e3b424b4860..1d1a9e768505 100644 --- a/drivers/media/test-drivers/vim2m.c +++ b/drivers/media/test-drivers/vim2m.c @@ -26,6 +26,7 @@ #include <media/v4l2-ctrls.h> #include <media/v4l2-event.h> #include <media/videobuf2-vmalloc.h> +#include <media/v4l2-common.h> MODULE_DESCRIPTION("Virtual device for mem2mem framework testing"); MODULE_AUTHOR("Pawel Osciak, <pawel@osciak.com>"); @@ -42,6 +43,10 @@ static unsigned int default_transtime = 40; /* Max 25 fps */ module_param(default_transtime, uint, 0644); MODULE_PARM_DESC(default_transtime, "default transaction time in ms"); +static unsigned int multiplanar = 1; +module_param(multiplanar, uint, 0644); +MODULE_PARM_DESC(multiplanar, "1 (default) creates a single planar device, 2 creates multiplanar device."); + #define MIN_W 32 #define MIN_H 32 #define MAX_W 640 @@ -134,7 +139,8 @@ static struct vim2m_fmt formats[] = { struct vim2m_q_data { unsigned int width; unsigned int height; - unsigned int sizeimage; + unsigned int num_mem_planes; + unsigned int sizeimage[VIDEO_MAX_PLANES]; unsigned int sequence; struct vim2m_fmt *fmt; }; @@ -193,6 +199,7 @@ struct vim2m_dev { struct mutex dev_mutex; struct v4l2_m2m_dev *m2m_dev; + bool multiplanar; }; struct vim2m_ctx { @@ -237,8 +244,10 @@ static struct vim2m_q_data *get_q_data(struct vim2m_ctx *ctx, { switch (type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT: + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: return &ctx->q_data[V4L2_M2M_SRC]; case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: return &ctx->q_data[V4L2_M2M_DST]; default: return NULL; @@ -249,8 +258,10 @@ static const char *type_name(enum v4l2_buf_type type) { switch (type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT: + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: return "Output"; case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: return "Capture"; default: return "Invalid"; @@ -720,6 +731,7 @@ static int vidioc_g_fmt(struct vim2m_ctx *ctx, struct v4l2_format *f) { struct vb2_queue *vq; struct vim2m_q_data *q_data; + int ret; vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); if (!vq) @@ -729,12 +741,12 @@ static int vidioc_g_fmt(struct vim2m_ctx *ctx, struct v4l2_format *f) if (!q_data) return -EINVAL; - f->fmt.pix.width = q_data->width; - f->fmt.pix.height = q_data->height; + ret = v4l2_fill_pixfmt(&f->fmt.pix, q_data->fmt->fourcc, + q_data->width, q_data->height); + if (ret) + return ret; + f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.pixelformat = q_data->fmt->fourcc; - f->fmt.pix.bytesperline = (q_data->width * q_data->fmt->depth) >> 3; - f->fmt.pix.sizeimage = q_data->sizeimage; f->fmt.pix.colorspace = ctx->colorspace; f->fmt.pix.xfer_func = ctx->xfer_func; f->fmt.pix.ycbcr_enc = ctx->ycbcr_enc; @@ -743,43 +755,102 @@ static int vidioc_g_fmt(struct vim2m_ctx *ctx, struct v4l2_format *f) return 0; } +static int vidioc_g_fmt_mplane(struct vim2m_ctx *ctx, struct v4l2_format *f) +{ + struct vb2_queue *vq; + struct vim2m_q_data *q_data; + int ret; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + q_data = get_q_data(ctx, f->type); + if (!q_data) + return -EINVAL; + + ret = v4l2_fill_pixfmt_mp(&f->fmt.pix_mp, q_data->fmt->fourcc, + q_data->width, q_data->height); + if (ret) + return ret; + + f->fmt.pix_mp.field = V4L2_FIELD_NONE; + f->fmt.pix_mp.colorspace = ctx->colorspace; + f->fmt.pix_mp.xfer_func = ctx->xfer_func; + f->fmt.pix_mp.ycbcr_enc = ctx->ycbcr_enc; + f->fmt.pix_mp.quantization = ctx->quant; + + return 0; +} + static int vidioc_g_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f) { + struct vim2m_dev *dev = video_drvdata(file); + + if (dev->multiplanar) + return -ENOTTY; + return vidioc_g_fmt(file2ctx(file), f); } static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { + struct vim2m_dev *dev = video_drvdata(file); + + if (dev->multiplanar) + return -ENOTTY; + return vidioc_g_fmt(file2ctx(file), f); } -static int vidioc_try_fmt(struct v4l2_format *f, struct vim2m_fmt *fmt) +static int vidioc_g_fmt_vid_out_mplane(struct file *file, void *priv, + struct v4l2_format *f) { - int walign, halign; - /* - * V4L2 specification specifies the driver corrects the - * format struct if any of the dimensions is unsupported - */ - if (f->fmt.pix.height < MIN_H) - f->fmt.pix.height = MIN_H; - else if (f->fmt.pix.height > MAX_H) - f->fmt.pix.height = MAX_H; - - if (f->fmt.pix.width < MIN_W) - f->fmt.pix.width = MIN_W; - else if (f->fmt.pix.width > MAX_W) - f->fmt.pix.width = MAX_W; - - get_alignment(f->fmt.pix.pixelformat, &walign, &halign); - f->fmt.pix.width &= ~(walign - 1); - f->fmt.pix.height &= ~(halign - 1); - f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; - f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + struct vim2m_dev *dev = video_drvdata(file); + + if (!dev->multiplanar) + return -ENOTTY; + + return vidioc_g_fmt_mplane(file2ctx(file), f); +} + +static int vidioc_g_fmt_vid_cap_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vim2m_dev *dev = video_drvdata(file); + + if (!dev->multiplanar) + return -ENOTTY; + + return vidioc_g_fmt_mplane(file2ctx(file), f); +} + +static int vidioc_try_fmt(struct v4l2_format *f, bool is_mplane) +{ + int walign, halign, ret; + int width = (is_mplane) ? f->fmt.pix_mp.width : f->fmt.pix.width; + int height = (is_mplane) ? f->fmt.pix_mp.height : f->fmt.pix.height; + u32 pixfmt = (is_mplane) ? f->fmt.pix_mp.pixelformat : + f->fmt.pix.pixelformat; + + width = clamp(width, MIN_W, MAX_W); + height = clamp(height, MIN_H, MAX_H); + + get_alignment(pixfmt, &walign, &halign); + width = ALIGN(width, walign); + height = ALIGN(height, halign); + f->fmt.pix.field = V4L2_FIELD_NONE; - return 0; + if (is_mplane) { + ret = v4l2_fill_pixfmt_mp(&f->fmt.pix_mp, pixfmt, width, + height); + } else { + ret = v4l2_fill_pixfmt(&f->fmt.pix, pixfmt, width, height); + } + return ret; } static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, @@ -787,6 +858,10 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, { struct vim2m_fmt *fmt; struct vim2m_ctx *ctx = file2ctx(file); + struct vim2m_dev *dev = video_drvdata(file); + + if (dev->multiplanar) + return -ENOTTY; fmt = find_format(f->fmt.pix.pixelformat); if (!fmt) { @@ -804,7 +879,36 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.ycbcr_enc = ctx->ycbcr_enc; f->fmt.pix.quantization = ctx->quant; - return vidioc_try_fmt(f, fmt); + return vidioc_try_fmt(f, false); +} + +static int vidioc_try_fmt_vid_cap_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vim2m_fmt *fmt; + struct vim2m_ctx *ctx = file2ctx(file); + struct vim2m_dev *dev = video_drvdata(file); + + if (!dev->multiplanar) + return -ENOTTY; + + fmt = find_format(f->fmt.pix_mp.pixelformat); + if (!fmt) { + f->fmt.pix_mp.pixelformat = formats[0].fourcc; + fmt = find_format(f->fmt.pix_mp.pixelformat); + } + if (!(fmt->types & MEM2MEM_CAPTURE)) { + v4l2_err(&ctx->dev->v4l2_dev, + "Fourcc format (0x%08x) invalid.\n", + f->fmt.pix.pixelformat); + return -EINVAL; + } + f->fmt.pix_mp.colorspace = ctx->colorspace; + f->fmt.pix_mp.xfer_func = ctx->xfer_func; + f->fmt.pix_mp.ycbcr_enc = ctx->ycbcr_enc; + f->fmt.pix_mp.quantization = ctx->quant; + + return vidioc_try_fmt(f, true); } static int vidioc_try_fmt_vid_out(struct file *file, void *priv, @@ -812,6 +916,10 @@ static int vidioc_try_fmt_vid_out(struct file *file, void *priv, { struct vim2m_fmt *fmt; struct vim2m_ctx *ctx = file2ctx(file); + struct vim2m_dev *dev = video_drvdata(file); + + if (dev->multiplanar) + return -ENOTTY; fmt = find_format(f->fmt.pix.pixelformat); if (!fmt) { @@ -827,13 +935,45 @@ static int vidioc_try_fmt_vid_out(struct file *file, void *priv, if (!f->fmt.pix.colorspace) f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709; - return vidioc_try_fmt(f, fmt); + return vidioc_try_fmt(f, false); +} + +static int vidioc_try_fmt_vid_out_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vim2m_fmt *fmt; + struct vim2m_ctx *ctx = file2ctx(file); + struct vim2m_dev *dev = video_drvdata(file); + + if (!dev->multiplanar) + return -ENOTTY; + + fmt = find_format(f->fmt.pix_mp.pixelformat); + if (!fmt) { + f->fmt.pix_mp.pixelformat = formats[0].fourcc; + fmt = find_format(f->fmt.pix_mp.pixelformat); + } + if (!(fmt->types & MEM2MEM_OUTPUT)) { + v4l2_err(&ctx->dev->v4l2_dev, + "Fourcc format (0x%08x) invalid.\n", + f->fmt.pix_mp.pixelformat); + return -EINVAL; + } + if (!f->fmt.pix_mp.colorspace) + f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_REC709; + + return vidioc_try_fmt(f, true); } static int vidioc_s_fmt(struct vim2m_ctx *ctx, struct v4l2_format *f) { struct vim2m_q_data *q_data; struct vb2_queue *vq; + unsigned int i; + bool is_mplane = ctx->dev->multiplanar; + u32 pixfmt = (is_mplane) ? f->fmt.pix_mp.pixelformat : f->fmt.pix.pixelformat; + u32 width = (is_mplane) ? f->fmt.pix_mp.width : f->fmt.pix.width; + u32 height = (is_mplane) ? f->fmt.pix_mp.height : f->fmt.pix.height; vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); if (!vq) @@ -848,11 +988,17 @@ static int vidioc_s_fmt(struct vim2m_ctx *ctx, struct v4l2_format *f) return -EBUSY; } - q_data->fmt = find_format(f->fmt.pix.pixelformat); - q_data->width = f->fmt.pix.width; - q_data->height = f->fmt.pix.height; - q_data->sizeimage = q_data->width * q_data->height - * q_data->fmt->depth >> 3; + q_data->fmt = find_format(pixfmt); + q_data->width = width; + q_data->height = height; + if (is_mplane) { + q_data->num_mem_planes = f->fmt.pix_mp.num_planes; + for (i = 0; i < f->fmt.pix_mp.num_planes; i++) + q_data->sizeimage[i] = f->fmt.pix_mp.plane_fmt[i].sizeimage; + } else { + q_data->sizeimage[0] = f->fmt.pix.sizeimage; + q_data->num_mem_planes = 1; + } dprintk(ctx->dev, 1, "Format for type %s: %dx%d (%d bpp), fmt: %c%c%c%c\n", @@ -870,6 +1016,10 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { int ret; + struct vim2m_dev *dev = video_drvdata(file); + + if (dev->multiplanar) + return -ENOTTY; ret = vidioc_try_fmt_vid_cap(file, priv, f); if (ret) @@ -878,12 +1028,32 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, return vidioc_s_fmt(file2ctx(file), f); } +static int vidioc_s_fmt_vid_cap_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + int ret; + struct vim2m_dev *dev = video_drvdata(file); + + if (!dev->multiplanar) + return -ENOTTY; + + ret = vidioc_try_fmt_vid_cap_mplane(file, priv, f); + if (ret) + return ret; + + return vidioc_s_fmt(file2ctx(file), f); +} + static int vidioc_s_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f) { struct vim2m_ctx *ctx = file2ctx(file); + struct vim2m_dev *dev = video_drvdata(file); int ret; + if (dev->multiplanar) + return -ENOTTY; + ret = vidioc_try_fmt_vid_out(file, priv, f); if (ret) return ret; @@ -898,6 +1068,30 @@ static int vidioc_s_fmt_vid_out(struct file *file, void *priv, return ret; } +static int vidioc_s_fmt_vid_out_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vim2m_ctx *ctx = file2ctx(file); + struct vim2m_dev *dev = video_drvdata(file); + int ret; + + if (!dev->multiplanar) + return -ENOTTY; + + ret = vidioc_try_fmt_vid_out_mplane(file, priv, f); + if (ret) + return ret; + + ret = vidioc_s_fmt(file2ctx(file), f); + if (!ret) { + ctx->colorspace = f->fmt.pix_mp.colorspace; + ctx->xfer_func = f->fmt.pix_mp.xfer_func; + ctx->ycbcr_enc = f->fmt.pix_mp.ycbcr_enc; + ctx->quant = f->fmt.pix_mp.quantization; + } + return ret; +} + static int vim2m_s_ctrl(struct v4l2_ctrl *ctrl) { struct vim2m_ctx *ctx = @@ -948,11 +1142,17 @@ static const struct v4l2_ioctl_ops vim2m_ioctl_ops = { .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt_vid_cap_mplane, + .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt_vid_cap_mplane, + .vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt_vid_cap_mplane, .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out, .vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out, .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out, .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out, + .vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt_vid_out_mplane, + .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt_vid_out_mplane, + .vidioc_s_fmt_vid_out_mplane = vidioc_s_fmt_vid_out_mplane, .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, @@ -981,23 +1181,32 @@ static int vim2m_queue_setup(struct vb2_queue *vq, { struct vim2m_ctx *ctx = vb2_get_drv_priv(vq); struct vim2m_q_data *q_data; - unsigned int size, count = *nbuffers; + unsigned int size, p, count = *nbuffers; q_data = get_q_data(ctx, vq->type); if (!q_data) return -EINVAL; - size = q_data->width * q_data->height * q_data->fmt->depth >> 3; + size = 0; + for (p = 0; p < q_data->num_mem_planes; p++) + size += q_data->sizeimage[p]; while (size * count > MEM2MEM_VID_MEM_LIMIT) (count)--; *nbuffers = count; - if (*nplanes) - return sizes[0] < size ? -EINVAL : 0; - - *nplanes = 1; - sizes[0] = size; + if (*nplanes) { + if (*nplanes != q_data->num_mem_planes) + return -EINVAL; + for (p = 0; p < q_data->num_mem_planes; p++) { + if (sizes[p] < q_data->sizeimage[p]) + return -EINVAL; + } + } else { + *nplanes = q_data->num_mem_planes; + for (p = 0; p < q_data->num_mem_planes; p++) + sizes[p] = q_data->sizeimage[p]; + } dprintk(ctx->dev, 1, "%s: get %d buffer(s) of size %d each.\n", type_name(vq->type), count, size); @@ -1024,21 +1233,24 @@ static int vim2m_buf_prepare(struct vb2_buffer *vb) { struct vim2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); struct vim2m_q_data *q_data; + unsigned int p; dprintk(ctx->dev, 2, "type: %s\n", type_name(vb->vb2_queue->type)); q_data = get_q_data(ctx, vb->vb2_queue->type); if (!q_data) return -EINVAL; - if (vb2_plane_size(vb, 0) < q_data->sizeimage) { - dprintk(ctx->dev, 1, - "%s data will not fit into plane (%lu < %lu)\n", - __func__, vb2_plane_size(vb, 0), - (long)q_data->sizeimage); - return -EINVAL; - } - vb2_set_plane_payload(vb, 0, q_data->sizeimage); + for (p = 0; p < q_data->num_mem_planes; p++) { + if (vb2_plane_size(vb, p) < q_data->sizeimage[p]) { + dprintk(ctx->dev, 1, + "%s data will not fit into plane (%lu < %lu)\n", + __func__, vb2_plane_size(vb, p), + (long)q_data->sizeimage[p]); + return -EINVAL; + } + vb2_set_plane_payload(vb, p, q_data->sizeimage[p]); + } return 0; } @@ -1100,8 +1312,6 @@ static const struct vb2_ops vim2m_qops = { .buf_queue = vim2m_buf_queue, .start_streaming = vim2m_start_streaming, .stop_streaming = vim2m_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .buf_request_complete = vim2m_buf_request_complete, }; @@ -1111,7 +1321,8 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, struct vim2m_ctx *ctx = priv; int ret; - src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + src_vq->type = (ctx->dev->multiplanar) ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE : + V4L2_BUF_TYPE_VIDEO_OUTPUT; src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; src_vq->drv_priv = ctx; src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); @@ -1125,7 +1336,8 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, if (ret) return ret; - dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + dst_vq->type = (ctx->dev->multiplanar) ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE : + V4L2_BUF_TYPE_VIDEO_CAPTURE; dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; dst_vq->drv_priv = ctx; dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); @@ -1199,10 +1411,11 @@ static int vim2m_open(struct file *file) ctx->q_data[V4L2_M2M_SRC].fmt = &formats[0]; ctx->q_data[V4L2_M2M_SRC].width = 640; ctx->q_data[V4L2_M2M_SRC].height = 480; - ctx->q_data[V4L2_M2M_SRC].sizeimage = + ctx->q_data[V4L2_M2M_SRC].sizeimage[0] = ctx->q_data[V4L2_M2M_SRC].width * ctx->q_data[V4L2_M2M_SRC].height * (ctx->q_data[V4L2_M2M_SRC].fmt->depth >> 3); + ctx->q_data[V4L2_M2M_SRC].num_mem_planes = 1; ctx->q_data[V4L2_M2M_DST] = ctx->q_data[V4L2_M2M_SRC]; ctx->colorspace = V4L2_COLORSPACE_REC709; @@ -1279,7 +1492,7 @@ static const struct video_device vim2m_videodev = { .ioctl_ops = &vim2m_ioctl_ops, .minor = -1, .release = vim2m_device_release, - .device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING, + .device_caps = V4L2_CAP_STREAMING, }; static const struct v4l2_m2m_ops m2m_ops = { @@ -1310,15 +1523,16 @@ static int vim2m_probe(struct platform_device *pdev) atomic_set(&dev->num_inst, 0); mutex_init(&dev->dev_mutex); + dev->multiplanar = (multiplanar == 2); + dev->vfd = vim2m_videodev; vfd = &dev->vfd; vfd->lock = &dev->dev_mutex; vfd->v4l2_dev = &dev->v4l2_dev; + vfd->device_caps |= (dev->multiplanar) ? V4L2_CAP_VIDEO_M2M_MPLANE : + V4L2_CAP_VIDEO_M2M; video_set_drvdata(vfd, dev); - v4l2_info(&dev->v4l2_dev, - "Device registered as /dev/video%d\n", vfd->num); - platform_set_drvdata(pdev, dev); dev->m2m_dev = v4l2_m2m_init(&m2m_ops); @@ -1345,6 +1559,9 @@ static int vim2m_probe(struct platform_device *pdev) goto error_m2m; } + v4l2_info(&dev->v4l2_dev, + "Device registered as /dev/video%d\n", vfd->num); + #ifdef CONFIG_MEDIA_CONTROLLER ret = v4l2_m2m_register_media_controller(dev->m2m_dev, vfd, MEDIA_ENT_F_PROC_VIDEO_SCALER); @@ -1394,7 +1611,7 @@ static void vim2m_remove(struct platform_device *pdev) static struct platform_driver vim2m_pdrv = { .probe = vim2m_probe, - .remove_new = vim2m_remove, + .remove = vim2m_remove, .driver = { .name = MEM2MEM_NAME, }, diff --git a/drivers/media/test-drivers/vimc/vimc-capture.c b/drivers/media/test-drivers/vimc/vimc-capture.c index 2a2d19d23bab..10df039278e7 100644 --- a/drivers/media/test-drivers/vimc/vimc-capture.c +++ b/drivers/media/test-drivers/vimc/vimc-capture.c @@ -221,6 +221,7 @@ static const struct v4l2_ioctl_ops vimc_capture_ioctl_ops = { .vidioc_expbuf = vb2_ioctl_expbuf, .vidioc_streamon = vb2_ioctl_streamon, .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_remove_bufs = vb2_ioctl_remove_bufs, }; static void vimc_capture_return_all_buffers(struct vimc_capture_device *vcapture, @@ -325,12 +326,6 @@ static const struct vb2_ops vimc_capture_qops = { .buf_queue = vimc_capture_buf_queue, .queue_setup = vimc_capture_queue_setup, .buf_prepare = vimc_capture_buffer_prepare, - /* - * Since q->lock is set we can use the standard - * vb2_ops_wait_prepare/finish helper functions. - */ - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; static const struct media_entity_operations vimc_capture_mops = { @@ -432,7 +427,7 @@ static struct vimc_ent_device *vimc_capture_add(struct vimc_device *vimc, q->mem_ops = vimc_allocator == VIMC_ALLOCATOR_DMA_CONTIG ? &vb2_dma_contig_memops : &vb2_vmalloc_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_queued_buffers = 2; + q->min_reqbufs_allocation = 2; q->lock = &vcapture->lock; q->dev = v4l2_dev->dev; @@ -493,7 +488,7 @@ err_free_vcapture: return ERR_PTR(ret); } -struct vimc_ent_type vimc_capture_type = { +const struct vimc_ent_type vimc_capture_type = { .add = vimc_capture_add, .unregister = vimc_capture_unregister, .release = vimc_capture_release diff --git a/drivers/media/test-drivers/vimc/vimc-common.c b/drivers/media/test-drivers/vimc/vimc-common.c index 2e72974e35b4..4f4fcb26e236 100644 --- a/drivers/media/test-drivers/vimc/vimc-common.c +++ b/drivers/media/test-drivers/vimc/vimc-common.c @@ -8,6 +8,8 @@ #include <linux/init.h> #include <linux/module.h> +#include <media/v4l2-ctrls.h> + #include "vimc-common.h" /* @@ -358,6 +360,7 @@ int vimc_ent_sd_register(struct vimc_ent_device *ved, u32 function, u16 num_pads, struct media_pad *pads, + const struct v4l2_subdev_internal_ops *int_ops, const struct v4l2_subdev_ops *sd_ops) { int ret; @@ -367,6 +370,7 @@ int vimc_ent_sd_register(struct vimc_ent_device *ved, /* Initialize the subdev */ v4l2_subdev_init(sd, sd_ops); + sd->internal_ops = int_ops; sd->entity.function = function; sd->entity.ops = &vimc_ent_sd_mops; sd->owner = THIS_MODULE; @@ -383,17 +387,36 @@ int vimc_ent_sd_register(struct vimc_ent_device *ved, if (ret) return ret; + /* + * Finalize the subdev initialization if it supports active states. Use + * the control handler lock as the state lock if available. + */ + if (int_ops && int_ops->init_state) { + if (sd->ctrl_handler) + sd->state_lock = sd->ctrl_handler->lock; + + ret = v4l2_subdev_init_finalize(sd); + if (ret) { + dev_err(v4l2_dev->dev, + "%s: subdev initialization failed (err=%d)\n", + name, ret); + goto err_clean_m_ent; + } + } + /* Register the subdev with the v4l2 and the media framework */ ret = v4l2_device_register_subdev(v4l2_dev, sd); if (ret) { dev_err(v4l2_dev->dev, "%s: subdev register failed (err=%d)\n", name, ret); - goto err_clean_m_ent; + goto err_clean_sd; } return 0; +err_clean_sd: + v4l2_subdev_cleanup(sd); err_clean_m_ent: media_entity_cleanup(&sd->entity); return ret; diff --git a/drivers/media/test-drivers/vimc/vimc-common.h b/drivers/media/test-drivers/vimc/vimc-common.h index 7641a101a728..7a45a2117748 100644 --- a/drivers/media/test-drivers/vimc/vimc-common.h +++ b/drivers/media/test-drivers/vimc/vimc-common.h @@ -156,7 +156,7 @@ struct vimc_ent_type { */ struct vimc_ent_config { const char *name; - struct vimc_ent_type *type; + const struct vimc_ent_type *type; }; /** @@ -167,11 +167,11 @@ struct vimc_ent_config { */ bool vimc_is_source(struct media_entity *ent); -extern struct vimc_ent_type vimc_sensor_type; -extern struct vimc_ent_type vimc_debayer_type; -extern struct vimc_ent_type vimc_scaler_type; -extern struct vimc_ent_type vimc_capture_type; -extern struct vimc_ent_type vimc_lens_type; +extern const struct vimc_ent_type vimc_sensor_type; +extern const struct vimc_ent_type vimc_debayer_type; +extern const struct vimc_ent_type vimc_scaler_type; +extern const struct vimc_ent_type vimc_capture_type; +extern const struct vimc_ent_type vimc_lens_type; /** * vimc_pix_map_by_index - get vimc_pix_map struct by its index @@ -215,6 +215,7 @@ const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat); * @num_pads: number of pads to initialize * @pads: the array of pads of the entity, the caller should set the * flags of the pads + * @int_ops: pointer to &struct v4l2_subdev_internal_ops. * @sd_ops: pointer to &struct v4l2_subdev_ops. * * Helper function initialize and register the struct vimc_ent_device and struct @@ -227,6 +228,7 @@ int vimc_ent_sd_register(struct vimc_ent_device *ved, u32 function, u16 num_pads, struct media_pad *pads, + const struct v4l2_subdev_internal_ops *int_ops, const struct v4l2_subdev_ops *sd_ops); /** diff --git a/drivers/media/test-drivers/vimc/vimc-core.c b/drivers/media/test-drivers/vimc/vimc-core.c index af127476e920..c812fa9f0650 100644 --- a/drivers/media/test-drivers/vimc/vimc-core.c +++ b/drivers/media/test-drivers/vimc/vimc-core.c @@ -81,7 +81,7 @@ struct vimc_pipeline_config { * Topology Configuration */ -static struct vimc_ent_config ent_config[] = { +static const struct vimc_ent_config ent_config[] = { [SENSOR_A] = { .name = "Sensor A", .type = &vimc_sensor_type @@ -410,7 +410,7 @@ static struct platform_device vimc_pdev = { static struct platform_driver vimc_pdrv = { .probe = vimc_probe, - .remove_new = vimc_remove, + .remove = vimc_remove, .driver = { .name = VIMC_PDEV_NAME, }, diff --git a/drivers/media/test-drivers/vimc/vimc-debayer.c b/drivers/media/test-drivers/vimc/vimc-debayer.c index d72ed086e00b..bbb7c7a86df0 100644 --- a/drivers/media/test-drivers/vimc/vimc-debayer.c +++ b/drivers/media/test-drivers/vimc/vimc-debayer.c @@ -15,6 +15,9 @@ #include "vimc-common.h" +/* TODO: Add support for more output formats, we only support RGB888 for now. */ +#define VIMC_DEBAYER_SOURCE_MBUS_FMT MEDIA_BUS_FMT_RGB888_1X24 + enum vimc_debayer_rgb_colors { VIMC_DEBAYER_RED = 0, VIMC_DEBAYER_GREEN = 1, @@ -29,19 +32,26 @@ struct vimc_debayer_pix_map { struct vimc_debayer_device { struct vimc_ent_device ved; struct v4l2_subdev sd; - /* The active format */ - struct v4l2_mbus_framefmt sink_fmt; - u32 src_code; + struct v4l2_ctrl_handler hdl; + struct media_pad pads[2]; + + u8 *src_frame; + void (*set_rgb_src)(struct vimc_debayer_device *vdebayer, unsigned int lin, unsigned int col, unsigned int rgb[3]); - /* Values calculated when the stream starts */ - u8 *src_frame; - const struct vimc_debayer_pix_map *sink_pix_map; - unsigned int sink_bpp; - unsigned int mean_win_size; - struct v4l2_ctrl_handler hdl; - struct media_pad pads[2]; + + /* + * Virtual "hardware" configuration, filled when the stream starts or + * when controls are set. + */ + struct { + const struct vimc_debayer_pix_map *sink_pix_map; + unsigned int sink_bpp; + struct v4l2_area size; + unsigned int mean_win_size; + u32 src_code; + } hw; }; static const struct v4l2_mbus_framefmt sink_fmt_default = { @@ -153,18 +163,14 @@ static bool vimc_debayer_src_code_is_valid(u32 code) static int vimc_debayer_init_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state) { - struct vimc_debayer_device *vdebayer = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *mf; - unsigned int i; mf = v4l2_subdev_state_get_format(sd_state, 0); *mf = sink_fmt_default; - for (i = 1; i < sd->entity.num_pads; i++) { - mf = v4l2_subdev_state_get_format(sd_state, i); - *mf = sink_fmt_default; - mf->code = vdebayer->src_code; - } + mf = v4l2_subdev_state_get_format(sd_state, 1); + *mf = sink_fmt_default; + mf->code = VIMC_DEBAYER_SOURCE_MBUS_FMT; return 0; } @@ -213,24 +219,6 @@ static int vimc_debayer_enum_frame_size(struct v4l2_subdev *sd, return 0; } -static int vimc_debayer_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct vimc_debayer_device *vdebayer = v4l2_get_subdevdata(sd); - - /* Get the current sink format */ - fmt->format = fmt->which == V4L2_SUBDEV_FORMAT_TRY ? - *v4l2_subdev_state_get_format(sd_state, 0) : - vdebayer->sink_fmt; - - /* Set the right code for the source pad */ - if (VIMC_IS_SRC(fmt->pad)) - fmt->format.code = vdebayer->src_code; - - return 0; -} - static void vimc_debayer_adjust_sink_fmt(struct v4l2_mbus_framefmt *fmt) { const struct vimc_debayer_pix_map *vpix; @@ -256,52 +244,42 @@ static int vimc_debayer_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_format *fmt) { struct vimc_debayer_device *vdebayer = v4l2_get_subdevdata(sd); - struct v4l2_mbus_framefmt *sink_fmt; - u32 *src_code; + struct v4l2_mbus_framefmt *format; - if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - /* Do not change the format while stream is on */ - if (vdebayer->src_frame) - return -EBUSY; - - sink_fmt = &vdebayer->sink_fmt; - src_code = &vdebayer->src_code; - } else { - sink_fmt = v4l2_subdev_state_get_format(sd_state, 0); - src_code = &v4l2_subdev_state_get_format(sd_state, 1)->code; - } + /* Do not change the format while stream is on. */ + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE && vdebayer->src_frame) + return -EBUSY; /* - * Do not change the format of the source pad, - * it is propagated from the sink + * Do not change the format of the source pad, it is propagated from + * the sink. */ - if (VIMC_IS_SRC(fmt->pad)) { - u32 code = fmt->format.code; + if (VIMC_IS_SRC(fmt->pad)) + return v4l2_subdev_get_fmt(sd, sd_state, fmt); - fmt->format = *sink_fmt; + /* Set the new format in the sink pad. */ + vimc_debayer_adjust_sink_fmt(&fmt->format); - if (vimc_debayer_src_code_is_valid(code)) - *src_code = code; + format = v4l2_subdev_state_get_format(sd_state, 0); - fmt->format.code = *src_code; - } else { - /* Set the new format in the sink pad */ - vimc_debayer_adjust_sink_fmt(&fmt->format); - - dev_dbg(vdebayer->ved.dev, "%s: sink format update: " - "old:%dx%d (0x%x, %d, %d, %d, %d) " - "new:%dx%d (0x%x, %d, %d, %d, %d)\n", vdebayer->sd.name, - /* old */ - sink_fmt->width, sink_fmt->height, sink_fmt->code, - sink_fmt->colorspace, sink_fmt->quantization, - sink_fmt->xfer_func, sink_fmt->ycbcr_enc, - /* new */ - fmt->format.width, fmt->format.height, fmt->format.code, - fmt->format.colorspace, fmt->format.quantization, - fmt->format.xfer_func, fmt->format.ycbcr_enc); - - *sink_fmt = fmt->format; - } + dev_dbg(vdebayer->ved.dev, "%s: sink format update: " + "old:%dx%d (0x%x, %d, %d, %d, %d) " + "new:%dx%d (0x%x, %d, %d, %d, %d)\n", vdebayer->sd.name, + /* old */ + format->width, format->height, format->code, + format->colorspace, format->quantization, + format->xfer_func, format->ycbcr_enc, + /* new */ + fmt->format.width, fmt->format.height, fmt->format.code, + fmt->format.colorspace, fmt->format.quantization, + fmt->format.xfer_func, fmt->format.ycbcr_enc); + + *format = fmt->format; + + /* Propagate the format to the source pad. */ + format = v4l2_subdev_state_get_format(sd_state, 1); + *format = fmt->format; + format->code = VIMC_DEBAYER_SOURCE_MBUS_FMT; return 0; } @@ -309,7 +287,7 @@ static int vimc_debayer_set_fmt(struct v4l2_subdev *sd, static const struct v4l2_subdev_pad_ops vimc_debayer_pad_ops = { .enum_mbus_code = vimc_debayer_enum_mbus_code, .enum_frame_size = vimc_debayer_enum_frame_size, - .get_fmt = vimc_debayer_get_fmt, + .get_fmt = v4l2_subdev_get_fmt, .set_fmt = vimc_debayer_set_fmt, }; @@ -321,8 +299,8 @@ static void vimc_debayer_process_rgb_frame(struct vimc_debayer_device *vdebayer, const struct vimc_pix_map *vpix; unsigned int i, index; - vpix = vimc_pix_map_by_code(vdebayer->src_code); - index = VIMC_FRAME_INDEX(lin, col, vdebayer->sink_fmt.width, 3); + vpix = vimc_pix_map_by_code(vdebayer->hw.src_code); + index = VIMC_FRAME_INDEX(lin, col, vdebayer->hw.size.width, 3); for (i = 0; i < 3; i++) { switch (vpix->pixelformat) { case V4L2_PIX_FMT_RGB24: @@ -340,24 +318,37 @@ static int vimc_debayer_s_stream(struct v4l2_subdev *sd, int enable) struct vimc_debayer_device *vdebayer = v4l2_get_subdevdata(sd); if (enable) { + const struct v4l2_mbus_framefmt *sink_fmt; + const struct v4l2_mbus_framefmt *src_fmt; + struct v4l2_subdev_state *state; const struct vimc_pix_map *vpix; unsigned int frame_size; if (vdebayer->src_frame) return 0; + state = v4l2_subdev_lock_and_get_active_state(sd); + sink_fmt = v4l2_subdev_state_get_format(state, 0); + src_fmt = v4l2_subdev_state_get_format(state, 1); + /* Calculate the frame size of the source pad */ - vpix = vimc_pix_map_by_code(vdebayer->src_code); - frame_size = vdebayer->sink_fmt.width * vdebayer->sink_fmt.height * - vpix->bpp; + vpix = vimc_pix_map_by_code(src_fmt->code); + frame_size = src_fmt->width * src_fmt->height * vpix->bpp; /* Save the bytes per pixel of the sink */ - vpix = vimc_pix_map_by_code(vdebayer->sink_fmt.code); - vdebayer->sink_bpp = vpix->bpp; + vpix = vimc_pix_map_by_code(sink_fmt->code); + vdebayer->hw.sink_bpp = vpix->bpp; /* Get the corresponding pixel map from the table */ - vdebayer->sink_pix_map = - vimc_debayer_pix_map_by_code(vdebayer->sink_fmt.code); + vdebayer->hw.sink_pix_map = + vimc_debayer_pix_map_by_code(sink_fmt->code); + + vdebayer->hw.size.width = sink_fmt->width; + vdebayer->hw.size.height = sink_fmt->height; + + vdebayer->hw.src_code = src_fmt->code; + + v4l2_subdev_unlock_state(state); /* * Allocate the frame buffer. Use vmalloc to be able to @@ -366,7 +357,6 @@ static int vimc_debayer_s_stream(struct v4l2_subdev *sd, int enable) vdebayer->src_frame = vmalloc(frame_size); if (!vdebayer->src_frame) return -ENOMEM; - } else { if (!vdebayer->src_frame) return 0; @@ -427,13 +417,13 @@ static void vimc_debayer_calc_rgb_sink(struct vimc_debayer_device *vdebayer, * the top left corner of the mean window (considering the current * pixel as the center) */ - seek = vdebayer->mean_win_size / 2; + seek = vdebayer->hw.mean_win_size / 2; /* Sum the values of the colors in the mean window */ dev_dbg(vdebayer->ved.dev, "deb: %s: --- Calc pixel %dx%d, window mean %d, seek %d ---\n", - vdebayer->sd.name, lin, col, vdebayer->sink_fmt.height, seek); + vdebayer->sd.name, lin, col, vdebayer->hw.size.height, seek); /* * Iterate through all the lines in the mean window, start @@ -442,7 +432,7 @@ static void vimc_debayer_calc_rgb_sink(struct vimc_debayer_device *vdebayer, * frame */ for (wlin = seek > lin ? 0 : lin - seek; - wlin < lin + seek + 1 && wlin < vdebayer->sink_fmt.height; + wlin < lin + seek + 1 && wlin < vdebayer->hw.size.height; wlin++) { /* @@ -452,17 +442,17 @@ static void vimc_debayer_calc_rgb_sink(struct vimc_debayer_device *vdebayer, * frame */ for (wcol = seek > col ? 0 : col - seek; - wcol < col + seek + 1 && wcol < vdebayer->sink_fmt.width; + wcol < col + seek + 1 && wcol < vdebayer->hw.size.width; wcol++) { enum vimc_debayer_rgb_colors color; unsigned int index; /* Check which color this pixel is */ - color = vdebayer->sink_pix_map->order[wlin % 2][wcol % 2]; + color = vdebayer->hw.sink_pix_map->order[wlin % 2][wcol % 2]; index = VIMC_FRAME_INDEX(wlin, wcol, - vdebayer->sink_fmt.width, - vdebayer->sink_bpp); + vdebayer->hw.size.width, + vdebayer->hw.sink_bpp); dev_dbg(vdebayer->ved.dev, "deb: %s: RGB CALC: frame index %d, win pos %dx%d, color %d\n", @@ -471,7 +461,7 @@ static void vimc_debayer_calc_rgb_sink(struct vimc_debayer_device *vdebayer, /* Get its value */ rgb[color] = rgb[color] + vimc_debayer_get_val(&frame[index], - vdebayer->sink_bpp); + vdebayer->hw.sink_bpp); /* Save how many values we already added */ n_rgb[color]++; @@ -509,8 +499,8 @@ static void *vimc_debayer_process_frame(struct vimc_ent_device *ved, if (!vdebayer->src_frame) return ERR_PTR(-EINVAL); - for (i = 0; i < vdebayer->sink_fmt.height; i++) - for (j = 0; j < vdebayer->sink_fmt.width; j++) { + for (i = 0; i < vdebayer->hw.size.height; i++) + for (j = 0; j < vdebayer->hw.size.width; j++) { vimc_debayer_calc_rgb_sink(vdebayer, sink_frame, i, j, rgb); vdebayer->set_rgb_src(vdebayer, i, j, rgb); } @@ -525,7 +515,7 @@ static int vimc_debayer_s_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case VIMC_CID_MEAN_WIN_SIZE: - vdebayer->mean_win_size = ctrl->val; + vdebayer->hw.mean_win_size = ctrl->val; break; default: return -EINVAL; @@ -543,6 +533,7 @@ static void vimc_debayer_release(struct vimc_ent_device *ved) container_of(ved, struct vimc_debayer_device, ved); v4l2_ctrl_handler_free(&vdebayer->hdl); + v4l2_subdev_cleanup(&vdebayer->sd); media_entity_cleanup(vdebayer->ved.ent); kfree(vdebayer); } @@ -594,25 +585,15 @@ static struct vimc_ent_device *vimc_debayer_add(struct vimc_device *vimc, ret = vimc_ent_sd_register(&vdebayer->ved, &vdebayer->sd, v4l2_dev, vcfg_name, MEDIA_ENT_F_PROC_VIDEO_PIXEL_ENC_CONV, 2, - vdebayer->pads, &vimc_debayer_ops); + vdebayer->pads, &vimc_debayer_internal_ops, + &vimc_debayer_ops); if (ret) goto err_free_hdl; - vdebayer->sd.internal_ops = &vimc_debayer_internal_ops; - vdebayer->ved.process_frame = vimc_debayer_process_frame; vdebayer->ved.dev = vimc->mdev.dev; - vdebayer->mean_win_size = vimc_debayer_ctrl_mean_win_size.def; + vdebayer->hw.mean_win_size = vimc_debayer_ctrl_mean_win_size.def; - /* Initialize the frame format */ - vdebayer->sink_fmt = sink_fmt_default; - /* - * TODO: Add support for more output formats, we only support - * RGB888 for now - * NOTE: the src format is always the same as the sink, except - * for the code - */ - vdebayer->src_code = MEDIA_BUS_FMT_RGB888_1X24; vdebayer->set_rgb_src = vimc_debayer_process_rgb_frame; return &vdebayer->ved; @@ -625,7 +606,7 @@ err_free_vdebayer: return ERR_PTR(ret); } -struct vimc_ent_type vimc_debayer_type = { +const struct vimc_ent_type vimc_debayer_type = { .add = vimc_debayer_add, .release = vimc_debayer_release }; diff --git a/drivers/media/test-drivers/vimc/vimc-lens.c b/drivers/media/test-drivers/vimc/vimc-lens.c index 3ce7f4b4d2cc..96399057a2b5 100644 --- a/drivers/media/test-drivers/vimc/vimc-lens.c +++ b/drivers/media/test-drivers/vimc/vimc-lens.c @@ -72,7 +72,7 @@ static struct vimc_ent_device *vimc_lens_add(struct vimc_device *vimc, ret = vimc_ent_sd_register(&vlens->ved, &vlens->sd, v4l2_dev, vcfg_name, MEDIA_ENT_F_LENS, 0, - NULL, &vimc_lens_ops); + NULL, NULL, &vimc_lens_ops); if (ret) goto err_free_hdl; @@ -92,11 +92,12 @@ static void vimc_lens_release(struct vimc_ent_device *ved) container_of(ved, struct vimc_lens_device, ved); v4l2_ctrl_handler_free(&vlens->hdl); + v4l2_subdev_cleanup(&vlens->sd); media_entity_cleanup(vlens->ved.ent); kfree(vlens); } -struct vimc_ent_type vimc_lens_type = { +const struct vimc_ent_type vimc_lens_type = { .add = vimc_lens_add, .release = vimc_lens_release }; diff --git a/drivers/media/test-drivers/vimc/vimc-scaler.c b/drivers/media/test-drivers/vimc/vimc-scaler.c index afe13d6af321..47d0d63865a0 100644 --- a/drivers/media/test-drivers/vimc/vimc-scaler.c +++ b/drivers/media/test-drivers/vimc/vimc-scaler.c @@ -15,7 +15,7 @@ #include "vimc-common.h" /* Pad identifier */ -enum vic_sca_pad { +enum vimc_scaler_pad { VIMC_SCALER_SINK = 0, VIMC_SCALER_SRC = 1, }; @@ -26,13 +26,20 @@ enum vic_sca_pad { struct vimc_scaler_device { struct vimc_ent_device ved; struct v4l2_subdev sd; - struct v4l2_rect crop_rect; - /* Frame format for both sink and src pad */ - struct v4l2_mbus_framefmt fmt[2]; - /* Values calculated when the stream starts */ - u8 *src_frame; - unsigned int bpp; struct media_pad pads[2]; + + u8 *src_frame; + + /* + * Virtual "hardware" configuration, filled when the stream starts or + * when controls are set. + */ + struct { + struct v4l2_mbus_framefmt sink_fmt; + struct v4l2_mbus_framefmt src_fmt; + struct v4l2_rect sink_crop; + unsigned int bpp; + } hw; }; static const struct v4l2_mbus_framefmt fmt_default = { @@ -132,39 +139,6 @@ static int vimc_scaler_enum_frame_size(struct v4l2_subdev *sd, return 0; } -static struct v4l2_mbus_framefmt * -vimc_scaler_pad_format(struct vimc_scaler_device *vscaler, - struct v4l2_subdev_state *sd_state, u32 pad, - enum v4l2_subdev_format_whence which) -{ - if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_state_get_format(sd_state, pad); - else - return &vscaler->fmt[pad]; -} - -static struct v4l2_rect * -vimc_scaler_pad_crop(struct vimc_scaler_device *vscaler, - struct v4l2_subdev_state *sd_state, - enum v4l2_subdev_format_whence which) -{ - if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_state_get_crop(sd_state, VIMC_SCALER_SINK); - else - return &vscaler->crop_rect; -} - -static int vimc_scaler_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *format) -{ - struct vimc_scaler_device *vscaler = v4l2_get_subdevdata(sd); - - format->format = *vimc_scaler_pad_format(vscaler, sd_state, format->pad, - format->which); - return 0; -} - static int vimc_scaler_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) @@ -176,7 +150,7 @@ static int vimc_scaler_set_fmt(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE && vscaler->src_frame) return -EBUSY; - fmt = vimc_scaler_pad_format(vscaler, sd_state, format->pad, format->which); + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); /* * The media bus code and colorspace can only be changed on the sink @@ -214,14 +188,13 @@ static int vimc_scaler_set_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *src_fmt; struct v4l2_rect *crop; - crop = vimc_scaler_pad_crop(vscaler, sd_state, format->which); + crop = v4l2_subdev_state_get_crop(sd_state, VIMC_SCALER_SINK); crop->width = fmt->width; crop->height = fmt->height; crop->top = 0; crop->left = 0; - src_fmt = vimc_scaler_pad_format(vscaler, sd_state, VIMC_SCALER_SRC, - format->which); + src_fmt = v4l2_subdev_state_get_format(sd_state, VIMC_SCALER_SRC); *src_fmt = *fmt; } @@ -234,7 +207,6 @@ static int vimc_scaler_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { - struct vimc_scaler_device *vscaler = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *sink_fmt; if (VIMC_IS_SRC(sel->pad)) @@ -242,11 +214,10 @@ static int vimc_scaler_get_selection(struct v4l2_subdev *sd, switch (sel->target) { case V4L2_SEL_TGT_CROP: - sel->r = *vimc_scaler_pad_crop(vscaler, sd_state, sel->which); + sel->r = *v4l2_subdev_state_get_crop(sd_state, VIMC_SCALER_SINK); break; case V4L2_SEL_TGT_CROP_BOUNDS: - sink_fmt = vimc_scaler_pad_format(vscaler, sd_state, VIMC_SCALER_SINK, - sel->which); + sink_fmt = v4l2_subdev_state_get_format(sd_state, VIMC_SCALER_SINK); sel->r = vimc_scaler_get_crop_bound_sink(sink_fmt); break; default: @@ -282,9 +253,8 @@ static int vimc_scaler_set_selection(struct v4l2_subdev *sd, if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE && vscaler->src_frame) return -EBUSY; - crop_rect = vimc_scaler_pad_crop(vscaler, sd_state, sel->which); - sink_fmt = vimc_scaler_pad_format(vscaler, sd_state, VIMC_SCALER_SINK, - sel->which); + crop_rect = v4l2_subdev_state_get_crop(sd_state, VIMC_SCALER_SINK); + sink_fmt = v4l2_subdev_state_get_format(sd_state, VIMC_SCALER_SINK); vimc_scaler_adjust_sink_crop(&sel->r, sink_fmt); *crop_rect = sel->r; @@ -294,7 +264,7 @@ static int vimc_scaler_set_selection(struct v4l2_subdev *sd, static const struct v4l2_subdev_pad_ops vimc_scaler_pad_ops = { .enum_mbus_code = vimc_scaler_enum_mbus_code, .enum_frame_size = vimc_scaler_enum_frame_size, - .get_fmt = vimc_scaler_get_fmt, + .get_fmt = v4l2_subdev_get_fmt, .set_fmt = vimc_scaler_set_fmt, .get_selection = vimc_scaler_get_selection, .set_selection = vimc_scaler_set_selection, @@ -305,27 +275,38 @@ static int vimc_scaler_s_stream(struct v4l2_subdev *sd, int enable) struct vimc_scaler_device *vscaler = v4l2_get_subdevdata(sd); if (enable) { - const struct vimc_pix_map *vpix; + struct v4l2_subdev_state *state; + const struct v4l2_mbus_framefmt *format; + const struct v4l2_rect *rect; unsigned int frame_size; if (vscaler->src_frame) return 0; - /* Save the bytes per pixel of the sink */ - vpix = vimc_pix_map_by_code(vscaler->fmt[VIMC_SCALER_SINK].code); - vscaler->bpp = vpix->bpp; + state = v4l2_subdev_lock_and_get_active_state(sd); - /* Calculate the frame size of the source pad */ - frame_size = vscaler->fmt[VIMC_SCALER_SRC].width - * vscaler->fmt[VIMC_SCALER_SRC].height * vscaler->bpp; + /* Save the bytes per pixel of the sink. */ + format = v4l2_subdev_state_get_format(state, VIMC_SCALER_SINK); + vscaler->hw.sink_fmt = *format; + vscaler->hw.bpp = vimc_pix_map_by_code(format->code)->bpp; - /* Allocate the frame buffer. Use vmalloc to be able to - * allocate a large amount of memory + /* Calculate the frame size of the source pad. */ + format = v4l2_subdev_state_get_format(state, VIMC_SCALER_SRC); + vscaler->hw.src_fmt = *format; + frame_size = format->width * format->height * vscaler->hw.bpp; + + rect = v4l2_subdev_state_get_crop(state, VIMC_SCALER_SINK); + vscaler->hw.sink_crop = *rect; + + v4l2_subdev_unlock_state(state); + + /* + * Allocate the frame buffer. Use vmalloc to be able to allocate + * a large amount of memory. */ vscaler->src_frame = vmalloc(frame_size); if (!vscaler->src_frame) return -ENOMEM; - } else { if (!vscaler->src_frame) return 0; @@ -353,9 +334,9 @@ static const struct v4l2_subdev_internal_ops vimc_scaler_internal_ops = { static void vimc_scaler_fill_src_frame(const struct vimc_scaler_device *const vscaler, const u8 *const sink_frame) { - const struct v4l2_mbus_framefmt *src_fmt = &vscaler->fmt[VIMC_SCALER_SRC]; - const struct v4l2_rect *r = &vscaler->crop_rect; - unsigned int snk_width = vscaler->fmt[VIMC_SCALER_SINK].width; + const struct v4l2_mbus_framefmt *sink_fmt = &vscaler->hw.sink_fmt; + const struct v4l2_mbus_framefmt *src_fmt = &vscaler->hw.src_fmt; + const struct v4l2_rect *r = &vscaler->hw.sink_crop; unsigned int src_x, src_y; u8 *walker = vscaler->src_frame; @@ -364,16 +345,16 @@ static void vimc_scaler_fill_src_frame(const struct vimc_scaler_device *const vs unsigned int snk_y, y_offset; snk_y = (src_y * r->height) / src_fmt->height + r->top; - y_offset = snk_y * snk_width * vscaler->bpp; + y_offset = snk_y * sink_fmt->width * vscaler->hw.bpp; for (src_x = 0; src_x < src_fmt->width; src_x++) { unsigned int snk_x, x_offset, index; snk_x = (src_x * r->width) / src_fmt->width + r->left; - x_offset = snk_x * vscaler->bpp; + x_offset = snk_x * vscaler->hw.bpp; index = y_offset + x_offset; - memcpy(walker, &sink_frame[index], vscaler->bpp); - walker += vscaler->bpp; + memcpy(walker, &sink_frame[index], vscaler->hw.bpp); + walker += vscaler->hw.bpp; } } } @@ -398,6 +379,7 @@ static void vimc_scaler_release(struct vimc_ent_device *ved) struct vimc_scaler_device *vscaler = container_of(ved, struct vimc_scaler_device, ved); + v4l2_subdev_cleanup(&vscaler->sd); media_entity_cleanup(vscaler->ved.ent); kfree(vscaler); } @@ -421,28 +403,20 @@ static struct vimc_ent_device *vimc_scaler_add(struct vimc_device *vimc, ret = vimc_ent_sd_register(&vscaler->ved, &vscaler->sd, v4l2_dev, vcfg_name, MEDIA_ENT_F_PROC_VIDEO_SCALER, 2, - vscaler->pads, &vimc_scaler_ops); + vscaler->pads, &vimc_scaler_internal_ops, + &vimc_scaler_ops); if (ret) { kfree(vscaler); return ERR_PTR(ret); } - vscaler->sd.internal_ops = &vimc_scaler_internal_ops; - vscaler->ved.process_frame = vimc_scaler_process_frame; vscaler->ved.dev = vimc->mdev.dev; - /* Initialize the frame format */ - vscaler->fmt[VIMC_SCALER_SINK] = fmt_default; - vscaler->fmt[VIMC_SCALER_SRC] = fmt_default; - - /* Initialize the crop selection */ - vscaler->crop_rect = crop_rect_default; - return &vscaler->ved; } -struct vimc_ent_type vimc_scaler_type = { +const struct vimc_ent_type vimc_scaler_type = { .add = vimc_scaler_add, .release = vimc_scaler_release }; diff --git a/drivers/media/test-drivers/vimc/vimc-sensor.c b/drivers/media/test-drivers/vimc/vimc-sensor.c index 5e34b1aed95e..027767777763 100644 --- a/drivers/media/test-drivers/vimc/vimc-sensor.c +++ b/drivers/media/test-drivers/vimc/vimc-sensor.c @@ -24,13 +24,20 @@ struct vimc_sensor_device { struct vimc_ent_device ved; struct v4l2_subdev sd; struct tpg_data tpg; - u8 *frame; - enum vimc_sensor_osd_mode osd_value; - u64 start_stream_ts; - /* The active format */ - struct v4l2_mbus_framefmt mbus_format; struct v4l2_ctrl_handler hdl; struct media_pad pad; + + u8 *frame; + + /* + * Virtual "hardware" configuration, filled when the stream starts or + * when controls are set. + */ + struct { + struct v4l2_area size; + enum vimc_sensor_osd_mode osd_value; + u64 start_stream_ts; + } hw; }; static const struct v4l2_mbus_framefmt fmt_default = { @@ -44,14 +51,10 @@ static const struct v4l2_mbus_framefmt fmt_default = { static int vimc_sensor_init_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state) { - unsigned int i; - - for (i = 0; i < sd->entity.num_pads; i++) { - struct v4l2_mbus_framefmt *mf; + struct v4l2_mbus_framefmt *mf; - mf = v4l2_subdev_state_get_format(sd_state, i); - *mf = fmt_default; - } + mf = v4l2_subdev_state_get_format(sd_state, 0); + *mf = fmt_default; return 0; } @@ -92,36 +95,22 @@ static int vimc_sensor_enum_frame_size(struct v4l2_subdev *sd, return 0; } -static int vimc_sensor_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) +static void vimc_sensor_tpg_s_format(struct vimc_sensor_device *vsensor, + const struct v4l2_mbus_framefmt *format) { - struct vimc_sensor_device *vsensor = - container_of(sd, struct vimc_sensor_device, sd); - - fmt->format = fmt->which == V4L2_SUBDEV_FORMAT_TRY ? - *v4l2_subdev_state_get_format(sd_state, fmt->pad) : - vsensor->mbus_format; + const struct vimc_pix_map *vpix = vimc_pix_map_by_code(format->code); - return 0; -} - -static void vimc_sensor_tpg_s_format(struct vimc_sensor_device *vsensor) -{ - const struct vimc_pix_map *vpix = - vimc_pix_map_by_code(vsensor->mbus_format.code); - - tpg_reset_source(&vsensor->tpg, vsensor->mbus_format.width, - vsensor->mbus_format.height, vsensor->mbus_format.field); - tpg_s_bytesperline(&vsensor->tpg, 0, vsensor->mbus_format.width * vpix->bpp); - tpg_s_buf_height(&vsensor->tpg, vsensor->mbus_format.height); + tpg_reset_source(&vsensor->tpg, format->width, format->height, + format->field); + tpg_s_bytesperline(&vsensor->tpg, 0, format->width * vpix->bpp); + tpg_s_buf_height(&vsensor->tpg, format->height); tpg_s_fourcc(&vsensor->tpg, vpix->pixelformat); /* TODO: add support for V4L2_FIELD_ALTERNATE */ - tpg_s_field(&vsensor->tpg, vsensor->mbus_format.field, false); - tpg_s_colorspace(&vsensor->tpg, vsensor->mbus_format.colorspace); - tpg_s_ycbcr_enc(&vsensor->tpg, vsensor->mbus_format.ycbcr_enc); - tpg_s_quantization(&vsensor->tpg, vsensor->mbus_format.quantization); - tpg_s_xfer_func(&vsensor->tpg, vsensor->mbus_format.xfer_func); + tpg_s_field(&vsensor->tpg, format->field, false); + tpg_s_colorspace(&vsensor->tpg, format->colorspace); + tpg_s_ycbcr_enc(&vsensor->tpg, format->ycbcr_enc); + tpg_s_quantization(&vsensor->tpg, format->quantization); + tpg_s_xfer_func(&vsensor->tpg, format->xfer_func); } static void vimc_sensor_adjust_fmt(struct v4l2_mbus_framefmt *fmt) @@ -152,15 +141,11 @@ static int vimc_sensor_set_fmt(struct v4l2_subdev *sd, struct vimc_sensor_device *vsensor = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *mf; - if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - /* Do not change the format while stream is on */ - if (vsensor->frame) - return -EBUSY; + /* Do not change the format while stream is on */ + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE && vsensor->frame) + return -EBUSY; - mf = &vsensor->mbus_format; - } else { - mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); - } + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); /* Set the new format */ vimc_sensor_adjust_fmt(&fmt->format); @@ -185,7 +170,7 @@ static int vimc_sensor_set_fmt(struct v4l2_subdev *sd, static const struct v4l2_subdev_pad_ops vimc_sensor_pad_ops = { .enum_mbus_code = vimc_sensor_enum_mbus_code, .enum_frame_size = vimc_sensor_enum_frame_size, - .get_fmt = vimc_sensor_get_fmt, + .get_fmt = v4l2_subdev_get_fmt, .set_fmt = vimc_sensor_set_fmt, }; @@ -202,7 +187,7 @@ static void *vimc_sensor_process_frame(struct vimc_ent_device *ved, tpg_fill_plane_buffer(&vsensor->tpg, 0, 0, vsensor->frame); tpg_calc_text_basep(&vsensor->tpg, basep, 0, vsensor->frame); - switch (vsensor->osd_value) { + switch (vsensor->hw.osd_value) { case VIMC_SENSOR_OSD_SHOW_ALL: { const char *order = tpg_g_color_order(&vsensor->tpg); @@ -216,15 +201,14 @@ static void *vimc_sensor_process_frame(struct vimc_ent_device *ved, vsensor->tpg.hue); tpg_gen_text(&vsensor->tpg, basep, line++ * line_height, 16, str); snprintf(str, sizeof(str), "sensor size: %dx%d", - vsensor->mbus_format.width, - vsensor->mbus_format.height); + vsensor->hw.size.width, vsensor->hw.size.height); tpg_gen_text(&vsensor->tpg, basep, line++ * line_height, 16, str); fallthrough; } case VIMC_SENSOR_OSD_SHOW_COUNTERS: { unsigned int ms; - ms = div_u64(ktime_get_ns() - vsensor->start_stream_ts, 1000000); + ms = div_u64(ktime_get_ns() - vsensor->hw.start_stream_ts, 1000000); snprintf(str, sizeof(str), "%02d:%02d:%02d:%03d", (ms / (60 * 60 * 1000)) % 24, (ms / (60 * 1000)) % 60, @@ -247,15 +231,25 @@ static int vimc_sensor_s_stream(struct v4l2_subdev *sd, int enable) container_of(sd, struct vimc_sensor_device, sd); if (enable) { + const struct v4l2_mbus_framefmt *format; + struct v4l2_subdev_state *state; const struct vimc_pix_map *vpix; unsigned int frame_size; - vsensor->start_stream_ts = ktime_get_ns(); + state = v4l2_subdev_lock_and_get_active_state(sd); + format = v4l2_subdev_state_get_format(state, 0); - /* Calculate the frame size */ - vpix = vimc_pix_map_by_code(vsensor->mbus_format.code); - frame_size = vsensor->mbus_format.width * vpix->bpp * - vsensor->mbus_format.height; + /* Configure the test pattern generator. */ + vimc_sensor_tpg_s_format(vsensor, format); + + /* Calculate the frame size. */ + vpix = vimc_pix_map_by_code(format->code); + frame_size = format->width * vpix->bpp * format->height; + + vsensor->hw.size.width = format->width; + vsensor->hw.size.height = format->height; + + v4l2_subdev_unlock_state(state); /* * Allocate the frame buffer. Use vmalloc to be able to @@ -265,9 +259,7 @@ static int vimc_sensor_s_stream(struct v4l2_subdev *sd, int enable) if (!vsensor->frame) return -ENOMEM; - /* configure the test pattern generator */ - vimc_sensor_tpg_s_format(vsensor); - + vsensor->hw.start_stream_ts = ktime_get_ns(); } else { vfree(vsensor->frame); @@ -325,7 +317,7 @@ static int vimc_sensor_s_ctrl(struct v4l2_ctrl *ctrl) tpg_s_saturation(&vsensor->tpg, ctrl->val); break; case VIMC_CID_OSD_TEXT_MODE: - vsensor->osd_value = ctrl->val; + vsensor->hw.osd_value = ctrl->val; break; default: return -EINVAL; @@ -344,6 +336,7 @@ static void vimc_sensor_release(struct vimc_ent_device *ved) v4l2_ctrl_handler_free(&vsensor->hdl); tpg_free(&vsensor->tpg); + v4l2_subdev_cleanup(&vsensor->sd); media_entity_cleanup(vsensor->ved.ent); kfree(vsensor); } @@ -417,8 +410,7 @@ static struct vimc_ent_device *vimc_sensor_add(struct vimc_device *vimc, } /* Initialize the test pattern generator */ - tpg_init(&vsensor->tpg, vsensor->mbus_format.width, - vsensor->mbus_format.height); + tpg_init(&vsensor->tpg, fmt_default.width, fmt_default.height); ret = tpg_alloc(&vsensor->tpg, VIMC_FRAME_MAX_WIDTH); if (ret) goto err_free_hdl; @@ -428,18 +420,13 @@ static struct vimc_ent_device *vimc_sensor_add(struct vimc_device *vimc, ret = vimc_ent_sd_register(&vsensor->ved, &vsensor->sd, v4l2_dev, vcfg_name, MEDIA_ENT_F_CAM_SENSOR, 1, &vsensor->pad, - &vimc_sensor_ops); + &vimc_sensor_internal_ops, &vimc_sensor_ops); if (ret) goto err_free_tpg; - vsensor->sd.internal_ops = &vimc_sensor_internal_ops; - vsensor->ved.process_frame = vimc_sensor_process_frame; vsensor->ved.dev = vimc->mdev.dev; - /* Initialize the frame format */ - vsensor->mbus_format = fmt_default; - return &vsensor->ved; err_free_tpg: @@ -452,7 +439,7 @@ err_free_vsensor: return ERR_PTR(ret); } -struct vimc_ent_type vimc_sensor_type = { +const struct vimc_ent_type vimc_sensor_type = { .add = vimc_sensor_add, .release = vimc_sensor_release }; diff --git a/drivers/media/test-drivers/vimc/vimc-streamer.c b/drivers/media/test-drivers/vimc/vimc-streamer.c index 807551a5143b..15d863f97cbf 100644 --- a/drivers/media/test-drivers/vimc/vimc-streamer.c +++ b/drivers/media/test-drivers/vimc/vimc-streamer.c @@ -59,6 +59,12 @@ static void vimc_streamer_pipeline_terminate(struct vimc_stream *stream) continue; sd = media_entity_to_v4l2_subdev(ved->ent); + /* + * Do not call .s_stream() to stop an already + * stopped/unstarted subdev. + */ + if (!v4l2_subdev_is_streaming(sd)) + continue; v4l2_subdev_call(sd, video, s_stream, 0); } } diff --git a/drivers/media/test-drivers/visl/visl-core.c b/drivers/media/test-drivers/visl/visl-core.c index c46464bcaf2e..5bf3136b36eb 100644 --- a/drivers/media/test-drivers/visl/visl-core.c +++ b/drivers/media/test-drivers/visl/visl-core.c @@ -161,9 +161,15 @@ static const struct visl_ctrl_desc visl_h264_ctrl_descs[] = { }, { .cfg.id = V4L2_CID_STATELESS_H264_DECODE_MODE, + .cfg.min = V4L2_STATELESS_H264_DECODE_MODE_SLICE_BASED, + .cfg.max = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, + .cfg.def = V4L2_STATELESS_H264_DECODE_MODE_SLICE_BASED, }, { .cfg.id = V4L2_CID_STATELESS_H264_START_CODE, + .cfg.min = V4L2_STATELESS_H264_START_CODE_NONE, + .cfg.max = V4L2_STATELESS_H264_START_CODE_ANNEX_B, + .cfg.def = V4L2_STATELESS_H264_START_CODE_NONE, }, { .cfg.id = V4L2_CID_STATELESS_H264_SLICE_PARAMS, @@ -198,9 +204,15 @@ static const struct visl_ctrl_desc visl_hevc_ctrl_descs[] = { }, { .cfg.id = V4L2_CID_STATELESS_HEVC_DECODE_MODE, + .cfg.min = V4L2_STATELESS_HEVC_DECODE_MODE_SLICE_BASED, + .cfg.max = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED, + .cfg.def = V4L2_STATELESS_HEVC_DECODE_MODE_SLICE_BASED, }, { .cfg.id = V4L2_CID_STATELESS_HEVC_START_CODE, + .cfg.min = V4L2_STATELESS_HEVC_START_CODE_NONE, + .cfg.max = V4L2_STATELESS_HEVC_START_CODE_ANNEX_B, + .cfg.def = V4L2_STATELESS_HEVC_START_CODE_NONE, }, { .cfg.id = V4L2_CID_STATELESS_HEVC_ENTRY_POINT_OFFSETS, @@ -523,7 +535,7 @@ static void visl_remove(struct platform_device *pdev) static struct platform_driver visl_pdrv = { .probe = visl_probe, - .remove_new = visl_remove, + .remove = visl_remove, .driver = { .name = VISL_NAME, }, diff --git a/drivers/media/test-drivers/visl/visl-video.c b/drivers/media/test-drivers/visl/visl-video.c index b9a4b44bd0ed..8be505d8908c 100644 --- a/drivers/media/test-drivers/visl/visl-video.c +++ b/drivers/media/test-drivers/visl/visl-video.c @@ -136,6 +136,12 @@ static const u32 visl_decoded_fmts[] = { V4L2_PIX_FMT_YUV420, }; +static const u32 visl_extended_decoded_fmts[] = { + V4L2_PIX_FMT_NV12, + V4L2_PIX_FMT_YUV420, + V4L2_PIX_FMT_P010, +}; + const struct visl_coded_format_desc visl_coded_fmts[] = { { .pixelformat = V4L2_PIX_FMT_FWHT_STATELESS, @@ -341,11 +347,21 @@ static int visl_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { struct visl_ctx *ctx = visl_file_to_ctx(file); + u32 index = f->index & ~V4L2_FMTDESC_FLAG_ENUM_ALL; + int max_fmts = ctx->coded_format_desc->num_decoded_fmts; + const u32 *decoded_fmts = ctx->coded_format_desc->decoded_fmts; + + if (f->index & V4L2_FMTDESC_FLAG_ENUM_ALL) { + max_fmts = ARRAY_SIZE(visl_extended_decoded_fmts); + decoded_fmts = visl_extended_decoded_fmts; + } + + f->index = index; - if (f->index >= ctx->coded_format_desc->num_decoded_fmts) + if (index >= max_fmts) return -EINVAL; - f->pixelformat = ctx->coded_format_desc->decoded_fmts[f->index]; + f->pixelformat = decoded_fmts[index]; return 0; } @@ -539,6 +555,7 @@ const struct v4l2_ioctl_ops visl_ioctl_ops = { .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + .vidioc_remove_bufs = v4l2_m2m_ioctl_remove_bufs, .vidioc_streamon = v4l2_m2m_ioctl_streamon, .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, @@ -715,8 +732,6 @@ static const struct vb2_ops visl_qops = { .buf_queue = visl_buf_queue, .start_streaming = visl_start_streaming, .stop_streaming = visl_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .buf_request_complete = visl_buf_request_complete, }; diff --git a/drivers/media/test-drivers/vivid/Kconfig b/drivers/media/test-drivers/vivid/Kconfig index ec2e71d76965..cc470070a7a5 100644 --- a/drivers/media/test-drivers/vivid/Kconfig +++ b/drivers/media/test-drivers/vivid/Kconfig @@ -1,9 +1,8 @@ # SPDX-License-Identifier: GPL-2.0-only config VIDEO_VIVID tristate "Virtual Video Test Driver" - depends on VIDEO_DEV && !SPARC32 && !SPARC64 && FB + depends on VIDEO_DEV && !SPARC32 && !SPARC64 depends on HAS_DMA - select FB_IOMEM_HELPERS select FONT_SUPPORT select FONT_8x16 select VIDEOBUF2_VMALLOC @@ -31,6 +30,16 @@ config VIDEO_VIVID_CEC When selected the vivid module will emulate the optional HDMI CEC feature. +config VIDEO_VIVID_OSD + bool "Enable Framebuffer for testing Output Overlay" + depends on VIDEO_VIVID && FB_CORE + depends on VIDEO_VIVID=m || FB_CORE=y + default y + select FB_IOMEM_HELPERS + help + When selected the vivid module will emulate a Framebuffer for + testing Output Overlay. + config VIDEO_VIVID_MAX_DEVS int "Maximum number of devices" depends on VIDEO_VIVID diff --git a/drivers/media/test-drivers/vivid/Makefile b/drivers/media/test-drivers/vivid/Makefile index b12ad0152a3e..284a59e97335 100644 --- a/drivers/media/test-drivers/vivid/Makefile +++ b/drivers/media/test-drivers/vivid/Makefile @@ -3,10 +3,13 @@ vivid-objs := vivid-core.o vivid-ctrls.o vivid-vid-common.o vivid-vbi-gen.o \ vivid-vid-cap.o vivid-vid-out.o vivid-kthread-cap.o vivid-kthread-out.o \ vivid-radio-rx.o vivid-radio-tx.o vivid-radio-common.o \ vivid-rds-gen.o vivid-sdr-cap.o vivid-vbi-cap.o vivid-vbi-out.o \ - vivid-osd.o vivid-meta-cap.o vivid-meta-out.o \ + vivid-meta-cap.o vivid-meta-out.o \ vivid-kthread-touch.o vivid-touch-cap.o ifeq ($(CONFIG_VIDEO_VIVID_CEC),y) vivid-objs += vivid-cec.o endif +ifeq ($(CONFIG_VIDEO_VIVID_OSD),y) + vivid-objs += vivid-osd.o +endif obj-$(CONFIG_VIDEO_VIVID) += vivid.o diff --git a/drivers/media/test-drivers/vivid/vivid-cec.c b/drivers/media/test-drivers/vivid/vivid-cec.c index 1f7469ff04d5..356a988dd6a1 100644 --- a/drivers/media/test-drivers/vivid/vivid-cec.c +++ b/drivers/media/test-drivers/vivid/vivid-cec.c @@ -23,7 +23,7 @@ struct xfer_on_bus { static bool find_dest_adap(struct vivid_dev *dev, struct cec_adapter *adap, u8 dest) { - unsigned int i; + unsigned int i, j; if (dest >= 0xf) return false; @@ -33,12 +33,29 @@ static bool find_dest_adap(struct vivid_dev *dev, cec_has_log_addr(dev->cec_rx_adap, dest)) return true; - for (i = 0; i < MAX_OUTPUTS && dev->cec_tx_adap[i]; i++) { - if (adap == dev->cec_tx_adap[i]) + for (i = 0, j = 0; i < dev->num_inputs; i++) { + unsigned int menu_idx = + dev->input_is_connected_to_output[i]; + + if (dev->input_type[i] != HDMI) + continue; + j++; + if (menu_idx < FIXED_MENU_ITEMS) + continue; + + struct vivid_dev *dev_tx = vivid_ctrl_hdmi_to_output_instance[menu_idx]; + unsigned int output = vivid_ctrl_hdmi_to_output_index[menu_idx]; + + if (!dev_tx) continue; - if (!dev->cec_tx_adap[i]->is_configured) + + unsigned int hdmi_output = dev_tx->output_to_iface_index[output]; + + if (adap == dev_tx->cec_tx_adap[hdmi_output]) continue; - if (cec_has_log_addr(dev->cec_tx_adap[i], dest)) + if (!dev_tx->cec_tx_adap[hdmi_output]->is_configured) + continue; + if (cec_has_log_addr(dev_tx->cec_tx_adap[hdmi_output], dest)) return true; } return false; @@ -96,7 +113,7 @@ static void adjust_sfts(struct vivid_dev *dev) int vivid_cec_bus_thread(void *_dev) { u32 last_sft; - unsigned int i; + unsigned int i, j; unsigned int dest; ktime_t start, end; s64 delta_us, retry_us; @@ -193,9 +210,27 @@ int vivid_cec_bus_thread(void *_dev) if (first_status == CEC_TX_STATUS_OK) { if (xfers_on_bus[first_idx].adap != dev->cec_rx_adap) cec_received_msg(dev->cec_rx_adap, &first_msg); - for (i = 0; i < MAX_OUTPUTS && dev->cec_tx_adap[i]; i++) - if (xfers_on_bus[first_idx].adap != dev->cec_tx_adap[i]) - cec_received_msg(dev->cec_tx_adap[i], &first_msg); + for (i = 0, j = 0; i < dev->num_inputs; i++) { + unsigned int menu_idx = + dev->input_is_connected_to_output[i]; + + if (dev->input_type[i] != HDMI) + continue; + j++; + if (menu_idx < FIXED_MENU_ITEMS) + continue; + + struct vivid_dev *dev_tx = vivid_ctrl_hdmi_to_output_instance[menu_idx]; + unsigned int output = vivid_ctrl_hdmi_to_output_index[menu_idx]; + + if (!dev_tx) + continue; + + unsigned int hdmi_output = dev_tx->output_to_iface_index[output]; + + if (xfers_on_bus[first_idx].adap != dev_tx->cec_tx_adap[hdmi_output]) + cec_received_msg(dev_tx->cec_tx_adap[hdmi_output], &first_msg); + } } end = ktime_get(); /* @@ -242,21 +277,36 @@ static int vivid_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, u32 signal_free_time, struct cec_msg *msg) { struct vivid_dev *dev = cec_get_drvdata(adap); + struct vivid_dev *dev_rx = dev; u8 idx = cec_msg_initiator(msg); + u8 output = 0; - spin_lock(&dev->cec_xfers_slock); - dev->xfers[idx].adap = adap; - memcpy(dev->xfers[idx].msg, msg->msg, CEC_MAX_MSG_SIZE); - dev->xfers[idx].len = msg->len; - dev->xfers[idx].sft = CEC_SIGNAL_FREE_TIME_RETRY; + if (dev->cec_rx_adap != adap) { + int i; + + for (i = 0; i < dev->num_hdmi_outputs; i++) + if (dev->cec_tx_adap[i] == adap) + break; + if (i == dev->num_hdmi_outputs) + return -ENONET; + output = dev->hdmi_index_to_output_index[i]; + dev_rx = dev->output_to_input_instance[output]; + if (!dev_rx) + return -ENONET; + } + spin_lock(&dev_rx->cec_xfers_slock); + dev_rx->xfers[idx].adap = adap; + memcpy(dev_rx->xfers[idx].msg, msg->msg, CEC_MAX_MSG_SIZE); + dev_rx->xfers[idx].len = msg->len; + dev_rx->xfers[idx].sft = CEC_SIGNAL_FREE_TIME_RETRY; if (signal_free_time > CEC_SIGNAL_FREE_TIME_RETRY) { - if (idx == dev->last_initiator) - dev->xfers[idx].sft = CEC_SIGNAL_FREE_TIME_NEXT_XFER; + if (idx == dev_rx->last_initiator) + dev_rx->xfers[idx].sft = CEC_SIGNAL_FREE_TIME_NEXT_XFER; else - dev->xfers[idx].sft = CEC_SIGNAL_FREE_TIME_NEW_INITIATOR; + dev_rx->xfers[idx].sft = CEC_SIGNAL_FREE_TIME_NEW_INITIATOR; } - spin_unlock(&dev->cec_xfers_slock); - wake_up_interruptible(&dev->kthread_waitq_cec); + spin_unlock(&dev_rx->cec_xfers_slock); + wake_up_interruptible(&dev_rx->kthread_waitq_cec); return 0; } @@ -266,15 +316,16 @@ static int vivid_received(struct cec_adapter *adap, struct cec_msg *msg) struct vivid_dev *dev = cec_get_drvdata(adap); struct cec_msg reply; u8 dest = cec_msg_destination(msg); - u8 disp_ctl; - char osd[14]; if (cec_msg_is_broadcast(msg)) dest = adap->log_addrs.log_addr[0]; cec_msg_init(&reply, dest, cec_msg_initiator(msg)); switch (cec_msg_opcode(msg)) { - case CEC_MSG_SET_OSD_STRING: + case CEC_MSG_SET_OSD_STRING: { + u8 disp_ctl; + char osd[14]; + if (!cec_is_sink(adap)) return -ENOMSG; cec_ops_set_osd_string(msg, &disp_ctl, osd); @@ -298,6 +349,47 @@ static int vivid_received(struct cec_adapter *adap, struct cec_msg *msg) break; } break; + } + case CEC_MSG_VENDOR_COMMAND_WITH_ID: { + u32 vendor_id; + u8 size; + const u8 *vendor_cmd; + + /* + * If we receive <Vendor Command With ID> with our vendor ID + * and with a payload of size 1, and the payload value is odd, + * then we reply with the same message, but with the payload + * byte incremented by 1. + * + * If the size is 1 and the payload value is even, then we + * ignore the message. + * + * The reason we reply to odd instead of even payload values + * is that it allows for testing of the corner case where the + * reply value is 0 (0xff + 1 % 256). + * + * For other sizes we Feature Abort. + * + * This is added for the specific purpose of testing the + * CEC_MSG_FL_REPLY_VENDOR_ID flag using vivid. + */ + cec_ops_vendor_command_with_id(msg, &vendor_id, &size, &vendor_cmd); + if (vendor_id != adap->log_addrs.vendor_id) + break; + if (size == 1) { + // Ignore even op values + if (!(vendor_cmd[0] & 1)) + break; + reply.len = msg->len; + memcpy(reply.msg + 1, msg->msg + 1, msg->len - 1); + reply.msg[msg->len - 1]++; + } else { + cec_msg_feature_abort(&reply, cec_msg_opcode(msg), + CEC_OP_ABORT_INVALID_OP); + } + cec_transmit_msg(adap, &reply, false); + break; + } default: return -ENOMSG; } diff --git a/drivers/media/test-drivers/vivid/vivid-core.c b/drivers/media/test-drivers/vivid/vivid-core.c index 159c72cbb5bf..8d56168c72aa 100644 --- a/drivers/media/test-drivers/vivid/vivid-core.c +++ b/drivers/media/test-drivers/vivid/vivid-core.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only + /* * vivid-core.c - A Virtual Video Test Driver, core initialization * @@ -42,15 +43,13 @@ #include "vivid-touch-cap.h" #define VIVID_MODULE_NAME "vivid" - -/* The maximum number of vivid devices */ -#define VIVID_MAX_DEVS CONFIG_VIDEO_VIVID_MAX_DEVS +#define MAX_STRING_LENGTH 23 MODULE_DESCRIPTION("Virtual Video Test Driver"); MODULE_AUTHOR("Hans Verkuil"); MODULE_LICENSE("GPL"); -static unsigned n_devs = 1; +unsigned int n_devs = 1; module_param(n_devs, uint, 0444); MODULE_PARM_DESC(n_devs, " number of driver instances to create"); @@ -126,7 +125,9 @@ MODULE_PARM_DESC(node_types, " node types, default is 0xe1d3d. Bitmask with the "\t\t bit 8: Video Output node\n" "\t\t bit 10-11: VBI Output node: 0 = none, 1 = raw vbi, 2 = sliced vbi, 3 = both\n" "\t\t bit 12: Radio Transmitter node\n" +#ifdef CONFIG_VIDEO_VIVID_OSD "\t\t bit 16: Framebuffer for testing output overlays\n" +#endif "\t\t bit 17: Metadata Capture node\n" "\t\t bit 18: Metadata Output node\n" "\t\t bit 19: Touch Capture node\n"); @@ -186,7 +187,32 @@ MODULE_PARM_DESC(supports_requests, " support for requests, default is 1.\n" "\t\t 1 == supports requests\n" "\t\t 2 == requires requests"); -static struct vivid_dev *vivid_devs[VIVID_MAX_DEVS]; +struct vivid_dev *vivid_devs[VIVID_MAX_DEVS]; + +DEFINE_SPINLOCK(hdmi_output_skip_mask_lock); +struct workqueue_struct *update_hdmi_ctrls_workqueue; +u64 hdmi_to_output_menu_skip_mask; +u64 hdmi_input_update_outputs_mask; + +struct vivid_dev *vivid_ctrl_hdmi_to_output_instance[MAX_MENU_ITEMS]; +unsigned int vivid_ctrl_hdmi_to_output_index[MAX_MENU_ITEMS]; + +char *vivid_ctrl_hdmi_to_output_strings[MAX_MENU_ITEMS + 1] = { + "Test Pattern Generator", + "None" +}; + +DEFINE_SPINLOCK(svid_output_skip_mask_lock); +struct workqueue_struct *update_svid_ctrls_workqueue; +u64 svid_to_output_menu_skip_mask; + +struct vivid_dev *vivid_ctrl_svid_to_output_instance[MAX_MENU_ITEMS]; +unsigned int vivid_ctrl_svid_to_output_index[MAX_MENU_ITEMS]; + +char *vivid_ctrl_svid_to_output_strings[MAX_MENU_ITEMS + 1] = { + "Test Pattern Generator", + "None" +}; const struct v4l2_rect vivid_min_rect = { 0, 0, MIN_WIDTH, MIN_HEIGHT @@ -218,7 +244,7 @@ static const u8 vivid_hdmi_edid[256] = { 0x5e, 0x5d, 0x10, 0x1f, 0x04, 0x13, 0x22, 0x21, 0x20, 0x05, 0x14, 0x02, 0x11, 0x01, 0x23, 0x09, 0x07, 0x07, 0x83, 0x01, 0x00, 0x00, 0x6d, 0x03, - 0x0c, 0x00, 0x10, 0x00, 0x00, 0x3c, 0x21, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x21, 0x00, 0x60, 0x01, 0x02, 0x03, 0x67, 0xd8, 0x5d, 0xc4, 0x01, 0x78, 0x00, 0x00, 0xe2, 0x00, 0xca, 0xe3, 0x05, 0x00, 0x00, 0xe3, 0x06, 0x01, 0x00, 0x4d, @@ -229,7 +255,7 @@ static const u8 vivid_hdmi_edid[256] = { 0x00, 0x00, 0x1a, 0x1a, 0x1d, 0x00, 0x80, 0x51, 0xd0, 0x1c, 0x20, 0x40, 0x80, 0x35, 0x00, 0xc0, 0x1c, 0x32, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, }; static int vidioc_querycap(struct file *file, void *priv, @@ -769,6 +795,7 @@ static const struct v4l2_ioctl_ops vivid_ioctl_ops = { .vidioc_expbuf = vb2_ioctl_expbuf, .vidioc_streamon = vb2_ioctl_streamon, .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_remove_bufs = vb2_ioctl_remove_bufs, .vidioc_enum_input = vivid_enum_input, .vidioc_g_input = vivid_g_input, @@ -826,6 +853,7 @@ static void vivid_dev_release(struct v4l2_device *v4l2_dev) { struct vivid_dev *dev = container_of(v4l2_dev, struct vivid_dev, v4l2_dev); + cancel_work_sync(&dev->update_hdmi_ctrl_work); vivid_free_controls(dev); v4l2_device_unregister(&dev->v4l2_dev); #ifdef CONFIG_MEDIA_CONTROLLER @@ -861,7 +889,7 @@ static const struct media_device_ops vivid_media_ops = { static int vivid_create_queue(struct vivid_dev *dev, struct vb2_queue *q, u32 buf_type, - unsigned int min_queued_buffers, + unsigned int min_reqbufs_allocation, const struct vb2_ops *ops) { if (buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->multiplanar) @@ -884,7 +912,7 @@ static int vivid_create_queue(struct vivid_dev *dev, * videobuf2-core.c to MAX_BUFFER_INDEX. */ if (buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - q->max_num_buffers = 64; + q->max_num_buffers = MAX_VID_CAP_BUFFERS; if (buf_type == V4L2_BUF_TYPE_SDR_CAPTURE) q->max_num_buffers = 1024; if (buf_type == V4L2_BUF_TYPE_VBI_CAPTURE) @@ -898,7 +926,7 @@ static int vivid_create_queue(struct vivid_dev *dev, q->mem_ops = allocators[dev->inst] == 1 ? &vb2_dma_contig_memops : &vb2_vmalloc_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_queued_buffers = supports_requests[dev->inst] ? 0 : min_queued_buffers; + q->min_reqbufs_allocation = min_reqbufs_allocation; q->lock = &dev->mutex; q->dev = dev->v4l2_dev.dev; q->supports_requests = supports_requests[dev->inst]; @@ -945,6 +973,7 @@ static int vivid_detect_feature_set(struct vivid_dev *dev, int inst, dev->num_inputs--; } dev->num_hdmi_inputs = in_type_counter[HDMI]; + dev->num_svid_inputs = in_type_counter[SVID]; /* how many outputs do we have and of what type? */ dev->num_outputs = num_outputs[inst]; @@ -959,7 +988,6 @@ static int vivid_detect_feature_set(struct vivid_dev *dev, int inst, for (i = 0; i < dev->num_outputs; i++) { dev->output_type[i] = ((output_types[inst] >> i) & 1) ? HDMI : SVID; dev->output_name_counter[i] = out_type_counter[dev->output_type[i]]++; - dev->display_present[i] = true; } dev->has_audio_outputs = out_type_counter[SVID]; if (out_type_counter[HDMI] == 16) { @@ -1045,9 +1073,11 @@ static int vivid_detect_feature_set(struct vivid_dev *dev, int inst, /* do we have a modulator? */ *has_modulator = dev->has_radio_tx; +#ifdef CONFIG_VIDEO_VIVID_OSD if (dev->has_vid_cap) /* do we have a framebuffer for overlay testing? */ dev->has_fb = node_type & 0x10000; +#endif /* can we do crop/compose/scaling while capturing? */ if (no_error_inj && *ccs_cap == -1) @@ -1364,7 +1394,7 @@ static int vivid_create_queues(struct vivid_dev *dev) if (dev->has_meta_out) { /* initialize meta_out queue */ ret = vivid_create_queue(dev, &dev->vb_meta_out_q, - V4L2_BUF_TYPE_META_OUTPUT, 1, + V4L2_BUF_TYPE_META_OUTPUT, 2, &vivid_meta_out_qops); if (ret) return ret; @@ -1373,7 +1403,7 @@ static int vivid_create_queues(struct vivid_dev *dev) if (dev->has_touch_cap) { /* initialize touch_cap queue */ ret = vivid_create_queue(dev, &dev->vb_touch_cap_q, - V4L2_BUF_TYPE_VIDEO_CAPTURE, 1, + V4L2_BUF_TYPE_VIDEO_CAPTURE, 2, &vivid_touch_cap_qops); if (ret) return ret; @@ -1384,15 +1414,12 @@ static int vivid_create_queues(struct vivid_dev *dev) ret = vivid_fb_init(dev); if (ret) return ret; - v4l2_info(&dev->v4l2_dev, "Framebuffer device registered as fb%d\n", - dev->fb_info.node); } return 0; } static int vivid_create_devnodes(struct platform_device *pdev, struct vivid_dev *dev, int inst, - unsigned int cec_tx_bus_cnt, v4l2_std_id tvnorms_cap, v4l2_std_id tvnorms_out, unsigned in_type_counter[4], @@ -1436,7 +1463,7 @@ static int vivid_create_devnodes(struct platform_device *pdev, return ret; } cec_s_phys_addr(dev->cec_rx_adap, 0, false); - v4l2_info(&dev->v4l2_dev, "CEC adapter %s registered for HDMI input 0\n", + v4l2_info(&dev->v4l2_dev, "CEC adapter %s registered for HDMI input\n", dev_name(&dev->cec_rx_adap->devnode.dev)); } #endif @@ -1479,10 +1506,10 @@ static int vivid_create_devnodes(struct platform_device *pdev, #endif #ifdef CONFIG_VIDEO_VIVID_CEC - for (i = 0; i < cec_tx_bus_cnt; i++) { + for (i = 0; i < dev->num_hdmi_outputs; i++) { ret = cec_register_adapter(dev->cec_tx_adap[i], &pdev->dev); if (ret < 0) { - for (; i < cec_tx_bus_cnt; i++) { + for (; i >= 0; i--) { cec_delete_adapter(dev->cec_tx_adap[i]); dev->cec_tx_adap[i] = NULL; } @@ -1490,10 +1517,6 @@ static int vivid_create_devnodes(struct platform_device *pdev, } v4l2_info(&dev->v4l2_dev, "CEC adapter %s registered for HDMI output %d\n", dev_name(&dev->cec_tx_adap[i]->devnode.dev), i); - if (i < out_type_counter[HDMI]) - cec_s_phys_addr(dev->cec_tx_adap[i], (i + 1) << 12, false); - else - cec_s_phys_addr(dev->cec_tx_adap[i], 0x1000, false); } #endif @@ -1733,6 +1756,47 @@ static int vivid_create_devnodes(struct platform_device *pdev, return 0; } +static void update_hdmi_ctrls_work_handler(struct work_struct *work) +{ + u64 skip_mask; + u64 update_mask; + + spin_lock(&hdmi_output_skip_mask_lock); + skip_mask = hdmi_to_output_menu_skip_mask; + update_mask = hdmi_input_update_outputs_mask; + hdmi_input_update_outputs_mask = 0; + spin_unlock(&hdmi_output_skip_mask_lock); + for (int i = 0; i < n_devs && vivid_devs[i]; i++) { + if (update_mask & (1 << i)) + vivid_update_connected_outputs(vivid_devs[i]); + for (int j = 0; j < vivid_devs[i]->num_hdmi_inputs; j++) { + struct v4l2_ctrl *c = vivid_devs[i]->ctrl_hdmi_to_output[j]; + + v4l2_ctrl_modify_range(c, c->minimum, c->maximum, + skip_mask & ~(1ULL << c->cur.val), + c->default_value); + } + } +} + +static void update_svid_ctrls_work_handler(struct work_struct *work) +{ + u64 skip_mask; + + spin_lock(&svid_output_skip_mask_lock); + skip_mask = svid_to_output_menu_skip_mask; + spin_unlock(&svid_output_skip_mask_lock); + for (int i = 0; i < n_devs && vivid_devs[i]; i++) { + for (int j = 0; j < vivid_devs[i]->num_svid_inputs; j++) { + struct v4l2_ctrl *c = vivid_devs[i]->ctrl_svid_to_output[j]; + + v4l2_ctrl_modify_range(c, c->minimum, c->maximum, + skip_mask & ~(1ULL << c->cur.val), + c->default_value); + } + } +} + static int vivid_create_instance(struct platform_device *pdev, int inst) { static const struct v4l2_dv_timings def_dv_timings = @@ -1746,7 +1810,6 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) struct vivid_dev *dev; unsigned node_type = node_types[inst]; v4l2_std_id tvnorms_cap = 0, tvnorms_out = 0; - unsigned int cec_tx_bus_cnt = 0; int ret; int i; @@ -1849,6 +1912,22 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) dev->edid_max_blocks = dev->edid_blocks = 2; memcpy(dev->edid, vivid_hdmi_edid, sizeof(vivid_hdmi_edid)); dev->radio_rds_init_time = ktime_get(); + INIT_WORK(&dev->update_hdmi_ctrl_work, update_hdmi_ctrls_work_handler); + INIT_WORK(&dev->update_svid_ctrl_work, update_svid_ctrls_work_handler); + for (int j = 0, k = 0; j < dev->num_inputs; ++j) + if (dev->input_type[j] == HDMI) + dev->hdmi_index_to_input_index[k++] = j; + for (int j = 0, k = 0; j < dev->num_outputs; ++j) + if (dev->output_type[j] == HDMI) { + dev->output_to_iface_index[j] = k; + dev->hdmi_index_to_output_index[k++] = j; + } + for (int j = 0, k = 0; j < dev->num_inputs; ++j) + if (dev->input_type[j] == SVID) + dev->svid_index_to_input_index[k++] = j; + for (int j = 0, k = 0; j < dev->num_outputs; ++j) + if (dev->output_type[j] == SVID) + dev->output_to_iface_index[j] = k++; /* create all controls */ ret = vivid_create_controls(dev, ccs_cap == -1, ccs_out == -1, no_error_inj, @@ -1859,8 +1938,6 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) goto unreg_dev; /* enable/disable interface specific controls */ - if (dev->num_outputs && dev->output_type[0] != HDMI) - v4l2_ctrl_activate(dev->ctrl_display_present, false); if (dev->num_inputs && dev->input_type[0] != HDMI) { v4l2_ctrl_activate(dev->ctrl_dv_timings_signal_mode, false); v4l2_ctrl_activate(dev->ctrl_dv_timings, false); @@ -1916,27 +1993,27 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) } if (dev->has_vid_out) { - for (i = 0; i < dev->num_outputs; i++) { + int j; + + for (i = j = 0; i < dev->num_outputs; i++) { struct cec_adapter *adap; if (dev->output_type[i] != HDMI) continue; - dev->cec_output2bus_map[i] = cec_tx_bus_cnt; - adap = vivid_cec_alloc_adap(dev, cec_tx_bus_cnt, true); + adap = vivid_cec_alloc_adap(dev, j, true); ret = PTR_ERR_OR_ZERO(adap); if (ret < 0) { - for (i = 0; i < dev->num_outputs; i++) - cec_delete_adapter(dev->cec_tx_adap[i]); + while (j--) + cec_delete_adapter(dev->cec_tx_adap[j]); goto unreg_dev; } - dev->cec_tx_adap[cec_tx_bus_cnt] = adap; - cec_tx_bus_cnt++; + dev->cec_tx_adap[j++] = adap; } } - if (dev->cec_rx_adap || cec_tx_bus_cnt) { + if (dev->cec_rx_adap || dev->num_hdmi_outputs) { init_waitqueue_head(&dev->kthread_waitq_cec); dev->kthread_cec = kthread_run(vivid_cec_bus_thread, dev, "vivid_cec-%s", dev->v4l2_dev.name); @@ -1962,7 +2039,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) v4l2_ctrl_handler_setup(&dev->ctrl_hdl_touch_cap); /* finally start creating the device nodes */ - ret = vivid_create_devnodes(pdev, dev, inst, cec_tx_bus_cnt, + ret = vivid_create_devnodes(pdev, dev, inst, tvnorms_cap, tvnorms_out, in_type_counter, out_type_counter); if (ret) @@ -1985,7 +2062,7 @@ unreg_dev: vb2_video_unregister_device(&dev->vid_out_dev); vb2_video_unregister_device(&dev->vid_cap_dev); cec_unregister_adapter(dev->cec_rx_adap); - for (i = 0; i < MAX_OUTPUTS; i++) + for (i = 0; i < MAX_HDMI_OUTPUTS; i++) cec_unregister_adapter(dev->cec_tx_adap[i]); if (dev->kthread_cec) kthread_stop(dev->kthread_cec); @@ -2032,6 +2109,42 @@ static int vivid_probe(struct platform_device *pdev) /* n_devs will reflect the actual number of allocated devices */ n_devs = i; + /* Determine qmenu items actually in use */ + int hdmi_count = FIXED_MENU_ITEMS; + int svid_count = FIXED_MENU_ITEMS; + + for (int i = 0; i < n_devs; i++) { + struct vivid_dev *dev = vivid_devs[i]; + + if (!dev->has_vid_out) + continue; + for (int j = 0; j < dev->num_outputs && hdmi_count < MAX_MENU_ITEMS; ++j) { + if (dev->output_type[j] == HDMI) { + vivid_ctrl_hdmi_to_output_instance[hdmi_count] = vivid_devs[i]; + vivid_ctrl_hdmi_to_output_index[hdmi_count++] = j; + } + } + for (int j = 0; j < dev->num_outputs && svid_count < MAX_MENU_ITEMS; ++j) { + if (dev->output_type[j] == SVID) { + vivid_ctrl_svid_to_output_instance[svid_count] = vivid_devs[i]; + vivid_ctrl_svid_to_output_index[svid_count++] = j; + } + } + } + hdmi_count = min(hdmi_count, MAX_MENU_ITEMS); + svid_count = min(svid_count, MAX_MENU_ITEMS); + for (int i = 0; i < n_devs; i++) { + for (int j = 0; j < vivid_devs[i]->num_hdmi_inputs; j++) { + struct v4l2_ctrl *c = vivid_devs[i]->ctrl_hdmi_to_output[j]; + + v4l2_ctrl_modify_range(c, c->minimum, hdmi_count - 1, 0, c->default_value); + } + for (int j = 0; j < vivid_devs[i]->num_svid_inputs; j++) { + struct v4l2_ctrl *c = vivid_devs[i]->ctrl_svid_to_output[j]; + + v4l2_ctrl_modify_range(c, c->minimum, svid_count - 1, 0, c->default_value); + } + } return ret; } @@ -2086,12 +2199,8 @@ static void vivid_remove(struct platform_device *pdev) video_device_node_name(&dev->radio_tx_dev)); video_unregister_device(&dev->radio_tx_dev); } - if (dev->has_fb) { - v4l2_info(&dev->v4l2_dev, "unregistering fb%d\n", - dev->fb_info.node); - unregister_framebuffer(&dev->fb_info); - vivid_fb_release_buffers(dev); - } + if (dev->has_fb) + vivid_fb_deinit(dev); if (dev->has_meta_cap) { v4l2_info(&dev->v4l2_dev, "unregistering %s\n", video_device_node_name(&dev->meta_cap_dev)); @@ -2108,7 +2217,7 @@ static void vivid_remove(struct platform_device *pdev) vb2_video_unregister_device(&dev->touch_cap_dev); } cec_unregister_adapter(dev->cec_rx_adap); - for (j = 0; j < MAX_OUTPUTS; j++) + for (j = 0; j < MAX_HDMI_OUTPUTS; j++) cec_unregister_adapter(dev->cec_tx_adap[j]); if (dev->kthread_cec) kthread_stop(dev->kthread_cec); @@ -2128,7 +2237,7 @@ static struct platform_device vivid_pdev = { static struct platform_driver vivid_pdrv = { .probe = vivid_probe, - .remove_new = vivid_remove, + .remove = vivid_remove, .driver = { .name = "vivid", }, @@ -2136,21 +2245,91 @@ static struct platform_driver vivid_pdrv = { static int __init vivid_init(void) { - int ret; - + int hdmi_count = FIXED_MENU_ITEMS; + int svid_count = FIXED_MENU_ITEMS; + int ret = -ENOMEM; + unsigned int ndevs; + + /* Sanity check, prevent insane number of vivid instances */ + if (n_devs > 64) + n_devs = 64; + ndevs = clamp_t(unsigned int, n_devs, 1, VIVID_MAX_DEVS); + + for (unsigned int i = 0; i < ndevs; i++) { + if (!(node_types[i] & (1 << 8))) + continue; + unsigned int n_outputs = min(num_outputs[i], MAX_OUTPUTS); + + for (u8 j = 0, k = 0; j < n_outputs && hdmi_count < MAX_MENU_ITEMS && + k < MAX_HDMI_OUTPUTS; ++j) { + if (output_types[i] & BIT(j)) { + vivid_ctrl_hdmi_to_output_strings[hdmi_count] = + kmalloc(MAX_STRING_LENGTH, GFP_KERNEL); + if (!vivid_ctrl_hdmi_to_output_strings[hdmi_count]) + goto free_output_strings; + snprintf(vivid_ctrl_hdmi_to_output_strings[hdmi_count], + MAX_STRING_LENGTH, "Output HDMI %03d-%d", + i & 0xff, k); + k++; + hdmi_count++; + } + } + for (u8 j = 0, k = 0; j < n_outputs && svid_count < MAX_MENU_ITEMS; ++j) { + if (!(output_types[i] & BIT(j))) { + vivid_ctrl_svid_to_output_strings[svid_count] = + kmalloc(MAX_STRING_LENGTH, GFP_KERNEL); + if (!vivid_ctrl_svid_to_output_strings[svid_count]) + goto free_output_strings; + snprintf(vivid_ctrl_svid_to_output_strings[svid_count], + MAX_STRING_LENGTH, "Output S-Video %03d-%d", + i & 0xff, k); + k++; + svid_count++; + } + } + } ret = platform_device_register(&vivid_pdev); if (ret) - return ret; - + goto free_output_strings; ret = platform_driver_register(&vivid_pdrv); if (ret) - platform_device_unregister(&vivid_pdev); + goto unreg_device; + /* Initialize workqueue before module is loaded */ + update_hdmi_ctrls_workqueue = create_workqueue("update_hdmi_ctrls_wq"); + if (!update_hdmi_ctrls_workqueue) { + ret = -ENOMEM; + goto unreg_driver; + } + update_svid_ctrls_workqueue = create_workqueue("update_svid_ctrls_wq"); + if (!update_svid_ctrls_workqueue) { + ret = -ENOMEM; + goto destroy_hdmi_wq; + } + return ret; + +destroy_hdmi_wq: + destroy_workqueue(update_hdmi_ctrls_workqueue); +unreg_driver: + platform_driver_register(&vivid_pdrv); +unreg_device: + platform_device_unregister(&vivid_pdev); +free_output_strings: + for (int i = FIXED_MENU_ITEMS; i < MAX_MENU_ITEMS; i++) { + kfree(vivid_ctrl_hdmi_to_output_strings[i]); + kfree(vivid_ctrl_svid_to_output_strings[i]); + } return ret; } static void __exit vivid_exit(void) { + for (int i = FIXED_MENU_ITEMS; i < MAX_MENU_ITEMS; i++) { + kfree(vivid_ctrl_hdmi_to_output_strings[i]); + kfree(vivid_ctrl_svid_to_output_strings[i]); + } + destroy_workqueue(update_svid_ctrls_workqueue); + destroy_workqueue(update_hdmi_ctrls_workqueue); platform_driver_unregister(&vivid_pdrv); platform_device_unregister(&vivid_pdev); } diff --git a/drivers/media/test-drivers/vivid/vivid-core.h b/drivers/media/test-drivers/vivid/vivid-core.h index cfb8e66083f6..571a6c222969 100644 --- a/drivers/media/test-drivers/vivid/vivid-core.h +++ b/drivers/media/test-drivers/vivid/vivid-core.h @@ -26,6 +26,8 @@ #define MAX_INPUTS 16 /* The maximum number of outputs */ #define MAX_OUTPUTS 16 +/* The maximum number of video capture buffers */ +#define MAX_VID_CAP_BUFFERS 64 /* The maximum up or down scaling factor is 4 */ #define MAX_ZOOM 4 /* The maximum image width/height are set to 4K DMT */ @@ -50,10 +52,95 @@ #define JIFFIES_PER_DAY (3600U * 24U * HZ) #define JIFFIES_RESYNC (JIFFIES_PER_DAY * (0xf0000000U / JIFFIES_PER_DAY)) +/* + * Maximum number of HDMI inputs allowed by vivid, due to limitations + * of the Physical Address in the EDID and used by CEC we stop at 15 + * inputs and outputs. + */ +#define MAX_HDMI_INPUTS 15 +#define MAX_HDMI_OUTPUTS 15 + +/* Maximum number of S-Video inputs allowed by vivid */ +#define MAX_SVID_INPUTS 16 + +/* The maximum number of items in a menu control */ +#define MAX_MENU_ITEMS BITS_PER_LONG_LONG + +/* Number of fixed menu items in the 'Connected To' menu controls */ +#define FIXED_MENU_ITEMS 2 + +/* The maximum number of vivid devices */ +#define VIVID_MAX_DEVS CONFIG_VIDEO_VIVID_MAX_DEVS + extern const struct v4l2_rect vivid_min_rect; extern const struct v4l2_rect vivid_max_rect; extern unsigned vivid_debug; +/* + * NULL-terminated string array for the HDMI 'Connected To' menu controls + * with the list of possible HDMI outputs. + * + * The first two items are fixed ("TPG" and "None"). + */ +extern char *vivid_ctrl_hdmi_to_output_strings[1 + MAX_MENU_ITEMS]; +/* Menu control skip mask of all HDMI outputs that are in use */ +extern u64 hdmi_to_output_menu_skip_mask; +/* + * Bitmask of which vivid instances need to update any connected + * HDMI outputs. + */ +extern u64 hdmi_input_update_outputs_mask; +/* + * Spinlock for access to hdmi_to_output_menu_skip_mask and + * hdmi_input_update_outputs_mask. + */ +extern spinlock_t hdmi_output_skip_mask_lock; +/* + * Workqueue that updates the menu controls whenever the HDMI menu skip mask + * changes. + */ +extern struct workqueue_struct *update_hdmi_ctrls_workqueue; + +/* + * The HDMI menu control value (index in the menu list) maps to an HDMI + * output that is part of the given vivid_dev instance and has the given + * output index (as returned by VIDIOC_G_OUTPUT). + * + * NULL/0 if not available. + */ +extern struct vivid_dev *vivid_ctrl_hdmi_to_output_instance[MAX_MENU_ITEMS]; +extern unsigned int vivid_ctrl_hdmi_to_output_index[MAX_MENU_ITEMS]; + +/* + * NULL-terminated string array for the S-Video 'Connected To' menu controls + * with the list of possible S-Video outputs. + * + * The first two items are fixed ("TPG" and "None"). + */ +extern char *vivid_ctrl_svid_to_output_strings[1 + MAX_MENU_ITEMS]; +/* Menu control skip mask of all S-Video outputs that are in use */ +extern u64 svid_to_output_menu_skip_mask; +/* Spinlock for access to svid_to_output_menu_skip_mask */ +extern spinlock_t svid_output_skip_mask_lock; +/* + * Workqueue that updates the menu controls whenever the S-Video menu skip mask + * changes. + */ +extern struct workqueue_struct *update_svid_ctrls_workqueue; + +/* + * The S-Video menu control value (index in the menu list) maps to an S-Video + * output that is part of the given vivid_dev instance and has the given + * output index (as returned by VIDIOC_G_OUTPUT). + * + * NULL/0 if not available. + */ +extern struct vivid_dev *vivid_ctrl_svid_to_output_instance[MAX_MENU_ITEMS]; +extern unsigned int vivid_ctrl_svid_to_output_index[MAX_MENU_ITEMS]; + +extern struct vivid_dev *vivid_devs[VIVID_MAX_DEVS]; +extern unsigned int n_devs; + struct vivid_fmt { u32 fourcc; /* v4l2 format id */ enum tgp_color_enc color_enc; @@ -118,7 +205,7 @@ struct vivid_cec_xfer { }; struct vivid_dev { - unsigned inst; + u8 inst; struct v4l2_device v4l2_dev; #ifdef CONFIG_MEDIA_CONTROLLER struct media_device mdev; @@ -161,6 +248,8 @@ struct vivid_dev { spinlock_t slock; struct mutex mutex; + struct work_struct update_hdmi_ctrl_work; + struct work_struct update_svid_ctrl_work; /* capabilities */ u32 vid_cap_caps; @@ -176,12 +265,13 @@ struct vivid_dev { /* supported features */ bool multiplanar; - unsigned num_inputs; - unsigned int num_hdmi_inputs; + u8 num_inputs; + u8 num_hdmi_inputs; + u8 num_svid_inputs; u8 input_type[MAX_INPUTS]; u8 input_name_counter[MAX_INPUTS]; - unsigned num_outputs; - unsigned int num_hdmi_outputs; + u8 num_outputs; + u8 num_hdmi_outputs; u8 output_type[MAX_OUTPUTS]; u8 output_name_counter[MAX_OUTPUTS]; bool has_audio_inputs; @@ -203,7 +293,20 @@ struct vivid_dev { bool has_tv_tuner; bool has_touch_cap; - bool can_loop_video; + /* Output index (0-MAX_OUTPUTS) to vivid instance of connected input */ + struct vivid_dev *output_to_input_instance[MAX_OUTPUTS]; + /* Output index (0-MAX_OUTPUTS) to input index (0-MAX_INPUTS) of connected input */ + u8 output_to_input_index[MAX_OUTPUTS]; + /* Output index (0-MAX_OUTPUTS) to HDMI or S-Video output index (0-MAX_HDMI/SVID_OUTPUTS) */ + u8 output_to_iface_index[MAX_OUTPUTS]; + /* ctrl_hdmi_to_output or ctrl_svid_to_output control value for each input */ + s32 input_is_connected_to_output[MAX_INPUTS]; + /* HDMI index (0-MAX_HDMI_OUTPUTS) to output index (0-MAX_OUTPUTS) */ + u8 hdmi_index_to_output_index[MAX_HDMI_OUTPUTS]; + /* HDMI index (0-MAX_HDMI_INPUTS) to input index (0-MAX_INPUTS) */ + u8 hdmi_index_to_input_index[MAX_HDMI_INPUTS]; + /* S-Video index (0-MAX_SVID_INPUTS) to input index (0-MAX_INPUTS) */ + u8 svid_index_to_input_index[MAX_SVID_INPUTS]; /* controls */ struct v4l2_ctrl *brightness; @@ -242,7 +345,6 @@ struct vivid_dev { struct v4l2_ctrl *ctrl_dv_timings_signal_mode; struct v4l2_ctrl *ctrl_dv_timings; }; - struct v4l2_ctrl *ctrl_display_present; struct v4l2_ctrl *ctrl_has_crop_cap; struct v4l2_ctrl *ctrl_has_compose_cap; struct v4l2_ctrl *ctrl_has_scaler_cap; @@ -276,6 +378,11 @@ struct vivid_dev { struct v4l2_ctrl *radio_rx_rds_psname; struct v4l2_ctrl *radio_rx_rds_radiotext; + struct v4l2_ctrl *ctrl_hdmi_to_output[MAX_HDMI_INPUTS]; + char ctrl_hdmi_to_output_names[MAX_HDMI_INPUTS][32]; + struct v4l2_ctrl *ctrl_svid_to_output[MAX_SVID_INPUTS]; + char ctrl_svid_to_output_names[MAX_SVID_INPUTS][32]; + unsigned input_brightness[MAX_INPUTS]; unsigned osd_mode; unsigned button_pressed; @@ -296,9 +403,11 @@ struct vivid_dev { int display_byte_stride; int bits_per_pixel; int bytes_per_pixel; +#ifdef CONFIG_VIDEO_VIVID_OSD struct fb_info fb_info; struct fb_var_screeninfo fb_defined; struct fb_fix_screeninfo fb_fix; +#endif /* Error injection */ bool disconnect_error; @@ -364,7 +473,6 @@ struct vivid_dev { u8 *scaled_line; u8 *blended_line; unsigned cur_scaled_line; - bool display_present[MAX_OUTPUTS]; /* Output Overlay */ void *fb_vbase_out; @@ -377,7 +485,7 @@ struct vivid_dev { /* video capture */ struct tpg_data tpg; unsigned ms_vid_cap; - bool must_blank[VIDEO_MAX_FRAME]; + bool must_blank[MAX_VID_CAP_BUFFERS]; const struct vivid_fmt *fmt_cap; struct v4l2_fract timeperframe_vid_cap; @@ -544,11 +652,10 @@ struct vivid_dev { /* CEC */ struct cec_adapter *cec_rx_adap; - struct cec_adapter *cec_tx_adap[MAX_OUTPUTS]; - u8 cec_output2bus_map[MAX_OUTPUTS]; + struct cec_adapter *cec_tx_adap[MAX_HDMI_OUTPUTS]; struct task_struct *kthread_cec; wait_queue_head_t kthread_waitq_cec; - struct vivid_cec_xfer xfers[MAX_OUTPUTS]; + struct vivid_cec_xfer xfers[MAX_OUTPUTS]; spinlock_t cec_xfers_slock; /* read and write cec messages */ u32 cec_sft; /* bus signal free time, in bit periods */ u8 last_initiator; diff --git a/drivers/media/test-drivers/vivid/vivid-ctrls.c b/drivers/media/test-drivers/vivid/vivid-ctrls.c index f2b20e25a7a4..e340df0b6261 100644 --- a/drivers/media/test-drivers/vivid/vivid-ctrls.c +++ b/drivers/media/test-drivers/vivid/vivid-ctrls.c @@ -18,7 +18,6 @@ #include "vivid-radio-common.h" #include "vivid-osd.h" #include "vivid-ctrls.h" -#include "vivid-cec.h" #define VIVID_CID_CUSTOM_BASE (V4L2_CID_USER_BASE | 0xf000) #define VIVID_CID_BUTTON (VIVID_CID_CUSTOM_BASE + 0) @@ -38,6 +37,7 @@ #define VIVID_CID_U8_PIXEL_ARRAY (VIVID_CID_CUSTOM_BASE + 14) #define VIVID_CID_S32_ARRAY (VIVID_CID_CUSTOM_BASE + 15) #define VIVID_CID_S64_ARRAY (VIVID_CID_CUSTOM_BASE + 16) +#define VIVID_CID_RECT (VIVID_CID_CUSTOM_BASE + 17) #define VIVID_CID_VIVID_BASE (0x00f00000 | 0xf000) #define VIVID_CID_VIVID_CLASS (0x00f00000 | 1) @@ -69,14 +69,12 @@ #define VIVID_CID_HAS_CROP_OUT (VIVID_CID_VIVID_BASE + 34) #define VIVID_CID_HAS_COMPOSE_OUT (VIVID_CID_VIVID_BASE + 35) #define VIVID_CID_HAS_SCALER_OUT (VIVID_CID_VIVID_BASE + 36) -#define VIVID_CID_LOOP_VIDEO (VIVID_CID_VIVID_BASE + 37) #define VIVID_CID_SEQ_WRAP (VIVID_CID_VIVID_BASE + 38) #define VIVID_CID_TIME_WRAP (VIVID_CID_VIVID_BASE + 39) #define VIVID_CID_MAX_EDID_BLOCKS (VIVID_CID_VIVID_BASE + 40) #define VIVID_CID_PERCENTAGE_FILL (VIVID_CID_VIVID_BASE + 41) #define VIVID_CID_REDUCED_FPS (VIVID_CID_VIVID_BASE + 42) #define VIVID_CID_HSV_ENC (VIVID_CID_VIVID_BASE + 43) -#define VIVID_CID_DISPLAY_PRESENT (VIVID_CID_VIVID_BASE + 44) #define VIVID_CID_STD_SIGNAL_MODE (VIVID_CID_VIVID_BASE + 60) #define VIVID_CID_STANDARD (VIVID_CID_VIVID_BASE + 61) @@ -104,6 +102,12 @@ #define VIVID_CID_META_CAP_GENERATE_PTS (VIVID_CID_VIVID_BASE + 111) #define VIVID_CID_META_CAP_GENERATE_SCR (VIVID_CID_VIVID_BASE + 112) +/* HDMI inputs are in the range 0-14. The next available CID is VIVID_CID_VIVID_BASE + 128 */ +#define VIVID_CID_HDMI_IS_CONNECTED_TO_OUTPUT(input) (VIVID_CID_VIVID_BASE + 113 + (input)) + +/* S-Video inputs are in the range 0-15. The next available CID is VIVID_CID_VIVID_BASE + 144 */ +#define VIVID_CID_SVID_IS_CONNECTED_TO_OUTPUT(input) (VIVID_CID_VIVID_BASE + 128 + (input)) + /* General User Controls */ static void vivid_unregister_dev(bool valid, struct video_device *vdev) @@ -357,6 +361,38 @@ static const struct v4l2_ctrl_config vivid_ctrl_ro_int32 = { .step = 1, }; +static const struct v4l2_rect rect_def = { + .top = 100, + .left = 200, + .width = 300, + .height = 400, +}; + +static const struct v4l2_rect rect_min = { + .top = 0, + .left = 0, + .width = 1, + .height = 1, +}; + +static const struct v4l2_rect rect_max = { + .top = 0, + .left = 0, + .width = 1000, + .height = 2000, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_rect = { + .ops = &vivid_user_gen_ctrl_ops, + .id = VIVID_CID_RECT, + .name = "Rect", + .type = V4L2_CTRL_TYPE_RECT, + .flags = V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX, + .p_def.p_const = &rect_def, + .p_min.p_const = &rect_min, + .p_max.p_const = &rect_max, +}; + /* Framebuffer Controls */ static int vivid_fb_s_ctrl(struct v4l2_ctrl *ctrl) @@ -366,7 +402,7 @@ static int vivid_fb_s_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case VIVID_CID_CLEAR_FB: - vivid_clear_fb(dev); + vivid_fb_clear(dev); break; } return 0; @@ -439,6 +475,33 @@ static const struct v4l2_ctrl_ops vivid_user_vid_ctrl_ops = { /* Video Capture Controls */ +static void vivid_update_power_present(struct vivid_dev *dev) +{ + unsigned int i, j; + + dev->power_present = 0; + for (i = 0, j = 0; + i < ARRAY_SIZE(dev->dv_timings_signal_mode); i++) { + if (dev->input_type[i] != HDMI) + continue; + /* + * If connected to TPG or HDMI output, and the signal + * mode is not NO_SIGNAL, then there is power present. + */ + if (dev->input_is_connected_to_output[i] != 1 && + dev->dv_timings_signal_mode[i] != NO_SIGNAL) + dev->power_present |= (1 << j); + j++; + } + + __v4l2_ctrl_s_ctrl(dev->ctrl_rx_power_present, + dev->power_present); + + v4l2_ctrl_activate(dev->ctrl_dv_timings, + dev->dv_timings_signal_mode[dev->input] == + SELECTED_DV_TIMINGS); +} + static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl) { static const u32 colorspaces[] = { @@ -453,7 +516,11 @@ static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl) V4L2_COLORSPACE_470_SYSTEM_BG, }; struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_vid_cap); - unsigned int i, j; + unsigned int i; + struct vivid_dev *output_inst = NULL; + int index = 0; + int hdmi_index, svid_index; + s32 input_index = 0; switch (ctrl->id) { case VIVID_CID_TEST_PATTERN: @@ -519,7 +586,7 @@ static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl) break; case VIVID_CID_PERCENTAGE_FILL: tpg_s_perc_fill(&dev->tpg, ctrl->val); - for (i = 0; i < VIDEO_MAX_FRAME; i++) + for (i = 0; i < MAX_VID_CAP_BUFFERS; i++) dev->must_blank[i] = ctrl->val < 100; break; case VIVID_CID_INSERT_SAV: @@ -569,25 +636,9 @@ static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl) dev->dv_timings_signal_mode[dev->input] = dev->ctrl_dv_timings_signal_mode->val; dev->query_dv_timings[dev->input] = dev->ctrl_dv_timings->val; - - dev->power_present = 0; - for (i = 0, j = 0; - i < ARRAY_SIZE(dev->dv_timings_signal_mode); - i++) - if (dev->input_type[i] == HDMI) { - if (dev->dv_timings_signal_mode[i] != NO_SIGNAL) - dev->power_present |= (1 << j); - j++; - } - __v4l2_ctrl_s_ctrl(dev->ctrl_rx_power_present, - dev->power_present); - - v4l2_ctrl_activate(dev->ctrl_dv_timings, - dev->dv_timings_signal_mode[dev->input] == - SELECTED_DV_TIMINGS); - + vivid_update_power_present(dev); vivid_update_quality(dev); - vivid_send_source_change(dev, HDMI); + vivid_send_input_source_change(dev, dev->input); break; case VIVID_CID_DV_TIMINGS_ASPECT_RATIO: dev->dv_timings_aspect_ratio[dev->input] = ctrl->val; @@ -604,6 +655,67 @@ static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl) if (dev->edid_blocks > dev->edid_max_blocks) dev->edid_blocks = dev->edid_max_blocks; break; + case VIVID_CID_HDMI_IS_CONNECTED_TO_OUTPUT(0) ... VIVID_CID_HDMI_IS_CONNECTED_TO_OUTPUT(14): + hdmi_index = ctrl->id - VIVID_CID_HDMI_IS_CONNECTED_TO_OUTPUT(0); + output_inst = vivid_ctrl_hdmi_to_output_instance[ctrl->cur.val]; + index = vivid_ctrl_hdmi_to_output_index[ctrl->cur.val]; + input_index = dev->hdmi_index_to_input_index[hdmi_index]; + dev->input_is_connected_to_output[input_index] = ctrl->val; + + if (output_inst) { + output_inst->output_to_input_instance[index] = NULL; + vivid_update_outputs(output_inst); + cec_phys_addr_invalidate(output_inst->cec_tx_adap[index]); + } + if (ctrl->val >= FIXED_MENU_ITEMS) { + output_inst = vivid_ctrl_hdmi_to_output_instance[ctrl->val]; + index = vivid_ctrl_hdmi_to_output_index[ctrl->val]; + output_inst->output_to_input_instance[index] = dev; + output_inst->output_to_input_index[index] = + dev->hdmi_index_to_input_index[hdmi_index]; + } + spin_lock(&hdmi_output_skip_mask_lock); + hdmi_to_output_menu_skip_mask &= ~(1ULL << ctrl->cur.val); + if (ctrl->val >= FIXED_MENU_ITEMS) + hdmi_to_output_menu_skip_mask |= 1ULL << ctrl->val; + spin_unlock(&hdmi_output_skip_mask_lock); + vivid_update_power_present(dev); + vivid_update_quality(dev); + vivid_send_input_source_change(dev, dev->hdmi_index_to_input_index[hdmi_index]); + if (ctrl->val < FIXED_MENU_ITEMS && ctrl->cur.val < FIXED_MENU_ITEMS) + break; + spin_lock(&hdmi_output_skip_mask_lock); + hdmi_input_update_outputs_mask |= 1 << dev->inst; + spin_unlock(&hdmi_output_skip_mask_lock); + queue_work(update_hdmi_ctrls_workqueue, &dev->update_hdmi_ctrl_work); + break; + case VIVID_CID_SVID_IS_CONNECTED_TO_OUTPUT(0) ... VIVID_CID_SVID_IS_CONNECTED_TO_OUTPUT(15): + svid_index = ctrl->id - VIVID_CID_SVID_IS_CONNECTED_TO_OUTPUT(0); + output_inst = vivid_ctrl_svid_to_output_instance[ctrl->cur.val]; + index = vivid_ctrl_svid_to_output_index[ctrl->cur.val]; + input_index = dev->svid_index_to_input_index[svid_index]; + dev->input_is_connected_to_output[input_index] = ctrl->val; + + if (output_inst) + output_inst->output_to_input_instance[index] = NULL; + if (ctrl->val >= FIXED_MENU_ITEMS) { + output_inst = vivid_ctrl_svid_to_output_instance[ctrl->val]; + index = vivid_ctrl_svid_to_output_index[ctrl->val]; + output_inst->output_to_input_instance[index] = dev; + output_inst->output_to_input_index[index] = + dev->svid_index_to_input_index[svid_index]; + } + spin_lock(&svid_output_skip_mask_lock); + svid_to_output_menu_skip_mask &= ~(1ULL << ctrl->cur.val); + if (ctrl->val >= FIXED_MENU_ITEMS) + svid_to_output_menu_skip_mask |= 1ULL << ctrl->val; + spin_unlock(&svid_output_skip_mask_lock); + vivid_update_quality(dev); + vivid_send_input_source_change(dev, dev->svid_index_to_input_index[svid_index]); + if (ctrl->val < FIXED_MENU_ITEMS && ctrl->cur.val < FIXED_MENU_ITEMS) + break; + queue_work(update_svid_ctrls_workqueue, &dev->update_svid_ctrl_work); + break; } return 0; } @@ -966,37 +1078,6 @@ static const struct v4l2_ctrl_config vivid_ctrl_limited_rgb_range = { }; -/* Video Loop Control */ - -static int vivid_loop_cap_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_loop_cap); - - switch (ctrl->id) { - case VIVID_CID_LOOP_VIDEO: - dev->loop_video = ctrl->val; - vivid_update_quality(dev); - vivid_send_source_change(dev, SVID); - vivid_send_source_change(dev, HDMI); - break; - } - return 0; -} - -static const struct v4l2_ctrl_ops vivid_loop_cap_ctrl_ops = { - .s_ctrl = vivid_loop_cap_s_ctrl, -}; - -static const struct v4l2_ctrl_config vivid_ctrl_loop_video = { - .ops = &vivid_loop_cap_ctrl_ops, - .id = VIVID_CID_LOOP_VIDEO, - .name = "Loop Video", - .type = V4L2_CTRL_TYPE_BOOLEAN, - .max = 1, - .step = 1, -}; - - /* VBI Capture Control */ static int vivid_vbi_cap_s_ctrl(struct v4l2_ctrl *ctrl) @@ -1031,8 +1112,6 @@ static int vivid_vid_out_s_ctrl(struct v4l2_ctrl *ctrl) { struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_vid_out); struct v4l2_bt_timings *bt = &dev->dv_timings_out.bt; - u32 display_present = 0; - unsigned int i, j, bus_idx; switch (ctrl->id) { case VIVID_CID_HAS_CROP_OUT: @@ -1063,39 +1142,11 @@ static int vivid_vid_out_s_ctrl(struct v4l2_ctrl *ctrl) V4L2_QUANTIZATION_LIM_RANGE : V4L2_QUANTIZATION_DEFAULT; } - if (dev->loop_video) - vivid_send_source_change(dev, HDMI); - break; - case VIVID_CID_DISPLAY_PRESENT: - if (dev->output_type[dev->output] != HDMI) - break; - - dev->display_present[dev->output] = ctrl->val; - for (i = 0, j = 0; i < dev->num_outputs; i++) - if (dev->output_type[i] == HDMI) - display_present |= - dev->display_present[i] << j++; + if (vivid_output_is_connected_to(dev)) { + struct vivid_dev *dev_rx = vivid_output_is_connected_to(dev); - __v4l2_ctrl_s_ctrl(dev->ctrl_tx_rxsense, display_present); - - if (dev->edid_blocks) { - __v4l2_ctrl_s_ctrl(dev->ctrl_tx_edid_present, - display_present); - __v4l2_ctrl_s_ctrl(dev->ctrl_tx_hotplug, - display_present); + vivid_send_source_change(dev_rx, HDMI); } - - bus_idx = dev->cec_output2bus_map[dev->output]; - if (!dev->cec_tx_adap[bus_idx]) - break; - - if (ctrl->val && dev->edid_blocks) - cec_s_phys_addr(dev->cec_tx_adap[bus_idx], - dev->cec_tx_adap[bus_idx]->phys_addr, - false); - else - cec_phys_addr_invalidate(dev->cec_tx_adap[bus_idx]); - break; } return 0; @@ -1135,16 +1186,6 @@ static const struct v4l2_ctrl_config vivid_ctrl_has_scaler_out = { .step = 1, }; -static const struct v4l2_ctrl_config vivid_ctrl_display_present = { - .ops = &vivid_vid_out_ctrl_ops, - .id = VIVID_CID_DISPLAY_PRESENT, - .name = "Display Present", - .type = V4L2_CTRL_TYPE_BOOLEAN, - .max = 1, - .def = 1, - .step = 1, -}; - /* Streaming Controls */ static int vivid_streaming_s_ctrl(struct v4l2_ctrl *ctrl) @@ -1677,6 +1718,7 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap, dev->int_menu = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_int_menu, NULL); dev->ro_int32 = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_ro_int32, NULL); v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_area, NULL); + v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_rect, NULL); v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_u32_array, NULL); v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_u32_dyn_array, NULL); v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_u16_matrix, NULL); @@ -1710,6 +1752,48 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap, v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_insert_eav, NULL); v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_insert_hdmi_video_guard_band, NULL); v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_reduced_fps, NULL); + + WARN_ON(dev->num_hdmi_inputs > MAX_HDMI_INPUTS); + WARN_ON(dev->num_svid_inputs > MAX_SVID_INPUTS); + + for (u8 i = 0; i < dev->num_hdmi_inputs; i++) { + snprintf(dev->ctrl_hdmi_to_output_names[i], + sizeof(dev->ctrl_hdmi_to_output_names[i]), + "HDMI %03u-%u Is Connected To", dev->inst, i); + } + + for (u8 i = 0; i < dev->num_hdmi_inputs; i++) { + struct v4l2_ctrl_config ctrl_config = { + .ops = &vivid_vid_cap_ctrl_ops, + .id = VIVID_CID_HDMI_IS_CONNECTED_TO_OUTPUT(i), + .name = dev->ctrl_hdmi_to_output_names[i], + .type = V4L2_CTRL_TYPE_MENU, + .max = 1, + .qmenu = (const char * const *)vivid_ctrl_hdmi_to_output_strings, + }; + dev->ctrl_hdmi_to_output[i] = v4l2_ctrl_new_custom(hdl_vid_cap, + &ctrl_config, NULL); + } + + for (u8 i = 0; i < dev->num_svid_inputs; i++) { + snprintf(dev->ctrl_svid_to_output_names[i], + sizeof(dev->ctrl_svid_to_output_names[i]), + "S-Video %03u-%u Is Connected To", dev->inst, i); + } + + for (u8 i = 0; i < dev->num_svid_inputs; i++) { + struct v4l2_ctrl_config ctrl_config = { + .ops = &vivid_vid_cap_ctrl_ops, + .id = VIVID_CID_SVID_IS_CONNECTED_TO_OUTPUT(i), + .name = dev->ctrl_svid_to_output_names[i], + .type = V4L2_CTRL_TYPE_MENU, + .max = 1, + .qmenu = (const char * const *)vivid_ctrl_svid_to_output_strings, + }; + dev->ctrl_svid_to_output[i] = v4l2_ctrl_new_custom(hdl_vid_cap, + &ctrl_config, NULL); + } + if (show_ccs_cap) { dev->ctrl_has_crop_cap = v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_has_crop_cap, NULL); @@ -1812,21 +1896,13 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap, dev->ctrl_tx_mode = v4l2_ctrl_new_std_menu(hdl_vid_out, NULL, V4L2_CID_DV_TX_MODE, V4L2_DV_TX_MODE_HDMI, 0, V4L2_DV_TX_MODE_HDMI); - dev->ctrl_display_present = v4l2_ctrl_new_custom(hdl_vid_out, - &vivid_ctrl_display_present, NULL); - dev->ctrl_tx_hotplug = v4l2_ctrl_new_std(hdl_vid_out, - NULL, V4L2_CID_DV_TX_HOTPLUG, 0, hdmi_output_mask, - 0, hdmi_output_mask); - dev->ctrl_tx_rxsense = v4l2_ctrl_new_std(hdl_vid_out, - NULL, V4L2_CID_DV_TX_RXSENSE, 0, hdmi_output_mask, - 0, hdmi_output_mask); - dev->ctrl_tx_edid_present = v4l2_ctrl_new_std(hdl_vid_out, - NULL, V4L2_CID_DV_TX_EDID_PRESENT, 0, hdmi_output_mask, - 0, hdmi_output_mask); + dev->ctrl_tx_hotplug = v4l2_ctrl_new_std(hdl_vid_out, NULL, + V4L2_CID_DV_TX_HOTPLUG, 0, hdmi_output_mask, 0, 0); + dev->ctrl_tx_rxsense = v4l2_ctrl_new_std(hdl_vid_out, NULL, + V4L2_CID_DV_TX_RXSENSE, 0, hdmi_output_mask, 0, 0); + dev->ctrl_tx_edid_present = v4l2_ctrl_new_std(hdl_vid_out, NULL, + V4L2_CID_DV_TX_EDID_PRESENT, 0, hdmi_output_mask, 0, 0); } - if ((dev->has_vid_cap && dev->has_vid_out) || - (dev->has_vbi_cap && dev->has_vbi_out)) - v4l2_ctrl_new_custom(hdl_loop_cap, &vivid_ctrl_loop_video, NULL); if (dev->has_fb) v4l2_ctrl_new_custom(hdl_fb, &vivid_ctrl_clear_fb, NULL); diff --git a/drivers/media/test-drivers/vivid/vivid-kthread-cap.c b/drivers/media/test-drivers/vivid/vivid-kthread-cap.c index 42048727d7ff..d845e1644649 100644 --- a/drivers/media/test-drivers/vivid/vivid-kthread-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-kthread-cap.c @@ -142,7 +142,7 @@ static void scale_line(const u8 *src, u8 *dst, unsigned srcw, unsigned dstw, uns * (loop_vid_overlay). Finally calculate the part of the capture buffer that * will receive that overlaid video. */ -static void vivid_precalc_copy_rects(struct vivid_dev *dev) +static void vivid_precalc_copy_rects(struct vivid_dev *dev, struct vivid_dev *out_dev) { /* Framebuffer rectangle */ struct v4l2_rect r_fb = { @@ -150,53 +150,53 @@ static void vivid_precalc_copy_rects(struct vivid_dev *dev) }; /* Overlay window rectangle in framebuffer coordinates */ struct v4l2_rect r_overlay = { - dev->overlay_out_left, dev->overlay_out_top, - dev->compose_out.width, dev->compose_out.height + out_dev->overlay_out_left, out_dev->overlay_out_top, + out_dev->compose_out.width, out_dev->compose_out.height }; - v4l2_rect_intersect(&dev->loop_vid_copy, &dev->crop_cap, &dev->compose_out); + v4l2_rect_intersect(&dev->loop_vid_copy, &dev->crop_cap, &out_dev->compose_out); dev->loop_vid_out = dev->loop_vid_copy; - v4l2_rect_scale(&dev->loop_vid_out, &dev->compose_out, &dev->crop_out); - dev->loop_vid_out.left += dev->crop_out.left; - dev->loop_vid_out.top += dev->crop_out.top; + v4l2_rect_scale(&dev->loop_vid_out, &out_dev->compose_out, &out_dev->crop_out); + dev->loop_vid_out.left += out_dev->crop_out.left; + dev->loop_vid_out.top += out_dev->crop_out.top; dev->loop_vid_cap = dev->loop_vid_copy; v4l2_rect_scale(&dev->loop_vid_cap, &dev->crop_cap, &dev->compose_cap); dprintk(dev, 1, - "loop_vid_copy: %dx%d@%dx%d loop_vid_out: %dx%d@%dx%d loop_vid_cap: %dx%d@%dx%d\n", - dev->loop_vid_copy.width, dev->loop_vid_copy.height, + "loop_vid_copy: (%d,%d)/%ux%u loop_vid_out: (%d,%d)/%ux%u loop_vid_cap: (%d,%d)/%ux%u\n", dev->loop_vid_copy.left, dev->loop_vid_copy.top, - dev->loop_vid_out.width, dev->loop_vid_out.height, + dev->loop_vid_copy.width, dev->loop_vid_copy.height, dev->loop_vid_out.left, dev->loop_vid_out.top, - dev->loop_vid_cap.width, dev->loop_vid_cap.height, - dev->loop_vid_cap.left, dev->loop_vid_cap.top); + dev->loop_vid_out.width, dev->loop_vid_out.height, + dev->loop_vid_cap.left, dev->loop_vid_cap.top, + dev->loop_vid_cap.width, dev->loop_vid_cap.height); v4l2_rect_intersect(&r_overlay, &r_fb, &r_overlay); /* shift r_overlay to the same origin as compose_out */ - r_overlay.left += dev->compose_out.left - dev->overlay_out_left; - r_overlay.top += dev->compose_out.top - dev->overlay_out_top; + r_overlay.left += out_dev->compose_out.left - out_dev->overlay_out_left; + r_overlay.top += out_dev->compose_out.top - out_dev->overlay_out_top; v4l2_rect_intersect(&dev->loop_vid_overlay, &r_overlay, &dev->loop_vid_copy); dev->loop_fb_copy = dev->loop_vid_overlay; /* shift dev->loop_fb_copy back again to the fb origin */ - dev->loop_fb_copy.left -= dev->compose_out.left - dev->overlay_out_left; - dev->loop_fb_copy.top -= dev->compose_out.top - dev->overlay_out_top; + dev->loop_fb_copy.left -= out_dev->compose_out.left - out_dev->overlay_out_left; + dev->loop_fb_copy.top -= out_dev->compose_out.top - out_dev->overlay_out_top; dev->loop_vid_overlay_cap = dev->loop_vid_overlay; v4l2_rect_scale(&dev->loop_vid_overlay_cap, &dev->crop_cap, &dev->compose_cap); dprintk(dev, 1, - "loop_fb_copy: %dx%d@%dx%d loop_vid_overlay: %dx%d@%dx%d loop_vid_overlay_cap: %dx%d@%dx%d\n", - dev->loop_fb_copy.width, dev->loop_fb_copy.height, + "loop_fb_copy: (%d,%d)/%ux%u loop_vid_overlay: (%d,%d)/%ux%u loop_vid_overlay_cap: (%d,%d)/%ux%u\n", dev->loop_fb_copy.left, dev->loop_fb_copy.top, - dev->loop_vid_overlay.width, dev->loop_vid_overlay.height, + dev->loop_fb_copy.width, dev->loop_fb_copy.height, dev->loop_vid_overlay.left, dev->loop_vid_overlay.top, - dev->loop_vid_overlay_cap.width, dev->loop_vid_overlay_cap.height, - dev->loop_vid_overlay_cap.left, dev->loop_vid_overlay_cap.top); + dev->loop_vid_overlay.width, dev->loop_vid_overlay.height, + dev->loop_vid_overlay_cap.left, dev->loop_vid_overlay_cap.top, + dev->loop_vid_overlay_cap.width, dev->loop_vid_overlay_cap.height); } static void *plane_vaddr(struct tpg_data *tpg, struct vivid_buffer *buf, @@ -213,24 +213,25 @@ static void *plane_vaddr(struct tpg_data *tpg, struct vivid_buffer *buf, return vbuf; } -static noinline_for_stack int vivid_copy_buffer(struct vivid_dev *dev, unsigned p, - u8 *vcapbuf, struct vivid_buffer *vid_cap_buf) +static noinline_for_stack int vivid_copy_buffer(struct vivid_dev *dev, + struct vivid_dev *out_dev, unsigned p, + u8 *vcapbuf, struct vivid_buffer *vid_cap_buf) { bool blank = dev->must_blank[vid_cap_buf->vb.vb2_buf.index]; struct tpg_data *tpg = &dev->tpg; struct vivid_buffer *vid_out_buf = NULL; - unsigned vdiv = dev->fmt_out->vdownsampling[p]; + unsigned vdiv = out_dev->fmt_out->vdownsampling[p]; unsigned twopixsize = tpg_g_twopixelsize(tpg, p); unsigned img_width = tpg_hdiv(tpg, p, dev->compose_cap.width); unsigned img_height = dev->compose_cap.height; unsigned stride_cap = tpg->bytesperline[p]; - unsigned stride_out = dev->bytesperline_out[p]; + unsigned stride_out = out_dev->bytesperline_out[p]; unsigned stride_osd = dev->display_byte_stride; unsigned hmax = (img_height * tpg->perc_fill) / 100; u8 *voutbuf; u8 *vosdbuf = NULL; unsigned y; - bool blend = dev->fbuf_out_flags; + bool blend = out_dev->fbuf_out_flags; /* Coarse scaling with Bresenham */ unsigned vid_out_int_part; unsigned vid_out_fract_part; @@ -247,8 +248,8 @@ static noinline_for_stack int vivid_copy_buffer(struct vivid_dev *dev, unsigned vid_out_int_part = dev->loop_vid_out.height / dev->loop_vid_cap.height; vid_out_fract_part = dev->loop_vid_out.height % dev->loop_vid_cap.height; - if (!list_empty(&dev->vid_out_active)) - vid_out_buf = list_entry(dev->vid_out_active.next, + if (!list_empty(&out_dev->vid_out_active)) + vid_out_buf = list_entry(out_dev->vid_out_active.next, struct vivid_buffer, list); if (vid_out_buf == NULL) return -ENODATA; @@ -256,8 +257,8 @@ static noinline_for_stack int vivid_copy_buffer(struct vivid_dev *dev, unsigned vid_cap_buf->vb.field = vid_out_buf->vb.field; voutbuf = plane_vaddr(tpg, vid_out_buf, p, - dev->bytesperline_out, dev->fmt_out_rect.height); - if (p < dev->fmt_out->buffers) + out_dev->bytesperline_out, out_dev->fmt_out_rect.height); + if (p < out_dev->fmt_out->buffers) voutbuf += vid_out_buf->vb.vb2_buf.planes[p].data_offset; voutbuf += tpg_hdiv(tpg, p, dev->loop_vid_out.left) + (dev->loop_vid_out.top / vdiv) * stride_out; @@ -274,7 +275,7 @@ static noinline_for_stack int vivid_copy_buffer(struct vivid_dev *dev, unsigned return 0; } - if (dev->overlay_out_enabled && + if (out_dev->overlay_out_enabled && dev->loop_vid_overlay.width && dev->loop_vid_overlay.height) { vosdbuf = dev->video_vbase; vosdbuf += (dev->loop_fb_copy.left * twopixsize) / 2 + @@ -385,6 +386,7 @@ update_vid_out_y: static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf) { + struct vivid_dev *out_dev = NULL; struct tpg_data *tpg = &dev->tpg; unsigned factor = V4L2_FIELD_HAS_T_OR_B(dev->field_cap) ? 2 : 1; unsigned line_height = 16 / factor; @@ -396,14 +398,6 @@ static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf) unsigned ms; char str[100]; s32 gain; - bool is_loop = false; - - if (dev->loop_video && dev->can_loop_video && - ((vivid_is_svid_cap(dev) && - !VIVID_INVALID_SIGNAL(dev->std_signal_mode[dev->input])) || - (vivid_is_hdmi_cap(dev) && - !VIVID_INVALID_SIGNAL(dev->dv_timings_signal_mode[dev->input])))) - is_loop = true; buf->vb.sequence = dev->vid_cap_seq_count; v4l2_ctrl_s_ctrl(dev->ro_int32, buf->vb.sequence & 0xff); @@ -428,7 +422,34 @@ static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf) dev->field_cap == V4L2_FIELD_ALTERNATE); tpg_s_perc_fill_blank(tpg, dev->must_blank[buf->vb.vb2_buf.index]); - vivid_precalc_copy_rects(dev); + if (vivid_vid_can_loop(dev) && + ((vivid_is_svid_cap(dev) && + !VIVID_INVALID_SIGNAL(dev->std_signal_mode[dev->input])) || + (vivid_is_hdmi_cap(dev) && + !VIVID_INVALID_SIGNAL(dev->dv_timings_signal_mode[dev->input])))) { + out_dev = vivid_input_is_connected_to(dev); + /* + * If the vivid instance of the output device is different + * from the vivid instance of this input device, then we + * must take care to properly serialize the output device to + * prevent that the buffer we are copying from is being freed. + * + * If the output device is part of the same instance, then the + * lock is already taken and there is no need to take the mutex. + * + * The problem with taking the mutex is that you can get + * deadlocked if instance A locks instance B and vice versa. + * It is not really worth trying to be very smart about this, + * so just try to take the lock, and if you can't, then just + * set out_dev to NULL and you will end up with a single frame + * of Noise (the default test pattern in this case). + */ + if (out_dev && dev != out_dev && !mutex_trylock(&out_dev->mutex)) + out_dev = NULL; + } + + if (out_dev) + vivid_precalc_copy_rects(dev, out_dev); for (p = 0; p < tpg_g_planes(tpg); p++) { void *vbuf = plane_vaddr(tpg, buf, p, @@ -445,10 +466,13 @@ static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf) vbuf += dev->fmt_cap->data_offset[p]; } tpg_calc_text_basep(tpg, basep, p, vbuf); - if (!is_loop || vivid_copy_buffer(dev, p, vbuf, buf)) + if (!out_dev || vivid_copy_buffer(dev, out_dev, p, vbuf, buf)) tpg_fill_plane_buffer(tpg, vivid_get_std_cap(dev), p, vbuf); } + if (out_dev && dev != out_dev) + mutex_unlock(&out_dev->mutex); + dev->must_blank[buf->vb.vb2_buf.index] = false; /* Updates stream time, only update at the start of a new frame. */ @@ -765,9 +789,14 @@ static int vivid_thread_vid_cap(void *data) next_jiffies_since_start = jiffies_since_start; wait_jiffies = next_jiffies_since_start - jiffies_since_start; - while (time_is_after_jiffies(cur_jiffies + wait_jiffies) && - !kthread_should_stop()) - schedule(); + if (!time_is_after_jiffies(cur_jiffies + wait_jiffies)) + continue; + + wait_queue_head_t wait; + + init_waitqueue_head(&wait); + wait_event_interruptible_timeout(wait, kthread_should_stop(), + cur_jiffies + wait_jiffies - jiffies); } dprintk(dev, 1, "Video Capture Thread End\n"); return 0; diff --git a/drivers/media/test-drivers/vivid/vivid-kthread-out.c b/drivers/media/test-drivers/vivid/vivid-kthread-out.c index fac6208b51da..015a7b166a1e 100644 --- a/drivers/media/test-drivers/vivid/vivid-kthread-out.c +++ b/drivers/media/test-drivers/vivid/vivid-kthread-out.c @@ -235,9 +235,14 @@ static int vivid_thread_vid_out(void *data) next_jiffies_since_start = jiffies_since_start; wait_jiffies = next_jiffies_since_start - jiffies_since_start; - while (time_is_after_jiffies(cur_jiffies + wait_jiffies) && - !kthread_should_stop()) - schedule(); + if (!time_is_after_jiffies(cur_jiffies + wait_jiffies)) + continue; + + wait_queue_head_t wait; + + init_waitqueue_head(&wait); + wait_event_interruptible_timeout(wait, kthread_should_stop(), + cur_jiffies + wait_jiffies - jiffies); } dprintk(dev, 1, "Video Output Thread End\n"); return 0; diff --git a/drivers/media/test-drivers/vivid/vivid-kthread-touch.c b/drivers/media/test-drivers/vivid/vivid-kthread-touch.c index fa711ee36a3f..c862689786b6 100644 --- a/drivers/media/test-drivers/vivid/vivid-kthread-touch.c +++ b/drivers/media/test-drivers/vivid/vivid-kthread-touch.c @@ -135,9 +135,14 @@ static int vivid_thread_touch_cap(void *data) next_jiffies_since_start = jiffies_since_start; wait_jiffies = next_jiffies_since_start - jiffies_since_start; - while (time_is_after_jiffies(cur_jiffies + wait_jiffies) && - !kthread_should_stop()) - schedule(); + if (!time_is_after_jiffies(cur_jiffies + wait_jiffies)) + continue; + + wait_queue_head_t wait; + + init_waitqueue_head(&wait); + wait_event_interruptible_timeout(wait, kthread_should_stop(), + cur_jiffies + wait_jiffies - jiffies); } dprintk(dev, 1, "Touch Capture Thread End\n"); return 0; diff --git a/drivers/media/test-drivers/vivid/vivid-meta-cap.c b/drivers/media/test-drivers/vivid/vivid-meta-cap.c index 0a718d037e59..c7aaecc0b5a2 100644 --- a/drivers/media/test-drivers/vivid/vivid-meta-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-meta-cap.c @@ -122,8 +122,6 @@ const struct vb2_ops vivid_meta_cap_qops = { .start_streaming = meta_cap_start_streaming, .stop_streaming = meta_cap_stop_streaming, .buf_request_complete = meta_cap_buf_request_complete, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; int vidioc_enum_fmt_meta_cap(struct file *file, void *priv, diff --git a/drivers/media/test-drivers/vivid/vivid-meta-out.c b/drivers/media/test-drivers/vivid/vivid-meta-out.c index 4a569a6e58be..55e5e5dec2f2 100644 --- a/drivers/media/test-drivers/vivid/vivid-meta-out.c +++ b/drivers/media/test-drivers/vivid/vivid-meta-out.c @@ -18,7 +18,6 @@ static int meta_out_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, struct device *alloc_devs[]) { struct vivid_dev *dev = vb2_get_drv_priv(vq); - unsigned int q_num_bufs = vb2_get_num_buffers(vq); unsigned int size = sizeof(struct vivid_meta_out_buf); if (!vivid_is_webcam(dev)) @@ -31,9 +30,6 @@ static int meta_out_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, sizes[0] = size; } - if (q_num_bufs + *nbuffers < 2) - *nbuffers = 2 - q_num_bufs; - *nplanes = 1; return 0; } @@ -126,8 +122,6 @@ const struct vb2_ops vivid_meta_out_qops = { .start_streaming = meta_out_start_streaming, .stop_streaming = meta_out_stop_streaming, .buf_request_complete = meta_out_buf_request_complete, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; int vidioc_enum_fmt_meta_out(struct file *file, void *priv, diff --git a/drivers/media/test-drivers/vivid/vivid-osd.c b/drivers/media/test-drivers/vivid/vivid-osd.c index 5c931b94a7b5..91ad9b314f2e 100644 --- a/drivers/media/test-drivers/vivid/vivid-osd.c +++ b/drivers/media/test-drivers/vivid/vivid-osd.c @@ -45,13 +45,18 @@ static const u16 rgb565[16] = { 0xffff, 0xffe0, 0x07ff, 0x07e0, 0xf81f, 0xf800, 0x001f, 0x0000 }; -void vivid_clear_fb(struct vivid_dev *dev) +unsigned int vivid_fb_green_bits(struct vivid_dev *dev) +{ + return dev->fb_defined.green.length; +} + +void vivid_fb_clear(struct vivid_dev *dev) { void *p = dev->video_vbase; const u16 *rgb = rgb555; unsigned x, y; - if (dev->fb_defined.green.length == 6) + if (vivid_fb_green_bits(dev) == 6) rgb = rgb565; for (y = 0; y < dev->display_height; y++) { @@ -333,7 +338,7 @@ static int vivid_fb_init_vidmode(struct vivid_dev *dev) } /* Release any memory we've grabbed */ -void vivid_fb_release_buffers(struct vivid_dev *dev) +static void vivid_fb_release_buffers(struct vivid_dev *dev) { if (dev->video_vbase == NULL) return; @@ -370,7 +375,7 @@ int vivid_fb_init(struct vivid_dev *dev) return ret; } - vivid_clear_fb(dev); + vivid_fb_clear(dev); /* Register the framebuffer */ if (register_framebuffer(&dev->fb_info) < 0) { @@ -380,6 +385,17 @@ int vivid_fb_init(struct vivid_dev *dev) /* Set the card to the requested mode */ vivid_fb_set_par(&dev->fb_info); + + v4l2_info(&dev->v4l2_dev, "Framebuffer device registered as fb%d\n", + dev->fb_info.node); + return 0; } + +void vivid_fb_deinit(struct vivid_dev *dev) +{ + v4l2_info(&dev->v4l2_dev, "unregistering fb%d\n", dev->fb_info.node); + unregister_framebuffer(&dev->fb_info); + vivid_fb_release_buffers(dev); +} diff --git a/drivers/media/test-drivers/vivid/vivid-osd.h b/drivers/media/test-drivers/vivid/vivid-osd.h index f9ac1af25dd3..c52280ebcd03 100644 --- a/drivers/media/test-drivers/vivid/vivid-osd.h +++ b/drivers/media/test-drivers/vivid/vivid-osd.h @@ -8,8 +8,23 @@ #ifndef _VIVID_OSD_H_ #define _VIVID_OSD_H_ +#ifdef CONFIG_VIDEO_VIVID_OSD int vivid_fb_init(struct vivid_dev *dev); -void vivid_fb_release_buffers(struct vivid_dev *dev); -void vivid_clear_fb(struct vivid_dev *dev); +void vivid_fb_deinit(struct vivid_dev *dev); +void vivid_fb_clear(struct vivid_dev *dev); +unsigned int vivid_fb_green_bits(struct vivid_dev *dev); +#else +static inline int vivid_fb_init(struct vivid_dev *dev) +{ + return -ENODEV; +} + +static inline void vivid_fb_deinit(struct vivid_dev *dev) {} +static inline void vivid_fb_clear(struct vivid_dev *dev) {} +static inline unsigned int vivid_fb_green_bits(struct vivid_dev *dev) +{ + return 5; +} +#endif #endif diff --git a/drivers/media/test-drivers/vivid/vivid-sdr-cap.c b/drivers/media/test-drivers/vivid/vivid-sdr-cap.c index a81f26b76988..c633fc2ed664 100644 --- a/drivers/media/test-drivers/vivid/vivid-sdr-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-sdr-cap.c @@ -206,9 +206,14 @@ static int vivid_thread_sdr_cap(void *data) next_jiffies_since_start = jiffies_since_start; wait_jiffies = next_jiffies_since_start - jiffies_since_start; - while (time_is_after_jiffies(cur_jiffies + wait_jiffies) && - !kthread_should_stop()) - schedule(); + if (!time_is_after_jiffies(cur_jiffies + wait_jiffies)) + continue; + + wait_queue_head_t wait; + + init_waitqueue_head(&wait); + wait_event_interruptible_timeout(wait, kthread_should_stop(), + cur_jiffies + wait_jiffies - jiffies); } dprintk(dev, 1, "SDR Capture Thread End\n"); return 0; @@ -219,8 +224,13 @@ static int sdr_cap_queue_setup(struct vb2_queue *vq, unsigned sizes[], struct device *alloc_devs[]) { /* 2 = max 16-bit sample returned */ - sizes[0] = SDR_CAP_SAMPLES_PER_BUF * 2; + u32 size = SDR_CAP_SAMPLES_PER_BUF * 2; + + if (*nplanes) + return sizes[0] < size ? -EINVAL : 0; + *nplanes = 1; + sizes[0] = size; return 0; } @@ -332,8 +342,6 @@ const struct vb2_ops vivid_sdr_cap_qops = { .start_streaming = sdr_cap_start_streaming, .stop_streaming = sdr_cap_stop_streaming, .buf_request_complete = sdr_cap_buf_request_complete, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; int vivid_sdr_enum_freq_bands(struct file *file, void *fh, diff --git a/drivers/media/test-drivers/vivid/vivid-touch-cap.c b/drivers/media/test-drivers/vivid/vivid-touch-cap.c index 4b3c6ea0afde..36a781fa17bc 100644 --- a/drivers/media/test-drivers/vivid/vivid-touch-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-touch-cap.c @@ -13,21 +13,17 @@ static int touch_cap_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, struct device *alloc_devs[]) { struct vivid_dev *dev = vb2_get_drv_priv(vq); - unsigned int q_num_bufs = vb2_get_num_buffers(vq); struct v4l2_pix_format *f = &dev->tch_format; unsigned int size = f->sizeimage; if (*nplanes) { - if (sizes[0] < size) + if (*nplanes != 1) return -EINVAL; - } else { - sizes[0] = size; + return sizes[0] < size ? -EINVAL : 0; } - if (q_num_bufs + *nbuffers < 2) - *nbuffers = 2 - q_num_bufs; - *nplanes = 1; + sizes[0] = size; return 0; } @@ -114,8 +110,6 @@ const struct vb2_ops vivid_touch_cap_qops = { .start_streaming = touch_cap_start_streaming, .stop_streaming = touch_cap_stop_streaming, .buf_request_complete = touch_cap_buf_request_complete, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; int vivid_enum_fmt_tch(struct file *file, void *priv, struct v4l2_fmtdesc *f) diff --git a/drivers/media/test-drivers/vivid/vivid-vbi-cap.c b/drivers/media/test-drivers/vivid/vivid-vbi-cap.c index 3840b3a664ac..a09f62c66c33 100644 --- a/drivers/media/test-drivers/vivid/vivid-vbi-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-vbi-cap.c @@ -14,6 +14,7 @@ #include "vivid-kthread-cap.h" #include "vivid-vbi-cap.h" #include "vivid-vbi-gen.h" +#include "vivid-vid-common.h" static void vivid_sliced_vbi_cap_fill(struct vivid_dev *dev, unsigned seqnr) { @@ -23,7 +24,7 @@ static void vivid_sliced_vbi_cap_fill(struct vivid_dev *dev, unsigned seqnr) vivid_vbi_gen_sliced(vbi_gen, is_60hz, seqnr); if (!is_60hz) { - if (dev->loop_video) { + if (vivid_vid_can_loop(dev)) { if (dev->vbi_out_have_wss) { vbi_gen->data[12].data[0] = dev->vbi_out_wss[0]; vbi_gen->data[12].data[1] = dev->vbi_out_wss[1]; @@ -47,7 +48,7 @@ static void vivid_sliced_vbi_cap_fill(struct vivid_dev *dev, unsigned seqnr) break; } } - } else if (dev->loop_video && is_60hz) { + } else if (vivid_vid_can_loop(dev) && is_60hz) { if (dev->vbi_out_have_cc[0]) { vbi_gen->data[0].data[0] = dev->vbi_out_cc[0][0]; vbi_gen->data[0].data[1] = dev->vbi_out_cc[0][1]; @@ -132,6 +133,8 @@ static int vbi_cap_queue_setup(struct vb2_queue *vq, if (!vivid_is_sdtv_cap(dev)) return -EINVAL; + if (*nplanes) + return sizes[0] < size ? -EINVAL : 0; sizes[0] = size; *nplanes = 1; @@ -227,8 +230,6 @@ const struct vb2_ops vivid_vbi_cap_qops = { .start_streaming = vbi_cap_start_streaming, .stop_streaming = vbi_cap_stop_streaming, .buf_request_complete = vbi_cap_buf_request_complete, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; int vidioc_g_fmt_vbi_cap(struct file *file, void *priv, diff --git a/drivers/media/test-drivers/vivid/vivid-vbi-out.c b/drivers/media/test-drivers/vivid/vivid-vbi-out.c index 434a10676417..b7a09d2f394e 100644 --- a/drivers/media/test-drivers/vivid/vivid-vbi-out.c +++ b/drivers/media/test-drivers/vivid/vivid-vbi-out.c @@ -28,6 +28,8 @@ static int vbi_out_queue_setup(struct vb2_queue *vq, if (!vivid_is_svid_out(dev)) return -EINVAL; + if (*nplanes) + return sizes[0] < size ? -EINVAL : 0; sizes[0] = size; *nplanes = 1; @@ -126,8 +128,6 @@ const struct vb2_ops vivid_vbi_out_qops = { .start_streaming = vbi_out_start_streaming, .stop_streaming = vbi_out_stop_streaming, .buf_request_complete = vbi_out_buf_request_complete, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; int vidioc_g_fmt_vbi_out(struct file *file, void *priv, diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.c b/drivers/media/test-drivers/vivid/vivid-vid-cap.c index 2804975fe278..84e9155b5815 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.c @@ -10,6 +10,7 @@ #include <linux/sched.h> #include <linux/vmalloc.h> #include <linux/videodev2.h> +#include <linux/prandom.h> #include <linux/v4l2-dv-timings.h> #include <media/v4l2-common.h> #include <media/v4l2-event.h> @@ -24,16 +25,18 @@ /* Sizes must be in increasing order */ static const struct v4l2_frmsize_discrete webcam_sizes[] = { { 320, 180 }, + { 320, 240 }, { 640, 360 }, { 640, 480 }, { 1280, 720 }, + { 1280, 960 }, + { 1600, 1200 }, { 1920, 1080 }, { 3840, 2160 }, }; /* - * Intervals must be in increasing order and there must be twice as many - * elements in this array as there are in webcam_sizes. + * Intervals must be in increasing order. */ static const struct v4l2_fract webcam_intervals[] = { { 1, 1 }, @@ -106,8 +109,9 @@ static int vid_cap_queue_setup(struct vb2_queue *vq, if (*nplanes != buffers) return -EINVAL; for (p = 0; p < buffers; p++) { - if (sizes[p] < tpg_g_line_width(&dev->tpg, p) * h + - dev->fmt_cap->data_offset[p]) + if (sizes[p] < tpg_g_line_width(&dev->tpg, p) * h / + dev->fmt_cap->vdownsampling[p] + + dev->fmt_cap->data_offset[p]) return -EINVAL; } } else { @@ -210,12 +214,9 @@ static int vid_cap_start_streaming(struct vb2_queue *vq, unsigned count) unsigned i; int err; - if (vb2_is_streaming(&dev->vb_vid_out_q)) - dev->can_loop_video = vivid_vid_can_loop(dev); - dev->vid_cap_seq_count = 0; dprintk(dev, 1, "%s\n", __func__); - for (i = 0; i < VIDEO_MAX_FRAME; i++) + for (i = 0; i < MAX_VID_CAP_BUFFERS; i++) dev->must_blank[i] = tpg_g_perc_fill(&dev->tpg) < 100; if (dev->start_streaming_error) { dev->start_streaming_error = false; @@ -242,7 +243,6 @@ static void vid_cap_stop_streaming(struct vb2_queue *vq) dprintk(dev, 1, "%s\n", __func__); vivid_stop_generating_vid_cap(dev, &dev->vid_cap_streaming); - dev->can_loop_video = false; } static void vid_cap_buf_request_complete(struct vb2_buffer *vb) @@ -260,8 +260,6 @@ const struct vb2_ops vivid_vid_cap_qops = { .start_streaming = vid_cap_start_streaming, .stop_streaming = vid_cap_stop_streaming, .buf_request_complete = vid_cap_buf_request_complete, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; /* @@ -273,7 +271,7 @@ void vivid_update_quality(struct vivid_dev *dev) { unsigned freq_modulus; - if (dev->loop_video && (vivid_is_svid_cap(dev) || vivid_is_hdmi_cap(dev))) { + if (dev->input_is_connected_to_output[dev->input]) { /* * The 'noise' will only be replaced by the actual video * if the output video matches the input video settings. @@ -487,35 +485,35 @@ static enum v4l2_field vivid_field_cap(struct vivid_dev *dev, enum v4l2_field fi static unsigned vivid_colorspace_cap(struct vivid_dev *dev) { - if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev)) + if (!vivid_input_is_connected_to(dev)) return tpg_g_colorspace(&dev->tpg); return dev->colorspace_out; } static unsigned vivid_xfer_func_cap(struct vivid_dev *dev) { - if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev)) + if (!vivid_input_is_connected_to(dev)) return tpg_g_xfer_func(&dev->tpg); return dev->xfer_func_out; } static unsigned vivid_ycbcr_enc_cap(struct vivid_dev *dev) { - if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev)) + if (!vivid_input_is_connected_to(dev)) return tpg_g_ycbcr_enc(&dev->tpg); return dev->ycbcr_enc_out; } static unsigned int vivid_hsv_enc_cap(struct vivid_dev *dev) { - if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev)) + if (!vivid_input_is_connected_to(dev)) return tpg_g_hsv_enc(&dev->tpg); return dev->hsv_enc_out; } static unsigned vivid_quantization_cap(struct vivid_dev *dev) { - if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev)) + if (!vivid_input_is_connected_to(dev)) return tpg_g_quantization(&dev->tpg); return dev->quantization_out; } @@ -950,8 +948,8 @@ int vivid_vid_cap_s_selection(struct file *file, void *fh, struct v4l2_selection if (dev->has_compose_cap) { v4l2_rect_set_min_size(compose, &min_rect); v4l2_rect_set_max_size(compose, &max_rect); - v4l2_rect_map_inside(compose, &fmt); } + v4l2_rect_map_inside(compose, &fmt); dev->fmt_cap_rect = fmt; tpg_s_buf_height(&dev->tpg, fmt.height); } else if (dev->has_compose_cap) { @@ -1072,13 +1070,13 @@ int vidioc_enum_input(struct file *file, void *priv, inp->type = V4L2_INPUT_TYPE_CAMERA; switch (dev->input_type[inp->index]) { case WEBCAM: - snprintf(inp->name, sizeof(inp->name), "Webcam %u", - dev->input_name_counter[inp->index]); + snprintf(inp->name, sizeof(inp->name), "Webcam %03u-%u", + dev->inst, dev->input_name_counter[inp->index]); inp->capabilities = 0; break; case TV: - snprintf(inp->name, sizeof(inp->name), "TV %u", - dev->input_name_counter[inp->index]); + snprintf(inp->name, sizeof(inp->name), "TV %03u-%u", + dev->inst, dev->input_name_counter[inp->index]); inp->type = V4L2_INPUT_TYPE_TUNER; inp->std = V4L2_STD_ALL; if (dev->has_audio_inputs) @@ -1086,16 +1084,16 @@ int vidioc_enum_input(struct file *file, void *priv, inp->capabilities = V4L2_IN_CAP_STD; break; case SVID: - snprintf(inp->name, sizeof(inp->name), "S-Video %u", - dev->input_name_counter[inp->index]); + snprintf(inp->name, sizeof(inp->name), "S-Video %03u-%u", + dev->inst, dev->input_name_counter[inp->index]); inp->std = V4L2_STD_ALL; if (dev->has_audio_inputs) inp->audioset = (1 << ARRAY_SIZE(vivid_audio_inputs)) - 1; inp->capabilities = V4L2_IN_CAP_STD; break; case HDMI: - snprintf(inp->name, sizeof(inp->name), "HDMI %u", - dev->input_name_counter[inp->index]); + snprintf(inp->name, sizeof(inp->name), "HDMI %03u-%u", + dev->inst, dev->input_name_counter[inp->index]); inp->capabilities = V4L2_IN_CAP_DV_TIMINGS; if (dev->edid_blocks == 0 || dev->dv_timings_signal_mode[dev->input] == NO_SIGNAL) @@ -1462,12 +1460,19 @@ static bool valid_cvt_gtf_timings(struct v4l2_dv_timings *timings) h_freq = (u32)bt->pixelclock / total_h_pixel; if (bt->standards == 0 || (bt->standards & V4L2_DV_BT_STD_CVT)) { + struct v4l2_dv_timings cvt = {}; + if (v4l2_detect_cvt(total_v_lines, h_freq, bt->vsync, bt->width, - bt->polarities, bt->interlaced, timings)) + bt->polarities, bt->interlaced, + &vivid_dv_timings_cap, &cvt) && + cvt.bt.width == bt->width && cvt.bt.height == bt->height) { + *timings = cvt; return true; + } } if (bt->standards == 0 || (bt->standards & V4L2_DV_BT_STD_GTF)) { + struct v4l2_dv_timings gtf = {}; struct v4l2_fract aspect_ratio; find_aspect_ratio(bt->width, bt->height, @@ -1475,8 +1480,12 @@ static bool valid_cvt_gtf_timings(struct v4l2_dv_timings *timings) &aspect_ratio.denominator); if (v4l2_detect_gtf(total_v_lines, h_freq, bt->vsync, bt->polarities, bt->interlaced, - aspect_ratio, timings)) + aspect_ratio, &vivid_dv_timings_cap, + >f) && + gtf.bt.width == bt->width && gtf.bt.height == bt->height) { + *timings = gtf; return true; + } } return false; } @@ -1537,13 +1546,65 @@ int vidioc_query_dv_timings(struct file *file, void *_fh, return 0; } +void vivid_update_outputs(struct vivid_dev *dev) +{ + u32 edid_present = 0; + + if (!dev || !dev->num_outputs) + return; + for (unsigned int i = 0, j = 0; i < dev->num_outputs; i++) { + if (dev->output_type[i] != HDMI) + continue; + + struct vivid_dev *dev_rx = dev->output_to_input_instance[i]; + + if (dev_rx && dev_rx->edid_blocks) + edid_present |= 1 << j; + j++; + } + v4l2_ctrl_s_ctrl(dev->ctrl_tx_edid_present, edid_present); + v4l2_ctrl_s_ctrl(dev->ctrl_tx_hotplug, edid_present); + v4l2_ctrl_s_ctrl(dev->ctrl_tx_rxsense, edid_present); +} + +void vivid_update_connected_outputs(struct vivid_dev *dev) +{ + u16 phys_addr = cec_get_edid_phys_addr(dev->edid, dev->edid_blocks * 128, NULL); + + for (unsigned int i = 0, j = 0; i < dev->num_inputs; i++) { + unsigned int menu_idx = + dev->input_is_connected_to_output[i]; + + if (dev->input_type[i] != HDMI) + continue; + j++; + if (menu_idx < FIXED_MENU_ITEMS) + continue; + + struct vivid_dev *dev_tx = vivid_ctrl_hdmi_to_output_instance[menu_idx]; + unsigned int output = vivid_ctrl_hdmi_to_output_index[menu_idx]; + + if (!dev_tx) + continue; + + unsigned int hdmi_output = dev_tx->output_to_iface_index[output]; + + vivid_update_outputs(dev_tx); + if (dev->edid_blocks) { + cec_s_phys_addr(dev_tx->cec_tx_adap[hdmi_output], + v4l2_phys_addr_for_input(phys_addr, j), + false); + } else { + cec_phys_addr_invalidate(dev_tx->cec_tx_adap[hdmi_output]); + } + } +} + int vidioc_s_edid(struct file *file, void *_fh, struct v4l2_edid *edid) { struct vivid_dev *dev = video_drvdata(file); u16 phys_addr; - u32 display_present = 0; - unsigned int i, j; int ret; memset(edid->reserved, 0, sizeof(edid->reserved)); @@ -1552,11 +1613,11 @@ int vidioc_s_edid(struct file *file, void *_fh, if (dev->input_type[edid->pad] != HDMI || edid->start_block) return -EINVAL; if (edid->blocks == 0) { + if (vb2_is_busy(&dev->vb_vid_cap_q)) + return -EBUSY; dev->edid_blocks = 0; - v4l2_ctrl_s_ctrl(dev->ctrl_tx_edid_present, 0); - v4l2_ctrl_s_ctrl(dev->ctrl_tx_hotplug, 0); - phys_addr = CEC_PHYS_ADDR_INVALID; - goto set_phys_addr; + vivid_update_connected_outputs(dev); + return 0; } if (edid->blocks > dev->edid_max_blocks) { edid->blocks = dev->edid_max_blocks; @@ -1573,24 +1634,7 @@ int vidioc_s_edid(struct file *file, void *_fh, dev->edid_blocks = edid->blocks; memcpy(dev->edid, edid->edid, edid->blocks * 128); - for (i = 0, j = 0; i < dev->num_outputs; i++) - if (dev->output_type[i] == HDMI) - display_present |= - dev->display_present[i] << j++; - - v4l2_ctrl_s_ctrl(dev->ctrl_tx_edid_present, display_present); - v4l2_ctrl_s_ctrl(dev->ctrl_tx_hotplug, display_present); - -set_phys_addr: - /* TODO: a proper hotplug detect cycle should be emulated here */ - cec_s_phys_addr(dev->cec_rx_adap, phys_addr, false); - - for (i = 0; i < MAX_OUTPUTS && dev->cec_tx_adap[i]; i++) - cec_s_phys_addr(dev->cec_tx_adap[i], - dev->display_present[i] ? - v4l2_phys_addr_for_input(phys_addr, i + 1) : - CEC_PHYS_ADDR_INVALID, - false); + vivid_update_connected_outputs(dev); return 0; } diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.h b/drivers/media/test-drivers/vivid/vivid-vid-cap.h index 949768652d38..7a8daf0af2ca 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-cap.h +++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.h @@ -10,6 +10,8 @@ void vivid_update_quality(struct vivid_dev *dev); void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls); +void vivid_update_outputs(struct vivid_dev *dev); +void vivid_update_connected_outputs(struct vivid_dev *dev); enum tpg_video_aspect vivid_get_video_aspect(const struct vivid_dev *dev); extern const v4l2_std_id vivid_standard[]; diff --git a/drivers/media/test-drivers/vivid/vivid-vid-common.c b/drivers/media/test-drivers/vivid/vivid-vid-common.c index 38d788b5cf19..df7678db67fb 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-common.c +++ b/drivers/media/test-drivers/vivid/vivid-vid-common.c @@ -769,14 +769,55 @@ const struct vivid_fmt *vivid_get_format(struct vivid_dev *dev, u32 pixelformat) return NULL; } +struct vivid_dev *vivid_output_is_connected_to(struct vivid_dev *dev) +{ + struct vivid_dev *input_inst = dev->output_to_input_instance[dev->output]; + + if (!input_inst) + return NULL; + if (input_inst->input != dev->output_to_input_index[dev->output]) + return NULL; + return input_inst; +} + +struct vivid_dev *vivid_input_is_connected_to(struct vivid_dev *dev) +{ + s32 connected_output = dev->input_is_connected_to_output[dev->input]; + + if (connected_output < FIXED_MENU_ITEMS) + return NULL; + struct vivid_dev *output_inst = NULL; + + if (vivid_is_hdmi_cap(dev)) { + output_inst = vivid_ctrl_hdmi_to_output_instance[connected_output]; + if (vivid_ctrl_hdmi_to_output_index[connected_output] != output_inst->output) + return NULL; + return output_inst; + } else if (vivid_is_svid_cap(dev)) { + output_inst = vivid_ctrl_svid_to_output_instance[connected_output]; + if (vivid_ctrl_svid_to_output_index[connected_output] != output_inst->output) + return NULL; + return output_inst; + } else { + return NULL; + } + return output_inst; +} + bool vivid_vid_can_loop(struct vivid_dev *dev) { - if (dev->src_rect.width != dev->sink_rect.width || - dev->src_rect.height != dev->sink_rect.height) + struct vivid_dev *output_inst = vivid_input_is_connected_to(dev); + + if (!output_inst) return false; - if (dev->fmt_cap->fourcc != dev->fmt_out->fourcc) + if (!vb2_is_streaming(&output_inst->vb_vid_out_q)) return false; - if (dev->field_cap != dev->field_out) + if (dev->src_rect.width != output_inst->sink_rect.width || + dev->src_rect.height != output_inst->sink_rect.height) + return false; + if (dev->fmt_cap->fourcc != output_inst->fmt_out->fourcc) + return false; + if (dev->field_cap != output_inst->field_out) return false; /* * While this can be supported, it is just too much work @@ -785,34 +826,34 @@ bool vivid_vid_can_loop(struct vivid_dev *dev) if (dev->field_cap == V4L2_FIELD_SEQ_TB || dev->field_cap == V4L2_FIELD_SEQ_BT) return false; - if (vivid_is_svid_cap(dev) && vivid_is_svid_out(dev)) { - if (!(dev->std_cap[dev->input] & V4L2_STD_525_60) != - !(dev->std_out & V4L2_STD_525_60)) - return false; + if (vivid_is_hdmi_cap(dev)) return true; - } - if (vivid_is_hdmi_cap(dev) && vivid_is_hdmi_out(dev)) - return true; - return false; + if (!(dev->std_cap[dev->input] & V4L2_STD_525_60) != + !(output_inst->std_out & V4L2_STD_525_60)) + return false; + return true; } -void vivid_send_source_change(struct vivid_dev *dev, unsigned type) +void vivid_send_input_source_change(struct vivid_dev *dev, unsigned int input_index) { struct v4l2_event ev = { .type = V4L2_EVENT_SOURCE_CHANGE, .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, }; - unsigned i; + ev.id = input_index; - for (i = 0; i < dev->num_inputs; i++) { - ev.id = i; - if (dev->input_type[i] == type) { - if (video_is_registered(&dev->vid_cap_dev) && dev->has_vid_cap) - v4l2_event_queue(&dev->vid_cap_dev, &ev); - if (video_is_registered(&dev->vbi_cap_dev) && dev->has_vbi_cap) - v4l2_event_queue(&dev->vbi_cap_dev, &ev); - } - } + if (video_is_registered(&dev->vid_cap_dev) && dev->has_vid_cap) + v4l2_event_queue(&dev->vid_cap_dev, &ev); + if (dev->input_type[input_index] == TV || dev->input_type[input_index] == SVID) + if (video_is_registered(&dev->vbi_cap_dev) && dev->has_vbi_cap) + v4l2_event_queue(&dev->vbi_cap_dev, &ev); +} + +void vivid_send_source_change(struct vivid_dev *dev, unsigned int type) +{ + for (int i = 0; i < dev->num_inputs; i++) + if (dev->input_type[i] == type) + vivid_send_input_source_change(dev, i); } /* @@ -1036,8 +1077,10 @@ int vidioc_g_edid(struct file *file, void *_fh, struct v4l2_edid *edid) { struct vivid_dev *dev = video_drvdata(file); + struct vivid_dev *dev_rx = dev; struct video_device *vdev = video_devdata(file); struct cec_adapter *adap; + unsigned int loc; memset(edid->reserved, 0, sizeof(edid->reserved)); if (vdev->vfl_dir == VFL_DIR_RX) { @@ -1047,29 +1090,48 @@ int vidioc_g_edid(struct file *file, void *_fh, return -EINVAL; adap = dev->cec_rx_adap; } else { - unsigned int bus_idx; - if (edid->pad >= dev->num_outputs) return -EINVAL; if (dev->output_type[edid->pad] != HDMI) return -EINVAL; - if (!dev->display_present[edid->pad]) + dev_rx = dev->output_to_input_instance[edid->pad]; + if (!dev_rx) return -ENODATA; - bus_idx = dev->cec_output2bus_map[edid->pad]; - adap = dev->cec_tx_adap[bus_idx]; + + unsigned int hdmi_output = dev->output_to_iface_index[edid->pad]; + + adap = dev->cec_tx_adap[hdmi_output]; } if (edid->start_block == 0 && edid->blocks == 0) { - edid->blocks = dev->edid_blocks; + edid->blocks = dev_rx->edid_blocks; return 0; } - if (dev->edid_blocks == 0) + if (dev_rx->edid_blocks == 0) return -ENODATA; - if (edid->start_block >= dev->edid_blocks) + if (edid->start_block >= dev_rx->edid_blocks) return -EINVAL; - if (edid->blocks > dev->edid_blocks - edid->start_block) - edid->blocks = dev->edid_blocks - edid->start_block; - if (adap) - v4l2_set_edid_phys_addr(dev->edid, dev->edid_blocks * 128, adap->phys_addr); - memcpy(edid->edid, dev->edid + edid->start_block * 128, edid->blocks * 128); + if (edid->blocks > dev_rx->edid_blocks - edid->start_block) + edid->blocks = dev_rx->edid_blocks - edid->start_block; + + memcpy(edid->edid, dev_rx->edid + edid->start_block * 128, edid->blocks * 128); + + loc = cec_get_edid_spa_location(dev_rx->edid, + dev_rx->edid_blocks * 128); + if (vdev->vfl_dir == VFL_DIR_TX && adap && loc && + loc >= edid->start_block * 128 && + loc < (edid->start_block + edid->blocks) * 128) { + unsigned int i; + u8 sum = 0; + + loc -= edid->start_block * 128; + edid->edid[loc] = adap->phys_addr >> 8; + edid->edid[loc + 1] = adap->phys_addr & 0xff; + loc &= ~0x7f; + + /* update the checksum */ + for (i = loc; i < loc + 127; i++) + sum += edid->edid[i]; + edid->edid[i] = 256 - sum; + } return 0; } diff --git a/drivers/media/test-drivers/vivid/vivid-vid-common.h b/drivers/media/test-drivers/vivid/vivid-vid-common.h index d908d9725283..c49ac85abaed 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-common.h +++ b/drivers/media/test-drivers/vivid/vivid-vid-common.h @@ -22,8 +22,11 @@ extern const struct v4l2_dv_timings_cap vivid_dv_timings_cap; const struct vivid_fmt *vivid_get_format(struct vivid_dev *dev, u32 pixelformat); +struct vivid_dev *vivid_input_is_connected_to(struct vivid_dev *dev); +struct vivid_dev *vivid_output_is_connected_to(struct vivid_dev *dev); bool vivid_vid_can_loop(struct vivid_dev *dev); -void vivid_send_source_change(struct vivid_dev *dev, unsigned type); +void vivid_send_source_change(struct vivid_dev *dev, unsigned int type); +void vivid_send_input_source_change(struct vivid_dev *dev, unsigned int input_index); int vivid_vid_adjust_sel(unsigned flags, struct v4l2_rect *r); diff --git a/drivers/media/test-drivers/vivid/vivid-vid-out.c b/drivers/media/test-drivers/vivid/vivid-vid-out.c index 1653b2988f7e..c3398bce6c15 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-out.c +++ b/drivers/media/test-drivers/vivid/vivid-vid-out.c @@ -16,6 +16,7 @@ #include <media/v4l2-rect.h> #include "vivid-core.h" +#include "vivid-osd.h" #include "vivid-vid-common.h" #include "vivid-kthread-out.h" #include "vivid-vid-out.h" @@ -63,14 +64,16 @@ static int vid_out_queue_setup(struct vb2_queue *vq, if (sizes[0] < size) return -EINVAL; for (p = 1; p < planes; p++) { - if (sizes[p] < dev->bytesperline_out[p] * h + - vfmt->data_offset[p]) + if (sizes[p] < dev->bytesperline_out[p] * h / + vfmt->vdownsampling[p] + + vfmt->data_offset[p]) return -EINVAL; } } else { for (p = 0; p < planes; p++) - sizes[p] = p ? dev->bytesperline_out[p] * h + - vfmt->data_offset[p] : size; + sizes[p] = p ? dev->bytesperline_out[p] * h / + vfmt->vdownsampling[p] + + vfmt->data_offset[p] : size; } *nplanes = planes; @@ -124,7 +127,7 @@ static int vid_out_buf_prepare(struct vb2_buffer *vb) for (p = 0; p < planes; p++) { if (p) - size = dev->bytesperline_out[p] * h; + size = dev->bytesperline_out[p] * h / vfmt->vdownsampling[p]; size += vb->planes[p].data_offset; if (vb2_get_plane_payload(vb, p) < size) { @@ -155,9 +158,6 @@ static int vid_out_start_streaming(struct vb2_queue *vq, unsigned count) struct vivid_dev *dev = vb2_get_drv_priv(vq); int err; - if (vb2_is_streaming(&dev->vb_vid_cap_q)) - dev->can_loop_video = vivid_vid_can_loop(dev); - dev->vid_out_seq_count = 0; dprintk(dev, 1, "%s\n", __func__); if (dev->start_streaming_error) { @@ -185,7 +185,6 @@ static void vid_out_stop_streaming(struct vb2_queue *vq) dprintk(dev, 1, "%s\n", __func__); vivid_stop_generating_vid_out(dev, &dev->vid_out_streaming); - dev->can_loop_video = false; } static void vid_out_buf_request_complete(struct vb2_buffer *vb) @@ -203,8 +202,6 @@ const struct vb2_ops vivid_vid_out_qops = { .start_streaming = vid_out_start_streaming, .stop_streaming = vid_out_stop_streaming, .buf_request_complete = vid_out_buf_request_complete, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; /* @@ -331,8 +328,8 @@ int vivid_g_fmt_vid_out(struct file *file, void *priv, for (p = 0; p < mp->num_planes; p++) { mp->plane_fmt[p].bytesperline = dev->bytesperline_out[p]; mp->plane_fmt[p].sizeimage = - mp->plane_fmt[p].bytesperline * mp->height + - fmt->data_offset[p]; + mp->plane_fmt[p].bytesperline * mp->height / + fmt->vdownsampling[p] + fmt->data_offset[p]; } for (p = fmt->buffers; p < fmt->planes; p++) { unsigned stride = dev->bytesperline_out[p]; @@ -562,9 +559,11 @@ set_colorspace: dev->xfer_func_out = mp->xfer_func; dev->ycbcr_enc_out = mp->ycbcr_enc; dev->quantization_out = mp->quantization; - if (dev->loop_video) { - vivid_send_source_change(dev, SVID); - vivid_send_source_change(dev, HDMI); + struct vivid_dev *in_dev = vivid_output_is_connected_to(dev); + + if (in_dev) { + vivid_send_source_change(in_dev, SVID); + vivid_send_source_change(in_dev, HDMI); } return 0; } @@ -909,7 +908,7 @@ int vivid_vid_out_g_fbuf(struct file *file, void *fh, a->base = (void *)dev->video_pbase; a->fmt.width = dev->display_width; a->fmt.height = dev->display_height; - if (dev->fb_defined.green.length == 5) + if (vivid_fb_green_bits(dev) == 5) a->fmt.pixelformat = V4L2_PIX_FMT_ARGB555; else a->fmt.pixelformat = V4L2_PIX_FMT_RGB565; @@ -964,16 +963,16 @@ int vidioc_enum_output(struct file *file, void *priv, out->type = V4L2_OUTPUT_TYPE_ANALOG; switch (dev->output_type[out->index]) { case SVID: - snprintf(out->name, sizeof(out->name), "S-Video %u", - dev->output_name_counter[out->index]); + snprintf(out->name, sizeof(out->name), "S-Video %03u-%u", + dev->inst, dev->output_name_counter[out->index]); out->std = V4L2_STD_ALL; if (dev->has_audio_outputs) out->audioset = (1 << ARRAY_SIZE(vivid_audio_outputs)) - 1; out->capabilities = V4L2_OUT_CAP_STD; break; case HDMI: - snprintf(out->name, sizeof(out->name), "HDMI %u", - dev->output_name_counter[out->index]); + snprintf(out->name, sizeof(out->name), "HDMI %03u-%u", + dev->inst, dev->output_name_counter[out->index]); out->capabilities = V4L2_OUT_CAP_DV_TIMINGS; break; } @@ -1014,11 +1013,6 @@ int vidioc_s_output(struct file *file, void *priv, unsigned o) dev->meta_out_dev.tvnorms = dev->vid_out_dev.tvnorms; vivid_update_format_out(dev); - v4l2_ctrl_activate(dev->ctrl_display_present, vivid_is_hdmi_out(dev)); - if (vivid_is_hdmi_out(dev)) - v4l2_ctrl_s_ctrl(dev->ctrl_display_present, - dev->display_present[dev->output]); - return 0; } |