brintos

brintos / linux-shallow public Read only

0
0
Text · 30.7 KiB · 60327f3 Raw
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