diff options
Diffstat (limited to 'drivers/media/pci/mgb4/mgb4_vin.c')
-rw-r--r-- | drivers/media/pci/mgb4/mgb4_vin.c | 193 |
1 files changed, 152 insertions, 41 deletions
diff --git a/drivers/media/pci/mgb4/mgb4_vin.c b/drivers/media/pci/mgb4/mgb4_vin.c index 2cd78c539889..e9332abb3172 100644 --- a/drivers/media/pci/mgb4/mgb4_vin.c +++ b/drivers/media/pci/mgb4/mgb4_vin.c @@ -18,6 +18,7 @@ #include <linux/workqueue.h> #include <linux/align.h> #include <linux/dma/amd_xdma.h> +#include <linux/v4l2-dv-timings.h> #include <media/v4l2-ioctl.h> #include <media/videobuf2-v4l2.h> #include <media/videobuf2-dma-sg.h> @@ -34,8 +35,8 @@ ATTRIBUTE_GROUPS(mgb4_fpdl3_in); ATTRIBUTE_GROUPS(mgb4_gmsl_in); static const struct mgb4_vin_config vin_cfg[] = { - {0, 0, 0, 6, {0x10, 0x00, 0x04, 0x08, 0x1C, 0x14, 0x18, 0x20, 0x24, 0x28}}, - {1, 1, 1, 7, {0x40, 0x30, 0x34, 0x38, 0x4C, 0x44, 0x48, 0x50, 0x54, 0x58}} + {0, 0, 0, 6, {0x10, 0x00, 0x04, 0x08, 0x1C, 0x14, 0x18, 0x20, 0x24, 0x28, 0xE8}}, + {1, 1, 1, 7, {0x40, 0x30, 0x34, 0x38, 0x4C, 0x44, 0x48, 0x50, 0x54, 0x58, 0xEC}} }; static const struct i2c_board_info fpdl3_deser_info[] = { @@ -76,6 +77,9 @@ static const struct v4l2_dv_timings_cap video_timings_cap = { }, }; +/* Dummy timings when no signal present */ +static const struct v4l2_dv_timings cea1080p60 = V4L2_DV_BT_CEA_1920X1080P60; + /* * Returns the video output connected with the given video input if the input * is in loopback mode. @@ -186,8 +190,11 @@ static int queue_setup(struct vb2_queue *q, unsigned int *nbuffers, struct device *alloc_devs[]) { struct mgb4_vin_dev *vindev = vb2_get_drv_priv(q); + struct mgb4_regs *video = &vindev->mgbdev->video; + u32 config = mgb4_read_reg(video, vindev->config->regs.config); + u32 pixelsize = (config & (1U << 16)) ? 2 : 4; unsigned int size = (vindev->timings.bt.width + vindev->padding) - * vindev->timings.bt.height * 4; + * vindev->timings.bt.height * pixelsize; /* * If I/O reconfiguration is in process, do not allow to start @@ -220,9 +227,12 @@ static int buffer_init(struct vb2_buffer *vb) static int buffer_prepare(struct vb2_buffer *vb) { struct mgb4_vin_dev *vindev = vb2_get_drv_priv(vb->vb2_queue); + struct mgb4_regs *video = &vindev->mgbdev->video; struct device *dev = &vindev->mgbdev->pdev->dev; + u32 config = mgb4_read_reg(video, vindev->config->regs.config); + u32 pixelsize = (config & (1U << 16)) ? 2 : 4; unsigned int size = (vindev->timings.bt.width + vindev->padding) - * vindev->timings.bt.height * 4; + * vindev->timings.bt.height * pixelsize; if (vb2_plane_size(vb, 0) < size) { dev_err(dev, "buffer too small (%lu < %u)\n", @@ -312,7 +322,8 @@ static int fh_open(struct file *file) if (!v4l2_fh_is_singular_file(file)) goto out; - get_timings(vindev, &vindev->timings); + if (get_timings(vindev, &vindev->timings) < 0) + vindev->timings = cea1080p60; set_loopback_padding(vindev, vindev->padding); out: @@ -359,33 +370,42 @@ static int vidioc_querycap(struct file *file, void *priv, static int vidioc_enum_fmt(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - if (f->index != 0) - return -EINVAL; - - f->pixelformat = V4L2_PIX_FMT_ABGR32; + struct mgb4_vin_dev *vindev = video_drvdata(file); + struct mgb4_regs *video = &vindev->mgbdev->video; - return 0; + if (f->index == 0) { + f->pixelformat = V4L2_PIX_FMT_ABGR32; + return 0; + } else if (f->index == 1 && has_yuv(video)) { + f->pixelformat = V4L2_PIX_FMT_YUYV; + return 0; + } else { + return -EINVAL; + } } static int vidioc_enum_frameintervals(struct file *file, void *priv, struct v4l2_frmivalenum *ival) { struct mgb4_vin_dev *vindev = video_drvdata(file); + struct mgb4_regs *video = &vindev->mgbdev->video; if (ival->index != 0) return -EINVAL; - if (ival->pixel_format != V4L2_PIX_FMT_ABGR32) + if (!(ival->pixel_format == V4L2_PIX_FMT_ABGR32 || + ((has_yuv(video) && ival->pixel_format == V4L2_PIX_FMT_YUYV)))) return -EINVAL; if (ival->width != vindev->timings.bt.width || ival->height != vindev->timings.bt.height) return -EINVAL; - ival->type = V4L2_FRMIVAL_TYPE_CONTINUOUS; - ival->stepwise.min.denominator = 60; - ival->stepwise.min.numerator = 1; - ival->stepwise.max.denominator = 1; - ival->stepwise.max.numerator = 1; - ival->stepwise.step = ival->stepwise.max; + ival->type = V4L2_FRMIVAL_TYPE_STEPWISE; + ival->stepwise.max.denominator = MGB4_HW_FREQ; + ival->stepwise.max.numerator = 0xFFFFFFFF; + ival->stepwise.min.denominator = vindev->timings.bt.pixelclock; + ival->stepwise.min.numerator = pixel_size(&vindev->timings); + ival->stepwise.step.denominator = MGB4_HW_FREQ; + ival->stepwise.step.numerator = 1; return 0; } @@ -393,13 +413,29 @@ static int vidioc_enum_frameintervals(struct file *file, void *priv, static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) { struct mgb4_vin_dev *vindev = video_drvdata(file); + struct mgb4_regs *video = &vindev->mgbdev->video; + u32 config = mgb4_read_reg(video, vindev->config->regs.config); - f->fmt.pix.pixelformat = V4L2_PIX_FMT_ABGR32; f->fmt.pix.width = vindev->timings.bt.width; f->fmt.pix.height = vindev->timings.bt.height; f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.colorspace = V4L2_COLORSPACE_RAW; - f->fmt.pix.bytesperline = (f->fmt.pix.width + vindev->padding) * 4; + + if (config & (1U << 16)) { + f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; + if (config & (1U << 20)) { + f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709; + } else { + if (config & (1U << 19)) + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + else + f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; + } + f->fmt.pix.bytesperline = (f->fmt.pix.width + vindev->padding) * 2; + } else { + f->fmt.pix.pixelformat = V4L2_PIX_FMT_ABGR32; + f->fmt.pix.colorspace = V4L2_COLORSPACE_RAW; + f->fmt.pix.bytesperline = (f->fmt.pix.width + vindev->padding) * 4; + } f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height; return 0; @@ -408,14 +444,30 @@ static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) { struct mgb4_vin_dev *vindev = video_drvdata(file); + struct mgb4_regs *video = &vindev->mgbdev->video; + u32 pixelsize; - f->fmt.pix.pixelformat = V4L2_PIX_FMT_ABGR32; f->fmt.pix.width = vindev->timings.bt.width; f->fmt.pix.height = vindev->timings.bt.height; f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.colorspace = V4L2_COLORSPACE_RAW; - f->fmt.pix.bytesperline = max(f->fmt.pix.width * 4, - ALIGN_DOWN(f->fmt.pix.bytesperline, 4)); + + if (has_yuv(video) && f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) { + pixelsize = 2; + if (!(f->fmt.pix.colorspace == V4L2_COLORSPACE_REC709 || + f->fmt.pix.colorspace == V4L2_COLORSPACE_SMPTE170M)) + f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; + } else { + pixelsize = 4; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_ABGR32; + f->fmt.pix.colorspace = V4L2_COLORSPACE_RAW; + } + + if (f->fmt.pix.bytesperline > f->fmt.pix.width * pixelsize && + f->fmt.pix.bytesperline < f->fmt.pix.width * pixelsize * 2) + f->fmt.pix.bytesperline = ALIGN(f->fmt.pix.bytesperline, + pixelsize); + else + f->fmt.pix.bytesperline = f->fmt.pix.width * pixelsize; f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height; return 0; @@ -425,13 +477,36 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) { struct mgb4_vin_dev *vindev = video_drvdata(file); struct mgb4_regs *video = &vindev->mgbdev->video; + u32 config, pixelsize; if (vb2_is_busy(&vindev->queue)) return -EBUSY; vidioc_try_fmt(file, priv, f); - vindev->padding = (f->fmt.pix.bytesperline - (f->fmt.pix.width * 4)) / 4; + config = mgb4_read_reg(video, vindev->config->regs.config); + if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) { + pixelsize = 2; + config |= 1U << 16; + + if (f->fmt.pix.colorspace == V4L2_COLORSPACE_REC709) { + config |= 1U << 20; + config |= 1U << 19; + } else if (f->fmt.pix.colorspace == V4L2_COLORSPACE_SMPTE170M) { + config &= ~(1U << 20); + config |= 1U << 19; + } else { + config &= ~(1U << 20); + config &= ~(1U << 19); + } + } else { + pixelsize = 4; + config &= ~(1U << 16); + } + mgb4_write_reg(video, vindev->config->regs.config, config); + + vindev->padding = (f->fmt.pix.bytesperline - (f->fmt.pix.width + * pixelsize)) / pixelsize; mgb4_write_reg(video, vindev->config->regs.padding, vindev->padding); set_loopback_padding(vindev, vindev->padding); @@ -467,7 +542,8 @@ static int vidioc_enum_framesizes(struct file *file, void *fh, { struct mgb4_vin_dev *vindev = video_drvdata(file); - if (fsize->index != 0 || fsize->pixel_format != V4L2_PIX_FMT_ABGR32) + if (fsize->index != 0 || !(fsize->pixel_format == V4L2_PIX_FMT_ABGR32 || + fsize->pixel_format == V4L2_PIX_FMT_YUYV)) return -EINVAL; fsize->discrete.width = vindev->timings.bt.width; @@ -488,27 +564,56 @@ static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) return 0; } -static int vidioc_parm(struct file *file, void *priv, - struct v4l2_streamparm *parm) +static int vidioc_g_parm(struct file *file, void *priv, + struct v4l2_streamparm *parm) { struct mgb4_vin_dev *vindev = video_drvdata(file); struct mgb4_regs *video = &vindev->mgbdev->video; - const struct mgb4_vin_regs *regs = &vindev->config->regs; - struct v4l2_fract timeperframe = { - .numerator = mgb4_read_reg(video, regs->frame_period), - .denominator = 125000000, - }; - - if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; + struct v4l2_fract *tpf = &parm->parm.output.timeperframe; + u32 timer; parm->parm.capture.readbuffers = 2; - parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; - parm->parm.capture.timeperframe = timeperframe; + + if (has_timeperframe(video)) { + timer = mgb4_read_reg(video, vindev->config->regs.timer); + if (timer < 0xFFFF) { + tpf->numerator = pixel_size(&vindev->timings); + tpf->denominator = vindev->timings.bt.pixelclock; + } else { + tpf->numerator = timer; + tpf->denominator = MGB4_HW_FREQ; + } + + parm->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + } return 0; } +static int vidioc_s_parm(struct file *file, void *priv, + struct v4l2_streamparm *parm) +{ + struct mgb4_vin_dev *vindev = video_drvdata(file); + struct mgb4_regs *video = &vindev->mgbdev->video; + struct v4l2_fract *tpf = &parm->parm.output.timeperframe; + u32 period, timer; + + if (has_timeperframe(video)) { + timer = tpf->denominator ? + MGB4_PERIOD(tpf->numerator, tpf->denominator) : 0; + if (timer) { + period = MGB4_PERIOD(pixel_size(&vindev->timings), + vindev->timings.bt.pixelclock); + if (timer < period) + timer = 0; + } + + mgb4_write_reg(video, vindev->config->regs.timer, timer); + } + + return vidioc_g_parm(file, priv, parm); +} + static int vidioc_s_dv_timings(struct file *file, void *fh, struct v4l2_dv_timings *timings) { @@ -592,8 +697,8 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_expbuf = vb2_ioctl_expbuf, .vidioc_streamon = vb2_ioctl_streamon, .vidioc_streamoff = vb2_ioctl_streamoff, - .vidioc_g_parm = vidioc_parm, - .vidioc_s_parm = vidioc_parm, + .vidioc_g_parm = vidioc_g_parm, + .vidioc_s_parm = vidioc_s_parm, .vidioc_dv_timings_cap = vidioc_dv_timings_cap, .vidioc_enum_dv_timings = vidioc_enum_dv_timings, .vidioc_g_dv_timings = vidioc_g_dv_timings, @@ -776,10 +881,16 @@ static void debugfs_init(struct mgb4_vin_dev *vindev) vindev->regs[7].offset = vindev->config->regs.signal2; vindev->regs[8].name = "PADDING_PIXELS"; vindev->regs[8].offset = vindev->config->regs.padding; + if (has_timeperframe(video)) { + vindev->regs[9].name = "TIMER"; + vindev->regs[9].offset = vindev->config->regs.timer; + vindev->regset.nregs = 10; + } else { + vindev->regset.nregs = 9; + } vindev->regset.base = video->membase; vindev->regset.regs = vindev->regs; - vindev->regset.nregs = ARRAY_SIZE(vindev->regs); debugfs_create_regset32("registers", 0444, vindev->debugfs, &vindev->regset); |