162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * vivid-vid-out.c - video output support functions. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/errno.h> 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/sched.h> 1162306a36Sopenharmony_ci#include <linux/videodev2.h> 1262306a36Sopenharmony_ci#include <linux/v4l2-dv-timings.h> 1362306a36Sopenharmony_ci#include <media/v4l2-common.h> 1462306a36Sopenharmony_ci#include <media/v4l2-event.h> 1562306a36Sopenharmony_ci#include <media/v4l2-dv-timings.h> 1662306a36Sopenharmony_ci#include <media/v4l2-rect.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "vivid-core.h" 1962306a36Sopenharmony_ci#include "vivid-vid-common.h" 2062306a36Sopenharmony_ci#include "vivid-kthread-out.h" 2162306a36Sopenharmony_ci#include "vivid-vid-out.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic int vid_out_queue_setup(struct vb2_queue *vq, 2462306a36Sopenharmony_ci unsigned *nbuffers, unsigned *nplanes, 2562306a36Sopenharmony_ci unsigned sizes[], struct device *alloc_devs[]) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci struct vivid_dev *dev = vb2_get_drv_priv(vq); 2862306a36Sopenharmony_ci const struct vivid_fmt *vfmt = dev->fmt_out; 2962306a36Sopenharmony_ci unsigned planes = vfmt->buffers; 3062306a36Sopenharmony_ci unsigned h = dev->fmt_out_rect.height; 3162306a36Sopenharmony_ci unsigned int size = dev->bytesperline_out[0] * h + vfmt->data_offset[0]; 3262306a36Sopenharmony_ci unsigned p; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci for (p = vfmt->buffers; p < vfmt->planes; p++) 3562306a36Sopenharmony_ci size += dev->bytesperline_out[p] * h / vfmt->vdownsampling[p] + 3662306a36Sopenharmony_ci vfmt->data_offset[p]; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci if (dev->field_out == V4L2_FIELD_ALTERNATE) { 3962306a36Sopenharmony_ci /* 4062306a36Sopenharmony_ci * You cannot use write() with FIELD_ALTERNATE since the field 4162306a36Sopenharmony_ci * information (TOP/BOTTOM) cannot be passed to the kernel. 4262306a36Sopenharmony_ci */ 4362306a36Sopenharmony_ci if (vb2_fileio_is_active(vq)) 4462306a36Sopenharmony_ci return -EINVAL; 4562306a36Sopenharmony_ci } 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci if (dev->queue_setup_error) { 4862306a36Sopenharmony_ci /* 4962306a36Sopenharmony_ci * Error injection: test what happens if queue_setup() returns 5062306a36Sopenharmony_ci * an error. 5162306a36Sopenharmony_ci */ 5262306a36Sopenharmony_ci dev->queue_setup_error = false; 5362306a36Sopenharmony_ci return -EINVAL; 5462306a36Sopenharmony_ci } 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if (*nplanes) { 5762306a36Sopenharmony_ci /* 5862306a36Sopenharmony_ci * Check if the number of requested planes match 5962306a36Sopenharmony_ci * the number of planes in the current format. You can't mix that. 6062306a36Sopenharmony_ci */ 6162306a36Sopenharmony_ci if (*nplanes != planes) 6262306a36Sopenharmony_ci return -EINVAL; 6362306a36Sopenharmony_ci if (sizes[0] < size) 6462306a36Sopenharmony_ci return -EINVAL; 6562306a36Sopenharmony_ci for (p = 1; p < planes; p++) { 6662306a36Sopenharmony_ci if (sizes[p] < dev->bytesperline_out[p] * h + 6762306a36Sopenharmony_ci vfmt->data_offset[p]) 6862306a36Sopenharmony_ci return -EINVAL; 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci } else { 7162306a36Sopenharmony_ci for (p = 0; p < planes; p++) 7262306a36Sopenharmony_ci sizes[p] = p ? dev->bytesperline_out[p] * h + 7362306a36Sopenharmony_ci vfmt->data_offset[p] : size; 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci if (vq->num_buffers + *nbuffers < 2) 7762306a36Sopenharmony_ci *nbuffers = 2 - vq->num_buffers; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci *nplanes = planes; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci dprintk(dev, 1, "%s: count=%d\n", __func__, *nbuffers); 8262306a36Sopenharmony_ci for (p = 0; p < planes; p++) 8362306a36Sopenharmony_ci dprintk(dev, 1, "%s: size[%u]=%u\n", __func__, p, sizes[p]); 8462306a36Sopenharmony_ci return 0; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic int vid_out_buf_out_validate(struct vb2_buffer *vb) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 9062306a36Sopenharmony_ci struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci dprintk(dev, 1, "%s\n", __func__); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci if (dev->field_out != V4L2_FIELD_ALTERNATE) 9562306a36Sopenharmony_ci vbuf->field = dev->field_out; 9662306a36Sopenharmony_ci else if (vbuf->field != V4L2_FIELD_TOP && 9762306a36Sopenharmony_ci vbuf->field != V4L2_FIELD_BOTTOM) 9862306a36Sopenharmony_ci return -EINVAL; 9962306a36Sopenharmony_ci return 0; 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic int vid_out_buf_prepare(struct vb2_buffer *vb) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); 10562306a36Sopenharmony_ci const struct vivid_fmt *vfmt = dev->fmt_out; 10662306a36Sopenharmony_ci unsigned int planes = vfmt->buffers; 10762306a36Sopenharmony_ci unsigned int h = dev->fmt_out_rect.height; 10862306a36Sopenharmony_ci unsigned int size = dev->bytesperline_out[0] * h; 10962306a36Sopenharmony_ci unsigned p; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci for (p = vfmt->buffers; p < vfmt->planes; p++) 11262306a36Sopenharmony_ci size += dev->bytesperline_out[p] * h / vfmt->vdownsampling[p]; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci dprintk(dev, 1, "%s\n", __func__); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci if (WARN_ON(NULL == dev->fmt_out)) 11762306a36Sopenharmony_ci return -EINVAL; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (dev->buf_prepare_error) { 12062306a36Sopenharmony_ci /* 12162306a36Sopenharmony_ci * Error injection: test what happens if buf_prepare() returns 12262306a36Sopenharmony_ci * an error. 12362306a36Sopenharmony_ci */ 12462306a36Sopenharmony_ci dev->buf_prepare_error = false; 12562306a36Sopenharmony_ci return -EINVAL; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci for (p = 0; p < planes; p++) { 12962306a36Sopenharmony_ci if (p) 13062306a36Sopenharmony_ci size = dev->bytesperline_out[p] * h; 13162306a36Sopenharmony_ci size += vb->planes[p].data_offset; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (vb2_get_plane_payload(vb, p) < size) { 13462306a36Sopenharmony_ci dprintk(dev, 1, "%s the payload is too small for plane %u (%lu < %u)\n", 13562306a36Sopenharmony_ci __func__, p, vb2_get_plane_payload(vb, p), size); 13662306a36Sopenharmony_ci return -EINVAL; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci return 0; 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic void vid_out_buf_queue(struct vb2_buffer *vb) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 14662306a36Sopenharmony_ci struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); 14762306a36Sopenharmony_ci struct vivid_buffer *buf = container_of(vbuf, struct vivid_buffer, vb); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci dprintk(dev, 1, "%s\n", __func__); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci spin_lock(&dev->slock); 15262306a36Sopenharmony_ci list_add_tail(&buf->list, &dev->vid_out_active); 15362306a36Sopenharmony_ci spin_unlock(&dev->slock); 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic int vid_out_start_streaming(struct vb2_queue *vq, unsigned count) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci struct vivid_dev *dev = vb2_get_drv_priv(vq); 15962306a36Sopenharmony_ci int err; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci if (vb2_is_streaming(&dev->vb_vid_cap_q)) 16262306a36Sopenharmony_ci dev->can_loop_video = vivid_vid_can_loop(dev); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci dev->vid_out_seq_count = 0; 16562306a36Sopenharmony_ci dprintk(dev, 1, "%s\n", __func__); 16662306a36Sopenharmony_ci if (dev->start_streaming_error) { 16762306a36Sopenharmony_ci dev->start_streaming_error = false; 16862306a36Sopenharmony_ci err = -EINVAL; 16962306a36Sopenharmony_ci } else { 17062306a36Sopenharmony_ci err = vivid_start_generating_vid_out(dev, &dev->vid_out_streaming); 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci if (err) { 17362306a36Sopenharmony_ci struct vivid_buffer *buf, *tmp; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci list_for_each_entry_safe(buf, tmp, &dev->vid_out_active, list) { 17662306a36Sopenharmony_ci list_del(&buf->list); 17762306a36Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, 17862306a36Sopenharmony_ci VB2_BUF_STATE_QUEUED); 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci return err; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci/* abort streaming and wait for last buffer */ 18562306a36Sopenharmony_cistatic void vid_out_stop_streaming(struct vb2_queue *vq) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci struct vivid_dev *dev = vb2_get_drv_priv(vq); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci dprintk(dev, 1, "%s\n", __func__); 19062306a36Sopenharmony_ci vivid_stop_generating_vid_out(dev, &dev->vid_out_streaming); 19162306a36Sopenharmony_ci dev->can_loop_video = false; 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic void vid_out_buf_request_complete(struct vb2_buffer *vb) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci v4l2_ctrl_request_complete(vb->req_obj.req, &dev->ctrl_hdl_vid_out); 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ciconst struct vb2_ops vivid_vid_out_qops = { 20262306a36Sopenharmony_ci .queue_setup = vid_out_queue_setup, 20362306a36Sopenharmony_ci .buf_out_validate = vid_out_buf_out_validate, 20462306a36Sopenharmony_ci .buf_prepare = vid_out_buf_prepare, 20562306a36Sopenharmony_ci .buf_queue = vid_out_buf_queue, 20662306a36Sopenharmony_ci .start_streaming = vid_out_start_streaming, 20762306a36Sopenharmony_ci .stop_streaming = vid_out_stop_streaming, 20862306a36Sopenharmony_ci .buf_request_complete = vid_out_buf_request_complete, 20962306a36Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 21062306a36Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 21162306a36Sopenharmony_ci}; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci/* 21462306a36Sopenharmony_ci * Called whenever the format has to be reset which can occur when 21562306a36Sopenharmony_ci * changing outputs, standard, timings, etc. 21662306a36Sopenharmony_ci */ 21762306a36Sopenharmony_civoid vivid_update_format_out(struct vivid_dev *dev) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci struct v4l2_bt_timings *bt = &dev->dv_timings_out.bt; 22062306a36Sopenharmony_ci unsigned size, p; 22162306a36Sopenharmony_ci u64 pixelclock; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci switch (dev->output_type[dev->output]) { 22462306a36Sopenharmony_ci case SVID: 22562306a36Sopenharmony_ci default: 22662306a36Sopenharmony_ci dev->field_out = dev->tv_field_out; 22762306a36Sopenharmony_ci dev->sink_rect.width = 720; 22862306a36Sopenharmony_ci if (dev->std_out & V4L2_STD_525_60) { 22962306a36Sopenharmony_ci dev->sink_rect.height = 480; 23062306a36Sopenharmony_ci dev->timeperframe_vid_out = (struct v4l2_fract) { 1001, 30000 }; 23162306a36Sopenharmony_ci dev->service_set_out = V4L2_SLICED_CAPTION_525; 23262306a36Sopenharmony_ci } else { 23362306a36Sopenharmony_ci dev->sink_rect.height = 576; 23462306a36Sopenharmony_ci dev->timeperframe_vid_out = (struct v4l2_fract) { 1000, 25000 }; 23562306a36Sopenharmony_ci dev->service_set_out = V4L2_SLICED_WSS_625 | V4L2_SLICED_TELETEXT_B; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci dev->colorspace_out = V4L2_COLORSPACE_SMPTE170M; 23862306a36Sopenharmony_ci break; 23962306a36Sopenharmony_ci case HDMI: 24062306a36Sopenharmony_ci dev->sink_rect.width = bt->width; 24162306a36Sopenharmony_ci dev->sink_rect.height = bt->height; 24262306a36Sopenharmony_ci size = V4L2_DV_BT_FRAME_WIDTH(bt) * V4L2_DV_BT_FRAME_HEIGHT(bt); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (can_reduce_fps(bt) && (bt->flags & V4L2_DV_FL_REDUCED_FPS)) 24562306a36Sopenharmony_ci pixelclock = div_u64(bt->pixelclock * 1000, 1001); 24662306a36Sopenharmony_ci else 24762306a36Sopenharmony_ci pixelclock = bt->pixelclock; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci dev->timeperframe_vid_out = (struct v4l2_fract) { 25062306a36Sopenharmony_ci size / 100, (u32)pixelclock / 100 25162306a36Sopenharmony_ci }; 25262306a36Sopenharmony_ci if (bt->interlaced) 25362306a36Sopenharmony_ci dev->field_out = V4L2_FIELD_ALTERNATE; 25462306a36Sopenharmony_ci else 25562306a36Sopenharmony_ci dev->field_out = V4L2_FIELD_NONE; 25662306a36Sopenharmony_ci if (!dev->dvi_d_out && (bt->flags & V4L2_DV_FL_IS_CE_VIDEO)) { 25762306a36Sopenharmony_ci if (bt->width == 720 && bt->height <= 576) 25862306a36Sopenharmony_ci dev->colorspace_out = V4L2_COLORSPACE_SMPTE170M; 25962306a36Sopenharmony_ci else 26062306a36Sopenharmony_ci dev->colorspace_out = V4L2_COLORSPACE_REC709; 26162306a36Sopenharmony_ci } else { 26262306a36Sopenharmony_ci dev->colorspace_out = V4L2_COLORSPACE_SRGB; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci break; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci dev->xfer_func_out = V4L2_XFER_FUNC_DEFAULT; 26762306a36Sopenharmony_ci dev->ycbcr_enc_out = V4L2_YCBCR_ENC_DEFAULT; 26862306a36Sopenharmony_ci dev->hsv_enc_out = V4L2_HSV_ENC_180; 26962306a36Sopenharmony_ci dev->quantization_out = V4L2_QUANTIZATION_DEFAULT; 27062306a36Sopenharmony_ci dev->compose_out = dev->sink_rect; 27162306a36Sopenharmony_ci dev->compose_bounds_out = dev->sink_rect; 27262306a36Sopenharmony_ci dev->crop_out = dev->compose_out; 27362306a36Sopenharmony_ci if (V4L2_FIELD_HAS_T_OR_B(dev->field_out)) 27462306a36Sopenharmony_ci dev->crop_out.height /= 2; 27562306a36Sopenharmony_ci dev->fmt_out_rect = dev->crop_out; 27662306a36Sopenharmony_ci for (p = 0; p < dev->fmt_out->planes; p++) 27762306a36Sopenharmony_ci dev->bytesperline_out[p] = 27862306a36Sopenharmony_ci (dev->sink_rect.width * dev->fmt_out->bit_depth[p]) / 8; 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci/* Map the field to something that is valid for the current output */ 28262306a36Sopenharmony_cistatic enum v4l2_field vivid_field_out(struct vivid_dev *dev, enum v4l2_field field) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci if (vivid_is_svid_out(dev)) { 28562306a36Sopenharmony_ci switch (field) { 28662306a36Sopenharmony_ci case V4L2_FIELD_INTERLACED_TB: 28762306a36Sopenharmony_ci case V4L2_FIELD_INTERLACED_BT: 28862306a36Sopenharmony_ci case V4L2_FIELD_SEQ_TB: 28962306a36Sopenharmony_ci case V4L2_FIELD_SEQ_BT: 29062306a36Sopenharmony_ci case V4L2_FIELD_ALTERNATE: 29162306a36Sopenharmony_ci return field; 29262306a36Sopenharmony_ci case V4L2_FIELD_INTERLACED: 29362306a36Sopenharmony_ci default: 29462306a36Sopenharmony_ci return V4L2_FIELD_INTERLACED; 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci if (vivid_is_hdmi_out(dev)) 29862306a36Sopenharmony_ci return dev->dv_timings_out.bt.interlaced ? V4L2_FIELD_ALTERNATE : 29962306a36Sopenharmony_ci V4L2_FIELD_NONE; 30062306a36Sopenharmony_ci return V4L2_FIELD_NONE; 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_cistatic enum tpg_pixel_aspect vivid_get_pixel_aspect(const struct vivid_dev *dev) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci if (vivid_is_svid_out(dev)) 30662306a36Sopenharmony_ci return (dev->std_out & V4L2_STD_525_60) ? 30762306a36Sopenharmony_ci TPG_PIXEL_ASPECT_NTSC : TPG_PIXEL_ASPECT_PAL; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci if (vivid_is_hdmi_out(dev) && 31062306a36Sopenharmony_ci dev->sink_rect.width == 720 && dev->sink_rect.height <= 576) 31162306a36Sopenharmony_ci return dev->sink_rect.height == 480 ? 31262306a36Sopenharmony_ci TPG_PIXEL_ASPECT_NTSC : TPG_PIXEL_ASPECT_PAL; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci return TPG_PIXEL_ASPECT_SQUARE; 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ciint vivid_g_fmt_vid_out(struct file *file, void *priv, 31862306a36Sopenharmony_ci struct v4l2_format *f) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 32162306a36Sopenharmony_ci struct v4l2_pix_format_mplane *mp = &f->fmt.pix_mp; 32262306a36Sopenharmony_ci const struct vivid_fmt *fmt = dev->fmt_out; 32362306a36Sopenharmony_ci unsigned p; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci mp->width = dev->fmt_out_rect.width; 32662306a36Sopenharmony_ci mp->height = dev->fmt_out_rect.height; 32762306a36Sopenharmony_ci mp->field = dev->field_out; 32862306a36Sopenharmony_ci mp->pixelformat = fmt->fourcc; 32962306a36Sopenharmony_ci mp->colorspace = dev->colorspace_out; 33062306a36Sopenharmony_ci mp->xfer_func = dev->xfer_func_out; 33162306a36Sopenharmony_ci mp->ycbcr_enc = dev->ycbcr_enc_out; 33262306a36Sopenharmony_ci mp->quantization = dev->quantization_out; 33362306a36Sopenharmony_ci mp->num_planes = fmt->buffers; 33462306a36Sopenharmony_ci for (p = 0; p < mp->num_planes; p++) { 33562306a36Sopenharmony_ci mp->plane_fmt[p].bytesperline = dev->bytesperline_out[p]; 33662306a36Sopenharmony_ci mp->plane_fmt[p].sizeimage = 33762306a36Sopenharmony_ci mp->plane_fmt[p].bytesperline * mp->height + 33862306a36Sopenharmony_ci fmt->data_offset[p]; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci for (p = fmt->buffers; p < fmt->planes; p++) { 34162306a36Sopenharmony_ci unsigned stride = dev->bytesperline_out[p]; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci mp->plane_fmt[0].sizeimage += 34462306a36Sopenharmony_ci (stride * mp->height) / fmt->vdownsampling[p]; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci return 0; 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ciint vivid_try_fmt_vid_out(struct file *file, void *priv, 35062306a36Sopenharmony_ci struct v4l2_format *f) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 35362306a36Sopenharmony_ci struct v4l2_bt_timings *bt = &dev->dv_timings_out.bt; 35462306a36Sopenharmony_ci struct v4l2_pix_format_mplane *mp = &f->fmt.pix_mp; 35562306a36Sopenharmony_ci struct v4l2_plane_pix_format *pfmt = mp->plane_fmt; 35662306a36Sopenharmony_ci const struct vivid_fmt *fmt; 35762306a36Sopenharmony_ci unsigned bytesperline, max_bpl; 35862306a36Sopenharmony_ci unsigned factor = 1; 35962306a36Sopenharmony_ci unsigned w, h; 36062306a36Sopenharmony_ci unsigned p; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci fmt = vivid_get_format(dev, mp->pixelformat); 36362306a36Sopenharmony_ci if (!fmt) { 36462306a36Sopenharmony_ci dprintk(dev, 1, "Fourcc format (0x%08x) unknown.\n", 36562306a36Sopenharmony_ci mp->pixelformat); 36662306a36Sopenharmony_ci mp->pixelformat = V4L2_PIX_FMT_YUYV; 36762306a36Sopenharmony_ci fmt = vivid_get_format(dev, mp->pixelformat); 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci mp->field = vivid_field_out(dev, mp->field); 37162306a36Sopenharmony_ci if (vivid_is_svid_out(dev)) { 37262306a36Sopenharmony_ci w = 720; 37362306a36Sopenharmony_ci h = (dev->std_out & V4L2_STD_525_60) ? 480 : 576; 37462306a36Sopenharmony_ci } else { 37562306a36Sopenharmony_ci w = dev->sink_rect.width; 37662306a36Sopenharmony_ci h = dev->sink_rect.height; 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci if (V4L2_FIELD_HAS_T_OR_B(mp->field)) 37962306a36Sopenharmony_ci factor = 2; 38062306a36Sopenharmony_ci if (!dev->has_scaler_out && !dev->has_crop_out && !dev->has_compose_out) { 38162306a36Sopenharmony_ci mp->width = w; 38262306a36Sopenharmony_ci mp->height = h / factor; 38362306a36Sopenharmony_ci } else { 38462306a36Sopenharmony_ci struct v4l2_rect r = { 0, 0, mp->width, mp->height * factor }; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci v4l2_rect_set_min_size(&r, &vivid_min_rect); 38762306a36Sopenharmony_ci v4l2_rect_set_max_size(&r, &vivid_max_rect); 38862306a36Sopenharmony_ci if (dev->has_scaler_out && !dev->has_crop_out) { 38962306a36Sopenharmony_ci struct v4l2_rect max_r = { 0, 0, MAX_ZOOM * w, MAX_ZOOM * h }; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci v4l2_rect_set_max_size(&r, &max_r); 39262306a36Sopenharmony_ci } else if (!dev->has_scaler_out && dev->has_compose_out && !dev->has_crop_out) { 39362306a36Sopenharmony_ci v4l2_rect_set_max_size(&r, &dev->sink_rect); 39462306a36Sopenharmony_ci } else if (!dev->has_scaler_out && !dev->has_compose_out) { 39562306a36Sopenharmony_ci v4l2_rect_set_min_size(&r, &dev->sink_rect); 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci mp->width = r.width; 39862306a36Sopenharmony_ci mp->height = r.height / factor; 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci /* This driver supports custom bytesperline values */ 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci mp->num_planes = fmt->buffers; 40462306a36Sopenharmony_ci for (p = 0; p < fmt->buffers; p++) { 40562306a36Sopenharmony_ci /* Calculate the minimum supported bytesperline value */ 40662306a36Sopenharmony_ci bytesperline = (mp->width * fmt->bit_depth[p]) >> 3; 40762306a36Sopenharmony_ci /* Calculate the maximum supported bytesperline value */ 40862306a36Sopenharmony_ci max_bpl = (MAX_ZOOM * MAX_WIDTH * fmt->bit_depth[p]) >> 3; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci if (pfmt[p].bytesperline > max_bpl) 41162306a36Sopenharmony_ci pfmt[p].bytesperline = max_bpl; 41262306a36Sopenharmony_ci if (pfmt[p].bytesperline < bytesperline) 41362306a36Sopenharmony_ci pfmt[p].bytesperline = bytesperline; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci pfmt[p].sizeimage = (pfmt[p].bytesperline * mp->height) / 41662306a36Sopenharmony_ci fmt->vdownsampling[p] + fmt->data_offset[p]; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci memset(pfmt[p].reserved, 0, sizeof(pfmt[p].reserved)); 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci for (p = fmt->buffers; p < fmt->planes; p++) 42162306a36Sopenharmony_ci pfmt[0].sizeimage += (pfmt[0].bytesperline * mp->height * 42262306a36Sopenharmony_ci (fmt->bit_depth[p] / fmt->vdownsampling[p])) / 42362306a36Sopenharmony_ci (fmt->bit_depth[0] / fmt->vdownsampling[0]); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci mp->xfer_func = V4L2_XFER_FUNC_DEFAULT; 42662306a36Sopenharmony_ci mp->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; 42762306a36Sopenharmony_ci mp->quantization = V4L2_QUANTIZATION_DEFAULT; 42862306a36Sopenharmony_ci if (vivid_is_svid_out(dev)) { 42962306a36Sopenharmony_ci mp->colorspace = V4L2_COLORSPACE_SMPTE170M; 43062306a36Sopenharmony_ci } else if (dev->dvi_d_out || !(bt->flags & V4L2_DV_FL_IS_CE_VIDEO)) { 43162306a36Sopenharmony_ci mp->colorspace = V4L2_COLORSPACE_SRGB; 43262306a36Sopenharmony_ci if (dev->dvi_d_out) 43362306a36Sopenharmony_ci mp->quantization = V4L2_QUANTIZATION_LIM_RANGE; 43462306a36Sopenharmony_ci } else if (bt->width == 720 && bt->height <= 576) { 43562306a36Sopenharmony_ci mp->colorspace = V4L2_COLORSPACE_SMPTE170M; 43662306a36Sopenharmony_ci } else if (mp->colorspace != V4L2_COLORSPACE_SMPTE170M && 43762306a36Sopenharmony_ci mp->colorspace != V4L2_COLORSPACE_REC709 && 43862306a36Sopenharmony_ci mp->colorspace != V4L2_COLORSPACE_OPRGB && 43962306a36Sopenharmony_ci mp->colorspace != V4L2_COLORSPACE_BT2020 && 44062306a36Sopenharmony_ci mp->colorspace != V4L2_COLORSPACE_SRGB) { 44162306a36Sopenharmony_ci mp->colorspace = V4L2_COLORSPACE_REC709; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci memset(mp->reserved, 0, sizeof(mp->reserved)); 44462306a36Sopenharmony_ci return 0; 44562306a36Sopenharmony_ci} 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ciint vivid_s_fmt_vid_out(struct file *file, void *priv, 44862306a36Sopenharmony_ci struct v4l2_format *f) 44962306a36Sopenharmony_ci{ 45062306a36Sopenharmony_ci struct v4l2_pix_format_mplane *mp = &f->fmt.pix_mp; 45162306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 45262306a36Sopenharmony_ci struct v4l2_rect *crop = &dev->crop_out; 45362306a36Sopenharmony_ci struct v4l2_rect *compose = &dev->compose_out; 45462306a36Sopenharmony_ci struct vb2_queue *q = &dev->vb_vid_out_q; 45562306a36Sopenharmony_ci int ret = vivid_try_fmt_vid_out(file, priv, f); 45662306a36Sopenharmony_ci unsigned factor = 1; 45762306a36Sopenharmony_ci unsigned p; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if (ret < 0) 46062306a36Sopenharmony_ci return ret; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci if (vb2_is_busy(q) && 46362306a36Sopenharmony_ci (vivid_is_svid_out(dev) || 46462306a36Sopenharmony_ci mp->width != dev->fmt_out_rect.width || 46562306a36Sopenharmony_ci mp->height != dev->fmt_out_rect.height || 46662306a36Sopenharmony_ci mp->pixelformat != dev->fmt_out->fourcc || 46762306a36Sopenharmony_ci mp->field != dev->field_out)) { 46862306a36Sopenharmony_ci dprintk(dev, 1, "%s device busy\n", __func__); 46962306a36Sopenharmony_ci return -EBUSY; 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci /* 47362306a36Sopenharmony_ci * Allow for changing the colorspace on the fly. Useful for testing 47462306a36Sopenharmony_ci * purposes, and it is something that HDMI transmitters are able 47562306a36Sopenharmony_ci * to do. 47662306a36Sopenharmony_ci */ 47762306a36Sopenharmony_ci if (vb2_is_busy(q)) 47862306a36Sopenharmony_ci goto set_colorspace; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci dev->fmt_out = vivid_get_format(dev, mp->pixelformat); 48162306a36Sopenharmony_ci if (V4L2_FIELD_HAS_T_OR_B(mp->field)) 48262306a36Sopenharmony_ci factor = 2; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci if (dev->has_scaler_out || dev->has_crop_out || dev->has_compose_out) { 48562306a36Sopenharmony_ci struct v4l2_rect r = { 0, 0, mp->width, mp->height }; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci if (dev->has_scaler_out) { 48862306a36Sopenharmony_ci if (dev->has_crop_out) 48962306a36Sopenharmony_ci v4l2_rect_map_inside(crop, &r); 49062306a36Sopenharmony_ci else 49162306a36Sopenharmony_ci *crop = r; 49262306a36Sopenharmony_ci if (dev->has_compose_out && !dev->has_crop_out) { 49362306a36Sopenharmony_ci struct v4l2_rect min_r = { 49462306a36Sopenharmony_ci 0, 0, 49562306a36Sopenharmony_ci r.width / MAX_ZOOM, 49662306a36Sopenharmony_ci factor * r.height / MAX_ZOOM 49762306a36Sopenharmony_ci }; 49862306a36Sopenharmony_ci struct v4l2_rect max_r = { 49962306a36Sopenharmony_ci 0, 0, 50062306a36Sopenharmony_ci r.width * MAX_ZOOM, 50162306a36Sopenharmony_ci factor * r.height * MAX_ZOOM 50262306a36Sopenharmony_ci }; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci v4l2_rect_set_min_size(compose, &min_r); 50562306a36Sopenharmony_ci v4l2_rect_set_max_size(compose, &max_r); 50662306a36Sopenharmony_ci v4l2_rect_map_inside(compose, &dev->compose_bounds_out); 50762306a36Sopenharmony_ci } else if (dev->has_compose_out) { 50862306a36Sopenharmony_ci struct v4l2_rect min_r = { 50962306a36Sopenharmony_ci 0, 0, 51062306a36Sopenharmony_ci crop->width / MAX_ZOOM, 51162306a36Sopenharmony_ci factor * crop->height / MAX_ZOOM 51262306a36Sopenharmony_ci }; 51362306a36Sopenharmony_ci struct v4l2_rect max_r = { 51462306a36Sopenharmony_ci 0, 0, 51562306a36Sopenharmony_ci crop->width * MAX_ZOOM, 51662306a36Sopenharmony_ci factor * crop->height * MAX_ZOOM 51762306a36Sopenharmony_ci }; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci v4l2_rect_set_min_size(compose, &min_r); 52062306a36Sopenharmony_ci v4l2_rect_set_max_size(compose, &max_r); 52162306a36Sopenharmony_ci v4l2_rect_map_inside(compose, &dev->compose_bounds_out); 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci } else if (dev->has_compose_out && !dev->has_crop_out) { 52462306a36Sopenharmony_ci v4l2_rect_set_size_to(crop, &r); 52562306a36Sopenharmony_ci r.height *= factor; 52662306a36Sopenharmony_ci v4l2_rect_set_size_to(compose, &r); 52762306a36Sopenharmony_ci v4l2_rect_map_inside(compose, &dev->compose_bounds_out); 52862306a36Sopenharmony_ci } else if (!dev->has_compose_out) { 52962306a36Sopenharmony_ci v4l2_rect_map_inside(crop, &r); 53062306a36Sopenharmony_ci r.height /= factor; 53162306a36Sopenharmony_ci v4l2_rect_set_size_to(compose, &r); 53262306a36Sopenharmony_ci } else { 53362306a36Sopenharmony_ci r.height *= factor; 53462306a36Sopenharmony_ci v4l2_rect_set_max_size(compose, &r); 53562306a36Sopenharmony_ci v4l2_rect_map_inside(compose, &dev->compose_bounds_out); 53662306a36Sopenharmony_ci crop->top *= factor; 53762306a36Sopenharmony_ci crop->height *= factor; 53862306a36Sopenharmony_ci v4l2_rect_set_size_to(crop, compose); 53962306a36Sopenharmony_ci v4l2_rect_map_inside(crop, &r); 54062306a36Sopenharmony_ci crop->top /= factor; 54162306a36Sopenharmony_ci crop->height /= factor; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci } else { 54462306a36Sopenharmony_ci struct v4l2_rect r = { 0, 0, mp->width, mp->height }; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci v4l2_rect_set_size_to(crop, &r); 54762306a36Sopenharmony_ci r.height /= factor; 54862306a36Sopenharmony_ci v4l2_rect_set_size_to(compose, &r); 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci dev->fmt_out_rect.width = mp->width; 55262306a36Sopenharmony_ci dev->fmt_out_rect.height = mp->height; 55362306a36Sopenharmony_ci for (p = 0; p < mp->num_planes; p++) 55462306a36Sopenharmony_ci dev->bytesperline_out[p] = mp->plane_fmt[p].bytesperline; 55562306a36Sopenharmony_ci for (p = dev->fmt_out->buffers; p < dev->fmt_out->planes; p++) 55662306a36Sopenharmony_ci dev->bytesperline_out[p] = 55762306a36Sopenharmony_ci (dev->bytesperline_out[0] * dev->fmt_out->bit_depth[p]) / 55862306a36Sopenharmony_ci dev->fmt_out->bit_depth[0]; 55962306a36Sopenharmony_ci dev->field_out = mp->field; 56062306a36Sopenharmony_ci if (vivid_is_svid_out(dev)) 56162306a36Sopenharmony_ci dev->tv_field_out = mp->field; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ciset_colorspace: 56462306a36Sopenharmony_ci dev->colorspace_out = mp->colorspace; 56562306a36Sopenharmony_ci dev->xfer_func_out = mp->xfer_func; 56662306a36Sopenharmony_ci dev->ycbcr_enc_out = mp->ycbcr_enc; 56762306a36Sopenharmony_ci dev->quantization_out = mp->quantization; 56862306a36Sopenharmony_ci if (dev->loop_video) { 56962306a36Sopenharmony_ci vivid_send_source_change(dev, SVID); 57062306a36Sopenharmony_ci vivid_send_source_change(dev, HDMI); 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci return 0; 57362306a36Sopenharmony_ci} 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ciint vidioc_g_fmt_vid_out_mplane(struct file *file, void *priv, 57662306a36Sopenharmony_ci struct v4l2_format *f) 57762306a36Sopenharmony_ci{ 57862306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci if (!dev->multiplanar) 58162306a36Sopenharmony_ci return -ENOTTY; 58262306a36Sopenharmony_ci return vivid_g_fmt_vid_out(file, priv, f); 58362306a36Sopenharmony_ci} 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ciint vidioc_try_fmt_vid_out_mplane(struct file *file, void *priv, 58662306a36Sopenharmony_ci struct v4l2_format *f) 58762306a36Sopenharmony_ci{ 58862306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if (!dev->multiplanar) 59162306a36Sopenharmony_ci return -ENOTTY; 59262306a36Sopenharmony_ci return vivid_try_fmt_vid_out(file, priv, f); 59362306a36Sopenharmony_ci} 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ciint vidioc_s_fmt_vid_out_mplane(struct file *file, void *priv, 59662306a36Sopenharmony_ci struct v4l2_format *f) 59762306a36Sopenharmony_ci{ 59862306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci if (!dev->multiplanar) 60162306a36Sopenharmony_ci return -ENOTTY; 60262306a36Sopenharmony_ci return vivid_s_fmt_vid_out(file, priv, f); 60362306a36Sopenharmony_ci} 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ciint vidioc_g_fmt_vid_out(struct file *file, void *priv, 60662306a36Sopenharmony_ci struct v4l2_format *f) 60762306a36Sopenharmony_ci{ 60862306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci if (dev->multiplanar) 61162306a36Sopenharmony_ci return -ENOTTY; 61262306a36Sopenharmony_ci return fmt_sp2mp_func(file, priv, f, vivid_g_fmt_vid_out); 61362306a36Sopenharmony_ci} 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ciint vidioc_try_fmt_vid_out(struct file *file, void *priv, 61662306a36Sopenharmony_ci struct v4l2_format *f) 61762306a36Sopenharmony_ci{ 61862306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci if (dev->multiplanar) 62162306a36Sopenharmony_ci return -ENOTTY; 62262306a36Sopenharmony_ci return fmt_sp2mp_func(file, priv, f, vivid_try_fmt_vid_out); 62362306a36Sopenharmony_ci} 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ciint vidioc_s_fmt_vid_out(struct file *file, void *priv, 62662306a36Sopenharmony_ci struct v4l2_format *f) 62762306a36Sopenharmony_ci{ 62862306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci if (dev->multiplanar) 63162306a36Sopenharmony_ci return -ENOTTY; 63262306a36Sopenharmony_ci return fmt_sp2mp_func(file, priv, f, vivid_s_fmt_vid_out); 63362306a36Sopenharmony_ci} 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ciint vivid_vid_out_g_selection(struct file *file, void *priv, 63662306a36Sopenharmony_ci struct v4l2_selection *sel) 63762306a36Sopenharmony_ci{ 63862306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci if (!dev->has_crop_out && !dev->has_compose_out) 64162306a36Sopenharmony_ci return -ENOTTY; 64262306a36Sopenharmony_ci if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) 64362306a36Sopenharmony_ci return -EINVAL; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci sel->r.left = sel->r.top = 0; 64662306a36Sopenharmony_ci switch (sel->target) { 64762306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP: 64862306a36Sopenharmony_ci if (!dev->has_crop_out) 64962306a36Sopenharmony_ci return -EINVAL; 65062306a36Sopenharmony_ci sel->r = dev->crop_out; 65162306a36Sopenharmony_ci break; 65262306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP_DEFAULT: 65362306a36Sopenharmony_ci if (!dev->has_crop_out) 65462306a36Sopenharmony_ci return -EINVAL; 65562306a36Sopenharmony_ci sel->r = dev->fmt_out_rect; 65662306a36Sopenharmony_ci break; 65762306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP_BOUNDS: 65862306a36Sopenharmony_ci if (!dev->has_crop_out) 65962306a36Sopenharmony_ci return -EINVAL; 66062306a36Sopenharmony_ci sel->r = vivid_max_rect; 66162306a36Sopenharmony_ci break; 66262306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE: 66362306a36Sopenharmony_ci if (!dev->has_compose_out) 66462306a36Sopenharmony_ci return -EINVAL; 66562306a36Sopenharmony_ci sel->r = dev->compose_out; 66662306a36Sopenharmony_ci break; 66762306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_DEFAULT: 66862306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_BOUNDS: 66962306a36Sopenharmony_ci if (!dev->has_compose_out) 67062306a36Sopenharmony_ci return -EINVAL; 67162306a36Sopenharmony_ci sel->r = dev->sink_rect; 67262306a36Sopenharmony_ci break; 67362306a36Sopenharmony_ci default: 67462306a36Sopenharmony_ci return -EINVAL; 67562306a36Sopenharmony_ci } 67662306a36Sopenharmony_ci return 0; 67762306a36Sopenharmony_ci} 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ciint vivid_vid_out_s_selection(struct file *file, void *fh, struct v4l2_selection *s) 68062306a36Sopenharmony_ci{ 68162306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 68262306a36Sopenharmony_ci struct v4l2_rect *crop = &dev->crop_out; 68362306a36Sopenharmony_ci struct v4l2_rect *compose = &dev->compose_out; 68462306a36Sopenharmony_ci unsigned factor = V4L2_FIELD_HAS_T_OR_B(dev->field_out) ? 2 : 1; 68562306a36Sopenharmony_ci int ret; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci if (!dev->has_crop_out && !dev->has_compose_out) 68862306a36Sopenharmony_ci return -ENOTTY; 68962306a36Sopenharmony_ci if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) 69062306a36Sopenharmony_ci return -EINVAL; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci switch (s->target) { 69362306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP: 69462306a36Sopenharmony_ci if (!dev->has_crop_out) 69562306a36Sopenharmony_ci return -EINVAL; 69662306a36Sopenharmony_ci ret = vivid_vid_adjust_sel(s->flags, &s->r); 69762306a36Sopenharmony_ci if (ret) 69862306a36Sopenharmony_ci return ret; 69962306a36Sopenharmony_ci v4l2_rect_set_min_size(&s->r, &vivid_min_rect); 70062306a36Sopenharmony_ci v4l2_rect_set_max_size(&s->r, &dev->fmt_out_rect); 70162306a36Sopenharmony_ci if (dev->has_scaler_out) { 70262306a36Sopenharmony_ci struct v4l2_rect max_rect = { 70362306a36Sopenharmony_ci 0, 0, 70462306a36Sopenharmony_ci dev->sink_rect.width * MAX_ZOOM, 70562306a36Sopenharmony_ci (dev->sink_rect.height / factor) * MAX_ZOOM 70662306a36Sopenharmony_ci }; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci v4l2_rect_set_max_size(&s->r, &max_rect); 70962306a36Sopenharmony_ci if (dev->has_compose_out) { 71062306a36Sopenharmony_ci struct v4l2_rect min_rect = { 71162306a36Sopenharmony_ci 0, 0, 71262306a36Sopenharmony_ci s->r.width / MAX_ZOOM, 71362306a36Sopenharmony_ci (s->r.height * factor) / MAX_ZOOM 71462306a36Sopenharmony_ci }; 71562306a36Sopenharmony_ci struct v4l2_rect max_rect = { 71662306a36Sopenharmony_ci 0, 0, 71762306a36Sopenharmony_ci s->r.width * MAX_ZOOM, 71862306a36Sopenharmony_ci (s->r.height * factor) * MAX_ZOOM 71962306a36Sopenharmony_ci }; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci v4l2_rect_set_min_size(compose, &min_rect); 72262306a36Sopenharmony_ci v4l2_rect_set_max_size(compose, &max_rect); 72362306a36Sopenharmony_ci v4l2_rect_map_inside(compose, &dev->compose_bounds_out); 72462306a36Sopenharmony_ci } 72562306a36Sopenharmony_ci } else if (dev->has_compose_out) { 72662306a36Sopenharmony_ci s->r.top *= factor; 72762306a36Sopenharmony_ci s->r.height *= factor; 72862306a36Sopenharmony_ci v4l2_rect_set_max_size(&s->r, &dev->sink_rect); 72962306a36Sopenharmony_ci v4l2_rect_set_size_to(compose, &s->r); 73062306a36Sopenharmony_ci v4l2_rect_map_inside(compose, &dev->compose_bounds_out); 73162306a36Sopenharmony_ci s->r.top /= factor; 73262306a36Sopenharmony_ci s->r.height /= factor; 73362306a36Sopenharmony_ci } else { 73462306a36Sopenharmony_ci v4l2_rect_set_size_to(&s->r, &dev->sink_rect); 73562306a36Sopenharmony_ci s->r.height /= factor; 73662306a36Sopenharmony_ci } 73762306a36Sopenharmony_ci v4l2_rect_map_inside(&s->r, &dev->fmt_out_rect); 73862306a36Sopenharmony_ci *crop = s->r; 73962306a36Sopenharmony_ci break; 74062306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE: 74162306a36Sopenharmony_ci if (!dev->has_compose_out) 74262306a36Sopenharmony_ci return -EINVAL; 74362306a36Sopenharmony_ci ret = vivid_vid_adjust_sel(s->flags, &s->r); 74462306a36Sopenharmony_ci if (ret) 74562306a36Sopenharmony_ci return ret; 74662306a36Sopenharmony_ci v4l2_rect_set_min_size(&s->r, &vivid_min_rect); 74762306a36Sopenharmony_ci v4l2_rect_set_max_size(&s->r, &dev->sink_rect); 74862306a36Sopenharmony_ci v4l2_rect_map_inside(&s->r, &dev->compose_bounds_out); 74962306a36Sopenharmony_ci s->r.top /= factor; 75062306a36Sopenharmony_ci s->r.height /= factor; 75162306a36Sopenharmony_ci if (dev->has_scaler_out) { 75262306a36Sopenharmony_ci struct v4l2_rect fmt = dev->fmt_out_rect; 75362306a36Sopenharmony_ci struct v4l2_rect max_rect = { 75462306a36Sopenharmony_ci 0, 0, 75562306a36Sopenharmony_ci s->r.width * MAX_ZOOM, 75662306a36Sopenharmony_ci s->r.height * MAX_ZOOM 75762306a36Sopenharmony_ci }; 75862306a36Sopenharmony_ci struct v4l2_rect min_rect = { 75962306a36Sopenharmony_ci 0, 0, 76062306a36Sopenharmony_ci s->r.width / MAX_ZOOM, 76162306a36Sopenharmony_ci s->r.height / MAX_ZOOM 76262306a36Sopenharmony_ci }; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci v4l2_rect_set_min_size(&fmt, &min_rect); 76562306a36Sopenharmony_ci if (!dev->has_crop_out) 76662306a36Sopenharmony_ci v4l2_rect_set_max_size(&fmt, &max_rect); 76762306a36Sopenharmony_ci if (!v4l2_rect_same_size(&dev->fmt_out_rect, &fmt) && 76862306a36Sopenharmony_ci vb2_is_busy(&dev->vb_vid_out_q)) 76962306a36Sopenharmony_ci return -EBUSY; 77062306a36Sopenharmony_ci if (dev->has_crop_out) { 77162306a36Sopenharmony_ci v4l2_rect_set_min_size(crop, &min_rect); 77262306a36Sopenharmony_ci v4l2_rect_set_max_size(crop, &max_rect); 77362306a36Sopenharmony_ci } 77462306a36Sopenharmony_ci dev->fmt_out_rect = fmt; 77562306a36Sopenharmony_ci } else if (dev->has_crop_out) { 77662306a36Sopenharmony_ci struct v4l2_rect fmt = dev->fmt_out_rect; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci v4l2_rect_set_min_size(&fmt, &s->r); 77962306a36Sopenharmony_ci if (!v4l2_rect_same_size(&dev->fmt_out_rect, &fmt) && 78062306a36Sopenharmony_ci vb2_is_busy(&dev->vb_vid_out_q)) 78162306a36Sopenharmony_ci return -EBUSY; 78262306a36Sopenharmony_ci dev->fmt_out_rect = fmt; 78362306a36Sopenharmony_ci v4l2_rect_set_size_to(crop, &s->r); 78462306a36Sopenharmony_ci v4l2_rect_map_inside(crop, &dev->fmt_out_rect); 78562306a36Sopenharmony_ci } else { 78662306a36Sopenharmony_ci if (!v4l2_rect_same_size(&s->r, &dev->fmt_out_rect) && 78762306a36Sopenharmony_ci vb2_is_busy(&dev->vb_vid_out_q)) 78862306a36Sopenharmony_ci return -EBUSY; 78962306a36Sopenharmony_ci v4l2_rect_set_size_to(&dev->fmt_out_rect, &s->r); 79062306a36Sopenharmony_ci v4l2_rect_set_size_to(crop, &s->r); 79162306a36Sopenharmony_ci crop->height /= factor; 79262306a36Sopenharmony_ci v4l2_rect_map_inside(crop, &dev->fmt_out_rect); 79362306a36Sopenharmony_ci } 79462306a36Sopenharmony_ci s->r.top *= factor; 79562306a36Sopenharmony_ci s->r.height *= factor; 79662306a36Sopenharmony_ci *compose = s->r; 79762306a36Sopenharmony_ci break; 79862306a36Sopenharmony_ci default: 79962306a36Sopenharmony_ci return -EINVAL; 80062306a36Sopenharmony_ci } 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci return 0; 80362306a36Sopenharmony_ci} 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ciint vivid_vid_out_g_pixelaspect(struct file *file, void *priv, 80662306a36Sopenharmony_ci int type, struct v4l2_fract *f) 80762306a36Sopenharmony_ci{ 80862306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci if (type != V4L2_BUF_TYPE_VIDEO_OUTPUT) 81162306a36Sopenharmony_ci return -EINVAL; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci switch (vivid_get_pixel_aspect(dev)) { 81462306a36Sopenharmony_ci case TPG_PIXEL_ASPECT_NTSC: 81562306a36Sopenharmony_ci f->numerator = 11; 81662306a36Sopenharmony_ci f->denominator = 10; 81762306a36Sopenharmony_ci break; 81862306a36Sopenharmony_ci case TPG_PIXEL_ASPECT_PAL: 81962306a36Sopenharmony_ci f->numerator = 54; 82062306a36Sopenharmony_ci f->denominator = 59; 82162306a36Sopenharmony_ci break; 82262306a36Sopenharmony_ci default: 82362306a36Sopenharmony_ci break; 82462306a36Sopenharmony_ci } 82562306a36Sopenharmony_ci return 0; 82662306a36Sopenharmony_ci} 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ciint vidioc_g_fmt_vid_out_overlay(struct file *file, void *priv, 82962306a36Sopenharmony_ci struct v4l2_format *f) 83062306a36Sopenharmony_ci{ 83162306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 83262306a36Sopenharmony_ci const struct v4l2_rect *compose = &dev->compose_out; 83362306a36Sopenharmony_ci struct v4l2_window *win = &f->fmt.win; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci if (!dev->has_fb) 83662306a36Sopenharmony_ci return -EINVAL; 83762306a36Sopenharmony_ci win->w.top = dev->overlay_out_top; 83862306a36Sopenharmony_ci win->w.left = dev->overlay_out_left; 83962306a36Sopenharmony_ci win->w.width = compose->width; 84062306a36Sopenharmony_ci win->w.height = compose->height; 84162306a36Sopenharmony_ci win->field = V4L2_FIELD_ANY; 84262306a36Sopenharmony_ci win->chromakey = dev->chromakey_out; 84362306a36Sopenharmony_ci win->global_alpha = dev->global_alpha_out; 84462306a36Sopenharmony_ci return 0; 84562306a36Sopenharmony_ci} 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ciint vidioc_try_fmt_vid_out_overlay(struct file *file, void *priv, 84862306a36Sopenharmony_ci struct v4l2_format *f) 84962306a36Sopenharmony_ci{ 85062306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 85162306a36Sopenharmony_ci const struct v4l2_rect *compose = &dev->compose_out; 85262306a36Sopenharmony_ci struct v4l2_window *win = &f->fmt.win; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci if (!dev->has_fb) 85562306a36Sopenharmony_ci return -EINVAL; 85662306a36Sopenharmony_ci win->w.left = clamp_t(int, win->w.left, 85762306a36Sopenharmony_ci -dev->display_width, dev->display_width); 85862306a36Sopenharmony_ci win->w.top = clamp_t(int, win->w.top, 85962306a36Sopenharmony_ci -dev->display_height, dev->display_height); 86062306a36Sopenharmony_ci win->w.width = compose->width; 86162306a36Sopenharmony_ci win->w.height = compose->height; 86262306a36Sopenharmony_ci /* 86362306a36Sopenharmony_ci * It makes no sense for an OSD to overlay only top or bottom fields, 86462306a36Sopenharmony_ci * so always set this to ANY. 86562306a36Sopenharmony_ci */ 86662306a36Sopenharmony_ci win->field = V4L2_FIELD_ANY; 86762306a36Sopenharmony_ci return 0; 86862306a36Sopenharmony_ci} 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ciint vidioc_s_fmt_vid_out_overlay(struct file *file, void *priv, 87162306a36Sopenharmony_ci struct v4l2_format *f) 87262306a36Sopenharmony_ci{ 87362306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 87462306a36Sopenharmony_ci struct v4l2_window *win = &f->fmt.win; 87562306a36Sopenharmony_ci int ret = vidioc_try_fmt_vid_out_overlay(file, priv, f); 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci if (ret) 87862306a36Sopenharmony_ci return ret; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci dev->overlay_out_top = win->w.top; 88162306a36Sopenharmony_ci dev->overlay_out_left = win->w.left; 88262306a36Sopenharmony_ci dev->chromakey_out = win->chromakey; 88362306a36Sopenharmony_ci dev->global_alpha_out = win->global_alpha; 88462306a36Sopenharmony_ci return ret; 88562306a36Sopenharmony_ci} 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ciint vivid_vid_out_overlay(struct file *file, void *fh, unsigned i) 88862306a36Sopenharmony_ci{ 88962306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci if (i && !dev->fmt_out->can_do_overlay) { 89262306a36Sopenharmony_ci dprintk(dev, 1, "unsupported output format for output overlay\n"); 89362306a36Sopenharmony_ci return -EINVAL; 89462306a36Sopenharmony_ci } 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci dev->overlay_out_enabled = i; 89762306a36Sopenharmony_ci return 0; 89862306a36Sopenharmony_ci} 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ciint vivid_vid_out_g_fbuf(struct file *file, void *fh, 90162306a36Sopenharmony_ci struct v4l2_framebuffer *a) 90262306a36Sopenharmony_ci{ 90362306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci a->capability = V4L2_FBUF_CAP_EXTERNOVERLAY | 90662306a36Sopenharmony_ci V4L2_FBUF_CAP_CHROMAKEY | 90762306a36Sopenharmony_ci V4L2_FBUF_CAP_SRC_CHROMAKEY | 90862306a36Sopenharmony_ci V4L2_FBUF_CAP_GLOBAL_ALPHA | 90962306a36Sopenharmony_ci V4L2_FBUF_CAP_LOCAL_ALPHA | 91062306a36Sopenharmony_ci V4L2_FBUF_CAP_LOCAL_INV_ALPHA; 91162306a36Sopenharmony_ci a->flags = V4L2_FBUF_FLAG_OVERLAY | dev->fbuf_out_flags; 91262306a36Sopenharmony_ci a->base = (void *)dev->video_pbase; 91362306a36Sopenharmony_ci a->fmt.width = dev->display_width; 91462306a36Sopenharmony_ci a->fmt.height = dev->display_height; 91562306a36Sopenharmony_ci if (dev->fb_defined.green.length == 5) 91662306a36Sopenharmony_ci a->fmt.pixelformat = V4L2_PIX_FMT_ARGB555; 91762306a36Sopenharmony_ci else 91862306a36Sopenharmony_ci a->fmt.pixelformat = V4L2_PIX_FMT_RGB565; 91962306a36Sopenharmony_ci a->fmt.bytesperline = dev->display_byte_stride; 92062306a36Sopenharmony_ci a->fmt.sizeimage = a->fmt.height * a->fmt.bytesperline; 92162306a36Sopenharmony_ci a->fmt.field = V4L2_FIELD_NONE; 92262306a36Sopenharmony_ci a->fmt.colorspace = V4L2_COLORSPACE_SRGB; 92362306a36Sopenharmony_ci a->fmt.priv = 0; 92462306a36Sopenharmony_ci return 0; 92562306a36Sopenharmony_ci} 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ciint vivid_vid_out_s_fbuf(struct file *file, void *fh, 92862306a36Sopenharmony_ci const struct v4l2_framebuffer *a) 92962306a36Sopenharmony_ci{ 93062306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 93162306a36Sopenharmony_ci const unsigned chroma_flags = V4L2_FBUF_FLAG_CHROMAKEY | 93262306a36Sopenharmony_ci V4L2_FBUF_FLAG_SRC_CHROMAKEY; 93362306a36Sopenharmony_ci const unsigned alpha_flags = V4L2_FBUF_FLAG_GLOBAL_ALPHA | 93462306a36Sopenharmony_ci V4L2_FBUF_FLAG_LOCAL_ALPHA | 93562306a36Sopenharmony_ci V4L2_FBUF_FLAG_LOCAL_INV_ALPHA; 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci if ((a->flags & chroma_flags) == chroma_flags) 93962306a36Sopenharmony_ci return -EINVAL; 94062306a36Sopenharmony_ci switch (a->flags & alpha_flags) { 94162306a36Sopenharmony_ci case 0: 94262306a36Sopenharmony_ci case V4L2_FBUF_FLAG_GLOBAL_ALPHA: 94362306a36Sopenharmony_ci case V4L2_FBUF_FLAG_LOCAL_ALPHA: 94462306a36Sopenharmony_ci case V4L2_FBUF_FLAG_LOCAL_INV_ALPHA: 94562306a36Sopenharmony_ci break; 94662306a36Sopenharmony_ci default: 94762306a36Sopenharmony_ci return -EINVAL; 94862306a36Sopenharmony_ci } 94962306a36Sopenharmony_ci dev->fbuf_out_flags &= ~(chroma_flags | alpha_flags); 95062306a36Sopenharmony_ci dev->fbuf_out_flags |= a->flags & (chroma_flags | alpha_flags); 95162306a36Sopenharmony_ci return 0; 95262306a36Sopenharmony_ci} 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_cistatic const struct v4l2_audioout vivid_audio_outputs[] = { 95562306a36Sopenharmony_ci { 0, "Line-Out 1" }, 95662306a36Sopenharmony_ci { 1, "Line-Out 2" }, 95762306a36Sopenharmony_ci}; 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ciint vidioc_enum_output(struct file *file, void *priv, 96062306a36Sopenharmony_ci struct v4l2_output *out) 96162306a36Sopenharmony_ci{ 96262306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci if (out->index >= dev->num_outputs) 96562306a36Sopenharmony_ci return -EINVAL; 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci out->type = V4L2_OUTPUT_TYPE_ANALOG; 96862306a36Sopenharmony_ci switch (dev->output_type[out->index]) { 96962306a36Sopenharmony_ci case SVID: 97062306a36Sopenharmony_ci snprintf(out->name, sizeof(out->name), "S-Video %u", 97162306a36Sopenharmony_ci dev->output_name_counter[out->index]); 97262306a36Sopenharmony_ci out->std = V4L2_STD_ALL; 97362306a36Sopenharmony_ci if (dev->has_audio_outputs) 97462306a36Sopenharmony_ci out->audioset = (1 << ARRAY_SIZE(vivid_audio_outputs)) - 1; 97562306a36Sopenharmony_ci out->capabilities = V4L2_OUT_CAP_STD; 97662306a36Sopenharmony_ci break; 97762306a36Sopenharmony_ci case HDMI: 97862306a36Sopenharmony_ci snprintf(out->name, sizeof(out->name), "HDMI %u", 97962306a36Sopenharmony_ci dev->output_name_counter[out->index]); 98062306a36Sopenharmony_ci out->capabilities = V4L2_OUT_CAP_DV_TIMINGS; 98162306a36Sopenharmony_ci break; 98262306a36Sopenharmony_ci } 98362306a36Sopenharmony_ci return 0; 98462306a36Sopenharmony_ci} 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ciint vidioc_g_output(struct file *file, void *priv, unsigned *o) 98762306a36Sopenharmony_ci{ 98862306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci *o = dev->output; 99162306a36Sopenharmony_ci return 0; 99262306a36Sopenharmony_ci} 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ciint vidioc_s_output(struct file *file, void *priv, unsigned o) 99562306a36Sopenharmony_ci{ 99662306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci if (o >= dev->num_outputs) 99962306a36Sopenharmony_ci return -EINVAL; 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci if (o == dev->output) 100262306a36Sopenharmony_ci return 0; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci if (vb2_is_busy(&dev->vb_vid_out_q) || 100562306a36Sopenharmony_ci vb2_is_busy(&dev->vb_vbi_out_q) || 100662306a36Sopenharmony_ci vb2_is_busy(&dev->vb_meta_out_q)) 100762306a36Sopenharmony_ci return -EBUSY; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci dev->output = o; 101062306a36Sopenharmony_ci dev->tv_audio_output = 0; 101162306a36Sopenharmony_ci if (dev->output_type[o] == SVID) 101262306a36Sopenharmony_ci dev->vid_out_dev.tvnorms = V4L2_STD_ALL; 101362306a36Sopenharmony_ci else 101462306a36Sopenharmony_ci dev->vid_out_dev.tvnorms = 0; 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci dev->vbi_out_dev.tvnorms = dev->vid_out_dev.tvnorms; 101762306a36Sopenharmony_ci dev->meta_out_dev.tvnorms = dev->vid_out_dev.tvnorms; 101862306a36Sopenharmony_ci vivid_update_format_out(dev); 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci v4l2_ctrl_activate(dev->ctrl_display_present, vivid_is_hdmi_out(dev)); 102162306a36Sopenharmony_ci if (vivid_is_hdmi_out(dev)) 102262306a36Sopenharmony_ci v4l2_ctrl_s_ctrl(dev->ctrl_display_present, 102362306a36Sopenharmony_ci dev->display_present[dev->output]); 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci return 0; 102662306a36Sopenharmony_ci} 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ciint vidioc_enumaudout(struct file *file, void *fh, struct v4l2_audioout *vout) 102962306a36Sopenharmony_ci{ 103062306a36Sopenharmony_ci if (vout->index >= ARRAY_SIZE(vivid_audio_outputs)) 103162306a36Sopenharmony_ci return -EINVAL; 103262306a36Sopenharmony_ci *vout = vivid_audio_outputs[vout->index]; 103362306a36Sopenharmony_ci return 0; 103462306a36Sopenharmony_ci} 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ciint vidioc_g_audout(struct file *file, void *fh, struct v4l2_audioout *vout) 103762306a36Sopenharmony_ci{ 103862306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci if (!vivid_is_svid_out(dev)) 104162306a36Sopenharmony_ci return -EINVAL; 104262306a36Sopenharmony_ci *vout = vivid_audio_outputs[dev->tv_audio_output]; 104362306a36Sopenharmony_ci return 0; 104462306a36Sopenharmony_ci} 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ciint vidioc_s_audout(struct file *file, void *fh, const struct v4l2_audioout *vout) 104762306a36Sopenharmony_ci{ 104862306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci if (!vivid_is_svid_out(dev)) 105162306a36Sopenharmony_ci return -EINVAL; 105262306a36Sopenharmony_ci if (vout->index >= ARRAY_SIZE(vivid_audio_outputs)) 105362306a36Sopenharmony_ci return -EINVAL; 105462306a36Sopenharmony_ci dev->tv_audio_output = vout->index; 105562306a36Sopenharmony_ci return 0; 105662306a36Sopenharmony_ci} 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ciint vivid_vid_out_s_std(struct file *file, void *priv, v4l2_std_id id) 105962306a36Sopenharmony_ci{ 106062306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci if (!vivid_is_svid_out(dev)) 106362306a36Sopenharmony_ci return -ENODATA; 106462306a36Sopenharmony_ci if (dev->std_out == id) 106562306a36Sopenharmony_ci return 0; 106662306a36Sopenharmony_ci if (vb2_is_busy(&dev->vb_vid_out_q) || vb2_is_busy(&dev->vb_vbi_out_q)) 106762306a36Sopenharmony_ci return -EBUSY; 106862306a36Sopenharmony_ci dev->std_out = id; 106962306a36Sopenharmony_ci vivid_update_format_out(dev); 107062306a36Sopenharmony_ci return 0; 107162306a36Sopenharmony_ci} 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_cistatic bool valid_cvt_gtf_timings(struct v4l2_dv_timings *timings) 107462306a36Sopenharmony_ci{ 107562306a36Sopenharmony_ci struct v4l2_bt_timings *bt = &timings->bt; 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci if ((bt->standards & (V4L2_DV_BT_STD_CVT | V4L2_DV_BT_STD_GTF)) && 107862306a36Sopenharmony_ci v4l2_valid_dv_timings(timings, &vivid_dv_timings_cap, NULL, NULL)) 107962306a36Sopenharmony_ci return true; 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci return false; 108262306a36Sopenharmony_ci} 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ciint vivid_vid_out_s_dv_timings(struct file *file, void *_fh, 108562306a36Sopenharmony_ci struct v4l2_dv_timings *timings) 108662306a36Sopenharmony_ci{ 108762306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 108862306a36Sopenharmony_ci if (!vivid_is_hdmi_out(dev)) 108962306a36Sopenharmony_ci return -ENODATA; 109062306a36Sopenharmony_ci if (!v4l2_find_dv_timings_cap(timings, &vivid_dv_timings_cap, 109162306a36Sopenharmony_ci 0, NULL, NULL) && 109262306a36Sopenharmony_ci !valid_cvt_gtf_timings(timings)) 109362306a36Sopenharmony_ci return -EINVAL; 109462306a36Sopenharmony_ci if (v4l2_match_dv_timings(timings, &dev->dv_timings_out, 0, true)) 109562306a36Sopenharmony_ci return 0; 109662306a36Sopenharmony_ci if (vb2_is_busy(&dev->vb_vid_out_q)) 109762306a36Sopenharmony_ci return -EBUSY; 109862306a36Sopenharmony_ci dev->dv_timings_out = *timings; 109962306a36Sopenharmony_ci vivid_update_format_out(dev); 110062306a36Sopenharmony_ci return 0; 110162306a36Sopenharmony_ci} 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ciint vivid_vid_out_g_parm(struct file *file, void *priv, 110462306a36Sopenharmony_ci struct v4l2_streamparm *parm) 110562306a36Sopenharmony_ci{ 110662306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci if (parm->type != (dev->multiplanar ? 110962306a36Sopenharmony_ci V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE : 111062306a36Sopenharmony_ci V4L2_BUF_TYPE_VIDEO_OUTPUT)) 111162306a36Sopenharmony_ci return -EINVAL; 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci parm->parm.output.capability = V4L2_CAP_TIMEPERFRAME; 111462306a36Sopenharmony_ci parm->parm.output.timeperframe = dev->timeperframe_vid_out; 111562306a36Sopenharmony_ci parm->parm.output.writebuffers = 1; 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci return 0; 111862306a36Sopenharmony_ci} 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ciint vidioc_subscribe_event(struct v4l2_fh *fh, 112162306a36Sopenharmony_ci const struct v4l2_event_subscription *sub) 112262306a36Sopenharmony_ci{ 112362306a36Sopenharmony_ci switch (sub->type) { 112462306a36Sopenharmony_ci case V4L2_EVENT_SOURCE_CHANGE: 112562306a36Sopenharmony_ci if (fh->vdev->vfl_dir == VFL_DIR_RX) 112662306a36Sopenharmony_ci return v4l2_src_change_event_subscribe(fh, sub); 112762306a36Sopenharmony_ci break; 112862306a36Sopenharmony_ci default: 112962306a36Sopenharmony_ci return v4l2_ctrl_subscribe_event(fh, sub); 113062306a36Sopenharmony_ci } 113162306a36Sopenharmony_ci return -EINVAL; 113262306a36Sopenharmony_ci} 1133