diff options
Diffstat (limited to 'drivers/media/pci/tw686x/tw686x-video.c')
-rw-r--r-- | drivers/media/pci/tw686x/tw686x-video.c | 182 |
1 files changed, 121 insertions, 61 deletions
diff --git a/drivers/media/pci/tw686x/tw686x-video.c b/drivers/media/pci/tw686x/tw686x-video.c index cdb16de770fe..c3fafa97b2d0 100644 --- a/drivers/media/pci/tw686x/tw686x-video.c +++ b/drivers/media/pci/tw686x/tw686x-video.c @@ -577,7 +577,7 @@ static int tw686x_buf_prepare(struct vb2_buffer *vb) return 0; } -static struct vb2_ops tw686x_video_qops = { +static const struct vb2_ops tw686x_video_qops = { .queue_setup = tw686x_queue_setup, .buf_queue = tw686x_buf_queue, .buf_prepare = tw686x_buf_prepare, @@ -672,30 +672,20 @@ static int tw686x_try_fmt_vid_cap(struct file *file, void *priv, return 0; } -static int tw686x_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) +static int tw686x_set_format(struct tw686x_video_channel *vc, + unsigned int pixelformat, unsigned int width, + unsigned int height, bool realloc) { - struct tw686x_video_channel *vc = video_drvdata(file); struct tw686x_dev *dev = vc->dev; - u32 val, width, line_width, height; - unsigned long bitsperframe; + u32 val, dma_width, dma_height, dma_line_width; int err, pb; - if (vb2_is_busy(&vc->vidq)) - return -EBUSY; - - bitsperframe = vc->width * vc->height * vc->format->depth; - err = tw686x_try_fmt_vid_cap(file, priv, f); - if (err) - return err; - - vc->format = format_by_fourcc(f->fmt.pix.pixelformat); - vc->width = f->fmt.pix.width; - vc->height = f->fmt.pix.height; + vc->format = format_by_fourcc(pixelformat); + vc->width = width; + vc->height = height; /* We need new DMA buffers if the framesize has changed */ - if (dev->dma_ops->alloc && - bitsperframe != vc->width * vc->height * vc->format->depth) { + if (dev->dma_ops->alloc && realloc) { for (pb = 0; pb < 2; pb++) dev->dma_ops->free(vc, pb); @@ -739,14 +729,36 @@ static int tw686x_s_fmt_vid_cap(struct file *file, void *priv, reg_write(vc->dev, VDMA_CHANNEL_CONFIG[vc->ch], val); /* Program the DMA frame size */ - width = (vc->width * 2) & 0x7ff; - height = vc->height / 2; - line_width = (vc->width * 2) & 0x7ff; - val = (height << 22) | (line_width << 11) | width; + dma_width = (vc->width * 2) & 0x7ff; + dma_height = vc->height / 2; + dma_line_width = (vc->width * 2) & 0x7ff; + val = (dma_height << 22) | (dma_line_width << 11) | dma_width; reg_write(vc->dev, VDMA_WHP[vc->ch], val); return 0; } +static int tw686x_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct tw686x_video_channel *vc = video_drvdata(file); + unsigned long area; + bool realloc; + int err; + + if (vb2_is_busy(&vc->vidq)) + return -EBUSY; + + area = vc->width * vc->height; + err = tw686x_try_fmt_vid_cap(file, priv, f); + if (err) + return err; + + realloc = area != (f->fmt.pix.width * f->fmt.pix.height); + return tw686x_set_format(vc, f->fmt.pix.pixelformat, + f->fmt.pix.width, f->fmt.pix.height, + realloc); +} + static int tw686x_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { @@ -763,17 +775,9 @@ static int tw686x_querycap(struct file *file, void *priv, return 0; } -static int tw686x_s_std(struct file *file, void *priv, v4l2_std_id id) +static int tw686x_set_standard(struct tw686x_video_channel *vc, v4l2_std_id id) { - struct tw686x_video_channel *vc = video_drvdata(file); - struct v4l2_format f; - u32 val, ret; - - if (vc->video_standard == id) - return 0; - - if (vb2_is_busy(&vc->vidq)) - return -EBUSY; + u32 val; if (id & V4L2_STD_NTSC) val = 0; @@ -802,14 +806,31 @@ static int tw686x_s_std(struct file *file, void *priv, v4l2_std_id id) val |= (1 << (SYS_MODE_DMA_SHIFT + vc->ch)); reg_write(vc->dev, VIDEO_CONTROL1, val); + return 0; +} + +static int tw686x_s_std(struct file *file, void *priv, v4l2_std_id id) +{ + struct tw686x_video_channel *vc = video_drvdata(file); + struct v4l2_format f; + int ret; + + if (vc->video_standard == id) + return 0; + + if (vb2_is_busy(&vc->vidq)) + return -EBUSY; + + ret = tw686x_set_standard(vc, id); + if (ret) + return ret; /* * Adjust format after V4L2_STD_525_60/V4L2_STD_625_50 change, * calling g_fmt and s_fmt will sanitize the height * according to the standard. */ - ret = tw686x_g_fmt_vid_cap(file, priv, &f); - if (!ret) - tw686x_s_fmt_vid_cap(file, priv, &f); + tw686x_g_fmt_vid_cap(file, priv, &f); + tw686x_s_fmt_vid_cap(file, priv, &f); /* * Frame decimation depends on the chosen standard, @@ -885,6 +906,42 @@ static int tw686x_g_std(struct file *file, void *priv, v4l2_std_id *id) return 0; } +static int tw686x_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + struct tw686x_video_channel *vc = video_drvdata(file); + + if (fsize->index) + return -EINVAL; + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise.max_width = TW686X_VIDEO_WIDTH; + fsize->stepwise.min_width = fsize->stepwise.max_width / 2; + fsize->stepwise.step_width = fsize->stepwise.min_width; + fsize->stepwise.max_height = TW686X_VIDEO_HEIGHT(vc->video_standard); + fsize->stepwise.min_height = fsize->stepwise.max_height / 2; + fsize->stepwise.step_height = fsize->stepwise.min_height; + return 0; +} + +static int tw686x_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *ival) +{ + struct tw686x_video_channel *vc = video_drvdata(file); + int max_fps = TW686X_MAX_FPS(vc->video_standard); + int max_rates = DIV_ROUND_UP(max_fps, 2); + + if (ival->index >= max_rates) + return -EINVAL; + + ival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + ival->discrete.numerator = 1; + if (ival->index < (max_rates - 1)) + ival->discrete.denominator = (ival->index + 1) * 2; + else + ival->discrete.denominator = max_fps; + return 0; +} + static int tw686x_g_parm(struct file *file, void *priv, struct v4l2_streamparm *sp) { @@ -928,10 +985,21 @@ static int tw686x_enum_fmt_vid_cap(struct file *file, void *priv, return 0; } +static void tw686x_set_input(struct tw686x_video_channel *vc, unsigned int i) +{ + u32 val; + + vc->input = i; + + val = reg_read(vc->dev, VDMA_CHANNEL_CONFIG[vc->ch]); + val &= ~(0x3 << 30); + val |= i << 30; + reg_write(vc->dev, VDMA_CHANNEL_CONFIG[vc->ch], val); +} + static int tw686x_s_input(struct file *file, void *priv, unsigned int i) { struct tw686x_video_channel *vc = video_drvdata(file); - u32 val; if (i >= TW686X_INPUTS_PER_CH) return -EINVAL; @@ -943,12 +1011,7 @@ static int tw686x_s_input(struct file *file, void *priv, unsigned int i) if (vb2_is_busy(&vc->vidq)) return -EBUSY; - vc->input = i; - - val = reg_read(vc->dev, VDMA_CHANNEL_CONFIG[vc->ch]); - val &= ~(0x3 << 30); - val |= i << 30; - reg_write(vc->dev, VDMA_CHANNEL_CONFIG[vc->ch], val); + tw686x_set_input(vc, i); return 0; } @@ -1007,6 +1070,8 @@ static const struct v4l2_ioctl_ops tw686x_video_ioctl_ops = { .vidioc_g_parm = tw686x_g_parm, .vidioc_s_parm = tw686x_s_parm, + .vidioc_enum_framesizes = tw686x_enum_framesizes, + .vidioc_enum_frameintervals = tw686x_enum_frameintervals, .vidioc_enum_input = tw686x_enum_input, .vidioc_g_input = tw686x_g_input, @@ -1093,8 +1158,7 @@ void tw686x_video_free(struct tw686x_dev *dev) for (ch = 0; ch < max_channels(dev); ch++) { struct tw686x_video_channel *vc = &dev->video_channels[ch]; - if (vc->device) - video_unregister_device(vc->device); + video_unregister_device(vc->device); if (dev->dma_ops->free) for (pb = 0; pb < 2; pb++) @@ -1104,7 +1168,7 @@ void tw686x_video_free(struct tw686x_dev *dev) int tw686x_video_init(struct tw686x_dev *dev) { - unsigned int ch, val, pb; + unsigned int ch, val; int err; if (dev->dma_mode == TW686X_DMA_MODE_MEMCPY) @@ -1138,27 +1202,23 @@ int tw686x_video_init(struct tw686x_dev *dev) vc->ch = ch; /* default settings */ - vc->format = &formats[0]; - vc->video_standard = V4L2_STD_NTSC; - vc->width = TW686X_VIDEO_WIDTH; - vc->height = TW686X_VIDEO_HEIGHT(vc->video_standard); - vc->input = 0; + err = tw686x_set_standard(vc, V4L2_STD_NTSC); + if (err) + goto error; - reg_write(vc->dev, SDT[ch], 0); - tw686x_set_framerate(vc, 30); + err = tw686x_set_format(vc, formats[0].fourcc, + TW686X_VIDEO_WIDTH, + TW686X_VIDEO_HEIGHT(vc->video_standard), + true); + if (err) + goto error; + tw686x_set_input(vc, 0); + tw686x_set_framerate(vc, 30); reg_write(dev, VDELAY_LO[ch], 0x14); reg_write(dev, HACTIVE_LO[ch], 0xd0); reg_write(dev, VIDEO_SIZE[ch], 0); - if (dev->dma_ops->alloc) { - for (pb = 0; pb < 2; pb++) { - err = dev->dma_ops->alloc(vc, pb); - if (err) - goto error; - } - } - vc->vidq.io_modes = VB2_READ | VB2_MMAP | VB2_DMABUF; vc->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; vc->vidq.drv_priv = vc; |