18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
58c2ecf20Sopenharmony_ci *  Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/kernel.h>
98c2ecf20Sopenharmony_ci#include <linux/slab.h>
108c2ecf20Sopenharmony_ci#include "pvrusb2-context.h"
118c2ecf20Sopenharmony_ci#include "pvrusb2-hdw.h"
128c2ecf20Sopenharmony_ci#include "pvrusb2.h"
138c2ecf20Sopenharmony_ci#include "pvrusb2-debug.h"
148c2ecf20Sopenharmony_ci#include "pvrusb2-v4l2.h"
158c2ecf20Sopenharmony_ci#include "pvrusb2-ioread.h"
168c2ecf20Sopenharmony_ci#include <linux/videodev2.h>
178c2ecf20Sopenharmony_ci#include <linux/module.h>
188c2ecf20Sopenharmony_ci#include <media/v4l2-dev.h>
198c2ecf20Sopenharmony_ci#include <media/v4l2-device.h>
208c2ecf20Sopenharmony_ci#include <media/v4l2-fh.h>
218c2ecf20Sopenharmony_ci#include <media/v4l2-common.h>
228c2ecf20Sopenharmony_ci#include <media/v4l2-ioctl.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistruct pvr2_v4l2_dev;
258c2ecf20Sopenharmony_cistruct pvr2_v4l2_fh;
268c2ecf20Sopenharmony_cistruct pvr2_v4l2;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistruct pvr2_v4l2_dev {
298c2ecf20Sopenharmony_ci	struct video_device devbase; /* MUST be first! */
308c2ecf20Sopenharmony_ci	struct pvr2_v4l2 *v4lp;
318c2ecf20Sopenharmony_ci	struct pvr2_context_stream *stream;
328c2ecf20Sopenharmony_ci	/* Information about this device: */
338c2ecf20Sopenharmony_ci	enum pvr2_config config; /* Expected stream format */
348c2ecf20Sopenharmony_ci	int v4l_type; /* V4L defined type for this device node */
358c2ecf20Sopenharmony_ci	enum pvr2_v4l_type minor_type; /* pvr2-understood minor device type */
368c2ecf20Sopenharmony_ci};
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistruct pvr2_v4l2_fh {
398c2ecf20Sopenharmony_ci	struct v4l2_fh fh;
408c2ecf20Sopenharmony_ci	struct pvr2_channel channel;
418c2ecf20Sopenharmony_ci	struct pvr2_v4l2_dev *pdi;
428c2ecf20Sopenharmony_ci	struct pvr2_ioread *rhp;
438c2ecf20Sopenharmony_ci	struct file *file;
448c2ecf20Sopenharmony_ci	wait_queue_head_t wait_data;
458c2ecf20Sopenharmony_ci	int fw_mode_flag;
468c2ecf20Sopenharmony_ci	/* Map contiguous ordinal value to input id */
478c2ecf20Sopenharmony_ci	unsigned char *input_map;
488c2ecf20Sopenharmony_ci	unsigned int input_cnt;
498c2ecf20Sopenharmony_ci};
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistruct pvr2_v4l2 {
528c2ecf20Sopenharmony_ci	struct pvr2_channel channel;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	/* streams - Note that these must be separately, individually,
558c2ecf20Sopenharmony_ci	 * allocated pointers.  This is because the v4l core is going to
568c2ecf20Sopenharmony_ci	 * manage their deletion - separately, individually...  */
578c2ecf20Sopenharmony_ci	struct pvr2_v4l2_dev *dev_video;
588c2ecf20Sopenharmony_ci	struct pvr2_v4l2_dev *dev_radio;
598c2ecf20Sopenharmony_ci};
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic int video_nr[PVR_NUM] = {[0 ... PVR_NUM-1] = -1};
628c2ecf20Sopenharmony_cimodule_param_array(video_nr, int, NULL, 0444);
638c2ecf20Sopenharmony_ciMODULE_PARM_DESC(video_nr, "Offset for device's video dev minor");
648c2ecf20Sopenharmony_cistatic int radio_nr[PVR_NUM] = {[0 ... PVR_NUM-1] = -1};
658c2ecf20Sopenharmony_cimodule_param_array(radio_nr, int, NULL, 0444);
668c2ecf20Sopenharmony_ciMODULE_PARM_DESC(radio_nr, "Offset for device's radio dev minor");
678c2ecf20Sopenharmony_cistatic int vbi_nr[PVR_NUM] = {[0 ... PVR_NUM-1] = -1};
688c2ecf20Sopenharmony_cimodule_param_array(vbi_nr, int, NULL, 0444);
698c2ecf20Sopenharmony_ciMODULE_PARM_DESC(vbi_nr, "Offset for device's vbi dev minor");
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci#define PVR_FORMAT_PIX  0
728c2ecf20Sopenharmony_ci#define PVR_FORMAT_VBI  1
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic struct v4l2_format pvr_format [] = {
758c2ecf20Sopenharmony_ci	[PVR_FORMAT_PIX] = {
768c2ecf20Sopenharmony_ci		.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE,
778c2ecf20Sopenharmony_ci		.fmt    = {
788c2ecf20Sopenharmony_ci			.pix        = {
798c2ecf20Sopenharmony_ci				.width          = 720,
808c2ecf20Sopenharmony_ci				.height         = 576,
818c2ecf20Sopenharmony_ci				.pixelformat    = V4L2_PIX_FMT_MPEG,
828c2ecf20Sopenharmony_ci				.field          = V4L2_FIELD_INTERLACED,
838c2ecf20Sopenharmony_ci				/* FIXME : Don't know what to put here... */
848c2ecf20Sopenharmony_ci				.sizeimage      = 32 * 1024,
858c2ecf20Sopenharmony_ci			}
868c2ecf20Sopenharmony_ci		}
878c2ecf20Sopenharmony_ci	},
888c2ecf20Sopenharmony_ci	[PVR_FORMAT_VBI] = {
898c2ecf20Sopenharmony_ci		.type   = V4L2_BUF_TYPE_VBI_CAPTURE,
908c2ecf20Sopenharmony_ci		.fmt    = {
918c2ecf20Sopenharmony_ci			.vbi        = {
928c2ecf20Sopenharmony_ci				.sampling_rate = 27000000,
938c2ecf20Sopenharmony_ci				.offset = 248,
948c2ecf20Sopenharmony_ci				.samples_per_line = 1443,
958c2ecf20Sopenharmony_ci				.sample_format = V4L2_PIX_FMT_GREY,
968c2ecf20Sopenharmony_ci				.start = { 0, 0 },
978c2ecf20Sopenharmony_ci				.count = { 0, 0 },
988c2ecf20Sopenharmony_ci				.flags = 0,
998c2ecf20Sopenharmony_ci			}
1008c2ecf20Sopenharmony_ci		}
1018c2ecf20Sopenharmony_ci	}
1028c2ecf20Sopenharmony_ci};
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci/*
1078c2ecf20Sopenharmony_ci * This is part of Video 4 Linux API. These procedures handle ioctl() calls.
1088c2ecf20Sopenharmony_ci */
1098c2ecf20Sopenharmony_cistatic int pvr2_querycap(struct file *file, void *priv, struct v4l2_capability *cap)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	struct pvr2_v4l2_fh *fh = file->private_data;
1128c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	strscpy(cap->driver, "pvrusb2", sizeof(cap->driver));
1158c2ecf20Sopenharmony_ci	strscpy(cap->bus_info, pvr2_hdw_get_bus_info(hdw),
1168c2ecf20Sopenharmony_ci		sizeof(cap->bus_info));
1178c2ecf20Sopenharmony_ci	strscpy(cap->card, pvr2_hdw_get_desc(hdw), sizeof(cap->card));
1188c2ecf20Sopenharmony_ci	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER |
1198c2ecf20Sopenharmony_ci			    V4L2_CAP_AUDIO | V4L2_CAP_RADIO |
1208c2ecf20Sopenharmony_ci			    V4L2_CAP_READWRITE | V4L2_CAP_DEVICE_CAPS;
1218c2ecf20Sopenharmony_ci	return 0;
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistatic int pvr2_g_std(struct file *file, void *priv, v4l2_std_id *std)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	struct pvr2_v4l2_fh *fh = file->private_data;
1278c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
1288c2ecf20Sopenharmony_ci	int val = 0;
1298c2ecf20Sopenharmony_ci	int ret;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	ret = pvr2_ctrl_get_value(
1328c2ecf20Sopenharmony_ci			pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_STDCUR), &val);
1338c2ecf20Sopenharmony_ci	*std = val;
1348c2ecf20Sopenharmony_ci	return ret;
1358c2ecf20Sopenharmony_ci}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic int pvr2_s_std(struct file *file, void *priv, v4l2_std_id std)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	struct pvr2_v4l2_fh *fh = file->private_data;
1408c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
1418c2ecf20Sopenharmony_ci	int ret;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	ret = pvr2_ctrl_set_value(
1448c2ecf20Sopenharmony_ci		pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_STDCUR), std);
1458c2ecf20Sopenharmony_ci	pvr2_hdw_commit_ctl(hdw);
1468c2ecf20Sopenharmony_ci	return ret;
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_cistatic int pvr2_querystd(struct file *file, void *priv, v4l2_std_id *std)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	struct pvr2_v4l2_fh *fh = file->private_data;
1528c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
1538c2ecf20Sopenharmony_ci	int val = 0;
1548c2ecf20Sopenharmony_ci	int ret;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	ret = pvr2_ctrl_get_value(
1578c2ecf20Sopenharmony_ci		pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_STDDETECT), &val);
1588c2ecf20Sopenharmony_ci	*std = val;
1598c2ecf20Sopenharmony_ci	return ret;
1608c2ecf20Sopenharmony_ci}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistatic int pvr2_enum_input(struct file *file, void *priv, struct v4l2_input *vi)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	struct pvr2_v4l2_fh *fh = file->private_data;
1658c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
1668c2ecf20Sopenharmony_ci	struct pvr2_ctrl *cptr;
1678c2ecf20Sopenharmony_ci	struct v4l2_input tmp;
1688c2ecf20Sopenharmony_ci	unsigned int cnt;
1698c2ecf20Sopenharmony_ci	int val;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	cptr = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT);
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	memset(&tmp, 0, sizeof(tmp));
1748c2ecf20Sopenharmony_ci	tmp.index = vi->index;
1758c2ecf20Sopenharmony_ci	if (vi->index >= fh->input_cnt)
1768c2ecf20Sopenharmony_ci		return -EINVAL;
1778c2ecf20Sopenharmony_ci	val = fh->input_map[vi->index];
1788c2ecf20Sopenharmony_ci	switch (val) {
1798c2ecf20Sopenharmony_ci	case PVR2_CVAL_INPUT_TV:
1808c2ecf20Sopenharmony_ci	case PVR2_CVAL_INPUT_DTV:
1818c2ecf20Sopenharmony_ci	case PVR2_CVAL_INPUT_RADIO:
1828c2ecf20Sopenharmony_ci		tmp.type = V4L2_INPUT_TYPE_TUNER;
1838c2ecf20Sopenharmony_ci		break;
1848c2ecf20Sopenharmony_ci	case PVR2_CVAL_INPUT_SVIDEO:
1858c2ecf20Sopenharmony_ci	case PVR2_CVAL_INPUT_COMPOSITE:
1868c2ecf20Sopenharmony_ci		tmp.type = V4L2_INPUT_TYPE_CAMERA;
1878c2ecf20Sopenharmony_ci		break;
1888c2ecf20Sopenharmony_ci	default:
1898c2ecf20Sopenharmony_ci		return -EINVAL;
1908c2ecf20Sopenharmony_ci	}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	cnt = 0;
1938c2ecf20Sopenharmony_ci	pvr2_ctrl_get_valname(cptr, val,
1948c2ecf20Sopenharmony_ci			tmp.name, sizeof(tmp.name) - 1, &cnt);
1958c2ecf20Sopenharmony_ci	tmp.name[cnt] = 0;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	/* Don't bother with audioset, since this driver currently
1988c2ecf20Sopenharmony_ci	   always switches the audio whenever the video is
1998c2ecf20Sopenharmony_ci	   switched. */
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	/* Handling std is a tougher problem.  It doesn't make
2028c2ecf20Sopenharmony_ci	   sense in cases where a device might be multi-standard.
2038c2ecf20Sopenharmony_ci	   We could just copy out the current value for the
2048c2ecf20Sopenharmony_ci	   standard, but it can change over time.  For now just
2058c2ecf20Sopenharmony_ci	   leave it zero. */
2068c2ecf20Sopenharmony_ci	*vi = tmp;
2078c2ecf20Sopenharmony_ci	return 0;
2088c2ecf20Sopenharmony_ci}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_cistatic int pvr2_g_input(struct file *file, void *priv, unsigned int *i)
2118c2ecf20Sopenharmony_ci{
2128c2ecf20Sopenharmony_ci	struct pvr2_v4l2_fh *fh = file->private_data;
2138c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
2148c2ecf20Sopenharmony_ci	unsigned int idx;
2158c2ecf20Sopenharmony_ci	struct pvr2_ctrl *cptr;
2168c2ecf20Sopenharmony_ci	int val;
2178c2ecf20Sopenharmony_ci	int ret;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	cptr = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT);
2208c2ecf20Sopenharmony_ci	val = 0;
2218c2ecf20Sopenharmony_ci	ret = pvr2_ctrl_get_value(cptr, &val);
2228c2ecf20Sopenharmony_ci	*i = 0;
2238c2ecf20Sopenharmony_ci	for (idx = 0; idx < fh->input_cnt; idx++) {
2248c2ecf20Sopenharmony_ci		if (fh->input_map[idx] == val) {
2258c2ecf20Sopenharmony_ci			*i = idx;
2268c2ecf20Sopenharmony_ci			break;
2278c2ecf20Sopenharmony_ci		}
2288c2ecf20Sopenharmony_ci	}
2298c2ecf20Sopenharmony_ci	return ret;
2308c2ecf20Sopenharmony_ci}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_cistatic int pvr2_s_input(struct file *file, void *priv, unsigned int inp)
2338c2ecf20Sopenharmony_ci{
2348c2ecf20Sopenharmony_ci	struct pvr2_v4l2_fh *fh = file->private_data;
2358c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
2368c2ecf20Sopenharmony_ci	int ret;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	if (inp >= fh->input_cnt)
2398c2ecf20Sopenharmony_ci		return -EINVAL;
2408c2ecf20Sopenharmony_ci	ret = pvr2_ctrl_set_value(
2418c2ecf20Sopenharmony_ci			pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT),
2428c2ecf20Sopenharmony_ci			fh->input_map[inp]);
2438c2ecf20Sopenharmony_ci	pvr2_hdw_commit_ctl(hdw);
2448c2ecf20Sopenharmony_ci	return ret;
2458c2ecf20Sopenharmony_ci}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_cistatic int pvr2_enumaudio(struct file *file, void *priv, struct v4l2_audio *vin)
2488c2ecf20Sopenharmony_ci{
2498c2ecf20Sopenharmony_ci	/* pkt: FIXME: We are returning one "fake" input here
2508c2ecf20Sopenharmony_ci	   which could very well be called "whatever_we_like".
2518c2ecf20Sopenharmony_ci	   This is for apps that want to see an audio input
2528c2ecf20Sopenharmony_ci	   just to feel comfortable, as well as to test if
2538c2ecf20Sopenharmony_ci	   it can do stereo or sth. There is actually no guarantee
2548c2ecf20Sopenharmony_ci	   that the actual audio input cannot change behind the app's
2558c2ecf20Sopenharmony_ci	   back, but most applications should not mind that either.
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	   Hopefully, mplayer people will work with us on this (this
2588c2ecf20Sopenharmony_ci	   whole mess is to support mplayer pvr://), or Hans will come
2598c2ecf20Sopenharmony_ci	   up with a more standard way to say "we have inputs but we
2608c2ecf20Sopenharmony_ci	   don 't want you to change them independent of video" which
2618c2ecf20Sopenharmony_ci	   will sort this mess.
2628c2ecf20Sopenharmony_ci	 */
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	if (vin->index > 0)
2658c2ecf20Sopenharmony_ci		return -EINVAL;
2668c2ecf20Sopenharmony_ci	strscpy(vin->name, "PVRUSB2 Audio", sizeof(vin->name));
2678c2ecf20Sopenharmony_ci	vin->capability = V4L2_AUDCAP_STEREO;
2688c2ecf20Sopenharmony_ci	return 0;
2698c2ecf20Sopenharmony_ci}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_cistatic int pvr2_g_audio(struct file *file, void *priv, struct v4l2_audio *vin)
2728c2ecf20Sopenharmony_ci{
2738c2ecf20Sopenharmony_ci	/* pkt: FIXME: see above comment (VIDIOC_ENUMAUDIO) */
2748c2ecf20Sopenharmony_ci	vin->index = 0;
2758c2ecf20Sopenharmony_ci	strscpy(vin->name, "PVRUSB2 Audio", sizeof(vin->name));
2768c2ecf20Sopenharmony_ci	vin->capability = V4L2_AUDCAP_STEREO;
2778c2ecf20Sopenharmony_ci	return 0;
2788c2ecf20Sopenharmony_ci}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_cistatic int pvr2_s_audio(struct file *file, void *priv, const struct v4l2_audio *vout)
2818c2ecf20Sopenharmony_ci{
2828c2ecf20Sopenharmony_ci	if (vout->index)
2838c2ecf20Sopenharmony_ci		return -EINVAL;
2848c2ecf20Sopenharmony_ci	return 0;
2858c2ecf20Sopenharmony_ci}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_cistatic int pvr2_g_tuner(struct file *file, void *priv, struct v4l2_tuner *vt)
2888c2ecf20Sopenharmony_ci{
2898c2ecf20Sopenharmony_ci	struct pvr2_v4l2_fh *fh = file->private_data;
2908c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	if (vt->index != 0)
2938c2ecf20Sopenharmony_ci		return -EINVAL; /* Only answer for the 1st tuner */
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	pvr2_hdw_execute_tuner_poll(hdw);
2968c2ecf20Sopenharmony_ci	return pvr2_hdw_get_tuner_status(hdw, vt);
2978c2ecf20Sopenharmony_ci}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_cistatic int pvr2_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *vt)
3008c2ecf20Sopenharmony_ci{
3018c2ecf20Sopenharmony_ci	struct pvr2_v4l2_fh *fh = file->private_data;
3028c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
3038c2ecf20Sopenharmony_ci	int ret;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	if (vt->index != 0)
3068c2ecf20Sopenharmony_ci		return -EINVAL;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	ret = pvr2_ctrl_set_value(
3098c2ecf20Sopenharmony_ci			pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_AUDIOMODE),
3108c2ecf20Sopenharmony_ci			vt->audmode);
3118c2ecf20Sopenharmony_ci	pvr2_hdw_commit_ctl(hdw);
3128c2ecf20Sopenharmony_ci	return ret;
3138c2ecf20Sopenharmony_ci}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_cistatic int pvr2_s_frequency(struct file *file, void *priv, const struct v4l2_frequency *vf)
3168c2ecf20Sopenharmony_ci{
3178c2ecf20Sopenharmony_ci	struct pvr2_v4l2_fh *fh = file->private_data;
3188c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
3198c2ecf20Sopenharmony_ci	unsigned long fv;
3208c2ecf20Sopenharmony_ci	struct v4l2_tuner vt;
3218c2ecf20Sopenharmony_ci	int cur_input;
3228c2ecf20Sopenharmony_ci	struct pvr2_ctrl *ctrlp;
3238c2ecf20Sopenharmony_ci	int ret;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	ret = pvr2_hdw_get_tuner_status(hdw, &vt);
3268c2ecf20Sopenharmony_ci	if (ret != 0)
3278c2ecf20Sopenharmony_ci		return ret;
3288c2ecf20Sopenharmony_ci	ctrlp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT);
3298c2ecf20Sopenharmony_ci	ret = pvr2_ctrl_get_value(ctrlp, &cur_input);
3308c2ecf20Sopenharmony_ci	if (ret != 0)
3318c2ecf20Sopenharmony_ci		return ret;
3328c2ecf20Sopenharmony_ci	if (vf->type == V4L2_TUNER_RADIO) {
3338c2ecf20Sopenharmony_ci		if (cur_input != PVR2_CVAL_INPUT_RADIO)
3348c2ecf20Sopenharmony_ci			pvr2_ctrl_set_value(ctrlp, PVR2_CVAL_INPUT_RADIO);
3358c2ecf20Sopenharmony_ci	} else {
3368c2ecf20Sopenharmony_ci		if (cur_input == PVR2_CVAL_INPUT_RADIO)
3378c2ecf20Sopenharmony_ci			pvr2_ctrl_set_value(ctrlp, PVR2_CVAL_INPUT_TV);
3388c2ecf20Sopenharmony_ci	}
3398c2ecf20Sopenharmony_ci	fv = vf->frequency;
3408c2ecf20Sopenharmony_ci	if (vt.capability & V4L2_TUNER_CAP_LOW)
3418c2ecf20Sopenharmony_ci		fv = (fv * 125) / 2;
3428c2ecf20Sopenharmony_ci	else
3438c2ecf20Sopenharmony_ci		fv = fv * 62500;
3448c2ecf20Sopenharmony_ci	ret = pvr2_ctrl_set_value(
3458c2ecf20Sopenharmony_ci			pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_FREQUENCY),fv);
3468c2ecf20Sopenharmony_ci	pvr2_hdw_commit_ctl(hdw);
3478c2ecf20Sopenharmony_ci	return ret;
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_cistatic int pvr2_g_frequency(struct file *file, void *priv, struct v4l2_frequency *vf)
3518c2ecf20Sopenharmony_ci{
3528c2ecf20Sopenharmony_ci	struct pvr2_v4l2_fh *fh = file->private_data;
3538c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
3548c2ecf20Sopenharmony_ci	int val = 0;
3558c2ecf20Sopenharmony_ci	int cur_input;
3568c2ecf20Sopenharmony_ci	struct v4l2_tuner vt;
3578c2ecf20Sopenharmony_ci	int ret;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	ret = pvr2_hdw_get_tuner_status(hdw, &vt);
3608c2ecf20Sopenharmony_ci	if (ret != 0)
3618c2ecf20Sopenharmony_ci		return ret;
3628c2ecf20Sopenharmony_ci	ret = pvr2_ctrl_get_value(
3638c2ecf20Sopenharmony_ci			pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_FREQUENCY),
3648c2ecf20Sopenharmony_ci			&val);
3658c2ecf20Sopenharmony_ci	if (ret != 0)
3668c2ecf20Sopenharmony_ci		return ret;
3678c2ecf20Sopenharmony_ci	pvr2_ctrl_get_value(
3688c2ecf20Sopenharmony_ci			pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT),
3698c2ecf20Sopenharmony_ci			&cur_input);
3708c2ecf20Sopenharmony_ci	if (cur_input == PVR2_CVAL_INPUT_RADIO)
3718c2ecf20Sopenharmony_ci		vf->type = V4L2_TUNER_RADIO;
3728c2ecf20Sopenharmony_ci	else
3738c2ecf20Sopenharmony_ci		vf->type = V4L2_TUNER_ANALOG_TV;
3748c2ecf20Sopenharmony_ci	if (vt.capability & V4L2_TUNER_CAP_LOW)
3758c2ecf20Sopenharmony_ci		val = (val * 2) / 125;
3768c2ecf20Sopenharmony_ci	else
3778c2ecf20Sopenharmony_ci		val /= 62500;
3788c2ecf20Sopenharmony_ci	vf->frequency = val;
3798c2ecf20Sopenharmony_ci	return 0;
3808c2ecf20Sopenharmony_ci}
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_cistatic int pvr2_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *fd)
3838c2ecf20Sopenharmony_ci{
3848c2ecf20Sopenharmony_ci	/* Only one format is supported: MPEG. */
3858c2ecf20Sopenharmony_ci	if (fd->index)
3868c2ecf20Sopenharmony_ci		return -EINVAL;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	fd->pixelformat = V4L2_PIX_FMT_MPEG;
3898c2ecf20Sopenharmony_ci	return 0;
3908c2ecf20Sopenharmony_ci}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_cistatic int pvr2_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *vf)
3938c2ecf20Sopenharmony_ci{
3948c2ecf20Sopenharmony_ci	struct pvr2_v4l2_fh *fh = file->private_data;
3958c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
3968c2ecf20Sopenharmony_ci	int val;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	memcpy(vf, &pvr_format[PVR_FORMAT_PIX], sizeof(struct v4l2_format));
3998c2ecf20Sopenharmony_ci	val = 0;
4008c2ecf20Sopenharmony_ci	pvr2_ctrl_get_value(
4018c2ecf20Sopenharmony_ci			pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_HRES),
4028c2ecf20Sopenharmony_ci			&val);
4038c2ecf20Sopenharmony_ci	vf->fmt.pix.width = val;
4048c2ecf20Sopenharmony_ci	val = 0;
4058c2ecf20Sopenharmony_ci	pvr2_ctrl_get_value(
4068c2ecf20Sopenharmony_ci			pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_VRES),
4078c2ecf20Sopenharmony_ci			&val);
4088c2ecf20Sopenharmony_ci	vf->fmt.pix.height = val;
4098c2ecf20Sopenharmony_ci	return 0;
4108c2ecf20Sopenharmony_ci}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_cistatic int pvr2_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *vf)
4138c2ecf20Sopenharmony_ci{
4148c2ecf20Sopenharmony_ci	struct pvr2_v4l2_fh *fh = file->private_data;
4158c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
4168c2ecf20Sopenharmony_ci	int lmin, lmax, ldef;
4178c2ecf20Sopenharmony_ci	struct pvr2_ctrl *hcp, *vcp;
4188c2ecf20Sopenharmony_ci	int h = vf->fmt.pix.height;
4198c2ecf20Sopenharmony_ci	int w = vf->fmt.pix.width;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	hcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_HRES);
4228c2ecf20Sopenharmony_ci	vcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_VRES);
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	lmin = pvr2_ctrl_get_min(hcp);
4258c2ecf20Sopenharmony_ci	lmax = pvr2_ctrl_get_max(hcp);
4268c2ecf20Sopenharmony_ci	pvr2_ctrl_get_def(hcp, &ldef);
4278c2ecf20Sopenharmony_ci	if (w == -1)
4288c2ecf20Sopenharmony_ci		w = ldef;
4298c2ecf20Sopenharmony_ci	else if (w < lmin)
4308c2ecf20Sopenharmony_ci		w = lmin;
4318c2ecf20Sopenharmony_ci	else if (w > lmax)
4328c2ecf20Sopenharmony_ci		w = lmax;
4338c2ecf20Sopenharmony_ci	lmin = pvr2_ctrl_get_min(vcp);
4348c2ecf20Sopenharmony_ci	lmax = pvr2_ctrl_get_max(vcp);
4358c2ecf20Sopenharmony_ci	pvr2_ctrl_get_def(vcp, &ldef);
4368c2ecf20Sopenharmony_ci	if (h == -1)
4378c2ecf20Sopenharmony_ci		h = ldef;
4388c2ecf20Sopenharmony_ci	else if (h < lmin)
4398c2ecf20Sopenharmony_ci		h = lmin;
4408c2ecf20Sopenharmony_ci	else if (h > lmax)
4418c2ecf20Sopenharmony_ci		h = lmax;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	memcpy(vf, &pvr_format[PVR_FORMAT_PIX],
4448c2ecf20Sopenharmony_ci			sizeof(struct v4l2_format));
4458c2ecf20Sopenharmony_ci	vf->fmt.pix.width = w;
4468c2ecf20Sopenharmony_ci	vf->fmt.pix.height = h;
4478c2ecf20Sopenharmony_ci	return 0;
4488c2ecf20Sopenharmony_ci}
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_cistatic int pvr2_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *vf)
4518c2ecf20Sopenharmony_ci{
4528c2ecf20Sopenharmony_ci	struct pvr2_v4l2_fh *fh = file->private_data;
4538c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
4548c2ecf20Sopenharmony_ci	struct pvr2_ctrl *hcp, *vcp;
4558c2ecf20Sopenharmony_ci	int ret = pvr2_try_fmt_vid_cap(file, fh, vf);
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	if (ret)
4588c2ecf20Sopenharmony_ci		return ret;
4598c2ecf20Sopenharmony_ci	hcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_HRES);
4608c2ecf20Sopenharmony_ci	vcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_VRES);
4618c2ecf20Sopenharmony_ci	pvr2_ctrl_set_value(hcp, vf->fmt.pix.width);
4628c2ecf20Sopenharmony_ci	pvr2_ctrl_set_value(vcp, vf->fmt.pix.height);
4638c2ecf20Sopenharmony_ci	pvr2_hdw_commit_ctl(hdw);
4648c2ecf20Sopenharmony_ci	return 0;
4658c2ecf20Sopenharmony_ci}
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_cistatic int pvr2_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
4688c2ecf20Sopenharmony_ci{
4698c2ecf20Sopenharmony_ci	struct pvr2_v4l2_fh *fh = file->private_data;
4708c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
4718c2ecf20Sopenharmony_ci	struct pvr2_v4l2_dev *pdi = fh->pdi;
4728c2ecf20Sopenharmony_ci	int ret;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	if (!fh->pdi->stream) {
4758c2ecf20Sopenharmony_ci		/* No stream defined for this node.  This means
4768c2ecf20Sopenharmony_ci		   that we're not currently allowed to stream from
4778c2ecf20Sopenharmony_ci		   this node. */
4788c2ecf20Sopenharmony_ci		return -EPERM;
4798c2ecf20Sopenharmony_ci	}
4808c2ecf20Sopenharmony_ci	ret = pvr2_hdw_set_stream_type(hdw, pdi->config);
4818c2ecf20Sopenharmony_ci	if (ret < 0)
4828c2ecf20Sopenharmony_ci		return ret;
4838c2ecf20Sopenharmony_ci	return pvr2_hdw_set_streaming(hdw, !0);
4848c2ecf20Sopenharmony_ci}
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_cistatic int pvr2_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
4878c2ecf20Sopenharmony_ci{
4888c2ecf20Sopenharmony_ci	struct pvr2_v4l2_fh *fh = file->private_data;
4898c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	if (!fh->pdi->stream) {
4928c2ecf20Sopenharmony_ci		/* No stream defined for this node.  This means
4938c2ecf20Sopenharmony_ci		   that we're not currently allowed to stream from
4948c2ecf20Sopenharmony_ci		   this node. */
4958c2ecf20Sopenharmony_ci		return -EPERM;
4968c2ecf20Sopenharmony_ci	}
4978c2ecf20Sopenharmony_ci	return pvr2_hdw_set_streaming(hdw, 0);
4988c2ecf20Sopenharmony_ci}
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_cistatic int pvr2_queryctrl(struct file *file, void *priv,
5018c2ecf20Sopenharmony_ci		struct v4l2_queryctrl *vc)
5028c2ecf20Sopenharmony_ci{
5038c2ecf20Sopenharmony_ci	struct pvr2_v4l2_fh *fh = file->private_data;
5048c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
5058c2ecf20Sopenharmony_ci	struct pvr2_ctrl *cptr;
5068c2ecf20Sopenharmony_ci	int val;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	if (vc->id & V4L2_CTRL_FLAG_NEXT_CTRL) {
5098c2ecf20Sopenharmony_ci		cptr = pvr2_hdw_get_ctrl_nextv4l(
5108c2ecf20Sopenharmony_ci				hdw, (vc->id & ~V4L2_CTRL_FLAG_NEXT_CTRL));
5118c2ecf20Sopenharmony_ci		if (cptr)
5128c2ecf20Sopenharmony_ci			vc->id = pvr2_ctrl_get_v4lid(cptr);
5138c2ecf20Sopenharmony_ci	} else {
5148c2ecf20Sopenharmony_ci		cptr = pvr2_hdw_get_ctrl_v4l(hdw, vc->id);
5158c2ecf20Sopenharmony_ci	}
5168c2ecf20Sopenharmony_ci	if (!cptr) {
5178c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_V4LIOCTL,
5188c2ecf20Sopenharmony_ci				"QUERYCTRL id=0x%x not implemented here",
5198c2ecf20Sopenharmony_ci				vc->id);
5208c2ecf20Sopenharmony_ci		return -EINVAL;
5218c2ecf20Sopenharmony_ci	}
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_V4LIOCTL,
5248c2ecf20Sopenharmony_ci			"QUERYCTRL id=0x%x mapping name=%s (%s)",
5258c2ecf20Sopenharmony_ci			vc->id, pvr2_ctrl_get_name(cptr),
5268c2ecf20Sopenharmony_ci			pvr2_ctrl_get_desc(cptr));
5278c2ecf20Sopenharmony_ci	strscpy(vc->name, pvr2_ctrl_get_desc(cptr), sizeof(vc->name));
5288c2ecf20Sopenharmony_ci	vc->flags = pvr2_ctrl_get_v4lflags(cptr);
5298c2ecf20Sopenharmony_ci	pvr2_ctrl_get_def(cptr, &val);
5308c2ecf20Sopenharmony_ci	vc->default_value = val;
5318c2ecf20Sopenharmony_ci	switch (pvr2_ctrl_get_type(cptr)) {
5328c2ecf20Sopenharmony_ci	case pvr2_ctl_enum:
5338c2ecf20Sopenharmony_ci		vc->type = V4L2_CTRL_TYPE_MENU;
5348c2ecf20Sopenharmony_ci		vc->minimum = 0;
5358c2ecf20Sopenharmony_ci		vc->maximum = pvr2_ctrl_get_cnt(cptr) - 1;
5368c2ecf20Sopenharmony_ci		vc->step = 1;
5378c2ecf20Sopenharmony_ci		break;
5388c2ecf20Sopenharmony_ci	case pvr2_ctl_bool:
5398c2ecf20Sopenharmony_ci		vc->type = V4L2_CTRL_TYPE_BOOLEAN;
5408c2ecf20Sopenharmony_ci		vc->minimum = 0;
5418c2ecf20Sopenharmony_ci		vc->maximum = 1;
5428c2ecf20Sopenharmony_ci		vc->step = 1;
5438c2ecf20Sopenharmony_ci		break;
5448c2ecf20Sopenharmony_ci	case pvr2_ctl_int:
5458c2ecf20Sopenharmony_ci		vc->type = V4L2_CTRL_TYPE_INTEGER;
5468c2ecf20Sopenharmony_ci		vc->minimum = pvr2_ctrl_get_min(cptr);
5478c2ecf20Sopenharmony_ci		vc->maximum = pvr2_ctrl_get_max(cptr);
5488c2ecf20Sopenharmony_ci		vc->step = 1;
5498c2ecf20Sopenharmony_ci		break;
5508c2ecf20Sopenharmony_ci	default:
5518c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_V4LIOCTL,
5528c2ecf20Sopenharmony_ci				"QUERYCTRL id=0x%x name=%s not mappable",
5538c2ecf20Sopenharmony_ci				vc->id, pvr2_ctrl_get_name(cptr));
5548c2ecf20Sopenharmony_ci		return -EINVAL;
5558c2ecf20Sopenharmony_ci	}
5568c2ecf20Sopenharmony_ci	return 0;
5578c2ecf20Sopenharmony_ci}
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_cistatic int pvr2_querymenu(struct file *file, void *priv, struct v4l2_querymenu *vm)
5608c2ecf20Sopenharmony_ci{
5618c2ecf20Sopenharmony_ci	struct pvr2_v4l2_fh *fh = file->private_data;
5628c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
5638c2ecf20Sopenharmony_ci	unsigned int cnt = 0;
5648c2ecf20Sopenharmony_ci	int ret;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	ret = pvr2_ctrl_get_valname(pvr2_hdw_get_ctrl_v4l(hdw, vm->id),
5678c2ecf20Sopenharmony_ci			vm->index,
5688c2ecf20Sopenharmony_ci			vm->name, sizeof(vm->name) - 1,
5698c2ecf20Sopenharmony_ci			&cnt);
5708c2ecf20Sopenharmony_ci	vm->name[cnt] = 0;
5718c2ecf20Sopenharmony_ci	return ret;
5728c2ecf20Sopenharmony_ci}
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_cistatic int pvr2_g_ctrl(struct file *file, void *priv, struct v4l2_control *vc)
5758c2ecf20Sopenharmony_ci{
5768c2ecf20Sopenharmony_ci	struct pvr2_v4l2_fh *fh = file->private_data;
5778c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
5788c2ecf20Sopenharmony_ci	int val = 0;
5798c2ecf20Sopenharmony_ci	int ret;
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	ret = pvr2_ctrl_get_value(pvr2_hdw_get_ctrl_v4l(hdw, vc->id),
5828c2ecf20Sopenharmony_ci			&val);
5838c2ecf20Sopenharmony_ci	vc->value = val;
5848c2ecf20Sopenharmony_ci	return ret;
5858c2ecf20Sopenharmony_ci}
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_cistatic int pvr2_s_ctrl(struct file *file, void *priv, struct v4l2_control *vc)
5888c2ecf20Sopenharmony_ci{
5898c2ecf20Sopenharmony_ci	struct pvr2_v4l2_fh *fh = file->private_data;
5908c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
5918c2ecf20Sopenharmony_ci	int ret;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	ret = pvr2_ctrl_set_value(pvr2_hdw_get_ctrl_v4l(hdw, vc->id),
5948c2ecf20Sopenharmony_ci			vc->value);
5958c2ecf20Sopenharmony_ci	pvr2_hdw_commit_ctl(hdw);
5968c2ecf20Sopenharmony_ci	return ret;
5978c2ecf20Sopenharmony_ci}
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_cistatic int pvr2_g_ext_ctrls(struct file *file, void *priv,
6008c2ecf20Sopenharmony_ci					struct v4l2_ext_controls *ctls)
6018c2ecf20Sopenharmony_ci{
6028c2ecf20Sopenharmony_ci	struct pvr2_v4l2_fh *fh = file->private_data;
6038c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
6048c2ecf20Sopenharmony_ci	struct v4l2_ext_control *ctrl;
6058c2ecf20Sopenharmony_ci	struct pvr2_ctrl *cptr;
6068c2ecf20Sopenharmony_ci	unsigned int idx;
6078c2ecf20Sopenharmony_ci	int val;
6088c2ecf20Sopenharmony_ci	int ret;
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	ret = 0;
6118c2ecf20Sopenharmony_ci	for (idx = 0; idx < ctls->count; idx++) {
6128c2ecf20Sopenharmony_ci		ctrl = ctls->controls + idx;
6138c2ecf20Sopenharmony_ci		cptr = pvr2_hdw_get_ctrl_v4l(hdw, ctrl->id);
6148c2ecf20Sopenharmony_ci		if (cptr) {
6158c2ecf20Sopenharmony_ci			if (ctls->which == V4L2_CTRL_WHICH_DEF_VAL)
6168c2ecf20Sopenharmony_ci				pvr2_ctrl_get_def(cptr, &val);
6178c2ecf20Sopenharmony_ci			else
6188c2ecf20Sopenharmony_ci				ret = pvr2_ctrl_get_value(cptr, &val);
6198c2ecf20Sopenharmony_ci		} else
6208c2ecf20Sopenharmony_ci			ret = -EINVAL;
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci		if (ret) {
6238c2ecf20Sopenharmony_ci			ctls->error_idx = idx;
6248c2ecf20Sopenharmony_ci			return ret;
6258c2ecf20Sopenharmony_ci		}
6268c2ecf20Sopenharmony_ci		/* Ensure that if read as a 64 bit value, the user
6278c2ecf20Sopenharmony_ci		   will still get a hopefully sane value */
6288c2ecf20Sopenharmony_ci		ctrl->value64 = 0;
6298c2ecf20Sopenharmony_ci		ctrl->value = val;
6308c2ecf20Sopenharmony_ci	}
6318c2ecf20Sopenharmony_ci	return 0;
6328c2ecf20Sopenharmony_ci}
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_cistatic int pvr2_s_ext_ctrls(struct file *file, void *priv,
6358c2ecf20Sopenharmony_ci		struct v4l2_ext_controls *ctls)
6368c2ecf20Sopenharmony_ci{
6378c2ecf20Sopenharmony_ci	struct pvr2_v4l2_fh *fh = file->private_data;
6388c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
6398c2ecf20Sopenharmony_ci	struct v4l2_ext_control *ctrl;
6408c2ecf20Sopenharmony_ci	unsigned int idx;
6418c2ecf20Sopenharmony_ci	int ret;
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	/* Default value cannot be changed */
6448c2ecf20Sopenharmony_ci	if (ctls->which == V4L2_CTRL_WHICH_DEF_VAL)
6458c2ecf20Sopenharmony_ci		return -EINVAL;
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	ret = 0;
6488c2ecf20Sopenharmony_ci	for (idx = 0; idx < ctls->count; idx++) {
6498c2ecf20Sopenharmony_ci		ctrl = ctls->controls + idx;
6508c2ecf20Sopenharmony_ci		ret = pvr2_ctrl_set_value(
6518c2ecf20Sopenharmony_ci				pvr2_hdw_get_ctrl_v4l(hdw, ctrl->id),
6528c2ecf20Sopenharmony_ci				ctrl->value);
6538c2ecf20Sopenharmony_ci		if (ret) {
6548c2ecf20Sopenharmony_ci			ctls->error_idx = idx;
6558c2ecf20Sopenharmony_ci			goto commit;
6568c2ecf20Sopenharmony_ci		}
6578c2ecf20Sopenharmony_ci	}
6588c2ecf20Sopenharmony_cicommit:
6598c2ecf20Sopenharmony_ci	pvr2_hdw_commit_ctl(hdw);
6608c2ecf20Sopenharmony_ci	return ret;
6618c2ecf20Sopenharmony_ci}
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_cistatic int pvr2_try_ext_ctrls(struct file *file, void *priv,
6648c2ecf20Sopenharmony_ci		struct v4l2_ext_controls *ctls)
6658c2ecf20Sopenharmony_ci{
6668c2ecf20Sopenharmony_ci	struct pvr2_v4l2_fh *fh = file->private_data;
6678c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
6688c2ecf20Sopenharmony_ci	struct v4l2_ext_control *ctrl;
6698c2ecf20Sopenharmony_ci	struct pvr2_ctrl *pctl;
6708c2ecf20Sopenharmony_ci	unsigned int idx;
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	/* For the moment just validate that the requested control
6738c2ecf20Sopenharmony_ci	   actually exists. */
6748c2ecf20Sopenharmony_ci	for (idx = 0; idx < ctls->count; idx++) {
6758c2ecf20Sopenharmony_ci		ctrl = ctls->controls + idx;
6768c2ecf20Sopenharmony_ci		pctl = pvr2_hdw_get_ctrl_v4l(hdw, ctrl->id);
6778c2ecf20Sopenharmony_ci		if (!pctl) {
6788c2ecf20Sopenharmony_ci			ctls->error_idx = idx;
6798c2ecf20Sopenharmony_ci			return -EINVAL;
6808c2ecf20Sopenharmony_ci		}
6818c2ecf20Sopenharmony_ci	}
6828c2ecf20Sopenharmony_ci	return 0;
6838c2ecf20Sopenharmony_ci}
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_cistatic int pvr2_g_pixelaspect(struct file *file, void *priv,
6868c2ecf20Sopenharmony_ci			      int type, struct v4l2_fract *f)
6878c2ecf20Sopenharmony_ci{
6888c2ecf20Sopenharmony_ci	struct pvr2_v4l2_fh *fh = file->private_data;
6898c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
6908c2ecf20Sopenharmony_ci	struct v4l2_cropcap cap = { .type = type };
6918c2ecf20Sopenharmony_ci	int ret;
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
6948c2ecf20Sopenharmony_ci		return -EINVAL;
6958c2ecf20Sopenharmony_ci	ret = pvr2_hdw_get_cropcap(hdw, &cap);
6968c2ecf20Sopenharmony_ci	if (!ret)
6978c2ecf20Sopenharmony_ci		*f = cap.pixelaspect;
6988c2ecf20Sopenharmony_ci	return ret;
6998c2ecf20Sopenharmony_ci}
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_cistatic int pvr2_g_selection(struct file *file, void *priv,
7028c2ecf20Sopenharmony_ci			    struct v4l2_selection *sel)
7038c2ecf20Sopenharmony_ci{
7048c2ecf20Sopenharmony_ci	struct pvr2_v4l2_fh *fh = file->private_data;
7058c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
7068c2ecf20Sopenharmony_ci	struct v4l2_cropcap cap;
7078c2ecf20Sopenharmony_ci	int val = 0;
7088c2ecf20Sopenharmony_ci	int ret;
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
7118c2ecf20Sopenharmony_ci		return -EINVAL;
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	switch (sel->target) {
7168c2ecf20Sopenharmony_ci	case V4L2_SEL_TGT_CROP:
7178c2ecf20Sopenharmony_ci		ret = pvr2_ctrl_get_value(
7188c2ecf20Sopenharmony_ci			  pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL), &val);
7198c2ecf20Sopenharmony_ci		if (ret != 0)
7208c2ecf20Sopenharmony_ci			return -EINVAL;
7218c2ecf20Sopenharmony_ci		sel->r.left = val;
7228c2ecf20Sopenharmony_ci		ret = pvr2_ctrl_get_value(
7238c2ecf20Sopenharmony_ci			  pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT), &val);
7248c2ecf20Sopenharmony_ci		if (ret != 0)
7258c2ecf20Sopenharmony_ci			return -EINVAL;
7268c2ecf20Sopenharmony_ci		sel->r.top = val;
7278c2ecf20Sopenharmony_ci		ret = pvr2_ctrl_get_value(
7288c2ecf20Sopenharmony_ci			  pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW), &val);
7298c2ecf20Sopenharmony_ci		if (ret != 0)
7308c2ecf20Sopenharmony_ci			return -EINVAL;
7318c2ecf20Sopenharmony_ci		sel->r.width = val;
7328c2ecf20Sopenharmony_ci		ret = pvr2_ctrl_get_value(
7338c2ecf20Sopenharmony_ci			  pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH), &val);
7348c2ecf20Sopenharmony_ci		if (ret != 0)
7358c2ecf20Sopenharmony_ci			return -EINVAL;
7368c2ecf20Sopenharmony_ci		sel->r.height = val;
7378c2ecf20Sopenharmony_ci		break;
7388c2ecf20Sopenharmony_ci	case V4L2_SEL_TGT_CROP_DEFAULT:
7398c2ecf20Sopenharmony_ci		ret = pvr2_hdw_get_cropcap(hdw, &cap);
7408c2ecf20Sopenharmony_ci		sel->r = cap.defrect;
7418c2ecf20Sopenharmony_ci		break;
7428c2ecf20Sopenharmony_ci	case V4L2_SEL_TGT_CROP_BOUNDS:
7438c2ecf20Sopenharmony_ci		ret = pvr2_hdw_get_cropcap(hdw, &cap);
7448c2ecf20Sopenharmony_ci		sel->r = cap.bounds;
7458c2ecf20Sopenharmony_ci		break;
7468c2ecf20Sopenharmony_ci	default:
7478c2ecf20Sopenharmony_ci		return -EINVAL;
7488c2ecf20Sopenharmony_ci	}
7498c2ecf20Sopenharmony_ci	return ret;
7508c2ecf20Sopenharmony_ci}
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_cistatic int pvr2_s_selection(struct file *file, void *priv,
7538c2ecf20Sopenharmony_ci			    struct v4l2_selection *sel)
7548c2ecf20Sopenharmony_ci{
7558c2ecf20Sopenharmony_ci	struct pvr2_v4l2_fh *fh = file->private_data;
7568c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
7578c2ecf20Sopenharmony_ci	int ret;
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
7608c2ecf20Sopenharmony_ci	    sel->target != V4L2_SEL_TGT_CROP)
7618c2ecf20Sopenharmony_ci		return -EINVAL;
7628c2ecf20Sopenharmony_ci	ret = pvr2_ctrl_set_value(
7638c2ecf20Sopenharmony_ci			pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL),
7648c2ecf20Sopenharmony_ci			sel->r.left);
7658c2ecf20Sopenharmony_ci	if (ret != 0)
7668c2ecf20Sopenharmony_ci		goto commit;
7678c2ecf20Sopenharmony_ci	ret = pvr2_ctrl_set_value(
7688c2ecf20Sopenharmony_ci			pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT),
7698c2ecf20Sopenharmony_ci			sel->r.top);
7708c2ecf20Sopenharmony_ci	if (ret != 0)
7718c2ecf20Sopenharmony_ci		goto commit;
7728c2ecf20Sopenharmony_ci	ret = pvr2_ctrl_set_value(
7738c2ecf20Sopenharmony_ci			pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW),
7748c2ecf20Sopenharmony_ci			sel->r.width);
7758c2ecf20Sopenharmony_ci	if (ret != 0)
7768c2ecf20Sopenharmony_ci		goto commit;
7778c2ecf20Sopenharmony_ci	ret = pvr2_ctrl_set_value(
7788c2ecf20Sopenharmony_ci			pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH),
7798c2ecf20Sopenharmony_ci			sel->r.height);
7808c2ecf20Sopenharmony_cicommit:
7818c2ecf20Sopenharmony_ci	pvr2_hdw_commit_ctl(hdw);
7828c2ecf20Sopenharmony_ci	return ret;
7838c2ecf20Sopenharmony_ci}
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_cistatic int pvr2_log_status(struct file *file, void *priv)
7868c2ecf20Sopenharmony_ci{
7878c2ecf20Sopenharmony_ci	struct pvr2_v4l2_fh *fh = file->private_data;
7888c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci	pvr2_hdw_trigger_module_log(hdw);
7918c2ecf20Sopenharmony_ci	return 0;
7928c2ecf20Sopenharmony_ci}
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_cistatic const struct v4l2_ioctl_ops pvr2_ioctl_ops = {
7958c2ecf20Sopenharmony_ci	.vidioc_querycap		    = pvr2_querycap,
7968c2ecf20Sopenharmony_ci	.vidioc_s_audio			    = pvr2_s_audio,
7978c2ecf20Sopenharmony_ci	.vidioc_g_audio			    = pvr2_g_audio,
7988c2ecf20Sopenharmony_ci	.vidioc_enumaudio		    = pvr2_enumaudio,
7998c2ecf20Sopenharmony_ci	.vidioc_enum_input		    = pvr2_enum_input,
8008c2ecf20Sopenharmony_ci	.vidioc_g_pixelaspect		    = pvr2_g_pixelaspect,
8018c2ecf20Sopenharmony_ci	.vidioc_s_selection		    = pvr2_s_selection,
8028c2ecf20Sopenharmony_ci	.vidioc_g_selection		    = pvr2_g_selection,
8038c2ecf20Sopenharmony_ci	.vidioc_g_input			    = pvr2_g_input,
8048c2ecf20Sopenharmony_ci	.vidioc_s_input			    = pvr2_s_input,
8058c2ecf20Sopenharmony_ci	.vidioc_g_frequency		    = pvr2_g_frequency,
8068c2ecf20Sopenharmony_ci	.vidioc_s_frequency		    = pvr2_s_frequency,
8078c2ecf20Sopenharmony_ci	.vidioc_s_tuner			    = pvr2_s_tuner,
8088c2ecf20Sopenharmony_ci	.vidioc_g_tuner			    = pvr2_g_tuner,
8098c2ecf20Sopenharmony_ci	.vidioc_g_std			    = pvr2_g_std,
8108c2ecf20Sopenharmony_ci	.vidioc_s_std			    = pvr2_s_std,
8118c2ecf20Sopenharmony_ci	.vidioc_querystd		    = pvr2_querystd,
8128c2ecf20Sopenharmony_ci	.vidioc_log_status		    = pvr2_log_status,
8138c2ecf20Sopenharmony_ci	.vidioc_enum_fmt_vid_cap	    = pvr2_enum_fmt_vid_cap,
8148c2ecf20Sopenharmony_ci	.vidioc_g_fmt_vid_cap		    = pvr2_g_fmt_vid_cap,
8158c2ecf20Sopenharmony_ci	.vidioc_s_fmt_vid_cap		    = pvr2_s_fmt_vid_cap,
8168c2ecf20Sopenharmony_ci	.vidioc_try_fmt_vid_cap		    = pvr2_try_fmt_vid_cap,
8178c2ecf20Sopenharmony_ci	.vidioc_streamon		    = pvr2_streamon,
8188c2ecf20Sopenharmony_ci	.vidioc_streamoff		    = pvr2_streamoff,
8198c2ecf20Sopenharmony_ci	.vidioc_queryctrl		    = pvr2_queryctrl,
8208c2ecf20Sopenharmony_ci	.vidioc_querymenu		    = pvr2_querymenu,
8218c2ecf20Sopenharmony_ci	.vidioc_g_ctrl			    = pvr2_g_ctrl,
8228c2ecf20Sopenharmony_ci	.vidioc_s_ctrl			    = pvr2_s_ctrl,
8238c2ecf20Sopenharmony_ci	.vidioc_g_ext_ctrls		    = pvr2_g_ext_ctrls,
8248c2ecf20Sopenharmony_ci	.vidioc_s_ext_ctrls		    = pvr2_s_ext_ctrls,
8258c2ecf20Sopenharmony_ci	.vidioc_try_ext_ctrls		    = pvr2_try_ext_ctrls,
8268c2ecf20Sopenharmony_ci};
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_cistatic void pvr2_v4l2_dev_destroy(struct pvr2_v4l2_dev *dip)
8298c2ecf20Sopenharmony_ci{
8308c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = dip->v4lp->channel.mc_head->hdw;
8318c2ecf20Sopenharmony_ci	enum pvr2_config cfg = dip->config;
8328c2ecf20Sopenharmony_ci	char msg[80];
8338c2ecf20Sopenharmony_ci	unsigned int mcnt;
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	/* Construct the unregistration message *before* we actually
8368c2ecf20Sopenharmony_ci	   perform the unregistration step.  By doing it this way we don't
8378c2ecf20Sopenharmony_ci	   have to worry about potentially touching deleted resources. */
8388c2ecf20Sopenharmony_ci	mcnt = scnprintf(msg, sizeof(msg) - 1,
8398c2ecf20Sopenharmony_ci			 "pvrusb2: unregistered device %s [%s]",
8408c2ecf20Sopenharmony_ci			 video_device_node_name(&dip->devbase),
8418c2ecf20Sopenharmony_ci			 pvr2_config_get_name(cfg));
8428c2ecf20Sopenharmony_ci	msg[mcnt] = 0;
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	pvr2_hdw_v4l_store_minor_number(hdw,dip->minor_type,-1);
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci	/* Paranoia */
8478c2ecf20Sopenharmony_ci	dip->v4lp = NULL;
8488c2ecf20Sopenharmony_ci	dip->stream = NULL;
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	/* Actual deallocation happens later when all internal references
8518c2ecf20Sopenharmony_ci	   are gone. */
8528c2ecf20Sopenharmony_ci	video_unregister_device(&dip->devbase);
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	pr_info("%s\n", msg);
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci}
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_cistatic void pvr2_v4l2_dev_disassociate_parent(struct pvr2_v4l2_dev *dip)
8608c2ecf20Sopenharmony_ci{
8618c2ecf20Sopenharmony_ci	if (!dip) return;
8628c2ecf20Sopenharmony_ci	if (!dip->devbase.v4l2_dev->dev) return;
8638c2ecf20Sopenharmony_ci	dip->devbase.v4l2_dev->dev = NULL;
8648c2ecf20Sopenharmony_ci	device_move(&dip->devbase.dev, NULL, DPM_ORDER_NONE);
8658c2ecf20Sopenharmony_ci}
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_cistatic void pvr2_v4l2_destroy_no_lock(struct pvr2_v4l2 *vp)
8698c2ecf20Sopenharmony_ci{
8708c2ecf20Sopenharmony_ci	if (vp->dev_video) {
8718c2ecf20Sopenharmony_ci		pvr2_v4l2_dev_destroy(vp->dev_video);
8728c2ecf20Sopenharmony_ci		vp->dev_video = NULL;
8738c2ecf20Sopenharmony_ci	}
8748c2ecf20Sopenharmony_ci	if (vp->dev_radio) {
8758c2ecf20Sopenharmony_ci		pvr2_v4l2_dev_destroy(vp->dev_radio);
8768c2ecf20Sopenharmony_ci		vp->dev_radio = NULL;
8778c2ecf20Sopenharmony_ci	}
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr2_v4l2 id=%p",vp);
8808c2ecf20Sopenharmony_ci	pvr2_channel_done(&vp->channel);
8818c2ecf20Sopenharmony_ci	kfree(vp);
8828c2ecf20Sopenharmony_ci}
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_cistatic void pvr2_video_device_release(struct video_device *vdev)
8868c2ecf20Sopenharmony_ci{
8878c2ecf20Sopenharmony_ci	struct pvr2_v4l2_dev *dev;
8888c2ecf20Sopenharmony_ci	dev = container_of(vdev,struct pvr2_v4l2_dev,devbase);
8898c2ecf20Sopenharmony_ci	kfree(dev);
8908c2ecf20Sopenharmony_ci}
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_cistatic void pvr2_v4l2_internal_check(struct pvr2_channel *chp)
8948c2ecf20Sopenharmony_ci{
8958c2ecf20Sopenharmony_ci	struct pvr2_v4l2 *vp;
8968c2ecf20Sopenharmony_ci	vp = container_of(chp,struct pvr2_v4l2,channel);
8978c2ecf20Sopenharmony_ci	if (!vp->channel.mc_head->disconnect_flag) return;
8988c2ecf20Sopenharmony_ci	pvr2_v4l2_dev_disassociate_parent(vp->dev_video);
8998c2ecf20Sopenharmony_ci	pvr2_v4l2_dev_disassociate_parent(vp->dev_radio);
9008c2ecf20Sopenharmony_ci	if (!list_empty(&vp->dev_video->devbase.fh_list) ||
9018c2ecf20Sopenharmony_ci	    (vp->dev_radio &&
9028c2ecf20Sopenharmony_ci	     !list_empty(&vp->dev_radio->devbase.fh_list))) {
9038c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_STRUCT,
9048c2ecf20Sopenharmony_ci			   "pvr2_v4l2 internal_check exit-empty id=%p", vp);
9058c2ecf20Sopenharmony_ci		return;
9068c2ecf20Sopenharmony_ci	}
9078c2ecf20Sopenharmony_ci	pvr2_v4l2_destroy_no_lock(vp);
9088c2ecf20Sopenharmony_ci}
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_cistatic int pvr2_v4l2_release(struct file *file)
9128c2ecf20Sopenharmony_ci{
9138c2ecf20Sopenharmony_ci	struct pvr2_v4l2_fh *fhp = file->private_data;
9148c2ecf20Sopenharmony_ci	struct pvr2_v4l2 *vp = fhp->pdi->v4lp;
9158c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = fhp->channel.mc_head->hdw;
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_OPEN_CLOSE,"pvr2_v4l2_release");
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	if (fhp->rhp) {
9208c2ecf20Sopenharmony_ci		struct pvr2_stream *sp;
9218c2ecf20Sopenharmony_ci		pvr2_hdw_set_streaming(hdw,0);
9228c2ecf20Sopenharmony_ci		sp = pvr2_ioread_get_stream(fhp->rhp);
9238c2ecf20Sopenharmony_ci		if (sp) pvr2_stream_set_callback(sp,NULL,NULL);
9248c2ecf20Sopenharmony_ci		pvr2_ioread_destroy(fhp->rhp);
9258c2ecf20Sopenharmony_ci		fhp->rhp = NULL;
9268c2ecf20Sopenharmony_ci	}
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci	v4l2_fh_del(&fhp->fh);
9298c2ecf20Sopenharmony_ci	v4l2_fh_exit(&fhp->fh);
9308c2ecf20Sopenharmony_ci	file->private_data = NULL;
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci	pvr2_channel_done(&fhp->channel);
9338c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_STRUCT,
9348c2ecf20Sopenharmony_ci		   "Destroying pvr_v4l2_fh id=%p",fhp);
9358c2ecf20Sopenharmony_ci	if (fhp->input_map) {
9368c2ecf20Sopenharmony_ci		kfree(fhp->input_map);
9378c2ecf20Sopenharmony_ci		fhp->input_map = NULL;
9388c2ecf20Sopenharmony_ci	}
9398c2ecf20Sopenharmony_ci	kfree(fhp);
9408c2ecf20Sopenharmony_ci	if (vp->channel.mc_head->disconnect_flag &&
9418c2ecf20Sopenharmony_ci	    list_empty(&vp->dev_video->devbase.fh_list) &&
9428c2ecf20Sopenharmony_ci	    (!vp->dev_radio ||
9438c2ecf20Sopenharmony_ci	     list_empty(&vp->dev_radio->devbase.fh_list))) {
9448c2ecf20Sopenharmony_ci		pvr2_v4l2_destroy_no_lock(vp);
9458c2ecf20Sopenharmony_ci	}
9468c2ecf20Sopenharmony_ci	return 0;
9478c2ecf20Sopenharmony_ci}
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_cistatic int pvr2_v4l2_open(struct file *file)
9518c2ecf20Sopenharmony_ci{
9528c2ecf20Sopenharmony_ci	struct pvr2_v4l2_dev *dip; /* Our own context pointer */
9538c2ecf20Sopenharmony_ci	struct pvr2_v4l2_fh *fhp;
9548c2ecf20Sopenharmony_ci	struct pvr2_v4l2 *vp;
9558c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw;
9568c2ecf20Sopenharmony_ci	unsigned int input_mask = 0;
9578c2ecf20Sopenharmony_ci	unsigned int input_cnt,idx;
9588c2ecf20Sopenharmony_ci	int ret = 0;
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci	dip = container_of(video_devdata(file),struct pvr2_v4l2_dev,devbase);
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci	vp = dip->v4lp;
9638c2ecf20Sopenharmony_ci	hdw = vp->channel.hdw;
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_OPEN_CLOSE,"pvr2_v4l2_open");
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci	if (!pvr2_hdw_dev_ok(hdw)) {
9688c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_OPEN_CLOSE,
9698c2ecf20Sopenharmony_ci			   "pvr2_v4l2_open: hardware not ready");
9708c2ecf20Sopenharmony_ci		return -EIO;
9718c2ecf20Sopenharmony_ci	}
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci	fhp = kzalloc(sizeof(*fhp),GFP_KERNEL);
9748c2ecf20Sopenharmony_ci	if (!fhp) {
9758c2ecf20Sopenharmony_ci		return -ENOMEM;
9768c2ecf20Sopenharmony_ci	}
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci	v4l2_fh_init(&fhp->fh, &dip->devbase);
9798c2ecf20Sopenharmony_ci	init_waitqueue_head(&fhp->wait_data);
9808c2ecf20Sopenharmony_ci	fhp->pdi = dip;
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr_v4l2_fh id=%p",fhp);
9838c2ecf20Sopenharmony_ci	pvr2_channel_init(&fhp->channel,vp->channel.mc_head);
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_ci	if (dip->v4l_type == VFL_TYPE_RADIO) {
9868c2ecf20Sopenharmony_ci		/* Opening device as a radio, legal input selection subset
9878c2ecf20Sopenharmony_ci		   is just the radio. */
9888c2ecf20Sopenharmony_ci		input_mask = (1 << PVR2_CVAL_INPUT_RADIO);
9898c2ecf20Sopenharmony_ci	} else {
9908c2ecf20Sopenharmony_ci		/* Opening the main V4L device, legal input selection
9918c2ecf20Sopenharmony_ci		   subset includes all analog inputs. */
9928c2ecf20Sopenharmony_ci		input_mask = ((1 << PVR2_CVAL_INPUT_RADIO) |
9938c2ecf20Sopenharmony_ci			      (1 << PVR2_CVAL_INPUT_TV) |
9948c2ecf20Sopenharmony_ci			      (1 << PVR2_CVAL_INPUT_COMPOSITE) |
9958c2ecf20Sopenharmony_ci			      (1 << PVR2_CVAL_INPUT_SVIDEO));
9968c2ecf20Sopenharmony_ci	}
9978c2ecf20Sopenharmony_ci	ret = pvr2_channel_limit_inputs(&fhp->channel,input_mask);
9988c2ecf20Sopenharmony_ci	if (ret) {
9998c2ecf20Sopenharmony_ci		pvr2_channel_done(&fhp->channel);
10008c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_STRUCT,
10018c2ecf20Sopenharmony_ci			   "Destroying pvr_v4l2_fh id=%p (input mask error)",
10028c2ecf20Sopenharmony_ci			   fhp);
10038c2ecf20Sopenharmony_ci		v4l2_fh_exit(&fhp->fh);
10048c2ecf20Sopenharmony_ci		kfree(fhp);
10058c2ecf20Sopenharmony_ci		return ret;
10068c2ecf20Sopenharmony_ci	}
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci	input_mask &= pvr2_hdw_get_input_available(hdw);
10098c2ecf20Sopenharmony_ci	input_cnt = 0;
10108c2ecf20Sopenharmony_ci	for (idx = 0; idx < (sizeof(input_mask) << 3); idx++) {
10118c2ecf20Sopenharmony_ci		if (input_mask & (1UL << idx)) input_cnt++;
10128c2ecf20Sopenharmony_ci	}
10138c2ecf20Sopenharmony_ci	fhp->input_cnt = input_cnt;
10148c2ecf20Sopenharmony_ci	fhp->input_map = kzalloc(input_cnt,GFP_KERNEL);
10158c2ecf20Sopenharmony_ci	if (!fhp->input_map) {
10168c2ecf20Sopenharmony_ci		pvr2_channel_done(&fhp->channel);
10178c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_STRUCT,
10188c2ecf20Sopenharmony_ci			   "Destroying pvr_v4l2_fh id=%p (input map failure)",
10198c2ecf20Sopenharmony_ci			   fhp);
10208c2ecf20Sopenharmony_ci		v4l2_fh_exit(&fhp->fh);
10218c2ecf20Sopenharmony_ci		kfree(fhp);
10228c2ecf20Sopenharmony_ci		return -ENOMEM;
10238c2ecf20Sopenharmony_ci	}
10248c2ecf20Sopenharmony_ci	input_cnt = 0;
10258c2ecf20Sopenharmony_ci	for (idx = 0; idx < (sizeof(input_mask) << 3); idx++) {
10268c2ecf20Sopenharmony_ci		if (!(input_mask & (1UL << idx))) continue;
10278c2ecf20Sopenharmony_ci		fhp->input_map[input_cnt++] = idx;
10288c2ecf20Sopenharmony_ci	}
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci	fhp->file = file;
10318c2ecf20Sopenharmony_ci	file->private_data = fhp;
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci	fhp->fw_mode_flag = pvr2_hdw_cpufw_get_enabled(hdw);
10348c2ecf20Sopenharmony_ci	v4l2_fh_add(&fhp->fh);
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ci	return 0;
10378c2ecf20Sopenharmony_ci}
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_cistatic void pvr2_v4l2_notify(struct pvr2_v4l2_fh *fhp)
10418c2ecf20Sopenharmony_ci{
10428c2ecf20Sopenharmony_ci	wake_up(&fhp->wait_data);
10438c2ecf20Sopenharmony_ci}
10448c2ecf20Sopenharmony_ci
10458c2ecf20Sopenharmony_cistatic int pvr2_v4l2_iosetup(struct pvr2_v4l2_fh *fh)
10468c2ecf20Sopenharmony_ci{
10478c2ecf20Sopenharmony_ci	int ret;
10488c2ecf20Sopenharmony_ci	struct pvr2_stream *sp;
10498c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw;
10508c2ecf20Sopenharmony_ci	if (fh->rhp) return 0;
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_ci	if (!fh->pdi->stream) {
10538c2ecf20Sopenharmony_ci		/* No stream defined for this node.  This means that we're
10548c2ecf20Sopenharmony_ci		   not currently allowed to stream from this node. */
10558c2ecf20Sopenharmony_ci		return -EPERM;
10568c2ecf20Sopenharmony_ci	}
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ci	/* First read() attempt.  Try to claim the stream and start
10598c2ecf20Sopenharmony_ci	   it... */
10608c2ecf20Sopenharmony_ci	if ((ret = pvr2_channel_claim_stream(&fh->channel,
10618c2ecf20Sopenharmony_ci					     fh->pdi->stream)) != 0) {
10628c2ecf20Sopenharmony_ci		/* Someone else must already have it */
10638c2ecf20Sopenharmony_ci		return ret;
10648c2ecf20Sopenharmony_ci	}
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_ci	fh->rhp = pvr2_channel_create_mpeg_stream(fh->pdi->stream);
10678c2ecf20Sopenharmony_ci	if (!fh->rhp) {
10688c2ecf20Sopenharmony_ci		pvr2_channel_claim_stream(&fh->channel,NULL);
10698c2ecf20Sopenharmony_ci		return -ENOMEM;
10708c2ecf20Sopenharmony_ci	}
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_ci	hdw = fh->channel.mc_head->hdw;
10738c2ecf20Sopenharmony_ci	sp = fh->pdi->stream->stream;
10748c2ecf20Sopenharmony_ci	pvr2_stream_set_callback(sp,(pvr2_stream_callback)pvr2_v4l2_notify,fh);
10758c2ecf20Sopenharmony_ci	pvr2_hdw_set_stream_type(hdw,fh->pdi->config);
10768c2ecf20Sopenharmony_ci	if ((ret = pvr2_hdw_set_streaming(hdw,!0)) < 0) return ret;
10778c2ecf20Sopenharmony_ci	return pvr2_ioread_set_enabled(fh->rhp,!0);
10788c2ecf20Sopenharmony_ci}
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_cistatic ssize_t pvr2_v4l2_read(struct file *file,
10828c2ecf20Sopenharmony_ci			      char __user *buff, size_t count, loff_t *ppos)
10838c2ecf20Sopenharmony_ci{
10848c2ecf20Sopenharmony_ci	struct pvr2_v4l2_fh *fh = file->private_data;
10858c2ecf20Sopenharmony_ci	int ret;
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci	if (fh->fw_mode_flag) {
10888c2ecf20Sopenharmony_ci		struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
10898c2ecf20Sopenharmony_ci		char *tbuf;
10908c2ecf20Sopenharmony_ci		int c1,c2;
10918c2ecf20Sopenharmony_ci		int tcnt = 0;
10928c2ecf20Sopenharmony_ci		unsigned int offs = *ppos;
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_ci		tbuf = kmalloc(PAGE_SIZE,GFP_KERNEL);
10958c2ecf20Sopenharmony_ci		if (!tbuf) return -ENOMEM;
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_ci		while (count) {
10988c2ecf20Sopenharmony_ci			c1 = count;
10998c2ecf20Sopenharmony_ci			if (c1 > PAGE_SIZE) c1 = PAGE_SIZE;
11008c2ecf20Sopenharmony_ci			c2 = pvr2_hdw_cpufw_get(hdw,offs,tbuf,c1);
11018c2ecf20Sopenharmony_ci			if (c2 < 0) {
11028c2ecf20Sopenharmony_ci				tcnt = c2;
11038c2ecf20Sopenharmony_ci				break;
11048c2ecf20Sopenharmony_ci			}
11058c2ecf20Sopenharmony_ci			if (!c2) break;
11068c2ecf20Sopenharmony_ci			if (copy_to_user(buff,tbuf,c2)) {
11078c2ecf20Sopenharmony_ci				tcnt = -EFAULT;
11088c2ecf20Sopenharmony_ci				break;
11098c2ecf20Sopenharmony_ci			}
11108c2ecf20Sopenharmony_ci			offs += c2;
11118c2ecf20Sopenharmony_ci			tcnt += c2;
11128c2ecf20Sopenharmony_ci			buff += c2;
11138c2ecf20Sopenharmony_ci			count -= c2;
11148c2ecf20Sopenharmony_ci			*ppos += c2;
11158c2ecf20Sopenharmony_ci		}
11168c2ecf20Sopenharmony_ci		kfree(tbuf);
11178c2ecf20Sopenharmony_ci		return tcnt;
11188c2ecf20Sopenharmony_ci	}
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_ci	if (!fh->rhp) {
11218c2ecf20Sopenharmony_ci		ret = pvr2_v4l2_iosetup(fh);
11228c2ecf20Sopenharmony_ci		if (ret) {
11238c2ecf20Sopenharmony_ci			return ret;
11248c2ecf20Sopenharmony_ci		}
11258c2ecf20Sopenharmony_ci	}
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_ci	for (;;) {
11288c2ecf20Sopenharmony_ci		ret = pvr2_ioread_read(fh->rhp,buff,count);
11298c2ecf20Sopenharmony_ci		if (ret >= 0) break;
11308c2ecf20Sopenharmony_ci		if (ret != -EAGAIN) break;
11318c2ecf20Sopenharmony_ci		if (file->f_flags & O_NONBLOCK) break;
11328c2ecf20Sopenharmony_ci		/* Doing blocking I/O.  Wait here. */
11338c2ecf20Sopenharmony_ci		ret = wait_event_interruptible(
11348c2ecf20Sopenharmony_ci			fh->wait_data,
11358c2ecf20Sopenharmony_ci			pvr2_ioread_avail(fh->rhp) >= 0);
11368c2ecf20Sopenharmony_ci		if (ret < 0) break;
11378c2ecf20Sopenharmony_ci	}
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ci	return ret;
11408c2ecf20Sopenharmony_ci}
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_cistatic __poll_t pvr2_v4l2_poll(struct file *file, poll_table *wait)
11448c2ecf20Sopenharmony_ci{
11458c2ecf20Sopenharmony_ci	__poll_t mask = 0;
11468c2ecf20Sopenharmony_ci	struct pvr2_v4l2_fh *fh = file->private_data;
11478c2ecf20Sopenharmony_ci	int ret;
11488c2ecf20Sopenharmony_ci
11498c2ecf20Sopenharmony_ci	if (fh->fw_mode_flag) {
11508c2ecf20Sopenharmony_ci		mask |= EPOLLIN | EPOLLRDNORM;
11518c2ecf20Sopenharmony_ci		return mask;
11528c2ecf20Sopenharmony_ci	}
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_ci	if (!fh->rhp) {
11558c2ecf20Sopenharmony_ci		ret = pvr2_v4l2_iosetup(fh);
11568c2ecf20Sopenharmony_ci		if (ret) return EPOLLERR;
11578c2ecf20Sopenharmony_ci	}
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci	poll_wait(file,&fh->wait_data,wait);
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci	if (pvr2_ioread_avail(fh->rhp) >= 0) {
11628c2ecf20Sopenharmony_ci		mask |= EPOLLIN | EPOLLRDNORM;
11638c2ecf20Sopenharmony_ci	}
11648c2ecf20Sopenharmony_ci
11658c2ecf20Sopenharmony_ci	return mask;
11668c2ecf20Sopenharmony_ci}
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_ci
11698c2ecf20Sopenharmony_cistatic const struct v4l2_file_operations vdev_fops = {
11708c2ecf20Sopenharmony_ci	.owner      = THIS_MODULE,
11718c2ecf20Sopenharmony_ci	.open       = pvr2_v4l2_open,
11728c2ecf20Sopenharmony_ci	.release    = pvr2_v4l2_release,
11738c2ecf20Sopenharmony_ci	.read       = pvr2_v4l2_read,
11748c2ecf20Sopenharmony_ci	.unlocked_ioctl = video_ioctl2,
11758c2ecf20Sopenharmony_ci	.poll       = pvr2_v4l2_poll,
11768c2ecf20Sopenharmony_ci};
11778c2ecf20Sopenharmony_ci
11788c2ecf20Sopenharmony_ci
11798c2ecf20Sopenharmony_cistatic const struct video_device vdev_template = {
11808c2ecf20Sopenharmony_ci	.fops       = &vdev_fops,
11818c2ecf20Sopenharmony_ci};
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_cistatic void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip,
11858c2ecf20Sopenharmony_ci			       struct pvr2_v4l2 *vp,
11868c2ecf20Sopenharmony_ci			       int v4l_type)
11878c2ecf20Sopenharmony_ci{
11888c2ecf20Sopenharmony_ci	int mindevnum;
11898c2ecf20Sopenharmony_ci	int unit_number;
11908c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw;
11918c2ecf20Sopenharmony_ci	int *nr_ptr = NULL;
11928c2ecf20Sopenharmony_ci	u32 caps = V4L2_CAP_TUNER | V4L2_CAP_READWRITE;
11938c2ecf20Sopenharmony_ci
11948c2ecf20Sopenharmony_ci	dip->v4lp = vp;
11958c2ecf20Sopenharmony_ci
11968c2ecf20Sopenharmony_ci	hdw = vp->channel.mc_head->hdw;
11978c2ecf20Sopenharmony_ci	dip->v4l_type = v4l_type;
11988c2ecf20Sopenharmony_ci	switch (v4l_type) {
11998c2ecf20Sopenharmony_ci	case VFL_TYPE_VIDEO:
12008c2ecf20Sopenharmony_ci		dip->stream = &vp->channel.mc_head->video_stream;
12018c2ecf20Sopenharmony_ci		dip->config = pvr2_config_mpeg;
12028c2ecf20Sopenharmony_ci		dip->minor_type = pvr2_v4l_type_video;
12038c2ecf20Sopenharmony_ci		nr_ptr = video_nr;
12048c2ecf20Sopenharmony_ci		caps |= V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_AUDIO;
12058c2ecf20Sopenharmony_ci		if (!dip->stream) {
12068c2ecf20Sopenharmony_ci			pr_err(KBUILD_MODNAME
12078c2ecf20Sopenharmony_ci				": Failed to set up pvrusb2 v4l video dev due to missing stream instance\n");
12088c2ecf20Sopenharmony_ci			return;
12098c2ecf20Sopenharmony_ci		}
12108c2ecf20Sopenharmony_ci		break;
12118c2ecf20Sopenharmony_ci	case VFL_TYPE_VBI:
12128c2ecf20Sopenharmony_ci		dip->config = pvr2_config_vbi;
12138c2ecf20Sopenharmony_ci		dip->minor_type = pvr2_v4l_type_vbi;
12148c2ecf20Sopenharmony_ci		nr_ptr = vbi_nr;
12158c2ecf20Sopenharmony_ci		caps |= V4L2_CAP_VBI_CAPTURE;
12168c2ecf20Sopenharmony_ci		break;
12178c2ecf20Sopenharmony_ci	case VFL_TYPE_RADIO:
12188c2ecf20Sopenharmony_ci		dip->stream = &vp->channel.mc_head->video_stream;
12198c2ecf20Sopenharmony_ci		dip->config = pvr2_config_mpeg;
12208c2ecf20Sopenharmony_ci		dip->minor_type = pvr2_v4l_type_radio;
12218c2ecf20Sopenharmony_ci		nr_ptr = radio_nr;
12228c2ecf20Sopenharmony_ci		caps |= V4L2_CAP_RADIO;
12238c2ecf20Sopenharmony_ci		break;
12248c2ecf20Sopenharmony_ci	default:
12258c2ecf20Sopenharmony_ci		/* Bail out (this should be impossible) */
12268c2ecf20Sopenharmony_ci		pr_err(KBUILD_MODNAME ": Failed to set up pvrusb2 v4l dev due to unrecognized config\n");
12278c2ecf20Sopenharmony_ci		return;
12288c2ecf20Sopenharmony_ci	}
12298c2ecf20Sopenharmony_ci
12308c2ecf20Sopenharmony_ci	dip->devbase = vdev_template;
12318c2ecf20Sopenharmony_ci	dip->devbase.release = pvr2_video_device_release;
12328c2ecf20Sopenharmony_ci	dip->devbase.ioctl_ops = &pvr2_ioctl_ops;
12338c2ecf20Sopenharmony_ci	dip->devbase.device_caps = caps;
12348c2ecf20Sopenharmony_ci	{
12358c2ecf20Sopenharmony_ci		int val;
12368c2ecf20Sopenharmony_ci		pvr2_ctrl_get_value(
12378c2ecf20Sopenharmony_ci			pvr2_hdw_get_ctrl_by_id(hdw,
12388c2ecf20Sopenharmony_ci						PVR2_CID_STDAVAIL), &val);
12398c2ecf20Sopenharmony_ci		dip->devbase.tvnorms = (v4l2_std_id)val;
12408c2ecf20Sopenharmony_ci	}
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci	mindevnum = -1;
12438c2ecf20Sopenharmony_ci	unit_number = pvr2_hdw_get_unit_number(hdw);
12448c2ecf20Sopenharmony_ci	if (nr_ptr && (unit_number >= 0) && (unit_number < PVR_NUM)) {
12458c2ecf20Sopenharmony_ci		mindevnum = nr_ptr[unit_number];
12468c2ecf20Sopenharmony_ci	}
12478c2ecf20Sopenharmony_ci	pvr2_hdw_set_v4l2_dev(hdw, &dip->devbase);
12488c2ecf20Sopenharmony_ci	if ((video_register_device(&dip->devbase,
12498c2ecf20Sopenharmony_ci				   dip->v4l_type, mindevnum) < 0) &&
12508c2ecf20Sopenharmony_ci	    (video_register_device(&dip->devbase,
12518c2ecf20Sopenharmony_ci				   dip->v4l_type, -1) < 0)) {
12528c2ecf20Sopenharmony_ci		pr_err(KBUILD_MODNAME
12538c2ecf20Sopenharmony_ci			": Failed to register pvrusb2 v4l device\n");
12548c2ecf20Sopenharmony_ci	}
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_ci	pr_info("pvrusb2: registered device %s [%s]\n",
12578c2ecf20Sopenharmony_ci	       video_device_node_name(&dip->devbase),
12588c2ecf20Sopenharmony_ci	       pvr2_config_get_name(dip->config));
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_ci	pvr2_hdw_v4l_store_minor_number(hdw,
12618c2ecf20Sopenharmony_ci					dip->minor_type,dip->devbase.minor);
12628c2ecf20Sopenharmony_ci}
12638c2ecf20Sopenharmony_ci
12648c2ecf20Sopenharmony_ci
12658c2ecf20Sopenharmony_cistruct pvr2_v4l2 *pvr2_v4l2_create(struct pvr2_context *mnp)
12668c2ecf20Sopenharmony_ci{
12678c2ecf20Sopenharmony_ci	struct pvr2_v4l2 *vp;
12688c2ecf20Sopenharmony_ci
12698c2ecf20Sopenharmony_ci	vp = kzalloc(sizeof(*vp),GFP_KERNEL);
12708c2ecf20Sopenharmony_ci	if (!vp) return vp;
12718c2ecf20Sopenharmony_ci	pvr2_channel_init(&vp->channel,mnp);
12728c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr2_v4l2 id=%p",vp);
12738c2ecf20Sopenharmony_ci
12748c2ecf20Sopenharmony_ci	vp->channel.check_func = pvr2_v4l2_internal_check;
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_ci	/* register streams */
12778c2ecf20Sopenharmony_ci	vp->dev_video = kzalloc(sizeof(*vp->dev_video),GFP_KERNEL);
12788c2ecf20Sopenharmony_ci	if (!vp->dev_video) goto fail;
12798c2ecf20Sopenharmony_ci	pvr2_v4l2_dev_init(vp->dev_video,vp,VFL_TYPE_VIDEO);
12808c2ecf20Sopenharmony_ci	if (pvr2_hdw_get_input_available(vp->channel.mc_head->hdw) &
12818c2ecf20Sopenharmony_ci	    (1 << PVR2_CVAL_INPUT_RADIO)) {
12828c2ecf20Sopenharmony_ci		vp->dev_radio = kzalloc(sizeof(*vp->dev_radio),GFP_KERNEL);
12838c2ecf20Sopenharmony_ci		if (!vp->dev_radio) goto fail;
12848c2ecf20Sopenharmony_ci		pvr2_v4l2_dev_init(vp->dev_radio,vp,VFL_TYPE_RADIO);
12858c2ecf20Sopenharmony_ci	}
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_ci	return vp;
12888c2ecf20Sopenharmony_ci fail:
12898c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_STRUCT,"Failure creating pvr2_v4l2 id=%p",vp);
12908c2ecf20Sopenharmony_ci	pvr2_v4l2_destroy_no_lock(vp);
12918c2ecf20Sopenharmony_ci	return NULL;
12928c2ecf20Sopenharmony_ci}
1293