1125 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * vivid-vid-out.c - video output support functions.4 *5 * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.6 */7 8#include <linux/errno.h>9#include <linux/kernel.h>10#include <linux/sched.h>11#include <linux/videodev2.h>12#include <linux/v4l2-dv-timings.h>13#include <media/v4l2-common.h>14#include <media/v4l2-event.h>15#include <media/v4l2-dv-timings.h>16#include <media/v4l2-rect.h>17 18#include "vivid-core.h"19#include "vivid-vid-common.h"20#include "vivid-kthread-out.h"21#include "vivid-vid-out.h"22 23static int vid_out_queue_setup(struct vb2_queue *vq,24 unsigned *nbuffers, unsigned *nplanes,25 unsigned sizes[], struct device *alloc_devs[])26{27 struct vivid_dev *dev = vb2_get_drv_priv(vq);28 const struct vivid_fmt *vfmt = dev->fmt_out;29 unsigned planes = vfmt->buffers;30 unsigned h = dev->fmt_out_rect.height;31 unsigned int size = dev->bytesperline_out[0] * h + vfmt->data_offset[0];32 unsigned p;33 34 for (p = vfmt->buffers; p < vfmt->planes; p++)35 size += dev->bytesperline_out[p] * h / vfmt->vdownsampling[p] +36 vfmt->data_offset[p];37 38 if (dev->field_out == V4L2_FIELD_ALTERNATE) {39 /*40 * You cannot use write() with FIELD_ALTERNATE since the field41 * information (TOP/BOTTOM) cannot be passed to the kernel.42 */43 if (vb2_fileio_is_active(vq))44 return -EINVAL;45 }46 47 if (dev->queue_setup_error) {48 /*49 * Error injection: test what happens if queue_setup() returns50 * an error.51 */52 dev->queue_setup_error = false;53 return -EINVAL;54 }55 56 if (*nplanes) {57 /*58 * Check if the number of requested planes match59 * the number of planes in the current format. You can't mix that.60 */61 if (*nplanes != planes)62 return -EINVAL;63 if (sizes[0] < size)64 return -EINVAL;65 for (p = 1; p < planes; p++) {66 if (sizes[p] < dev->bytesperline_out[p] * h /67 vfmt->vdownsampling[p] +68 vfmt->data_offset[p])69 return -EINVAL;70 }71 } else {72 for (p = 0; p < planes; p++)73 sizes[p] = p ? dev->bytesperline_out[p] * h /74 vfmt->vdownsampling[p] +75 vfmt->data_offset[p] : size;76 }77 78 *nplanes = planes;79 80 dprintk(dev, 1, "%s: count=%u\n", __func__, *nbuffers);81 for (p = 0; p < planes; p++)82 dprintk(dev, 1, "%s: size[%u]=%u\n", __func__, p, sizes[p]);83 return 0;84}85 86static int vid_out_buf_out_validate(struct vb2_buffer *vb)87{88 struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);89 struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);90 91 dprintk(dev, 1, "%s\n", __func__);92 93 if (dev->field_out != V4L2_FIELD_ALTERNATE)94 vbuf->field = dev->field_out;95 else if (vbuf->field != V4L2_FIELD_TOP &&96 vbuf->field != V4L2_FIELD_BOTTOM)97 return -EINVAL;98 return 0;99}100 101static int vid_out_buf_prepare(struct vb2_buffer *vb)102{103 struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);104 const struct vivid_fmt *vfmt = dev->fmt_out;105 unsigned int planes = vfmt->buffers;106 unsigned int h = dev->fmt_out_rect.height;107 unsigned int size = dev->bytesperline_out[0] * h;108 unsigned p;109 110 for (p = vfmt->buffers; p < vfmt->planes; p++)111 size += dev->bytesperline_out[p] * h / vfmt->vdownsampling[p];112 113 dprintk(dev, 1, "%s\n", __func__);114 115 if (WARN_ON(NULL == dev->fmt_out))116 return -EINVAL;117 118 if (dev->buf_prepare_error) {119 /*120 * Error injection: test what happens if buf_prepare() returns121 * an error.122 */123 dev->buf_prepare_error = false;124 return -EINVAL;125 }126 127 for (p = 0; p < planes; p++) {128 if (p)129 size = dev->bytesperline_out[p] * h / vfmt->vdownsampling[p];130 size += vb->planes[p].data_offset;131 132 if (vb2_get_plane_payload(vb, p) < size) {133 dprintk(dev, 1, "%s the payload is too small for plane %u (%lu < %u)\n",134 __func__, p, vb2_get_plane_payload(vb, p), size);135 return -EINVAL;136 }137 }138 139 return 0;140}141 142static void vid_out_buf_queue(struct vb2_buffer *vb)143{144 struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);145 struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);146 struct vivid_buffer *buf = container_of(vbuf, struct vivid_buffer, vb);147 148 dprintk(dev, 1, "%s\n", __func__);149 150 spin_lock(&dev->slock);151 list_add_tail(&buf->list, &dev->vid_out_active);152 spin_unlock(&dev->slock);153}154 155static int vid_out_start_streaming(struct vb2_queue *vq, unsigned count)156{157 struct vivid_dev *dev = vb2_get_drv_priv(vq);158 int err;159 160 dev->vid_out_seq_count = 0;161 dprintk(dev, 1, "%s\n", __func__);162 if (dev->start_streaming_error) {163 dev->start_streaming_error = false;164 err = -EINVAL;165 } else {166 err = vivid_start_generating_vid_out(dev, &dev->vid_out_streaming);167 }168 if (err) {169 struct vivid_buffer *buf, *tmp;170 171 list_for_each_entry_safe(buf, tmp, &dev->vid_out_active, list) {172 list_del(&buf->list);173 vb2_buffer_done(&buf->vb.vb2_buf,174 VB2_BUF_STATE_QUEUED);175 }176 }177 return err;178}179 180/* abort streaming and wait for last buffer */181static void vid_out_stop_streaming(struct vb2_queue *vq)182{183 struct vivid_dev *dev = vb2_get_drv_priv(vq);184 185 dprintk(dev, 1, "%s\n", __func__);186 vivid_stop_generating_vid_out(dev, &dev->vid_out_streaming);187}188 189static void vid_out_buf_request_complete(struct vb2_buffer *vb)190{191 struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);192 193 v4l2_ctrl_request_complete(vb->req_obj.req, &dev->ctrl_hdl_vid_out);194}195 196const struct vb2_ops vivid_vid_out_qops = {197 .queue_setup = vid_out_queue_setup,198 .buf_out_validate = vid_out_buf_out_validate,199 .buf_prepare = vid_out_buf_prepare,200 .buf_queue = vid_out_buf_queue,201 .start_streaming = vid_out_start_streaming,202 .stop_streaming = vid_out_stop_streaming,203 .buf_request_complete = vid_out_buf_request_complete,204 .wait_prepare = vb2_ops_wait_prepare,205 .wait_finish = vb2_ops_wait_finish,206};207 208/*209 * Called whenever the format has to be reset which can occur when210 * changing outputs, standard, timings, etc.211 */212void vivid_update_format_out(struct vivid_dev *dev)213{214 struct v4l2_bt_timings *bt = &dev->dv_timings_out.bt;215 unsigned size, p;216 u64 pixelclock;217 218 switch (dev->output_type[dev->output]) {219 case SVID:220 default:221 dev->field_out = dev->tv_field_out;222 dev->sink_rect.width = 720;223 if (dev->std_out & V4L2_STD_525_60) {224 dev->sink_rect.height = 480;225 dev->timeperframe_vid_out = (struct v4l2_fract) { 1001, 30000 };226 dev->service_set_out = V4L2_SLICED_CAPTION_525;227 } else {228 dev->sink_rect.height = 576;229 dev->timeperframe_vid_out = (struct v4l2_fract) { 1000, 25000 };230 dev->service_set_out = V4L2_SLICED_WSS_625 | V4L2_SLICED_TELETEXT_B;231 }232 dev->colorspace_out = V4L2_COLORSPACE_SMPTE170M;233 break;234 case HDMI:235 dev->sink_rect.width = bt->width;236 dev->sink_rect.height = bt->height;237 size = V4L2_DV_BT_FRAME_WIDTH(bt) * V4L2_DV_BT_FRAME_HEIGHT(bt);238 239 if (can_reduce_fps(bt) && (bt->flags & V4L2_DV_FL_REDUCED_FPS))240 pixelclock = div_u64(bt->pixelclock * 1000, 1001);241 else242 pixelclock = bt->pixelclock;243 244 dev->timeperframe_vid_out = (struct v4l2_fract) {245 size / 100, (u32)pixelclock / 100246 };247 if (bt->interlaced)248 dev->field_out = V4L2_FIELD_ALTERNATE;249 else250 dev->field_out = V4L2_FIELD_NONE;251 if (!dev->dvi_d_out && (bt->flags & V4L2_DV_FL_IS_CE_VIDEO)) {252 if (bt->width == 720 && bt->height <= 576)253 dev->colorspace_out = V4L2_COLORSPACE_SMPTE170M;254 else255 dev->colorspace_out = V4L2_COLORSPACE_REC709;256 } else {257 dev->colorspace_out = V4L2_COLORSPACE_SRGB;258 }259 break;260 }261 dev->xfer_func_out = V4L2_XFER_FUNC_DEFAULT;262 dev->ycbcr_enc_out = V4L2_YCBCR_ENC_DEFAULT;263 dev->hsv_enc_out = V4L2_HSV_ENC_180;264 dev->quantization_out = V4L2_QUANTIZATION_DEFAULT;265 dev->compose_out = dev->sink_rect;266 dev->compose_bounds_out = dev->sink_rect;267 dev->crop_out = dev->compose_out;268 if (V4L2_FIELD_HAS_T_OR_B(dev->field_out))269 dev->crop_out.height /= 2;270 dev->fmt_out_rect = dev->crop_out;271 for (p = 0; p < dev->fmt_out->planes; p++)272 dev->bytesperline_out[p] =273 (dev->sink_rect.width * dev->fmt_out->bit_depth[p]) / 8;274}275 276/* Map the field to something that is valid for the current output */277static enum v4l2_field vivid_field_out(struct vivid_dev *dev, enum v4l2_field field)278{279 if (vivid_is_svid_out(dev)) {280 switch (field) {281 case V4L2_FIELD_INTERLACED_TB:282 case V4L2_FIELD_INTERLACED_BT:283 case V4L2_FIELD_SEQ_TB:284 case V4L2_FIELD_SEQ_BT:285 case V4L2_FIELD_ALTERNATE:286 return field;287 case V4L2_FIELD_INTERLACED:288 default:289 return V4L2_FIELD_INTERLACED;290 }291 }292 if (vivid_is_hdmi_out(dev))293 return dev->dv_timings_out.bt.interlaced ? V4L2_FIELD_ALTERNATE :294 V4L2_FIELD_NONE;295 return V4L2_FIELD_NONE;296}297 298static enum tpg_pixel_aspect vivid_get_pixel_aspect(const struct vivid_dev *dev)299{300 if (vivid_is_svid_out(dev))301 return (dev->std_out & V4L2_STD_525_60) ?302 TPG_PIXEL_ASPECT_NTSC : TPG_PIXEL_ASPECT_PAL;303 304 if (vivid_is_hdmi_out(dev) &&305 dev->sink_rect.width == 720 && dev->sink_rect.height <= 576)306 return dev->sink_rect.height == 480 ?307 TPG_PIXEL_ASPECT_NTSC : TPG_PIXEL_ASPECT_PAL;308 309 return TPG_PIXEL_ASPECT_SQUARE;310}311 312int vivid_g_fmt_vid_out(struct file *file, void *priv,313 struct v4l2_format *f)314{315 struct vivid_dev *dev = video_drvdata(file);316 struct v4l2_pix_format_mplane *mp = &f->fmt.pix_mp;317 const struct vivid_fmt *fmt = dev->fmt_out;318 unsigned p;319 320 mp->width = dev->fmt_out_rect.width;321 mp->height = dev->fmt_out_rect.height;322 mp->field = dev->field_out;323 mp->pixelformat = fmt->fourcc;324 mp->colorspace = dev->colorspace_out;325 mp->xfer_func = dev->xfer_func_out;326 mp->ycbcr_enc = dev->ycbcr_enc_out;327 mp->quantization = dev->quantization_out;328 mp->num_planes = fmt->buffers;329 for (p = 0; p < mp->num_planes; p++) {330 mp->plane_fmt[p].bytesperline = dev->bytesperline_out[p];331 mp->plane_fmt[p].sizeimage =332 mp->plane_fmt[p].bytesperline * mp->height /333 fmt->vdownsampling[p] + fmt->data_offset[p];334 }335 for (p = fmt->buffers; p < fmt->planes; p++) {336 unsigned stride = dev->bytesperline_out[p];337 338 mp->plane_fmt[0].sizeimage +=339 (stride * mp->height) / fmt->vdownsampling[p];340 }341 return 0;342}343 344int vivid_try_fmt_vid_out(struct file *file, void *priv,345 struct v4l2_format *f)346{347 struct vivid_dev *dev = video_drvdata(file);348 struct v4l2_bt_timings *bt = &dev->dv_timings_out.bt;349 struct v4l2_pix_format_mplane *mp = &f->fmt.pix_mp;350 struct v4l2_plane_pix_format *pfmt = mp->plane_fmt;351 const struct vivid_fmt *fmt;352 unsigned bytesperline, max_bpl;353 unsigned factor = 1;354 unsigned w, h;355 unsigned p;356 357 fmt = vivid_get_format(dev, mp->pixelformat);358 if (!fmt) {359 dprintk(dev, 1, "Fourcc format (0x%08x) unknown.\n",360 mp->pixelformat);361 mp->pixelformat = V4L2_PIX_FMT_YUYV;362 fmt = vivid_get_format(dev, mp->pixelformat);363 }364 365 mp->field = vivid_field_out(dev, mp->field);366 if (vivid_is_svid_out(dev)) {367 w = 720;368 h = (dev->std_out & V4L2_STD_525_60) ? 480 : 576;369 } else {370 w = dev->sink_rect.width;371 h = dev->sink_rect.height;372 }373 if (V4L2_FIELD_HAS_T_OR_B(mp->field))374 factor = 2;375 if (!dev->has_scaler_out && !dev->has_crop_out && !dev->has_compose_out) {376 mp->width = w;377 mp->height = h / factor;378 } else {379 struct v4l2_rect r = { 0, 0, mp->width, mp->height * factor };380 381 v4l2_rect_set_min_size(&r, &vivid_min_rect);382 v4l2_rect_set_max_size(&r, &vivid_max_rect);383 if (dev->has_scaler_out && !dev->has_crop_out) {384 struct v4l2_rect max_r = { 0, 0, MAX_ZOOM * w, MAX_ZOOM * h };385 386 v4l2_rect_set_max_size(&r, &max_r);387 } else if (!dev->has_scaler_out && dev->has_compose_out && !dev->has_crop_out) {388 v4l2_rect_set_max_size(&r, &dev->sink_rect);389 } else if (!dev->has_scaler_out && !dev->has_compose_out) {390 v4l2_rect_set_min_size(&r, &dev->sink_rect);391 }392 mp->width = r.width;393 mp->height = r.height / factor;394 }395 396 /* This driver supports custom bytesperline values */397 398 mp->num_planes = fmt->buffers;399 for (p = 0; p < fmt->buffers; p++) {400 /* Calculate the minimum supported bytesperline value */401 bytesperline = (mp->width * fmt->bit_depth[p]) >> 3;402 /* Calculate the maximum supported bytesperline value */403 max_bpl = (MAX_ZOOM * MAX_WIDTH * fmt->bit_depth[p]) >> 3;404 405 if (pfmt[p].bytesperline > max_bpl)406 pfmt[p].bytesperline = max_bpl;407 if (pfmt[p].bytesperline < bytesperline)408 pfmt[p].bytesperline = bytesperline;409 410 pfmt[p].sizeimage = (pfmt[p].bytesperline * mp->height) /411 fmt->vdownsampling[p] + fmt->data_offset[p];412 413 memset(pfmt[p].reserved, 0, sizeof(pfmt[p].reserved));414 }415 for (p = fmt->buffers; p < fmt->planes; p++)416 pfmt[0].sizeimage += (pfmt[0].bytesperline * mp->height *417 (fmt->bit_depth[p] / fmt->vdownsampling[p])) /418 (fmt->bit_depth[0] / fmt->vdownsampling[0]);419 420 mp->xfer_func = V4L2_XFER_FUNC_DEFAULT;421 mp->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;422 mp->quantization = V4L2_QUANTIZATION_DEFAULT;423 if (vivid_is_svid_out(dev)) {424 mp->colorspace = V4L2_COLORSPACE_SMPTE170M;425 } else if (dev->dvi_d_out || !(bt->flags & V4L2_DV_FL_IS_CE_VIDEO)) {426 mp->colorspace = V4L2_COLORSPACE_SRGB;427 if (dev->dvi_d_out)428 mp->quantization = V4L2_QUANTIZATION_LIM_RANGE;429 } else if (bt->width == 720 && bt->height <= 576) {430 mp->colorspace = V4L2_COLORSPACE_SMPTE170M;431 } else if (mp->colorspace != V4L2_COLORSPACE_SMPTE170M &&432 mp->colorspace != V4L2_COLORSPACE_REC709 &&433 mp->colorspace != V4L2_COLORSPACE_OPRGB &&434 mp->colorspace != V4L2_COLORSPACE_BT2020 &&435 mp->colorspace != V4L2_COLORSPACE_SRGB) {436 mp->colorspace = V4L2_COLORSPACE_REC709;437 }438 memset(mp->reserved, 0, sizeof(mp->reserved));439 return 0;440}441 442int vivid_s_fmt_vid_out(struct file *file, void *priv,443 struct v4l2_format *f)444{445 struct v4l2_pix_format_mplane *mp = &f->fmt.pix_mp;446 struct vivid_dev *dev = video_drvdata(file);447 struct v4l2_rect *crop = &dev->crop_out;448 struct v4l2_rect *compose = &dev->compose_out;449 struct vb2_queue *q = &dev->vb_vid_out_q;450 int ret = vivid_try_fmt_vid_out(file, priv, f);451 unsigned factor = 1;452 unsigned p;453 454 if (ret < 0)455 return ret;456 457 if (vb2_is_busy(q) &&458 (vivid_is_svid_out(dev) ||459 mp->width != dev->fmt_out_rect.width ||460 mp->height != dev->fmt_out_rect.height ||461 mp->pixelformat != dev->fmt_out->fourcc ||462 mp->field != dev->field_out)) {463 dprintk(dev, 1, "%s device busy\n", __func__);464 return -EBUSY;465 }466 467 /*468 * Allow for changing the colorspace on the fly. Useful for testing469 * purposes, and it is something that HDMI transmitters are able470 * to do.471 */472 if (vb2_is_busy(q))473 goto set_colorspace;474 475 dev->fmt_out = vivid_get_format(dev, mp->pixelformat);476 if (V4L2_FIELD_HAS_T_OR_B(mp->field))477 factor = 2;478 479 if (dev->has_scaler_out || dev->has_crop_out || dev->has_compose_out) {480 struct v4l2_rect r = { 0, 0, mp->width, mp->height };481 482 if (dev->has_scaler_out) {483 if (dev->has_crop_out)484 v4l2_rect_map_inside(crop, &r);485 else486 *crop = r;487 if (dev->has_compose_out && !dev->has_crop_out) {488 struct v4l2_rect min_r = {489 0, 0,490 r.width / MAX_ZOOM,491 factor * r.height / MAX_ZOOM492 };493 struct v4l2_rect max_r = {494 0, 0,495 r.width * MAX_ZOOM,496 factor * r.height * MAX_ZOOM497 };498 499 v4l2_rect_set_min_size(compose, &min_r);500 v4l2_rect_set_max_size(compose, &max_r);501 v4l2_rect_map_inside(compose, &dev->compose_bounds_out);502 } else if (dev->has_compose_out) {503 struct v4l2_rect min_r = {504 0, 0,505 crop->width / MAX_ZOOM,506 factor * crop->height / MAX_ZOOM507 };508 struct v4l2_rect max_r = {509 0, 0,510 crop->width * MAX_ZOOM,511 factor * crop->height * MAX_ZOOM512 };513 514 v4l2_rect_set_min_size(compose, &min_r);515 v4l2_rect_set_max_size(compose, &max_r);516 v4l2_rect_map_inside(compose, &dev->compose_bounds_out);517 }518 } else if (dev->has_compose_out && !dev->has_crop_out) {519 v4l2_rect_set_size_to(crop, &r);520 r.height *= factor;521 v4l2_rect_set_size_to(compose, &r);522 v4l2_rect_map_inside(compose, &dev->compose_bounds_out);523 } else if (!dev->has_compose_out) {524 v4l2_rect_map_inside(crop, &r);525 r.height /= factor;526 v4l2_rect_set_size_to(compose, &r);527 } else {528 r.height *= factor;529 v4l2_rect_set_max_size(compose, &r);530 v4l2_rect_map_inside(compose, &dev->compose_bounds_out);531 crop->top *= factor;532 crop->height *= factor;533 v4l2_rect_set_size_to(crop, compose);534 v4l2_rect_map_inside(crop, &r);535 crop->top /= factor;536 crop->height /= factor;537 }538 } else {539 struct v4l2_rect r = { 0, 0, mp->width, mp->height };540 541 v4l2_rect_set_size_to(crop, &r);542 r.height /= factor;543 v4l2_rect_set_size_to(compose, &r);544 }545 546 dev->fmt_out_rect.width = mp->width;547 dev->fmt_out_rect.height = mp->height;548 for (p = 0; p < mp->num_planes; p++)549 dev->bytesperline_out[p] = mp->plane_fmt[p].bytesperline;550 for (p = dev->fmt_out->buffers; p < dev->fmt_out->planes; p++)551 dev->bytesperline_out[p] =552 (dev->bytesperline_out[0] * dev->fmt_out->bit_depth[p]) /553 dev->fmt_out->bit_depth[0];554 dev->field_out = mp->field;555 if (vivid_is_svid_out(dev))556 dev->tv_field_out = mp->field;557 558set_colorspace:559 dev->colorspace_out = mp->colorspace;560 dev->xfer_func_out = mp->xfer_func;561 dev->ycbcr_enc_out = mp->ycbcr_enc;562 dev->quantization_out = mp->quantization;563 struct vivid_dev *in_dev = vivid_output_is_connected_to(dev);564 565 if (in_dev) {566 vivid_send_source_change(in_dev, SVID);567 vivid_send_source_change(in_dev, HDMI);568 }569 return 0;570}571 572int vidioc_g_fmt_vid_out_mplane(struct file *file, void *priv,573 struct v4l2_format *f)574{575 struct vivid_dev *dev = video_drvdata(file);576 577 if (!dev->multiplanar)578 return -ENOTTY;579 return vivid_g_fmt_vid_out(file, priv, f);580}581 582int vidioc_try_fmt_vid_out_mplane(struct file *file, void *priv,583 struct v4l2_format *f)584{585 struct vivid_dev *dev = video_drvdata(file);586 587 if (!dev->multiplanar)588 return -ENOTTY;589 return vivid_try_fmt_vid_out(file, priv, f);590}591 592int vidioc_s_fmt_vid_out_mplane(struct file *file, void *priv,593 struct v4l2_format *f)594{595 struct vivid_dev *dev = video_drvdata(file);596 597 if (!dev->multiplanar)598 return -ENOTTY;599 return vivid_s_fmt_vid_out(file, priv, f);600}601 602int vidioc_g_fmt_vid_out(struct file *file, void *priv,603 struct v4l2_format *f)604{605 struct vivid_dev *dev = video_drvdata(file);606 607 if (dev->multiplanar)608 return -ENOTTY;609 return fmt_sp2mp_func(file, priv, f, vivid_g_fmt_vid_out);610}611 612int vidioc_try_fmt_vid_out(struct file *file, void *priv,613 struct v4l2_format *f)614{615 struct vivid_dev *dev = video_drvdata(file);616 617 if (dev->multiplanar)618 return -ENOTTY;619 return fmt_sp2mp_func(file, priv, f, vivid_try_fmt_vid_out);620}621 622int vidioc_s_fmt_vid_out(struct file *file, void *priv,623 struct v4l2_format *f)624{625 struct vivid_dev *dev = video_drvdata(file);626 627 if (dev->multiplanar)628 return -ENOTTY;629 return fmt_sp2mp_func(file, priv, f, vivid_s_fmt_vid_out);630}631 632int vivid_vid_out_g_selection(struct file *file, void *priv,633 struct v4l2_selection *sel)634{635 struct vivid_dev *dev = video_drvdata(file);636 637 if (!dev->has_crop_out && !dev->has_compose_out)638 return -ENOTTY;639 if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)640 return -EINVAL;641 642 sel->r.left = sel->r.top = 0;643 switch (sel->target) {644 case V4L2_SEL_TGT_CROP:645 if (!dev->has_crop_out)646 return -EINVAL;647 sel->r = dev->crop_out;648 break;649 case V4L2_SEL_TGT_CROP_DEFAULT:650 if (!dev->has_crop_out)651 return -EINVAL;652 sel->r = dev->fmt_out_rect;653 break;654 case V4L2_SEL_TGT_CROP_BOUNDS:655 if (!dev->has_crop_out)656 return -EINVAL;657 sel->r = vivid_max_rect;658 break;659 case V4L2_SEL_TGT_COMPOSE:660 if (!dev->has_compose_out)661 return -EINVAL;662 sel->r = dev->compose_out;663 break;664 case V4L2_SEL_TGT_COMPOSE_DEFAULT:665 case V4L2_SEL_TGT_COMPOSE_BOUNDS:666 if (!dev->has_compose_out)667 return -EINVAL;668 sel->r = dev->sink_rect;669 break;670 default:671 return -EINVAL;672 }673 return 0;674}675 676int vivid_vid_out_s_selection(struct file *file, void *fh, struct v4l2_selection *s)677{678 struct vivid_dev *dev = video_drvdata(file);679 struct v4l2_rect *crop = &dev->crop_out;680 struct v4l2_rect *compose = &dev->compose_out;681 unsigned factor = V4L2_FIELD_HAS_T_OR_B(dev->field_out) ? 2 : 1;682 int ret;683 684 if (!dev->has_crop_out && !dev->has_compose_out)685 return -ENOTTY;686 if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)687 return -EINVAL;688 689 switch (s->target) {690 case V4L2_SEL_TGT_CROP:691 if (!dev->has_crop_out)692 return -EINVAL;693 ret = vivid_vid_adjust_sel(s->flags, &s->r);694 if (ret)695 return ret;696 v4l2_rect_set_min_size(&s->r, &vivid_min_rect);697 v4l2_rect_set_max_size(&s->r, &dev->fmt_out_rect);698 if (dev->has_scaler_out) {699 struct v4l2_rect max_rect = {700 0, 0,701 dev->sink_rect.width * MAX_ZOOM,702 (dev->sink_rect.height / factor) * MAX_ZOOM703 };704 705 v4l2_rect_set_max_size(&s->r, &max_rect);706 if (dev->has_compose_out) {707 struct v4l2_rect min_rect = {708 0, 0,709 s->r.width / MAX_ZOOM,710 (s->r.height * factor) / MAX_ZOOM711 };712 struct v4l2_rect max_rect = {713 0, 0,714 s->r.width * MAX_ZOOM,715 (s->r.height * factor) * MAX_ZOOM716 };717 718 v4l2_rect_set_min_size(compose, &min_rect);719 v4l2_rect_set_max_size(compose, &max_rect);720 v4l2_rect_map_inside(compose, &dev->compose_bounds_out);721 }722 } else if (dev->has_compose_out) {723 s->r.top *= factor;724 s->r.height *= factor;725 v4l2_rect_set_max_size(&s->r, &dev->sink_rect);726 v4l2_rect_set_size_to(compose, &s->r);727 v4l2_rect_map_inside(compose, &dev->compose_bounds_out);728 s->r.top /= factor;729 s->r.height /= factor;730 } else {731 v4l2_rect_set_size_to(&s->r, &dev->sink_rect);732 s->r.height /= factor;733 }734 v4l2_rect_map_inside(&s->r, &dev->fmt_out_rect);735 *crop = s->r;736 break;737 case V4L2_SEL_TGT_COMPOSE:738 if (!dev->has_compose_out)739 return -EINVAL;740 ret = vivid_vid_adjust_sel(s->flags, &s->r);741 if (ret)742 return ret;743 v4l2_rect_set_min_size(&s->r, &vivid_min_rect);744 v4l2_rect_set_max_size(&s->r, &dev->sink_rect);745 v4l2_rect_map_inside(&s->r, &dev->compose_bounds_out);746 s->r.top /= factor;747 s->r.height /= factor;748 if (dev->has_scaler_out) {749 struct v4l2_rect fmt = dev->fmt_out_rect;750 struct v4l2_rect max_rect = {751 0, 0,752 s->r.width * MAX_ZOOM,753 s->r.height * MAX_ZOOM754 };755 struct v4l2_rect min_rect = {756 0, 0,757 s->r.width / MAX_ZOOM,758 s->r.height / MAX_ZOOM759 };760 761 v4l2_rect_set_min_size(&fmt, &min_rect);762 if (!dev->has_crop_out)763 v4l2_rect_set_max_size(&fmt, &max_rect);764 if (!v4l2_rect_same_size(&dev->fmt_out_rect, &fmt) &&765 vb2_is_busy(&dev->vb_vid_out_q))766 return -EBUSY;767 if (dev->has_crop_out) {768 v4l2_rect_set_min_size(crop, &min_rect);769 v4l2_rect_set_max_size(crop, &max_rect);770 }771 dev->fmt_out_rect = fmt;772 } else if (dev->has_crop_out) {773 struct v4l2_rect fmt = dev->fmt_out_rect;774 775 v4l2_rect_set_min_size(&fmt, &s->r);776 if (!v4l2_rect_same_size(&dev->fmt_out_rect, &fmt) &&777 vb2_is_busy(&dev->vb_vid_out_q))778 return -EBUSY;779 dev->fmt_out_rect = fmt;780 v4l2_rect_set_size_to(crop, &s->r);781 v4l2_rect_map_inside(crop, &dev->fmt_out_rect);782 } else {783 if (!v4l2_rect_same_size(&s->r, &dev->fmt_out_rect) &&784 vb2_is_busy(&dev->vb_vid_out_q))785 return -EBUSY;786 v4l2_rect_set_size_to(&dev->fmt_out_rect, &s->r);787 v4l2_rect_set_size_to(crop, &s->r);788 crop->height /= factor;789 v4l2_rect_map_inside(crop, &dev->fmt_out_rect);790 }791 s->r.top *= factor;792 s->r.height *= factor;793 *compose = s->r;794 break;795 default:796 return -EINVAL;797 }798 799 return 0;800}801 802int vivid_vid_out_g_pixelaspect(struct file *file, void *priv,803 int type, struct v4l2_fract *f)804{805 struct vivid_dev *dev = video_drvdata(file);806 807 if (type != V4L2_BUF_TYPE_VIDEO_OUTPUT)808 return -EINVAL;809 810 switch (vivid_get_pixel_aspect(dev)) {811 case TPG_PIXEL_ASPECT_NTSC:812 f->numerator = 11;813 f->denominator = 10;814 break;815 case TPG_PIXEL_ASPECT_PAL:816 f->numerator = 54;817 f->denominator = 59;818 break;819 default:820 break;821 }822 return 0;823}824 825int vidioc_g_fmt_vid_out_overlay(struct file *file, void *priv,826 struct v4l2_format *f)827{828 struct vivid_dev *dev = video_drvdata(file);829 const struct v4l2_rect *compose = &dev->compose_out;830 struct v4l2_window *win = &f->fmt.win;831 832 if (!dev->has_fb)833 return -EINVAL;834 win->w.top = dev->overlay_out_top;835 win->w.left = dev->overlay_out_left;836 win->w.width = compose->width;837 win->w.height = compose->height;838 win->field = V4L2_FIELD_ANY;839 win->chromakey = dev->chromakey_out;840 win->global_alpha = dev->global_alpha_out;841 return 0;842}843 844int vidioc_try_fmt_vid_out_overlay(struct file *file, void *priv,845 struct v4l2_format *f)846{847 struct vivid_dev *dev = video_drvdata(file);848 const struct v4l2_rect *compose = &dev->compose_out;849 struct v4l2_window *win = &f->fmt.win;850 851 if (!dev->has_fb)852 return -EINVAL;853 win->w.left = clamp_t(int, win->w.left,854 -dev->display_width, dev->display_width);855 win->w.top = clamp_t(int, win->w.top,856 -dev->display_height, dev->display_height);857 win->w.width = compose->width;858 win->w.height = compose->height;859 /*860 * It makes no sense for an OSD to overlay only top or bottom fields,861 * so always set this to ANY.862 */863 win->field = V4L2_FIELD_ANY;864 return 0;865}866 867int vidioc_s_fmt_vid_out_overlay(struct file *file, void *priv,868 struct v4l2_format *f)869{870 struct vivid_dev *dev = video_drvdata(file);871 struct v4l2_window *win = &f->fmt.win;872 int ret = vidioc_try_fmt_vid_out_overlay(file, priv, f);873 874 if (ret)875 return ret;876 877 dev->overlay_out_top = win->w.top;878 dev->overlay_out_left = win->w.left;879 dev->chromakey_out = win->chromakey;880 dev->global_alpha_out = win->global_alpha;881 return ret;882}883 884int vivid_vid_out_overlay(struct file *file, void *fh, unsigned i)885{886 struct vivid_dev *dev = video_drvdata(file);887 888 if (i && !dev->fmt_out->can_do_overlay) {889 dprintk(dev, 1, "unsupported output format for output overlay\n");890 return -EINVAL;891 }892 893 dev->overlay_out_enabled = i;894 return 0;895}896 897int vivid_vid_out_g_fbuf(struct file *file, void *fh,898 struct v4l2_framebuffer *a)899{900 struct vivid_dev *dev = video_drvdata(file);901 902 a->capability = V4L2_FBUF_CAP_EXTERNOVERLAY |903 V4L2_FBUF_CAP_CHROMAKEY |904 V4L2_FBUF_CAP_SRC_CHROMAKEY |905 V4L2_FBUF_CAP_GLOBAL_ALPHA |906 V4L2_FBUF_CAP_LOCAL_ALPHA |907 V4L2_FBUF_CAP_LOCAL_INV_ALPHA;908 a->flags = V4L2_FBUF_FLAG_OVERLAY | dev->fbuf_out_flags;909 a->base = (void *)dev->video_pbase;910 a->fmt.width = dev->display_width;911 a->fmt.height = dev->display_height;912 if (dev->fb_defined.green.length == 5)913 a->fmt.pixelformat = V4L2_PIX_FMT_ARGB555;914 else915 a->fmt.pixelformat = V4L2_PIX_FMT_RGB565;916 a->fmt.bytesperline = dev->display_byte_stride;917 a->fmt.sizeimage = a->fmt.height * a->fmt.bytesperline;918 a->fmt.field = V4L2_FIELD_NONE;919 a->fmt.colorspace = V4L2_COLORSPACE_SRGB;920 a->fmt.priv = 0;921 return 0;922}923 924int vivid_vid_out_s_fbuf(struct file *file, void *fh,925 const struct v4l2_framebuffer *a)926{927 struct vivid_dev *dev = video_drvdata(file);928 const unsigned chroma_flags = V4L2_FBUF_FLAG_CHROMAKEY |929 V4L2_FBUF_FLAG_SRC_CHROMAKEY;930 const unsigned alpha_flags = V4L2_FBUF_FLAG_GLOBAL_ALPHA |931 V4L2_FBUF_FLAG_LOCAL_ALPHA |932 V4L2_FBUF_FLAG_LOCAL_INV_ALPHA;933 934 935 if ((a->flags & chroma_flags) == chroma_flags)936 return -EINVAL;937 switch (a->flags & alpha_flags) {938 case 0:939 case V4L2_FBUF_FLAG_GLOBAL_ALPHA:940 case V4L2_FBUF_FLAG_LOCAL_ALPHA:941 case V4L2_FBUF_FLAG_LOCAL_INV_ALPHA:942 break;943 default:944 return -EINVAL;945 }946 dev->fbuf_out_flags &= ~(chroma_flags | alpha_flags);947 dev->fbuf_out_flags |= a->flags & (chroma_flags | alpha_flags);948 return 0;949}950 951static const struct v4l2_audioout vivid_audio_outputs[] = {952 { 0, "Line-Out 1" },953 { 1, "Line-Out 2" },954};955 956int vidioc_enum_output(struct file *file, void *priv,957 struct v4l2_output *out)958{959 struct vivid_dev *dev = video_drvdata(file);960 961 if (out->index >= dev->num_outputs)962 return -EINVAL;963 964 out->type = V4L2_OUTPUT_TYPE_ANALOG;965 switch (dev->output_type[out->index]) {966 case SVID:967 snprintf(out->name, sizeof(out->name), "S-Video %03u-%u",968 dev->inst, dev->output_name_counter[out->index]);969 out->std = V4L2_STD_ALL;970 if (dev->has_audio_outputs)971 out->audioset = (1 << ARRAY_SIZE(vivid_audio_outputs)) - 1;972 out->capabilities = V4L2_OUT_CAP_STD;973 break;974 case HDMI:975 snprintf(out->name, sizeof(out->name), "HDMI %03u-%u",976 dev->inst, dev->output_name_counter[out->index]);977 out->capabilities = V4L2_OUT_CAP_DV_TIMINGS;978 break;979 }980 return 0;981}982 983int vidioc_g_output(struct file *file, void *priv, unsigned *o)984{985 struct vivid_dev *dev = video_drvdata(file);986 987 *o = dev->output;988 return 0;989}990 991int vidioc_s_output(struct file *file, void *priv, unsigned o)992{993 struct vivid_dev *dev = video_drvdata(file);994 995 if (o >= dev->num_outputs)996 return -EINVAL;997 998 if (o == dev->output)999 return 0;1000 1001 if (vb2_is_busy(&dev->vb_vid_out_q) ||1002 vb2_is_busy(&dev->vb_vbi_out_q) ||1003 vb2_is_busy(&dev->vb_meta_out_q))1004 return -EBUSY;1005 1006 dev->output = o;1007 dev->tv_audio_output = 0;1008 if (dev->output_type[o] == SVID)1009 dev->vid_out_dev.tvnorms = V4L2_STD_ALL;1010 else1011 dev->vid_out_dev.tvnorms = 0;1012 1013 dev->vbi_out_dev.tvnorms = dev->vid_out_dev.tvnorms;1014 dev->meta_out_dev.tvnorms = dev->vid_out_dev.tvnorms;1015 vivid_update_format_out(dev);1016 1017 return 0;1018}1019 1020int vidioc_enumaudout(struct file *file, void *fh, struct v4l2_audioout *vout)1021{1022 if (vout->index >= ARRAY_SIZE(vivid_audio_outputs))1023 return -EINVAL;1024 *vout = vivid_audio_outputs[vout->index];1025 return 0;1026}1027 1028int vidioc_g_audout(struct file *file, void *fh, struct v4l2_audioout *vout)1029{1030 struct vivid_dev *dev = video_drvdata(file);1031 1032 if (!vivid_is_svid_out(dev))1033 return -EINVAL;1034 *vout = vivid_audio_outputs[dev->tv_audio_output];1035 return 0;1036}1037 1038int vidioc_s_audout(struct file *file, void *fh, const struct v4l2_audioout *vout)1039{1040 struct vivid_dev *dev = video_drvdata(file);1041 1042 if (!vivid_is_svid_out(dev))1043 return -EINVAL;1044 if (vout->index >= ARRAY_SIZE(vivid_audio_outputs))1045 return -EINVAL;1046 dev->tv_audio_output = vout->index;1047 return 0;1048}1049 1050int vivid_vid_out_s_std(struct file *file, void *priv, v4l2_std_id id)1051{1052 struct vivid_dev *dev = video_drvdata(file);1053 1054 if (!vivid_is_svid_out(dev))1055 return -ENODATA;1056 if (dev->std_out == id)1057 return 0;1058 if (vb2_is_busy(&dev->vb_vid_out_q) || vb2_is_busy(&dev->vb_vbi_out_q))1059 return -EBUSY;1060 dev->std_out = id;1061 vivid_update_format_out(dev);1062 return 0;1063}1064 1065static bool valid_cvt_gtf_timings(struct v4l2_dv_timings *timings)1066{1067 struct v4l2_bt_timings *bt = &timings->bt;1068 1069 if ((bt->standards & (V4L2_DV_BT_STD_CVT | V4L2_DV_BT_STD_GTF)) &&1070 v4l2_valid_dv_timings(timings, &vivid_dv_timings_cap, NULL, NULL))1071 return true;1072 1073 return false;1074}1075 1076int vivid_vid_out_s_dv_timings(struct file *file, void *_fh,1077 struct v4l2_dv_timings *timings)1078{1079 struct vivid_dev *dev = video_drvdata(file);1080 if (!vivid_is_hdmi_out(dev))1081 return -ENODATA;1082 if (!v4l2_find_dv_timings_cap(timings, &vivid_dv_timings_cap,1083 0, NULL, NULL) &&1084 !valid_cvt_gtf_timings(timings))1085 return -EINVAL;1086 if (v4l2_match_dv_timings(timings, &dev->dv_timings_out, 0, true))1087 return 0;1088 if (vb2_is_busy(&dev->vb_vid_out_q))1089 return -EBUSY;1090 dev->dv_timings_out = *timings;1091 vivid_update_format_out(dev);1092 return 0;1093}1094 1095int vivid_vid_out_g_parm(struct file *file, void *priv,1096 struct v4l2_streamparm *parm)1097{1098 struct vivid_dev *dev = video_drvdata(file);1099 1100 if (parm->type != (dev->multiplanar ?1101 V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE :1102 V4L2_BUF_TYPE_VIDEO_OUTPUT))1103 return -EINVAL;1104 1105 parm->parm.output.capability = V4L2_CAP_TIMEPERFRAME;1106 parm->parm.output.timeperframe = dev->timeperframe_vid_out;1107 parm->parm.output.writebuffers = 1;1108 1109 return 0;1110}1111 1112int vidioc_subscribe_event(struct v4l2_fh *fh,1113 const struct v4l2_event_subscription *sub)1114{1115 switch (sub->type) {1116 case V4L2_EVENT_SOURCE_CHANGE:1117 if (fh->vdev->vfl_dir == VFL_DIR_RX)1118 return v4l2_src_change_event_subscribe(fh, sub);1119 break;1120 default:1121 return v4l2_ctrl_subscribe_event(fh, sub);1122 }1123 return -EINVAL;1124}1125