18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * vivid-vid-out.c - video output support functions. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/errno.h> 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/sched.h> 118c2ecf20Sopenharmony_ci#include <linux/videodev2.h> 128c2ecf20Sopenharmony_ci#include <linux/v4l2-dv-timings.h> 138c2ecf20Sopenharmony_ci#include <media/v4l2-common.h> 148c2ecf20Sopenharmony_ci#include <media/v4l2-event.h> 158c2ecf20Sopenharmony_ci#include <media/v4l2-dv-timings.h> 168c2ecf20Sopenharmony_ci#include <media/v4l2-rect.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "vivid-core.h" 198c2ecf20Sopenharmony_ci#include "vivid-vid-common.h" 208c2ecf20Sopenharmony_ci#include "vivid-kthread-out.h" 218c2ecf20Sopenharmony_ci#include "vivid-vid-out.h" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic int vid_out_queue_setup(struct vb2_queue *vq, 248c2ecf20Sopenharmony_ci unsigned *nbuffers, unsigned *nplanes, 258c2ecf20Sopenharmony_ci unsigned sizes[], struct device *alloc_devs[]) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci struct vivid_dev *dev = vb2_get_drv_priv(vq); 288c2ecf20Sopenharmony_ci const struct vivid_fmt *vfmt = dev->fmt_out; 298c2ecf20Sopenharmony_ci unsigned planes = vfmt->buffers; 308c2ecf20Sopenharmony_ci unsigned h = dev->fmt_out_rect.height; 318c2ecf20Sopenharmony_ci unsigned int size = dev->bytesperline_out[0] * h + vfmt->data_offset[0]; 328c2ecf20Sopenharmony_ci unsigned p; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci for (p = vfmt->buffers; p < vfmt->planes; p++) 358c2ecf20Sopenharmony_ci size += dev->bytesperline_out[p] * h / vfmt->vdownsampling[p] + 368c2ecf20Sopenharmony_ci vfmt->data_offset[p]; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci if (dev->field_out == V4L2_FIELD_ALTERNATE) { 398c2ecf20Sopenharmony_ci /* 408c2ecf20Sopenharmony_ci * You cannot use write() with FIELD_ALTERNATE since the field 418c2ecf20Sopenharmony_ci * information (TOP/BOTTOM) cannot be passed to the kernel. 428c2ecf20Sopenharmony_ci */ 438c2ecf20Sopenharmony_ci if (vb2_fileio_is_active(vq)) 448c2ecf20Sopenharmony_ci return -EINVAL; 458c2ecf20Sopenharmony_ci } 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci if (dev->queue_setup_error) { 488c2ecf20Sopenharmony_ci /* 498c2ecf20Sopenharmony_ci * Error injection: test what happens if queue_setup() returns 508c2ecf20Sopenharmony_ci * an error. 518c2ecf20Sopenharmony_ci */ 528c2ecf20Sopenharmony_ci dev->queue_setup_error = false; 538c2ecf20Sopenharmony_ci return -EINVAL; 548c2ecf20Sopenharmony_ci } 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci if (*nplanes) { 578c2ecf20Sopenharmony_ci /* 588c2ecf20Sopenharmony_ci * Check if the number of requested planes match 598c2ecf20Sopenharmony_ci * the number of planes in the current format. You can't mix that. 608c2ecf20Sopenharmony_ci */ 618c2ecf20Sopenharmony_ci if (*nplanes != planes) 628c2ecf20Sopenharmony_ci return -EINVAL; 638c2ecf20Sopenharmony_ci if (sizes[0] < size) 648c2ecf20Sopenharmony_ci return -EINVAL; 658c2ecf20Sopenharmony_ci for (p = 1; p < planes; p++) { 668c2ecf20Sopenharmony_ci if (sizes[p] < dev->bytesperline_out[p] * h + 678c2ecf20Sopenharmony_ci vfmt->data_offset[p]) 688c2ecf20Sopenharmony_ci return -EINVAL; 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci } else { 718c2ecf20Sopenharmony_ci for (p = 0; p < planes; p++) 728c2ecf20Sopenharmony_ci sizes[p] = p ? dev->bytesperline_out[p] * h + 738c2ecf20Sopenharmony_ci vfmt->data_offset[p] : size; 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci if (vq->num_buffers + *nbuffers < 2) 778c2ecf20Sopenharmony_ci *nbuffers = 2 - vq->num_buffers; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci *nplanes = planes; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci dprintk(dev, 1, "%s: count=%d\n", __func__, *nbuffers); 828c2ecf20Sopenharmony_ci for (p = 0; p < planes; p++) 838c2ecf20Sopenharmony_ci dprintk(dev, 1, "%s: size[%u]=%u\n", __func__, p, sizes[p]); 848c2ecf20Sopenharmony_ci return 0; 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic int vid_out_buf_out_validate(struct vb2_buffer *vb) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 908c2ecf20Sopenharmony_ci struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci dprintk(dev, 1, "%s\n", __func__); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci if (dev->field_out != V4L2_FIELD_ALTERNATE) 958c2ecf20Sopenharmony_ci vbuf->field = dev->field_out; 968c2ecf20Sopenharmony_ci else if (vbuf->field != V4L2_FIELD_TOP && 978c2ecf20Sopenharmony_ci vbuf->field != V4L2_FIELD_BOTTOM) 988c2ecf20Sopenharmony_ci return -EINVAL; 998c2ecf20Sopenharmony_ci return 0; 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic int vid_out_buf_prepare(struct vb2_buffer *vb) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); 1058c2ecf20Sopenharmony_ci const struct vivid_fmt *vfmt = dev->fmt_out; 1068c2ecf20Sopenharmony_ci unsigned int planes = vfmt->buffers; 1078c2ecf20Sopenharmony_ci unsigned int h = dev->fmt_out_rect.height; 1088c2ecf20Sopenharmony_ci unsigned int size = dev->bytesperline_out[0] * h; 1098c2ecf20Sopenharmony_ci unsigned p; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci for (p = vfmt->buffers; p < vfmt->planes; p++) 1128c2ecf20Sopenharmony_ci size += dev->bytesperline_out[p] * h / vfmt->vdownsampling[p]; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci dprintk(dev, 1, "%s\n", __func__); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (WARN_ON(NULL == dev->fmt_out)) 1178c2ecf20Sopenharmony_ci return -EINVAL; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (dev->buf_prepare_error) { 1208c2ecf20Sopenharmony_ci /* 1218c2ecf20Sopenharmony_ci * Error injection: test what happens if buf_prepare() returns 1228c2ecf20Sopenharmony_ci * an error. 1238c2ecf20Sopenharmony_ci */ 1248c2ecf20Sopenharmony_ci dev->buf_prepare_error = false; 1258c2ecf20Sopenharmony_ci return -EINVAL; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci for (p = 0; p < planes; p++) { 1298c2ecf20Sopenharmony_ci if (p) 1308c2ecf20Sopenharmony_ci size = dev->bytesperline_out[p] * h; 1318c2ecf20Sopenharmony_ci size += vb->planes[p].data_offset; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci if (vb2_get_plane_payload(vb, p) < size) { 1348c2ecf20Sopenharmony_ci dprintk(dev, 1, "%s the payload is too small for plane %u (%lu < %u)\n", 1358c2ecf20Sopenharmony_ci __func__, p, vb2_get_plane_payload(vb, p), size); 1368c2ecf20Sopenharmony_ci return -EINVAL; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci return 0; 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic void vid_out_buf_queue(struct vb2_buffer *vb) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 1468c2ecf20Sopenharmony_ci struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); 1478c2ecf20Sopenharmony_ci struct vivid_buffer *buf = container_of(vbuf, struct vivid_buffer, vb); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci dprintk(dev, 1, "%s\n", __func__); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci spin_lock(&dev->slock); 1528c2ecf20Sopenharmony_ci list_add_tail(&buf->list, &dev->vid_out_active); 1538c2ecf20Sopenharmony_ci spin_unlock(&dev->slock); 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic int vid_out_start_streaming(struct vb2_queue *vq, unsigned count) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci struct vivid_dev *dev = vb2_get_drv_priv(vq); 1598c2ecf20Sopenharmony_ci int err; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci if (vb2_is_streaming(&dev->vb_vid_cap_q)) 1628c2ecf20Sopenharmony_ci dev->can_loop_video = vivid_vid_can_loop(dev); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci dev->vid_out_seq_count = 0; 1658c2ecf20Sopenharmony_ci dprintk(dev, 1, "%s\n", __func__); 1668c2ecf20Sopenharmony_ci if (dev->start_streaming_error) { 1678c2ecf20Sopenharmony_ci dev->start_streaming_error = false; 1688c2ecf20Sopenharmony_ci err = -EINVAL; 1698c2ecf20Sopenharmony_ci } else { 1708c2ecf20Sopenharmony_ci err = vivid_start_generating_vid_out(dev, &dev->vid_out_streaming); 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci if (err) { 1738c2ecf20Sopenharmony_ci struct vivid_buffer *buf, *tmp; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci list_for_each_entry_safe(buf, tmp, &dev->vid_out_active, list) { 1768c2ecf20Sopenharmony_ci list_del(&buf->list); 1778c2ecf20Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, 1788c2ecf20Sopenharmony_ci VB2_BUF_STATE_QUEUED); 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci return err; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci/* abort streaming and wait for last buffer */ 1858c2ecf20Sopenharmony_cistatic void vid_out_stop_streaming(struct vb2_queue *vq) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci struct vivid_dev *dev = vb2_get_drv_priv(vq); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci dprintk(dev, 1, "%s\n", __func__); 1908c2ecf20Sopenharmony_ci vivid_stop_generating_vid_out(dev, &dev->vid_out_streaming); 1918c2ecf20Sopenharmony_ci dev->can_loop_video = false; 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic void vid_out_buf_request_complete(struct vb2_buffer *vb) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci v4l2_ctrl_request_complete(vb->req_obj.req, &dev->ctrl_hdl_vid_out); 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ciconst struct vb2_ops vivid_vid_out_qops = { 2028c2ecf20Sopenharmony_ci .queue_setup = vid_out_queue_setup, 2038c2ecf20Sopenharmony_ci .buf_out_validate = vid_out_buf_out_validate, 2048c2ecf20Sopenharmony_ci .buf_prepare = vid_out_buf_prepare, 2058c2ecf20Sopenharmony_ci .buf_queue = vid_out_buf_queue, 2068c2ecf20Sopenharmony_ci .start_streaming = vid_out_start_streaming, 2078c2ecf20Sopenharmony_ci .stop_streaming = vid_out_stop_streaming, 2088c2ecf20Sopenharmony_ci .buf_request_complete = vid_out_buf_request_complete, 2098c2ecf20Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 2108c2ecf20Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 2118c2ecf20Sopenharmony_ci}; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci/* 2148c2ecf20Sopenharmony_ci * Called whenever the format has to be reset which can occur when 2158c2ecf20Sopenharmony_ci * changing outputs, standard, timings, etc. 2168c2ecf20Sopenharmony_ci */ 2178c2ecf20Sopenharmony_civoid vivid_update_format_out(struct vivid_dev *dev) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci struct v4l2_bt_timings *bt = &dev->dv_timings_out.bt; 2208c2ecf20Sopenharmony_ci unsigned size, p; 2218c2ecf20Sopenharmony_ci u64 pixelclock; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci switch (dev->output_type[dev->output]) { 2248c2ecf20Sopenharmony_ci case SVID: 2258c2ecf20Sopenharmony_ci default: 2268c2ecf20Sopenharmony_ci dev->field_out = dev->tv_field_out; 2278c2ecf20Sopenharmony_ci dev->sink_rect.width = 720; 2288c2ecf20Sopenharmony_ci if (dev->std_out & V4L2_STD_525_60) { 2298c2ecf20Sopenharmony_ci dev->sink_rect.height = 480; 2308c2ecf20Sopenharmony_ci dev->timeperframe_vid_out = (struct v4l2_fract) { 1001, 30000 }; 2318c2ecf20Sopenharmony_ci dev->service_set_out = V4L2_SLICED_CAPTION_525; 2328c2ecf20Sopenharmony_ci } else { 2338c2ecf20Sopenharmony_ci dev->sink_rect.height = 576; 2348c2ecf20Sopenharmony_ci dev->timeperframe_vid_out = (struct v4l2_fract) { 1000, 25000 }; 2358c2ecf20Sopenharmony_ci dev->service_set_out = V4L2_SLICED_WSS_625 | V4L2_SLICED_TELETEXT_B; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci dev->colorspace_out = V4L2_COLORSPACE_SMPTE170M; 2388c2ecf20Sopenharmony_ci break; 2398c2ecf20Sopenharmony_ci case HDMI: 2408c2ecf20Sopenharmony_ci dev->sink_rect.width = bt->width; 2418c2ecf20Sopenharmony_ci dev->sink_rect.height = bt->height; 2428c2ecf20Sopenharmony_ci size = V4L2_DV_BT_FRAME_WIDTH(bt) * V4L2_DV_BT_FRAME_HEIGHT(bt); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci if (can_reduce_fps(bt) && (bt->flags & V4L2_DV_FL_REDUCED_FPS)) 2458c2ecf20Sopenharmony_ci pixelclock = div_u64(bt->pixelclock * 1000, 1001); 2468c2ecf20Sopenharmony_ci else 2478c2ecf20Sopenharmony_ci pixelclock = bt->pixelclock; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci dev->timeperframe_vid_out = (struct v4l2_fract) { 2508c2ecf20Sopenharmony_ci size / 100, (u32)pixelclock / 100 2518c2ecf20Sopenharmony_ci }; 2528c2ecf20Sopenharmony_ci if (bt->interlaced) 2538c2ecf20Sopenharmony_ci dev->field_out = V4L2_FIELD_ALTERNATE; 2548c2ecf20Sopenharmony_ci else 2558c2ecf20Sopenharmony_ci dev->field_out = V4L2_FIELD_NONE; 2568c2ecf20Sopenharmony_ci if (!dev->dvi_d_out && (bt->flags & V4L2_DV_FL_IS_CE_VIDEO)) { 2578c2ecf20Sopenharmony_ci if (bt->width == 720 && bt->height <= 576) 2588c2ecf20Sopenharmony_ci dev->colorspace_out = V4L2_COLORSPACE_SMPTE170M; 2598c2ecf20Sopenharmony_ci else 2608c2ecf20Sopenharmony_ci dev->colorspace_out = V4L2_COLORSPACE_REC709; 2618c2ecf20Sopenharmony_ci } else { 2628c2ecf20Sopenharmony_ci dev->colorspace_out = V4L2_COLORSPACE_SRGB; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci break; 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci dev->xfer_func_out = V4L2_XFER_FUNC_DEFAULT; 2678c2ecf20Sopenharmony_ci dev->ycbcr_enc_out = V4L2_YCBCR_ENC_DEFAULT; 2688c2ecf20Sopenharmony_ci dev->hsv_enc_out = V4L2_HSV_ENC_180; 2698c2ecf20Sopenharmony_ci dev->quantization_out = V4L2_QUANTIZATION_DEFAULT; 2708c2ecf20Sopenharmony_ci dev->compose_out = dev->sink_rect; 2718c2ecf20Sopenharmony_ci dev->compose_bounds_out = dev->sink_rect; 2728c2ecf20Sopenharmony_ci dev->crop_out = dev->compose_out; 2738c2ecf20Sopenharmony_ci if (V4L2_FIELD_HAS_T_OR_B(dev->field_out)) 2748c2ecf20Sopenharmony_ci dev->crop_out.height /= 2; 2758c2ecf20Sopenharmony_ci dev->fmt_out_rect = dev->crop_out; 2768c2ecf20Sopenharmony_ci for (p = 0; p < dev->fmt_out->planes; p++) 2778c2ecf20Sopenharmony_ci dev->bytesperline_out[p] = 2788c2ecf20Sopenharmony_ci (dev->sink_rect.width * dev->fmt_out->bit_depth[p]) / 8; 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci/* Map the field to something that is valid for the current output */ 2828c2ecf20Sopenharmony_cistatic enum v4l2_field vivid_field_out(struct vivid_dev *dev, enum v4l2_field field) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci if (vivid_is_svid_out(dev)) { 2858c2ecf20Sopenharmony_ci switch (field) { 2868c2ecf20Sopenharmony_ci case V4L2_FIELD_INTERLACED_TB: 2878c2ecf20Sopenharmony_ci case V4L2_FIELD_INTERLACED_BT: 2888c2ecf20Sopenharmony_ci case V4L2_FIELD_SEQ_TB: 2898c2ecf20Sopenharmony_ci case V4L2_FIELD_SEQ_BT: 2908c2ecf20Sopenharmony_ci case V4L2_FIELD_ALTERNATE: 2918c2ecf20Sopenharmony_ci return field; 2928c2ecf20Sopenharmony_ci case V4L2_FIELD_INTERLACED: 2938c2ecf20Sopenharmony_ci default: 2948c2ecf20Sopenharmony_ci return V4L2_FIELD_INTERLACED; 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci if (vivid_is_hdmi_out(dev)) 2988c2ecf20Sopenharmony_ci return dev->dv_timings_out.bt.interlaced ? V4L2_FIELD_ALTERNATE : 2998c2ecf20Sopenharmony_ci V4L2_FIELD_NONE; 3008c2ecf20Sopenharmony_ci return V4L2_FIELD_NONE; 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic enum tpg_pixel_aspect vivid_get_pixel_aspect(const struct vivid_dev *dev) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci if (vivid_is_svid_out(dev)) 3068c2ecf20Sopenharmony_ci return (dev->std_out & V4L2_STD_525_60) ? 3078c2ecf20Sopenharmony_ci TPG_PIXEL_ASPECT_NTSC : TPG_PIXEL_ASPECT_PAL; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci if (vivid_is_hdmi_out(dev) && 3108c2ecf20Sopenharmony_ci dev->sink_rect.width == 720 && dev->sink_rect.height <= 576) 3118c2ecf20Sopenharmony_ci return dev->sink_rect.height == 480 ? 3128c2ecf20Sopenharmony_ci TPG_PIXEL_ASPECT_NTSC : TPG_PIXEL_ASPECT_PAL; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci return TPG_PIXEL_ASPECT_SQUARE; 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ciint vivid_g_fmt_vid_out(struct file *file, void *priv, 3188c2ecf20Sopenharmony_ci struct v4l2_format *f) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 3218c2ecf20Sopenharmony_ci struct v4l2_pix_format_mplane *mp = &f->fmt.pix_mp; 3228c2ecf20Sopenharmony_ci const struct vivid_fmt *fmt = dev->fmt_out; 3238c2ecf20Sopenharmony_ci unsigned p; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci mp->width = dev->fmt_out_rect.width; 3268c2ecf20Sopenharmony_ci mp->height = dev->fmt_out_rect.height; 3278c2ecf20Sopenharmony_ci mp->field = dev->field_out; 3288c2ecf20Sopenharmony_ci mp->pixelformat = fmt->fourcc; 3298c2ecf20Sopenharmony_ci mp->colorspace = dev->colorspace_out; 3308c2ecf20Sopenharmony_ci mp->xfer_func = dev->xfer_func_out; 3318c2ecf20Sopenharmony_ci mp->ycbcr_enc = dev->ycbcr_enc_out; 3328c2ecf20Sopenharmony_ci mp->quantization = dev->quantization_out; 3338c2ecf20Sopenharmony_ci mp->num_planes = fmt->buffers; 3348c2ecf20Sopenharmony_ci for (p = 0; p < mp->num_planes; p++) { 3358c2ecf20Sopenharmony_ci mp->plane_fmt[p].bytesperline = dev->bytesperline_out[p]; 3368c2ecf20Sopenharmony_ci mp->plane_fmt[p].sizeimage = 3378c2ecf20Sopenharmony_ci mp->plane_fmt[p].bytesperline * mp->height + 3388c2ecf20Sopenharmony_ci fmt->data_offset[p]; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci for (p = fmt->buffers; p < fmt->planes; p++) { 3418c2ecf20Sopenharmony_ci unsigned stride = dev->bytesperline_out[p]; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci mp->plane_fmt[0].sizeimage += 3448c2ecf20Sopenharmony_ci (stride * mp->height) / fmt->vdownsampling[p]; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci return 0; 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ciint vivid_try_fmt_vid_out(struct file *file, void *priv, 3508c2ecf20Sopenharmony_ci struct v4l2_format *f) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 3538c2ecf20Sopenharmony_ci struct v4l2_bt_timings *bt = &dev->dv_timings_out.bt; 3548c2ecf20Sopenharmony_ci struct v4l2_pix_format_mplane *mp = &f->fmt.pix_mp; 3558c2ecf20Sopenharmony_ci struct v4l2_plane_pix_format *pfmt = mp->plane_fmt; 3568c2ecf20Sopenharmony_ci const struct vivid_fmt *fmt; 3578c2ecf20Sopenharmony_ci unsigned bytesperline, max_bpl; 3588c2ecf20Sopenharmony_ci unsigned factor = 1; 3598c2ecf20Sopenharmony_ci unsigned w, h; 3608c2ecf20Sopenharmony_ci unsigned p; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci fmt = vivid_get_format(dev, mp->pixelformat); 3638c2ecf20Sopenharmony_ci if (!fmt) { 3648c2ecf20Sopenharmony_ci dprintk(dev, 1, "Fourcc format (0x%08x) unknown.\n", 3658c2ecf20Sopenharmony_ci mp->pixelformat); 3668c2ecf20Sopenharmony_ci mp->pixelformat = V4L2_PIX_FMT_YUYV; 3678c2ecf20Sopenharmony_ci fmt = vivid_get_format(dev, mp->pixelformat); 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci mp->field = vivid_field_out(dev, mp->field); 3718c2ecf20Sopenharmony_ci if (vivid_is_svid_out(dev)) { 3728c2ecf20Sopenharmony_ci w = 720; 3738c2ecf20Sopenharmony_ci h = (dev->std_out & V4L2_STD_525_60) ? 480 : 576; 3748c2ecf20Sopenharmony_ci } else { 3758c2ecf20Sopenharmony_ci w = dev->sink_rect.width; 3768c2ecf20Sopenharmony_ci h = dev->sink_rect.height; 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci if (V4L2_FIELD_HAS_T_OR_B(mp->field)) 3798c2ecf20Sopenharmony_ci factor = 2; 3808c2ecf20Sopenharmony_ci if (!dev->has_scaler_out && !dev->has_crop_out && !dev->has_compose_out) { 3818c2ecf20Sopenharmony_ci mp->width = w; 3828c2ecf20Sopenharmony_ci mp->height = h / factor; 3838c2ecf20Sopenharmony_ci } else { 3848c2ecf20Sopenharmony_ci struct v4l2_rect r = { 0, 0, mp->width, mp->height * factor }; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci v4l2_rect_set_min_size(&r, &vivid_min_rect); 3878c2ecf20Sopenharmony_ci v4l2_rect_set_max_size(&r, &vivid_max_rect); 3888c2ecf20Sopenharmony_ci if (dev->has_scaler_out && !dev->has_crop_out) { 3898c2ecf20Sopenharmony_ci struct v4l2_rect max_r = { 0, 0, MAX_ZOOM * w, MAX_ZOOM * h }; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci v4l2_rect_set_max_size(&r, &max_r); 3928c2ecf20Sopenharmony_ci } else if (!dev->has_scaler_out && dev->has_compose_out && !dev->has_crop_out) { 3938c2ecf20Sopenharmony_ci v4l2_rect_set_max_size(&r, &dev->sink_rect); 3948c2ecf20Sopenharmony_ci } else if (!dev->has_scaler_out && !dev->has_compose_out) { 3958c2ecf20Sopenharmony_ci v4l2_rect_set_min_size(&r, &dev->sink_rect); 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci mp->width = r.width; 3988c2ecf20Sopenharmony_ci mp->height = r.height / factor; 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci /* This driver supports custom bytesperline values */ 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci mp->num_planes = fmt->buffers; 4048c2ecf20Sopenharmony_ci for (p = 0; p < fmt->buffers; p++) { 4058c2ecf20Sopenharmony_ci /* Calculate the minimum supported bytesperline value */ 4068c2ecf20Sopenharmony_ci bytesperline = (mp->width * fmt->bit_depth[p]) >> 3; 4078c2ecf20Sopenharmony_ci /* Calculate the maximum supported bytesperline value */ 4088c2ecf20Sopenharmony_ci max_bpl = (MAX_ZOOM * MAX_WIDTH * fmt->bit_depth[p]) >> 3; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci if (pfmt[p].bytesperline > max_bpl) 4118c2ecf20Sopenharmony_ci pfmt[p].bytesperline = max_bpl; 4128c2ecf20Sopenharmony_ci if (pfmt[p].bytesperline < bytesperline) 4138c2ecf20Sopenharmony_ci pfmt[p].bytesperline = bytesperline; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci pfmt[p].sizeimage = (pfmt[p].bytesperline * mp->height) / 4168c2ecf20Sopenharmony_ci fmt->vdownsampling[p] + fmt->data_offset[p]; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci memset(pfmt[p].reserved, 0, sizeof(pfmt[p].reserved)); 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci for (p = fmt->buffers; p < fmt->planes; p++) 4218c2ecf20Sopenharmony_ci pfmt[0].sizeimage += (pfmt[0].bytesperline * mp->height * 4228c2ecf20Sopenharmony_ci (fmt->bit_depth[p] / fmt->vdownsampling[p])) / 4238c2ecf20Sopenharmony_ci (fmt->bit_depth[0] / fmt->vdownsampling[0]); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci mp->xfer_func = V4L2_XFER_FUNC_DEFAULT; 4268c2ecf20Sopenharmony_ci mp->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; 4278c2ecf20Sopenharmony_ci mp->quantization = V4L2_QUANTIZATION_DEFAULT; 4288c2ecf20Sopenharmony_ci if (vivid_is_svid_out(dev)) { 4298c2ecf20Sopenharmony_ci mp->colorspace = V4L2_COLORSPACE_SMPTE170M; 4308c2ecf20Sopenharmony_ci } else if (dev->dvi_d_out || !(bt->flags & V4L2_DV_FL_IS_CE_VIDEO)) { 4318c2ecf20Sopenharmony_ci mp->colorspace = V4L2_COLORSPACE_SRGB; 4328c2ecf20Sopenharmony_ci if (dev->dvi_d_out) 4338c2ecf20Sopenharmony_ci mp->quantization = V4L2_QUANTIZATION_LIM_RANGE; 4348c2ecf20Sopenharmony_ci } else if (bt->width == 720 && bt->height <= 576) { 4358c2ecf20Sopenharmony_ci mp->colorspace = V4L2_COLORSPACE_SMPTE170M; 4368c2ecf20Sopenharmony_ci } else if (mp->colorspace != V4L2_COLORSPACE_SMPTE170M && 4378c2ecf20Sopenharmony_ci mp->colorspace != V4L2_COLORSPACE_REC709 && 4388c2ecf20Sopenharmony_ci mp->colorspace != V4L2_COLORSPACE_OPRGB && 4398c2ecf20Sopenharmony_ci mp->colorspace != V4L2_COLORSPACE_BT2020 && 4408c2ecf20Sopenharmony_ci mp->colorspace != V4L2_COLORSPACE_SRGB) { 4418c2ecf20Sopenharmony_ci mp->colorspace = V4L2_COLORSPACE_REC709; 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci memset(mp->reserved, 0, sizeof(mp->reserved)); 4448c2ecf20Sopenharmony_ci return 0; 4458c2ecf20Sopenharmony_ci} 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ciint vivid_s_fmt_vid_out(struct file *file, void *priv, 4488c2ecf20Sopenharmony_ci struct v4l2_format *f) 4498c2ecf20Sopenharmony_ci{ 4508c2ecf20Sopenharmony_ci struct v4l2_pix_format_mplane *mp = &f->fmt.pix_mp; 4518c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 4528c2ecf20Sopenharmony_ci struct v4l2_rect *crop = &dev->crop_out; 4538c2ecf20Sopenharmony_ci struct v4l2_rect *compose = &dev->compose_out; 4548c2ecf20Sopenharmony_ci struct vb2_queue *q = &dev->vb_vid_out_q; 4558c2ecf20Sopenharmony_ci int ret = vivid_try_fmt_vid_out(file, priv, f); 4568c2ecf20Sopenharmony_ci unsigned factor = 1; 4578c2ecf20Sopenharmony_ci unsigned p; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci if (ret < 0) 4608c2ecf20Sopenharmony_ci return ret; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci if (vb2_is_busy(q) && 4638c2ecf20Sopenharmony_ci (vivid_is_svid_out(dev) || 4648c2ecf20Sopenharmony_ci mp->width != dev->fmt_out_rect.width || 4658c2ecf20Sopenharmony_ci mp->height != dev->fmt_out_rect.height || 4668c2ecf20Sopenharmony_ci mp->pixelformat != dev->fmt_out->fourcc || 4678c2ecf20Sopenharmony_ci mp->field != dev->field_out)) { 4688c2ecf20Sopenharmony_ci dprintk(dev, 1, "%s device busy\n", __func__); 4698c2ecf20Sopenharmony_ci return -EBUSY; 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci /* 4738c2ecf20Sopenharmony_ci * Allow for changing the colorspace on the fly. Useful for testing 4748c2ecf20Sopenharmony_ci * purposes, and it is something that HDMI transmitters are able 4758c2ecf20Sopenharmony_ci * to do. 4768c2ecf20Sopenharmony_ci */ 4778c2ecf20Sopenharmony_ci if (vb2_is_busy(q)) 4788c2ecf20Sopenharmony_ci goto set_colorspace; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci dev->fmt_out = vivid_get_format(dev, mp->pixelformat); 4818c2ecf20Sopenharmony_ci if (V4L2_FIELD_HAS_T_OR_B(mp->field)) 4828c2ecf20Sopenharmony_ci factor = 2; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci if (dev->has_scaler_out || dev->has_crop_out || dev->has_compose_out) { 4858c2ecf20Sopenharmony_ci struct v4l2_rect r = { 0, 0, mp->width, mp->height }; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci if (dev->has_scaler_out) { 4888c2ecf20Sopenharmony_ci if (dev->has_crop_out) 4898c2ecf20Sopenharmony_ci v4l2_rect_map_inside(crop, &r); 4908c2ecf20Sopenharmony_ci else 4918c2ecf20Sopenharmony_ci *crop = r; 4928c2ecf20Sopenharmony_ci if (dev->has_compose_out && !dev->has_crop_out) { 4938c2ecf20Sopenharmony_ci struct v4l2_rect min_r = { 4948c2ecf20Sopenharmony_ci 0, 0, 4958c2ecf20Sopenharmony_ci r.width / MAX_ZOOM, 4968c2ecf20Sopenharmony_ci factor * r.height / MAX_ZOOM 4978c2ecf20Sopenharmony_ci }; 4988c2ecf20Sopenharmony_ci struct v4l2_rect max_r = { 4998c2ecf20Sopenharmony_ci 0, 0, 5008c2ecf20Sopenharmony_ci r.width * MAX_ZOOM, 5018c2ecf20Sopenharmony_ci factor * r.height * MAX_ZOOM 5028c2ecf20Sopenharmony_ci }; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci v4l2_rect_set_min_size(compose, &min_r); 5058c2ecf20Sopenharmony_ci v4l2_rect_set_max_size(compose, &max_r); 5068c2ecf20Sopenharmony_ci v4l2_rect_map_inside(compose, &dev->compose_bounds_out); 5078c2ecf20Sopenharmony_ci } else if (dev->has_compose_out) { 5088c2ecf20Sopenharmony_ci struct v4l2_rect min_r = { 5098c2ecf20Sopenharmony_ci 0, 0, 5108c2ecf20Sopenharmony_ci crop->width / MAX_ZOOM, 5118c2ecf20Sopenharmony_ci factor * crop->height / MAX_ZOOM 5128c2ecf20Sopenharmony_ci }; 5138c2ecf20Sopenharmony_ci struct v4l2_rect max_r = { 5148c2ecf20Sopenharmony_ci 0, 0, 5158c2ecf20Sopenharmony_ci crop->width * MAX_ZOOM, 5168c2ecf20Sopenharmony_ci factor * crop->height * MAX_ZOOM 5178c2ecf20Sopenharmony_ci }; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci v4l2_rect_set_min_size(compose, &min_r); 5208c2ecf20Sopenharmony_ci v4l2_rect_set_max_size(compose, &max_r); 5218c2ecf20Sopenharmony_ci v4l2_rect_map_inside(compose, &dev->compose_bounds_out); 5228c2ecf20Sopenharmony_ci } 5238c2ecf20Sopenharmony_ci } else if (dev->has_compose_out && !dev->has_crop_out) { 5248c2ecf20Sopenharmony_ci v4l2_rect_set_size_to(crop, &r); 5258c2ecf20Sopenharmony_ci r.height *= factor; 5268c2ecf20Sopenharmony_ci v4l2_rect_set_size_to(compose, &r); 5278c2ecf20Sopenharmony_ci v4l2_rect_map_inside(compose, &dev->compose_bounds_out); 5288c2ecf20Sopenharmony_ci } else if (!dev->has_compose_out) { 5298c2ecf20Sopenharmony_ci v4l2_rect_map_inside(crop, &r); 5308c2ecf20Sopenharmony_ci r.height /= factor; 5318c2ecf20Sopenharmony_ci v4l2_rect_set_size_to(compose, &r); 5328c2ecf20Sopenharmony_ci } else { 5338c2ecf20Sopenharmony_ci r.height *= factor; 5348c2ecf20Sopenharmony_ci v4l2_rect_set_max_size(compose, &r); 5358c2ecf20Sopenharmony_ci v4l2_rect_map_inside(compose, &dev->compose_bounds_out); 5368c2ecf20Sopenharmony_ci crop->top *= factor; 5378c2ecf20Sopenharmony_ci crop->height *= factor; 5388c2ecf20Sopenharmony_ci v4l2_rect_set_size_to(crop, compose); 5398c2ecf20Sopenharmony_ci v4l2_rect_map_inside(crop, &r); 5408c2ecf20Sopenharmony_ci crop->top /= factor; 5418c2ecf20Sopenharmony_ci crop->height /= factor; 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci } else { 5448c2ecf20Sopenharmony_ci struct v4l2_rect r = { 0, 0, mp->width, mp->height }; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci v4l2_rect_set_size_to(crop, &r); 5478c2ecf20Sopenharmony_ci r.height /= factor; 5488c2ecf20Sopenharmony_ci v4l2_rect_set_size_to(compose, &r); 5498c2ecf20Sopenharmony_ci } 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci dev->fmt_out_rect.width = mp->width; 5528c2ecf20Sopenharmony_ci dev->fmt_out_rect.height = mp->height; 5538c2ecf20Sopenharmony_ci for (p = 0; p < mp->num_planes; p++) 5548c2ecf20Sopenharmony_ci dev->bytesperline_out[p] = mp->plane_fmt[p].bytesperline; 5558c2ecf20Sopenharmony_ci for (p = dev->fmt_out->buffers; p < dev->fmt_out->planes; p++) 5568c2ecf20Sopenharmony_ci dev->bytesperline_out[p] = 5578c2ecf20Sopenharmony_ci (dev->bytesperline_out[0] * dev->fmt_out->bit_depth[p]) / 5588c2ecf20Sopenharmony_ci dev->fmt_out->bit_depth[0]; 5598c2ecf20Sopenharmony_ci dev->field_out = mp->field; 5608c2ecf20Sopenharmony_ci if (vivid_is_svid_out(dev)) 5618c2ecf20Sopenharmony_ci dev->tv_field_out = mp->field; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ciset_colorspace: 5648c2ecf20Sopenharmony_ci dev->colorspace_out = mp->colorspace; 5658c2ecf20Sopenharmony_ci dev->xfer_func_out = mp->xfer_func; 5668c2ecf20Sopenharmony_ci dev->ycbcr_enc_out = mp->ycbcr_enc; 5678c2ecf20Sopenharmony_ci dev->quantization_out = mp->quantization; 5688c2ecf20Sopenharmony_ci if (dev->loop_video) { 5698c2ecf20Sopenharmony_ci vivid_send_source_change(dev, SVID); 5708c2ecf20Sopenharmony_ci vivid_send_source_change(dev, HDMI); 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci return 0; 5738c2ecf20Sopenharmony_ci} 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ciint vidioc_g_fmt_vid_out_mplane(struct file *file, void *priv, 5768c2ecf20Sopenharmony_ci struct v4l2_format *f) 5778c2ecf20Sopenharmony_ci{ 5788c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci if (!dev->multiplanar) 5818c2ecf20Sopenharmony_ci return -ENOTTY; 5828c2ecf20Sopenharmony_ci return vivid_g_fmt_vid_out(file, priv, f); 5838c2ecf20Sopenharmony_ci} 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ciint vidioc_try_fmt_vid_out_mplane(struct file *file, void *priv, 5868c2ecf20Sopenharmony_ci struct v4l2_format *f) 5878c2ecf20Sopenharmony_ci{ 5888c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci if (!dev->multiplanar) 5918c2ecf20Sopenharmony_ci return -ENOTTY; 5928c2ecf20Sopenharmony_ci return vivid_try_fmt_vid_out(file, priv, f); 5938c2ecf20Sopenharmony_ci} 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ciint vidioc_s_fmt_vid_out_mplane(struct file *file, void *priv, 5968c2ecf20Sopenharmony_ci struct v4l2_format *f) 5978c2ecf20Sopenharmony_ci{ 5988c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci if (!dev->multiplanar) 6018c2ecf20Sopenharmony_ci return -ENOTTY; 6028c2ecf20Sopenharmony_ci return vivid_s_fmt_vid_out(file, priv, f); 6038c2ecf20Sopenharmony_ci} 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ciint vidioc_g_fmt_vid_out(struct file *file, void *priv, 6068c2ecf20Sopenharmony_ci struct v4l2_format *f) 6078c2ecf20Sopenharmony_ci{ 6088c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci if (dev->multiplanar) 6118c2ecf20Sopenharmony_ci return -ENOTTY; 6128c2ecf20Sopenharmony_ci return fmt_sp2mp_func(file, priv, f, vivid_g_fmt_vid_out); 6138c2ecf20Sopenharmony_ci} 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ciint vidioc_try_fmt_vid_out(struct file *file, void *priv, 6168c2ecf20Sopenharmony_ci struct v4l2_format *f) 6178c2ecf20Sopenharmony_ci{ 6188c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci if (dev->multiplanar) 6218c2ecf20Sopenharmony_ci return -ENOTTY; 6228c2ecf20Sopenharmony_ci return fmt_sp2mp_func(file, priv, f, vivid_try_fmt_vid_out); 6238c2ecf20Sopenharmony_ci} 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ciint vidioc_s_fmt_vid_out(struct file *file, void *priv, 6268c2ecf20Sopenharmony_ci struct v4l2_format *f) 6278c2ecf20Sopenharmony_ci{ 6288c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci if (dev->multiplanar) 6318c2ecf20Sopenharmony_ci return -ENOTTY; 6328c2ecf20Sopenharmony_ci return fmt_sp2mp_func(file, priv, f, vivid_s_fmt_vid_out); 6338c2ecf20Sopenharmony_ci} 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ciint vivid_vid_out_g_selection(struct file *file, void *priv, 6368c2ecf20Sopenharmony_ci struct v4l2_selection *sel) 6378c2ecf20Sopenharmony_ci{ 6388c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci if (!dev->has_crop_out && !dev->has_compose_out) 6418c2ecf20Sopenharmony_ci return -ENOTTY; 6428c2ecf20Sopenharmony_ci if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) 6438c2ecf20Sopenharmony_ci return -EINVAL; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci sel->r.left = sel->r.top = 0; 6468c2ecf20Sopenharmony_ci switch (sel->target) { 6478c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP: 6488c2ecf20Sopenharmony_ci if (!dev->has_crop_out) 6498c2ecf20Sopenharmony_ci return -EINVAL; 6508c2ecf20Sopenharmony_ci sel->r = dev->crop_out; 6518c2ecf20Sopenharmony_ci break; 6528c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP_DEFAULT: 6538c2ecf20Sopenharmony_ci if (!dev->has_crop_out) 6548c2ecf20Sopenharmony_ci return -EINVAL; 6558c2ecf20Sopenharmony_ci sel->r = dev->fmt_out_rect; 6568c2ecf20Sopenharmony_ci break; 6578c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP_BOUNDS: 6588c2ecf20Sopenharmony_ci if (!dev->has_crop_out) 6598c2ecf20Sopenharmony_ci return -EINVAL; 6608c2ecf20Sopenharmony_ci sel->r = vivid_max_rect; 6618c2ecf20Sopenharmony_ci break; 6628c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE: 6638c2ecf20Sopenharmony_ci if (!dev->has_compose_out) 6648c2ecf20Sopenharmony_ci return -EINVAL; 6658c2ecf20Sopenharmony_ci sel->r = dev->compose_out; 6668c2ecf20Sopenharmony_ci break; 6678c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_DEFAULT: 6688c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_BOUNDS: 6698c2ecf20Sopenharmony_ci if (!dev->has_compose_out) 6708c2ecf20Sopenharmony_ci return -EINVAL; 6718c2ecf20Sopenharmony_ci sel->r = dev->sink_rect; 6728c2ecf20Sopenharmony_ci break; 6738c2ecf20Sopenharmony_ci default: 6748c2ecf20Sopenharmony_ci return -EINVAL; 6758c2ecf20Sopenharmony_ci } 6768c2ecf20Sopenharmony_ci return 0; 6778c2ecf20Sopenharmony_ci} 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ciint vivid_vid_out_s_selection(struct file *file, void *fh, struct v4l2_selection *s) 6808c2ecf20Sopenharmony_ci{ 6818c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 6828c2ecf20Sopenharmony_ci struct v4l2_rect *crop = &dev->crop_out; 6838c2ecf20Sopenharmony_ci struct v4l2_rect *compose = &dev->compose_out; 6848c2ecf20Sopenharmony_ci unsigned factor = V4L2_FIELD_HAS_T_OR_B(dev->field_out) ? 2 : 1; 6858c2ecf20Sopenharmony_ci int ret; 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci if (!dev->has_crop_out && !dev->has_compose_out) 6888c2ecf20Sopenharmony_ci return -ENOTTY; 6898c2ecf20Sopenharmony_ci if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) 6908c2ecf20Sopenharmony_ci return -EINVAL; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci switch (s->target) { 6938c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP: 6948c2ecf20Sopenharmony_ci if (!dev->has_crop_out) 6958c2ecf20Sopenharmony_ci return -EINVAL; 6968c2ecf20Sopenharmony_ci ret = vivid_vid_adjust_sel(s->flags, &s->r); 6978c2ecf20Sopenharmony_ci if (ret) 6988c2ecf20Sopenharmony_ci return ret; 6998c2ecf20Sopenharmony_ci v4l2_rect_set_min_size(&s->r, &vivid_min_rect); 7008c2ecf20Sopenharmony_ci v4l2_rect_set_max_size(&s->r, &dev->fmt_out_rect); 7018c2ecf20Sopenharmony_ci if (dev->has_scaler_out) { 7028c2ecf20Sopenharmony_ci struct v4l2_rect max_rect = { 7038c2ecf20Sopenharmony_ci 0, 0, 7048c2ecf20Sopenharmony_ci dev->sink_rect.width * MAX_ZOOM, 7058c2ecf20Sopenharmony_ci (dev->sink_rect.height / factor) * MAX_ZOOM 7068c2ecf20Sopenharmony_ci }; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci v4l2_rect_set_max_size(&s->r, &max_rect); 7098c2ecf20Sopenharmony_ci if (dev->has_compose_out) { 7108c2ecf20Sopenharmony_ci struct v4l2_rect min_rect = { 7118c2ecf20Sopenharmony_ci 0, 0, 7128c2ecf20Sopenharmony_ci s->r.width / MAX_ZOOM, 7138c2ecf20Sopenharmony_ci (s->r.height * factor) / MAX_ZOOM 7148c2ecf20Sopenharmony_ci }; 7158c2ecf20Sopenharmony_ci struct v4l2_rect max_rect = { 7168c2ecf20Sopenharmony_ci 0, 0, 7178c2ecf20Sopenharmony_ci s->r.width * MAX_ZOOM, 7188c2ecf20Sopenharmony_ci (s->r.height * factor) * MAX_ZOOM 7198c2ecf20Sopenharmony_ci }; 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci v4l2_rect_set_min_size(compose, &min_rect); 7228c2ecf20Sopenharmony_ci v4l2_rect_set_max_size(compose, &max_rect); 7238c2ecf20Sopenharmony_ci v4l2_rect_map_inside(compose, &dev->compose_bounds_out); 7248c2ecf20Sopenharmony_ci } 7258c2ecf20Sopenharmony_ci } else if (dev->has_compose_out) { 7268c2ecf20Sopenharmony_ci s->r.top *= factor; 7278c2ecf20Sopenharmony_ci s->r.height *= factor; 7288c2ecf20Sopenharmony_ci v4l2_rect_set_max_size(&s->r, &dev->sink_rect); 7298c2ecf20Sopenharmony_ci v4l2_rect_set_size_to(compose, &s->r); 7308c2ecf20Sopenharmony_ci v4l2_rect_map_inside(compose, &dev->compose_bounds_out); 7318c2ecf20Sopenharmony_ci s->r.top /= factor; 7328c2ecf20Sopenharmony_ci s->r.height /= factor; 7338c2ecf20Sopenharmony_ci } else { 7348c2ecf20Sopenharmony_ci v4l2_rect_set_size_to(&s->r, &dev->sink_rect); 7358c2ecf20Sopenharmony_ci s->r.height /= factor; 7368c2ecf20Sopenharmony_ci } 7378c2ecf20Sopenharmony_ci v4l2_rect_map_inside(&s->r, &dev->fmt_out_rect); 7388c2ecf20Sopenharmony_ci *crop = s->r; 7398c2ecf20Sopenharmony_ci break; 7408c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE: 7418c2ecf20Sopenharmony_ci if (!dev->has_compose_out) 7428c2ecf20Sopenharmony_ci return -EINVAL; 7438c2ecf20Sopenharmony_ci ret = vivid_vid_adjust_sel(s->flags, &s->r); 7448c2ecf20Sopenharmony_ci if (ret) 7458c2ecf20Sopenharmony_ci return ret; 7468c2ecf20Sopenharmony_ci v4l2_rect_set_min_size(&s->r, &vivid_min_rect); 7478c2ecf20Sopenharmony_ci v4l2_rect_set_max_size(&s->r, &dev->sink_rect); 7488c2ecf20Sopenharmony_ci v4l2_rect_map_inside(&s->r, &dev->compose_bounds_out); 7498c2ecf20Sopenharmony_ci s->r.top /= factor; 7508c2ecf20Sopenharmony_ci s->r.height /= factor; 7518c2ecf20Sopenharmony_ci if (dev->has_scaler_out) { 7528c2ecf20Sopenharmony_ci struct v4l2_rect fmt = dev->fmt_out_rect; 7538c2ecf20Sopenharmony_ci struct v4l2_rect max_rect = { 7548c2ecf20Sopenharmony_ci 0, 0, 7558c2ecf20Sopenharmony_ci s->r.width * MAX_ZOOM, 7568c2ecf20Sopenharmony_ci s->r.height * MAX_ZOOM 7578c2ecf20Sopenharmony_ci }; 7588c2ecf20Sopenharmony_ci struct v4l2_rect min_rect = { 7598c2ecf20Sopenharmony_ci 0, 0, 7608c2ecf20Sopenharmony_ci s->r.width / MAX_ZOOM, 7618c2ecf20Sopenharmony_ci s->r.height / MAX_ZOOM 7628c2ecf20Sopenharmony_ci }; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci v4l2_rect_set_min_size(&fmt, &min_rect); 7658c2ecf20Sopenharmony_ci if (!dev->has_crop_out) 7668c2ecf20Sopenharmony_ci v4l2_rect_set_max_size(&fmt, &max_rect); 7678c2ecf20Sopenharmony_ci if (!v4l2_rect_same_size(&dev->fmt_out_rect, &fmt) && 7688c2ecf20Sopenharmony_ci vb2_is_busy(&dev->vb_vid_out_q)) 7698c2ecf20Sopenharmony_ci return -EBUSY; 7708c2ecf20Sopenharmony_ci if (dev->has_crop_out) { 7718c2ecf20Sopenharmony_ci v4l2_rect_set_min_size(crop, &min_rect); 7728c2ecf20Sopenharmony_ci v4l2_rect_set_max_size(crop, &max_rect); 7738c2ecf20Sopenharmony_ci } 7748c2ecf20Sopenharmony_ci dev->fmt_out_rect = fmt; 7758c2ecf20Sopenharmony_ci } else if (dev->has_crop_out) { 7768c2ecf20Sopenharmony_ci struct v4l2_rect fmt = dev->fmt_out_rect; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci v4l2_rect_set_min_size(&fmt, &s->r); 7798c2ecf20Sopenharmony_ci if (!v4l2_rect_same_size(&dev->fmt_out_rect, &fmt) && 7808c2ecf20Sopenharmony_ci vb2_is_busy(&dev->vb_vid_out_q)) 7818c2ecf20Sopenharmony_ci return -EBUSY; 7828c2ecf20Sopenharmony_ci dev->fmt_out_rect = fmt; 7838c2ecf20Sopenharmony_ci v4l2_rect_set_size_to(crop, &s->r); 7848c2ecf20Sopenharmony_ci v4l2_rect_map_inside(crop, &dev->fmt_out_rect); 7858c2ecf20Sopenharmony_ci } else { 7868c2ecf20Sopenharmony_ci if (!v4l2_rect_same_size(&s->r, &dev->fmt_out_rect) && 7878c2ecf20Sopenharmony_ci vb2_is_busy(&dev->vb_vid_out_q)) 7888c2ecf20Sopenharmony_ci return -EBUSY; 7898c2ecf20Sopenharmony_ci v4l2_rect_set_size_to(&dev->fmt_out_rect, &s->r); 7908c2ecf20Sopenharmony_ci v4l2_rect_set_size_to(crop, &s->r); 7918c2ecf20Sopenharmony_ci crop->height /= factor; 7928c2ecf20Sopenharmony_ci v4l2_rect_map_inside(crop, &dev->fmt_out_rect); 7938c2ecf20Sopenharmony_ci } 7948c2ecf20Sopenharmony_ci s->r.top *= factor; 7958c2ecf20Sopenharmony_ci s->r.height *= factor; 7968c2ecf20Sopenharmony_ci if (dev->bitmap_out && (compose->width != s->r.width || 7978c2ecf20Sopenharmony_ci compose->height != s->r.height)) { 7988c2ecf20Sopenharmony_ci vfree(dev->bitmap_out); 7998c2ecf20Sopenharmony_ci dev->bitmap_out = NULL; 8008c2ecf20Sopenharmony_ci } 8018c2ecf20Sopenharmony_ci *compose = s->r; 8028c2ecf20Sopenharmony_ci break; 8038c2ecf20Sopenharmony_ci default: 8048c2ecf20Sopenharmony_ci return -EINVAL; 8058c2ecf20Sopenharmony_ci } 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci return 0; 8088c2ecf20Sopenharmony_ci} 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ciint vivid_vid_out_g_pixelaspect(struct file *file, void *priv, 8118c2ecf20Sopenharmony_ci int type, struct v4l2_fract *f) 8128c2ecf20Sopenharmony_ci{ 8138c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci if (type != V4L2_BUF_TYPE_VIDEO_OUTPUT) 8168c2ecf20Sopenharmony_ci return -EINVAL; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci switch (vivid_get_pixel_aspect(dev)) { 8198c2ecf20Sopenharmony_ci case TPG_PIXEL_ASPECT_NTSC: 8208c2ecf20Sopenharmony_ci f->numerator = 11; 8218c2ecf20Sopenharmony_ci f->denominator = 10; 8228c2ecf20Sopenharmony_ci break; 8238c2ecf20Sopenharmony_ci case TPG_PIXEL_ASPECT_PAL: 8248c2ecf20Sopenharmony_ci f->numerator = 54; 8258c2ecf20Sopenharmony_ci f->denominator = 59; 8268c2ecf20Sopenharmony_ci break; 8278c2ecf20Sopenharmony_ci default: 8288c2ecf20Sopenharmony_ci break; 8298c2ecf20Sopenharmony_ci } 8308c2ecf20Sopenharmony_ci return 0; 8318c2ecf20Sopenharmony_ci} 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ciint vidioc_g_fmt_vid_out_overlay(struct file *file, void *priv, 8348c2ecf20Sopenharmony_ci struct v4l2_format *f) 8358c2ecf20Sopenharmony_ci{ 8368c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 8378c2ecf20Sopenharmony_ci const struct v4l2_rect *compose = &dev->compose_out; 8388c2ecf20Sopenharmony_ci struct v4l2_window *win = &f->fmt.win; 8398c2ecf20Sopenharmony_ci unsigned clipcount = win->clipcount; 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci if (!dev->has_fb) 8428c2ecf20Sopenharmony_ci return -EINVAL; 8438c2ecf20Sopenharmony_ci win->w.top = dev->overlay_out_top; 8448c2ecf20Sopenharmony_ci win->w.left = dev->overlay_out_left; 8458c2ecf20Sopenharmony_ci win->w.width = compose->width; 8468c2ecf20Sopenharmony_ci win->w.height = compose->height; 8478c2ecf20Sopenharmony_ci win->clipcount = dev->clipcount_out; 8488c2ecf20Sopenharmony_ci win->field = V4L2_FIELD_ANY; 8498c2ecf20Sopenharmony_ci win->chromakey = dev->chromakey_out; 8508c2ecf20Sopenharmony_ci win->global_alpha = dev->global_alpha_out; 8518c2ecf20Sopenharmony_ci if (clipcount > dev->clipcount_out) 8528c2ecf20Sopenharmony_ci clipcount = dev->clipcount_out; 8538c2ecf20Sopenharmony_ci if (dev->bitmap_out == NULL) 8548c2ecf20Sopenharmony_ci win->bitmap = NULL; 8558c2ecf20Sopenharmony_ci else if (win->bitmap) { 8568c2ecf20Sopenharmony_ci if (copy_to_user(win->bitmap, dev->bitmap_out, 8578c2ecf20Sopenharmony_ci ((dev->compose_out.width + 7) / 8) * dev->compose_out.height)) 8588c2ecf20Sopenharmony_ci return -EFAULT; 8598c2ecf20Sopenharmony_ci } 8608c2ecf20Sopenharmony_ci if (clipcount && win->clips) { 8618c2ecf20Sopenharmony_ci if (copy_to_user(win->clips, dev->clips_out, 8628c2ecf20Sopenharmony_ci clipcount * sizeof(dev->clips_out[0]))) 8638c2ecf20Sopenharmony_ci return -EFAULT; 8648c2ecf20Sopenharmony_ci } 8658c2ecf20Sopenharmony_ci return 0; 8668c2ecf20Sopenharmony_ci} 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ciint vidioc_try_fmt_vid_out_overlay(struct file *file, void *priv, 8698c2ecf20Sopenharmony_ci struct v4l2_format *f) 8708c2ecf20Sopenharmony_ci{ 8718c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 8728c2ecf20Sopenharmony_ci const struct v4l2_rect *compose = &dev->compose_out; 8738c2ecf20Sopenharmony_ci struct v4l2_window *win = &f->fmt.win; 8748c2ecf20Sopenharmony_ci int i, j; 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci if (!dev->has_fb) 8778c2ecf20Sopenharmony_ci return -EINVAL; 8788c2ecf20Sopenharmony_ci win->w.left = clamp_t(int, win->w.left, 8798c2ecf20Sopenharmony_ci -dev->display_width, dev->display_width); 8808c2ecf20Sopenharmony_ci win->w.top = clamp_t(int, win->w.top, 8818c2ecf20Sopenharmony_ci -dev->display_height, dev->display_height); 8828c2ecf20Sopenharmony_ci win->w.width = compose->width; 8838c2ecf20Sopenharmony_ci win->w.height = compose->height; 8848c2ecf20Sopenharmony_ci /* 8858c2ecf20Sopenharmony_ci * It makes no sense for an OSD to overlay only top or bottom fields, 8868c2ecf20Sopenharmony_ci * so always set this to ANY. 8878c2ecf20Sopenharmony_ci */ 8888c2ecf20Sopenharmony_ci win->field = V4L2_FIELD_ANY; 8898c2ecf20Sopenharmony_ci if (win->clipcount && !win->clips) 8908c2ecf20Sopenharmony_ci win->clipcount = 0; 8918c2ecf20Sopenharmony_ci if (win->clipcount > MAX_CLIPS) 8928c2ecf20Sopenharmony_ci win->clipcount = MAX_CLIPS; 8938c2ecf20Sopenharmony_ci if (win->clipcount) { 8948c2ecf20Sopenharmony_ci if (copy_from_user(dev->try_clips_out, win->clips, 8958c2ecf20Sopenharmony_ci win->clipcount * sizeof(dev->clips_out[0]))) 8968c2ecf20Sopenharmony_ci return -EFAULT; 8978c2ecf20Sopenharmony_ci for (i = 0; i < win->clipcount; i++) { 8988c2ecf20Sopenharmony_ci struct v4l2_rect *r = &dev->try_clips_out[i].c; 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci r->top = clamp_t(s32, r->top, 0, dev->display_height - 1); 9018c2ecf20Sopenharmony_ci r->height = clamp_t(s32, r->height, 1, dev->display_height - r->top); 9028c2ecf20Sopenharmony_ci r->left = clamp_t(u32, r->left, 0, dev->display_width - 1); 9038c2ecf20Sopenharmony_ci r->width = clamp_t(u32, r->width, 1, dev->display_width - r->left); 9048c2ecf20Sopenharmony_ci } 9058c2ecf20Sopenharmony_ci /* 9068c2ecf20Sopenharmony_ci * Yeah, so sue me, it's an O(n^2) algorithm. But n is a small 9078c2ecf20Sopenharmony_ci * number and it's typically a one-time deal. 9088c2ecf20Sopenharmony_ci */ 9098c2ecf20Sopenharmony_ci for (i = 0; i < win->clipcount - 1; i++) { 9108c2ecf20Sopenharmony_ci struct v4l2_rect *r1 = &dev->try_clips_out[i].c; 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci for (j = i + 1; j < win->clipcount; j++) { 9138c2ecf20Sopenharmony_ci struct v4l2_rect *r2 = &dev->try_clips_out[j].c; 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci if (v4l2_rect_overlap(r1, r2)) 9168c2ecf20Sopenharmony_ci return -EINVAL; 9178c2ecf20Sopenharmony_ci } 9188c2ecf20Sopenharmony_ci } 9198c2ecf20Sopenharmony_ci if (copy_to_user(win->clips, dev->try_clips_out, 9208c2ecf20Sopenharmony_ci win->clipcount * sizeof(dev->clips_out[0]))) 9218c2ecf20Sopenharmony_ci return -EFAULT; 9228c2ecf20Sopenharmony_ci } 9238c2ecf20Sopenharmony_ci return 0; 9248c2ecf20Sopenharmony_ci} 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ciint vidioc_s_fmt_vid_out_overlay(struct file *file, void *priv, 9278c2ecf20Sopenharmony_ci struct v4l2_format *f) 9288c2ecf20Sopenharmony_ci{ 9298c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 9308c2ecf20Sopenharmony_ci const struct v4l2_rect *compose = &dev->compose_out; 9318c2ecf20Sopenharmony_ci struct v4l2_window *win = &f->fmt.win; 9328c2ecf20Sopenharmony_ci int ret = vidioc_try_fmt_vid_out_overlay(file, priv, f); 9338c2ecf20Sopenharmony_ci unsigned bitmap_size = ((compose->width + 7) / 8) * compose->height; 9348c2ecf20Sopenharmony_ci unsigned clips_size = win->clipcount * sizeof(dev->clips_out[0]); 9358c2ecf20Sopenharmony_ci void *new_bitmap = NULL; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci if (ret) 9388c2ecf20Sopenharmony_ci return ret; 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci if (win->bitmap) { 9418c2ecf20Sopenharmony_ci new_bitmap = vzalloc(bitmap_size); 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci if (!new_bitmap) 9448c2ecf20Sopenharmony_ci return -ENOMEM; 9458c2ecf20Sopenharmony_ci if (copy_from_user(new_bitmap, win->bitmap, bitmap_size)) { 9468c2ecf20Sopenharmony_ci vfree(new_bitmap); 9478c2ecf20Sopenharmony_ci return -EFAULT; 9488c2ecf20Sopenharmony_ci } 9498c2ecf20Sopenharmony_ci } 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci dev->overlay_out_top = win->w.top; 9528c2ecf20Sopenharmony_ci dev->overlay_out_left = win->w.left; 9538c2ecf20Sopenharmony_ci vfree(dev->bitmap_out); 9548c2ecf20Sopenharmony_ci dev->bitmap_out = new_bitmap; 9558c2ecf20Sopenharmony_ci dev->clipcount_out = win->clipcount; 9568c2ecf20Sopenharmony_ci if (dev->clipcount_out) 9578c2ecf20Sopenharmony_ci memcpy(dev->clips_out, dev->try_clips_out, clips_size); 9588c2ecf20Sopenharmony_ci dev->chromakey_out = win->chromakey; 9598c2ecf20Sopenharmony_ci dev->global_alpha_out = win->global_alpha; 9608c2ecf20Sopenharmony_ci return ret; 9618c2ecf20Sopenharmony_ci} 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ciint vivid_vid_out_overlay(struct file *file, void *fh, unsigned i) 9648c2ecf20Sopenharmony_ci{ 9658c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci if (i && !dev->fmt_out->can_do_overlay) { 9688c2ecf20Sopenharmony_ci dprintk(dev, 1, "unsupported output format for output overlay\n"); 9698c2ecf20Sopenharmony_ci return -EINVAL; 9708c2ecf20Sopenharmony_ci } 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci dev->overlay_out_enabled = i; 9738c2ecf20Sopenharmony_ci return 0; 9748c2ecf20Sopenharmony_ci} 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ciint vivid_vid_out_g_fbuf(struct file *file, void *fh, 9778c2ecf20Sopenharmony_ci struct v4l2_framebuffer *a) 9788c2ecf20Sopenharmony_ci{ 9798c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci a->capability = V4L2_FBUF_CAP_EXTERNOVERLAY | 9828c2ecf20Sopenharmony_ci V4L2_FBUF_CAP_BITMAP_CLIPPING | 9838c2ecf20Sopenharmony_ci V4L2_FBUF_CAP_LIST_CLIPPING | 9848c2ecf20Sopenharmony_ci V4L2_FBUF_CAP_CHROMAKEY | 9858c2ecf20Sopenharmony_ci V4L2_FBUF_CAP_SRC_CHROMAKEY | 9868c2ecf20Sopenharmony_ci V4L2_FBUF_CAP_GLOBAL_ALPHA | 9878c2ecf20Sopenharmony_ci V4L2_FBUF_CAP_LOCAL_ALPHA | 9888c2ecf20Sopenharmony_ci V4L2_FBUF_CAP_LOCAL_INV_ALPHA; 9898c2ecf20Sopenharmony_ci a->flags = V4L2_FBUF_FLAG_OVERLAY | dev->fbuf_out_flags; 9908c2ecf20Sopenharmony_ci a->base = (void *)dev->video_pbase; 9918c2ecf20Sopenharmony_ci a->fmt.width = dev->display_width; 9928c2ecf20Sopenharmony_ci a->fmt.height = dev->display_height; 9938c2ecf20Sopenharmony_ci if (dev->fb_defined.green.length == 5) 9948c2ecf20Sopenharmony_ci a->fmt.pixelformat = V4L2_PIX_FMT_ARGB555; 9958c2ecf20Sopenharmony_ci else 9968c2ecf20Sopenharmony_ci a->fmt.pixelformat = V4L2_PIX_FMT_RGB565; 9978c2ecf20Sopenharmony_ci a->fmt.bytesperline = dev->display_byte_stride; 9988c2ecf20Sopenharmony_ci a->fmt.sizeimage = a->fmt.height * a->fmt.bytesperline; 9998c2ecf20Sopenharmony_ci a->fmt.field = V4L2_FIELD_NONE; 10008c2ecf20Sopenharmony_ci a->fmt.colorspace = V4L2_COLORSPACE_SRGB; 10018c2ecf20Sopenharmony_ci a->fmt.priv = 0; 10028c2ecf20Sopenharmony_ci return 0; 10038c2ecf20Sopenharmony_ci} 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ciint vivid_vid_out_s_fbuf(struct file *file, void *fh, 10068c2ecf20Sopenharmony_ci const struct v4l2_framebuffer *a) 10078c2ecf20Sopenharmony_ci{ 10088c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 10098c2ecf20Sopenharmony_ci const unsigned chroma_flags = V4L2_FBUF_FLAG_CHROMAKEY | 10108c2ecf20Sopenharmony_ci V4L2_FBUF_FLAG_SRC_CHROMAKEY; 10118c2ecf20Sopenharmony_ci const unsigned alpha_flags = V4L2_FBUF_FLAG_GLOBAL_ALPHA | 10128c2ecf20Sopenharmony_ci V4L2_FBUF_FLAG_LOCAL_ALPHA | 10138c2ecf20Sopenharmony_ci V4L2_FBUF_FLAG_LOCAL_INV_ALPHA; 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci if ((a->flags & chroma_flags) == chroma_flags) 10178c2ecf20Sopenharmony_ci return -EINVAL; 10188c2ecf20Sopenharmony_ci switch (a->flags & alpha_flags) { 10198c2ecf20Sopenharmony_ci case 0: 10208c2ecf20Sopenharmony_ci case V4L2_FBUF_FLAG_GLOBAL_ALPHA: 10218c2ecf20Sopenharmony_ci case V4L2_FBUF_FLAG_LOCAL_ALPHA: 10228c2ecf20Sopenharmony_ci case V4L2_FBUF_FLAG_LOCAL_INV_ALPHA: 10238c2ecf20Sopenharmony_ci break; 10248c2ecf20Sopenharmony_ci default: 10258c2ecf20Sopenharmony_ci return -EINVAL; 10268c2ecf20Sopenharmony_ci } 10278c2ecf20Sopenharmony_ci dev->fbuf_out_flags &= ~(chroma_flags | alpha_flags); 10288c2ecf20Sopenharmony_ci dev->fbuf_out_flags |= a->flags & (chroma_flags | alpha_flags); 10298c2ecf20Sopenharmony_ci return 0; 10308c2ecf20Sopenharmony_ci} 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_cistatic const struct v4l2_audioout vivid_audio_outputs[] = { 10338c2ecf20Sopenharmony_ci { 0, "Line-Out 1" }, 10348c2ecf20Sopenharmony_ci { 1, "Line-Out 2" }, 10358c2ecf20Sopenharmony_ci}; 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ciint vidioc_enum_output(struct file *file, void *priv, 10388c2ecf20Sopenharmony_ci struct v4l2_output *out) 10398c2ecf20Sopenharmony_ci{ 10408c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci if (out->index >= dev->num_outputs) 10438c2ecf20Sopenharmony_ci return -EINVAL; 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci out->type = V4L2_OUTPUT_TYPE_ANALOG; 10468c2ecf20Sopenharmony_ci switch (dev->output_type[out->index]) { 10478c2ecf20Sopenharmony_ci case SVID: 10488c2ecf20Sopenharmony_ci snprintf(out->name, sizeof(out->name), "S-Video %u", 10498c2ecf20Sopenharmony_ci dev->output_name_counter[out->index]); 10508c2ecf20Sopenharmony_ci out->std = V4L2_STD_ALL; 10518c2ecf20Sopenharmony_ci if (dev->has_audio_outputs) 10528c2ecf20Sopenharmony_ci out->audioset = (1 << ARRAY_SIZE(vivid_audio_outputs)) - 1; 10538c2ecf20Sopenharmony_ci out->capabilities = V4L2_OUT_CAP_STD; 10548c2ecf20Sopenharmony_ci break; 10558c2ecf20Sopenharmony_ci case HDMI: 10568c2ecf20Sopenharmony_ci snprintf(out->name, sizeof(out->name), "HDMI %u", 10578c2ecf20Sopenharmony_ci dev->output_name_counter[out->index]); 10588c2ecf20Sopenharmony_ci out->capabilities = V4L2_OUT_CAP_DV_TIMINGS; 10598c2ecf20Sopenharmony_ci break; 10608c2ecf20Sopenharmony_ci } 10618c2ecf20Sopenharmony_ci return 0; 10628c2ecf20Sopenharmony_ci} 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ciint vidioc_g_output(struct file *file, void *priv, unsigned *o) 10658c2ecf20Sopenharmony_ci{ 10668c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci *o = dev->output; 10698c2ecf20Sopenharmony_ci return 0; 10708c2ecf20Sopenharmony_ci} 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ciint vidioc_s_output(struct file *file, void *priv, unsigned o) 10738c2ecf20Sopenharmony_ci{ 10748c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci if (o >= dev->num_outputs) 10778c2ecf20Sopenharmony_ci return -EINVAL; 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci if (o == dev->output) 10808c2ecf20Sopenharmony_ci return 0; 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci if (vb2_is_busy(&dev->vb_vid_out_q) || 10838c2ecf20Sopenharmony_ci vb2_is_busy(&dev->vb_vbi_out_q) || 10848c2ecf20Sopenharmony_ci vb2_is_busy(&dev->vb_meta_out_q)) 10858c2ecf20Sopenharmony_ci return -EBUSY; 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci dev->output = o; 10888c2ecf20Sopenharmony_ci dev->tv_audio_output = 0; 10898c2ecf20Sopenharmony_ci if (dev->output_type[o] == SVID) 10908c2ecf20Sopenharmony_ci dev->vid_out_dev.tvnorms = V4L2_STD_ALL; 10918c2ecf20Sopenharmony_ci else 10928c2ecf20Sopenharmony_ci dev->vid_out_dev.tvnorms = 0; 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci dev->vbi_out_dev.tvnorms = dev->vid_out_dev.tvnorms; 10958c2ecf20Sopenharmony_ci dev->meta_out_dev.tvnorms = dev->vid_out_dev.tvnorms; 10968c2ecf20Sopenharmony_ci vivid_update_format_out(dev); 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci v4l2_ctrl_activate(dev->ctrl_display_present, vivid_is_hdmi_out(dev)); 10998c2ecf20Sopenharmony_ci if (vivid_is_hdmi_out(dev)) 11008c2ecf20Sopenharmony_ci v4l2_ctrl_s_ctrl(dev->ctrl_display_present, 11018c2ecf20Sopenharmony_ci dev->display_present[dev->output]); 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci return 0; 11048c2ecf20Sopenharmony_ci} 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ciint vidioc_enumaudout(struct file *file, void *fh, struct v4l2_audioout *vout) 11078c2ecf20Sopenharmony_ci{ 11088c2ecf20Sopenharmony_ci if (vout->index >= ARRAY_SIZE(vivid_audio_outputs)) 11098c2ecf20Sopenharmony_ci return -EINVAL; 11108c2ecf20Sopenharmony_ci *vout = vivid_audio_outputs[vout->index]; 11118c2ecf20Sopenharmony_ci return 0; 11128c2ecf20Sopenharmony_ci} 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ciint vidioc_g_audout(struct file *file, void *fh, struct v4l2_audioout *vout) 11158c2ecf20Sopenharmony_ci{ 11168c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci if (!vivid_is_svid_out(dev)) 11198c2ecf20Sopenharmony_ci return -EINVAL; 11208c2ecf20Sopenharmony_ci *vout = vivid_audio_outputs[dev->tv_audio_output]; 11218c2ecf20Sopenharmony_ci return 0; 11228c2ecf20Sopenharmony_ci} 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ciint vidioc_s_audout(struct file *file, void *fh, const struct v4l2_audioout *vout) 11258c2ecf20Sopenharmony_ci{ 11268c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci if (!vivid_is_svid_out(dev)) 11298c2ecf20Sopenharmony_ci return -EINVAL; 11308c2ecf20Sopenharmony_ci if (vout->index >= ARRAY_SIZE(vivid_audio_outputs)) 11318c2ecf20Sopenharmony_ci return -EINVAL; 11328c2ecf20Sopenharmony_ci dev->tv_audio_output = vout->index; 11338c2ecf20Sopenharmony_ci return 0; 11348c2ecf20Sopenharmony_ci} 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ciint vivid_vid_out_s_std(struct file *file, void *priv, v4l2_std_id id) 11378c2ecf20Sopenharmony_ci{ 11388c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci if (!vivid_is_svid_out(dev)) 11418c2ecf20Sopenharmony_ci return -ENODATA; 11428c2ecf20Sopenharmony_ci if (dev->std_out == id) 11438c2ecf20Sopenharmony_ci return 0; 11448c2ecf20Sopenharmony_ci if (vb2_is_busy(&dev->vb_vid_out_q) || vb2_is_busy(&dev->vb_vbi_out_q)) 11458c2ecf20Sopenharmony_ci return -EBUSY; 11468c2ecf20Sopenharmony_ci dev->std_out = id; 11478c2ecf20Sopenharmony_ci vivid_update_format_out(dev); 11488c2ecf20Sopenharmony_ci return 0; 11498c2ecf20Sopenharmony_ci} 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_cistatic bool valid_cvt_gtf_timings(struct v4l2_dv_timings *timings) 11528c2ecf20Sopenharmony_ci{ 11538c2ecf20Sopenharmony_ci struct v4l2_bt_timings *bt = &timings->bt; 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci if ((bt->standards & (V4L2_DV_BT_STD_CVT | V4L2_DV_BT_STD_GTF)) && 11568c2ecf20Sopenharmony_ci v4l2_valid_dv_timings(timings, &vivid_dv_timings_cap, NULL, NULL)) 11578c2ecf20Sopenharmony_ci return true; 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci return false; 11608c2ecf20Sopenharmony_ci} 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ciint vivid_vid_out_s_dv_timings(struct file *file, void *_fh, 11638c2ecf20Sopenharmony_ci struct v4l2_dv_timings *timings) 11648c2ecf20Sopenharmony_ci{ 11658c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 11668c2ecf20Sopenharmony_ci if (!vivid_is_hdmi_out(dev)) 11678c2ecf20Sopenharmony_ci return -ENODATA; 11688c2ecf20Sopenharmony_ci if (!v4l2_find_dv_timings_cap(timings, &vivid_dv_timings_cap, 11698c2ecf20Sopenharmony_ci 0, NULL, NULL) && 11708c2ecf20Sopenharmony_ci !valid_cvt_gtf_timings(timings)) 11718c2ecf20Sopenharmony_ci return -EINVAL; 11728c2ecf20Sopenharmony_ci if (v4l2_match_dv_timings(timings, &dev->dv_timings_out, 0, true)) 11738c2ecf20Sopenharmony_ci return 0; 11748c2ecf20Sopenharmony_ci if (vb2_is_busy(&dev->vb_vid_out_q)) 11758c2ecf20Sopenharmony_ci return -EBUSY; 11768c2ecf20Sopenharmony_ci dev->dv_timings_out = *timings; 11778c2ecf20Sopenharmony_ci vivid_update_format_out(dev); 11788c2ecf20Sopenharmony_ci return 0; 11798c2ecf20Sopenharmony_ci} 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ciint vivid_vid_out_g_parm(struct file *file, void *priv, 11828c2ecf20Sopenharmony_ci struct v4l2_streamparm *parm) 11838c2ecf20Sopenharmony_ci{ 11848c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci if (parm->type != (dev->multiplanar ? 11878c2ecf20Sopenharmony_ci V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE : 11888c2ecf20Sopenharmony_ci V4L2_BUF_TYPE_VIDEO_OUTPUT)) 11898c2ecf20Sopenharmony_ci return -EINVAL; 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci parm->parm.output.capability = V4L2_CAP_TIMEPERFRAME; 11928c2ecf20Sopenharmony_ci parm->parm.output.timeperframe = dev->timeperframe_vid_out; 11938c2ecf20Sopenharmony_ci parm->parm.output.writebuffers = 1; 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci return 0; 11968c2ecf20Sopenharmony_ci} 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ciint vidioc_subscribe_event(struct v4l2_fh *fh, 11998c2ecf20Sopenharmony_ci const struct v4l2_event_subscription *sub) 12008c2ecf20Sopenharmony_ci{ 12018c2ecf20Sopenharmony_ci switch (sub->type) { 12028c2ecf20Sopenharmony_ci case V4L2_EVENT_SOURCE_CHANGE: 12038c2ecf20Sopenharmony_ci if (fh->vdev->vfl_dir == VFL_DIR_RX) 12048c2ecf20Sopenharmony_ci return v4l2_src_change_event_subscribe(fh, sub); 12058c2ecf20Sopenharmony_ci break; 12068c2ecf20Sopenharmony_ci default: 12078c2ecf20Sopenharmony_ci return v4l2_ctrl_subscribe_event(fh, sub); 12088c2ecf20Sopenharmony_ci } 12098c2ecf20Sopenharmony_ci return -EINVAL; 12108c2ecf20Sopenharmony_ci} 1211