162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2005 Mike Isely <isely@pobox.com> 562306a36Sopenharmony_ci * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/slab.h> 1062306a36Sopenharmony_ci#include "pvrusb2-context.h" 1162306a36Sopenharmony_ci#include "pvrusb2-hdw.h" 1262306a36Sopenharmony_ci#include "pvrusb2.h" 1362306a36Sopenharmony_ci#include "pvrusb2-debug.h" 1462306a36Sopenharmony_ci#include "pvrusb2-v4l2.h" 1562306a36Sopenharmony_ci#include "pvrusb2-ioread.h" 1662306a36Sopenharmony_ci#include <linux/videodev2.h> 1762306a36Sopenharmony_ci#include <linux/module.h> 1862306a36Sopenharmony_ci#include <media/v4l2-dev.h> 1962306a36Sopenharmony_ci#include <media/v4l2-device.h> 2062306a36Sopenharmony_ci#include <media/v4l2-fh.h> 2162306a36Sopenharmony_ci#include <media/v4l2-common.h> 2262306a36Sopenharmony_ci#include <media/v4l2-ioctl.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistruct pvr2_v4l2_dev; 2562306a36Sopenharmony_cistruct pvr2_v4l2_fh; 2662306a36Sopenharmony_cistruct pvr2_v4l2; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistruct pvr2_v4l2_dev { 2962306a36Sopenharmony_ci struct video_device devbase; /* MUST be first! */ 3062306a36Sopenharmony_ci struct pvr2_v4l2 *v4lp; 3162306a36Sopenharmony_ci struct pvr2_context_stream *stream; 3262306a36Sopenharmony_ci /* Information about this device: */ 3362306a36Sopenharmony_ci enum pvr2_config config; /* Expected stream format */ 3462306a36Sopenharmony_ci int v4l_type; /* V4L defined type for this device node */ 3562306a36Sopenharmony_ci enum pvr2_v4l_type minor_type; /* pvr2-understood minor device type */ 3662306a36Sopenharmony_ci}; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistruct pvr2_v4l2_fh { 3962306a36Sopenharmony_ci struct v4l2_fh fh; 4062306a36Sopenharmony_ci struct pvr2_channel channel; 4162306a36Sopenharmony_ci struct pvr2_v4l2_dev *pdi; 4262306a36Sopenharmony_ci struct pvr2_ioread *rhp; 4362306a36Sopenharmony_ci struct file *file; 4462306a36Sopenharmony_ci wait_queue_head_t wait_data; 4562306a36Sopenharmony_ci int fw_mode_flag; 4662306a36Sopenharmony_ci /* Map contiguous ordinal value to input id */ 4762306a36Sopenharmony_ci unsigned char *input_map; 4862306a36Sopenharmony_ci unsigned int input_cnt; 4962306a36Sopenharmony_ci}; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistruct pvr2_v4l2 { 5262306a36Sopenharmony_ci struct pvr2_channel channel; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci /* streams - Note that these must be separately, individually, 5562306a36Sopenharmony_ci * allocated pointers. This is because the v4l core is going to 5662306a36Sopenharmony_ci * manage their deletion - separately, individually... */ 5762306a36Sopenharmony_ci struct pvr2_v4l2_dev *dev_video; 5862306a36Sopenharmony_ci struct pvr2_v4l2_dev *dev_radio; 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic int video_nr[PVR_NUM] = {[0 ... PVR_NUM-1] = -1}; 6262306a36Sopenharmony_cimodule_param_array(video_nr, int, NULL, 0444); 6362306a36Sopenharmony_ciMODULE_PARM_DESC(video_nr, "Offset for device's video dev minor"); 6462306a36Sopenharmony_cistatic int radio_nr[PVR_NUM] = {[0 ... PVR_NUM-1] = -1}; 6562306a36Sopenharmony_cimodule_param_array(radio_nr, int, NULL, 0444); 6662306a36Sopenharmony_ciMODULE_PARM_DESC(radio_nr, "Offset for device's radio dev minor"); 6762306a36Sopenharmony_cistatic int vbi_nr[PVR_NUM] = {[0 ... PVR_NUM-1] = -1}; 6862306a36Sopenharmony_cimodule_param_array(vbi_nr, int, NULL, 0444); 6962306a36Sopenharmony_ciMODULE_PARM_DESC(vbi_nr, "Offset for device's vbi dev minor"); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci#define PVR_FORMAT_PIX 0 7262306a36Sopenharmony_ci#define PVR_FORMAT_VBI 1 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic struct v4l2_format pvr_format [] = { 7562306a36Sopenharmony_ci [PVR_FORMAT_PIX] = { 7662306a36Sopenharmony_ci .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, 7762306a36Sopenharmony_ci .fmt = { 7862306a36Sopenharmony_ci .pix = { 7962306a36Sopenharmony_ci .width = 720, 8062306a36Sopenharmony_ci .height = 576, 8162306a36Sopenharmony_ci .pixelformat = V4L2_PIX_FMT_MPEG, 8262306a36Sopenharmony_ci .field = V4L2_FIELD_INTERLACED, 8362306a36Sopenharmony_ci /* FIXME : Don't know what to put here... */ 8462306a36Sopenharmony_ci .sizeimage = 32 * 1024, 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci }, 8862306a36Sopenharmony_ci [PVR_FORMAT_VBI] = { 8962306a36Sopenharmony_ci .type = V4L2_BUF_TYPE_VBI_CAPTURE, 9062306a36Sopenharmony_ci .fmt = { 9162306a36Sopenharmony_ci .vbi = { 9262306a36Sopenharmony_ci .sampling_rate = 27000000, 9362306a36Sopenharmony_ci .offset = 248, 9462306a36Sopenharmony_ci .samples_per_line = 1443, 9562306a36Sopenharmony_ci .sample_format = V4L2_PIX_FMT_GREY, 9662306a36Sopenharmony_ci .start = { 0, 0 }, 9762306a36Sopenharmony_ci .count = { 0, 0 }, 9862306a36Sopenharmony_ci .flags = 0, 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci}; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci/* 10762306a36Sopenharmony_ci * This is part of Video 4 Linux API. These procedures handle ioctl() calls. 10862306a36Sopenharmony_ci */ 10962306a36Sopenharmony_cistatic int pvr2_querycap(struct file *file, void *priv, struct v4l2_capability *cap) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci struct pvr2_v4l2_fh *fh = file->private_data; 11262306a36Sopenharmony_ci struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci strscpy(cap->driver, "pvrusb2", sizeof(cap->driver)); 11562306a36Sopenharmony_ci strscpy(cap->bus_info, pvr2_hdw_get_bus_info(hdw), 11662306a36Sopenharmony_ci sizeof(cap->bus_info)); 11762306a36Sopenharmony_ci strscpy(cap->card, pvr2_hdw_get_desc(hdw), sizeof(cap->card)); 11862306a36Sopenharmony_ci cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | 11962306a36Sopenharmony_ci V4L2_CAP_AUDIO | V4L2_CAP_RADIO | 12062306a36Sopenharmony_ci V4L2_CAP_READWRITE | V4L2_CAP_DEVICE_CAPS; 12162306a36Sopenharmony_ci return 0; 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic int pvr2_g_std(struct file *file, void *priv, v4l2_std_id *std) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci struct pvr2_v4l2_fh *fh = file->private_data; 12762306a36Sopenharmony_ci struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; 12862306a36Sopenharmony_ci int val = 0; 12962306a36Sopenharmony_ci int ret; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci ret = pvr2_ctrl_get_value( 13262306a36Sopenharmony_ci pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_STDCUR), &val); 13362306a36Sopenharmony_ci *std = val; 13462306a36Sopenharmony_ci return ret; 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic int pvr2_s_std(struct file *file, void *priv, v4l2_std_id std) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci struct pvr2_v4l2_fh *fh = file->private_data; 14062306a36Sopenharmony_ci struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; 14162306a36Sopenharmony_ci int ret; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci ret = pvr2_ctrl_set_value( 14462306a36Sopenharmony_ci pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_STDCUR), std); 14562306a36Sopenharmony_ci pvr2_hdw_commit_ctl(hdw); 14662306a36Sopenharmony_ci return ret; 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic int pvr2_querystd(struct file *file, void *priv, v4l2_std_id *std) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci struct pvr2_v4l2_fh *fh = file->private_data; 15262306a36Sopenharmony_ci struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; 15362306a36Sopenharmony_ci int val = 0; 15462306a36Sopenharmony_ci int ret; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci ret = pvr2_ctrl_get_value( 15762306a36Sopenharmony_ci pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_STDDETECT), &val); 15862306a36Sopenharmony_ci *std = val; 15962306a36Sopenharmony_ci return ret; 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic int pvr2_enum_input(struct file *file, void *priv, struct v4l2_input *vi) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci struct pvr2_v4l2_fh *fh = file->private_data; 16562306a36Sopenharmony_ci struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; 16662306a36Sopenharmony_ci struct pvr2_ctrl *cptr; 16762306a36Sopenharmony_ci struct v4l2_input tmp; 16862306a36Sopenharmony_ci unsigned int cnt; 16962306a36Sopenharmony_ci int val; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci cptr = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci memset(&tmp, 0, sizeof(tmp)); 17462306a36Sopenharmony_ci tmp.index = vi->index; 17562306a36Sopenharmony_ci if (vi->index >= fh->input_cnt) 17662306a36Sopenharmony_ci return -EINVAL; 17762306a36Sopenharmony_ci val = fh->input_map[vi->index]; 17862306a36Sopenharmony_ci switch (val) { 17962306a36Sopenharmony_ci case PVR2_CVAL_INPUT_TV: 18062306a36Sopenharmony_ci case PVR2_CVAL_INPUT_DTV: 18162306a36Sopenharmony_ci case PVR2_CVAL_INPUT_RADIO: 18262306a36Sopenharmony_ci tmp.type = V4L2_INPUT_TYPE_TUNER; 18362306a36Sopenharmony_ci break; 18462306a36Sopenharmony_ci case PVR2_CVAL_INPUT_SVIDEO: 18562306a36Sopenharmony_ci case PVR2_CVAL_INPUT_COMPOSITE: 18662306a36Sopenharmony_ci tmp.type = V4L2_INPUT_TYPE_CAMERA; 18762306a36Sopenharmony_ci break; 18862306a36Sopenharmony_ci default: 18962306a36Sopenharmony_ci return -EINVAL; 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci cnt = 0; 19362306a36Sopenharmony_ci pvr2_ctrl_get_valname(cptr, val, 19462306a36Sopenharmony_ci tmp.name, sizeof(tmp.name) - 1, &cnt); 19562306a36Sopenharmony_ci tmp.name[cnt] = 0; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* Don't bother with audioset, since this driver currently 19862306a36Sopenharmony_ci always switches the audio whenever the video is 19962306a36Sopenharmony_ci switched. */ 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci /* Handling std is a tougher problem. It doesn't make 20262306a36Sopenharmony_ci sense in cases where a device might be multi-standard. 20362306a36Sopenharmony_ci We could just copy out the current value for the 20462306a36Sopenharmony_ci standard, but it can change over time. For now just 20562306a36Sopenharmony_ci leave it zero. */ 20662306a36Sopenharmony_ci *vi = tmp; 20762306a36Sopenharmony_ci return 0; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic int pvr2_g_input(struct file *file, void *priv, unsigned int *i) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci struct pvr2_v4l2_fh *fh = file->private_data; 21362306a36Sopenharmony_ci struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; 21462306a36Sopenharmony_ci unsigned int idx; 21562306a36Sopenharmony_ci struct pvr2_ctrl *cptr; 21662306a36Sopenharmony_ci int val; 21762306a36Sopenharmony_ci int ret; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci cptr = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT); 22062306a36Sopenharmony_ci val = 0; 22162306a36Sopenharmony_ci ret = pvr2_ctrl_get_value(cptr, &val); 22262306a36Sopenharmony_ci *i = 0; 22362306a36Sopenharmony_ci for (idx = 0; idx < fh->input_cnt; idx++) { 22462306a36Sopenharmony_ci if (fh->input_map[idx] == val) { 22562306a36Sopenharmony_ci *i = idx; 22662306a36Sopenharmony_ci break; 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci return ret; 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic int pvr2_s_input(struct file *file, void *priv, unsigned int inp) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci struct pvr2_v4l2_fh *fh = file->private_data; 23562306a36Sopenharmony_ci struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; 23662306a36Sopenharmony_ci int ret; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if (inp >= fh->input_cnt) 23962306a36Sopenharmony_ci return -EINVAL; 24062306a36Sopenharmony_ci ret = pvr2_ctrl_set_value( 24162306a36Sopenharmony_ci pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT), 24262306a36Sopenharmony_ci fh->input_map[inp]); 24362306a36Sopenharmony_ci pvr2_hdw_commit_ctl(hdw); 24462306a36Sopenharmony_ci return ret; 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic int pvr2_enumaudio(struct file *file, void *priv, struct v4l2_audio *vin) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci /* pkt: FIXME: We are returning one "fake" input here 25062306a36Sopenharmony_ci which could very well be called "whatever_we_like". 25162306a36Sopenharmony_ci This is for apps that want to see an audio input 25262306a36Sopenharmony_ci just to feel comfortable, as well as to test if 25362306a36Sopenharmony_ci it can do stereo or sth. There is actually no guarantee 25462306a36Sopenharmony_ci that the actual audio input cannot change behind the app's 25562306a36Sopenharmony_ci back, but most applications should not mind that either. 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci Hopefully, mplayer people will work with us on this (this 25862306a36Sopenharmony_ci whole mess is to support mplayer pvr://), or Hans will come 25962306a36Sopenharmony_ci up with a more standard way to say "we have inputs but we 26062306a36Sopenharmony_ci don 't want you to change them independent of video" which 26162306a36Sopenharmony_ci will sort this mess. 26262306a36Sopenharmony_ci */ 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (vin->index > 0) 26562306a36Sopenharmony_ci return -EINVAL; 26662306a36Sopenharmony_ci strscpy(vin->name, "PVRUSB2 Audio", sizeof(vin->name)); 26762306a36Sopenharmony_ci vin->capability = V4L2_AUDCAP_STEREO; 26862306a36Sopenharmony_ci return 0; 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic int pvr2_g_audio(struct file *file, void *priv, struct v4l2_audio *vin) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci /* pkt: FIXME: see above comment (VIDIOC_ENUMAUDIO) */ 27462306a36Sopenharmony_ci vin->index = 0; 27562306a36Sopenharmony_ci strscpy(vin->name, "PVRUSB2 Audio", sizeof(vin->name)); 27662306a36Sopenharmony_ci vin->capability = V4L2_AUDCAP_STEREO; 27762306a36Sopenharmony_ci return 0; 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic int pvr2_s_audio(struct file *file, void *priv, const struct v4l2_audio *vout) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci if (vout->index) 28362306a36Sopenharmony_ci return -EINVAL; 28462306a36Sopenharmony_ci return 0; 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_cistatic int pvr2_g_tuner(struct file *file, void *priv, struct v4l2_tuner *vt) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci struct pvr2_v4l2_fh *fh = file->private_data; 29062306a36Sopenharmony_ci struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci if (vt->index != 0) 29362306a36Sopenharmony_ci return -EINVAL; /* Only answer for the 1st tuner */ 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci pvr2_hdw_execute_tuner_poll(hdw); 29662306a36Sopenharmony_ci return pvr2_hdw_get_tuner_status(hdw, vt); 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic int pvr2_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *vt) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci struct pvr2_v4l2_fh *fh = file->private_data; 30262306a36Sopenharmony_ci struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; 30362306a36Sopenharmony_ci int ret; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (vt->index != 0) 30662306a36Sopenharmony_ci return -EINVAL; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci ret = pvr2_ctrl_set_value( 30962306a36Sopenharmony_ci pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_AUDIOMODE), 31062306a36Sopenharmony_ci vt->audmode); 31162306a36Sopenharmony_ci pvr2_hdw_commit_ctl(hdw); 31262306a36Sopenharmony_ci return ret; 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_cistatic int pvr2_s_frequency(struct file *file, void *priv, const struct v4l2_frequency *vf) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci struct pvr2_v4l2_fh *fh = file->private_data; 31862306a36Sopenharmony_ci struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; 31962306a36Sopenharmony_ci unsigned long fv; 32062306a36Sopenharmony_ci struct v4l2_tuner vt; 32162306a36Sopenharmony_ci int cur_input; 32262306a36Sopenharmony_ci struct pvr2_ctrl *ctrlp; 32362306a36Sopenharmony_ci int ret; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci ret = pvr2_hdw_get_tuner_status(hdw, &vt); 32662306a36Sopenharmony_ci if (ret != 0) 32762306a36Sopenharmony_ci return ret; 32862306a36Sopenharmony_ci ctrlp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT); 32962306a36Sopenharmony_ci ret = pvr2_ctrl_get_value(ctrlp, &cur_input); 33062306a36Sopenharmony_ci if (ret != 0) 33162306a36Sopenharmony_ci return ret; 33262306a36Sopenharmony_ci if (vf->type == V4L2_TUNER_RADIO) { 33362306a36Sopenharmony_ci if (cur_input != PVR2_CVAL_INPUT_RADIO) 33462306a36Sopenharmony_ci pvr2_ctrl_set_value(ctrlp, PVR2_CVAL_INPUT_RADIO); 33562306a36Sopenharmony_ci } else { 33662306a36Sopenharmony_ci if (cur_input == PVR2_CVAL_INPUT_RADIO) 33762306a36Sopenharmony_ci pvr2_ctrl_set_value(ctrlp, PVR2_CVAL_INPUT_TV); 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci fv = vf->frequency; 34062306a36Sopenharmony_ci if (vt.capability & V4L2_TUNER_CAP_LOW) 34162306a36Sopenharmony_ci fv = (fv * 125) / 2; 34262306a36Sopenharmony_ci else 34362306a36Sopenharmony_ci fv = fv * 62500; 34462306a36Sopenharmony_ci ret = pvr2_ctrl_set_value( 34562306a36Sopenharmony_ci pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_FREQUENCY),fv); 34662306a36Sopenharmony_ci pvr2_hdw_commit_ctl(hdw); 34762306a36Sopenharmony_ci return ret; 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic int pvr2_g_frequency(struct file *file, void *priv, struct v4l2_frequency *vf) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci struct pvr2_v4l2_fh *fh = file->private_data; 35362306a36Sopenharmony_ci struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; 35462306a36Sopenharmony_ci int val = 0; 35562306a36Sopenharmony_ci int cur_input; 35662306a36Sopenharmony_ci struct v4l2_tuner vt; 35762306a36Sopenharmony_ci int ret; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci ret = pvr2_hdw_get_tuner_status(hdw, &vt); 36062306a36Sopenharmony_ci if (ret != 0) 36162306a36Sopenharmony_ci return ret; 36262306a36Sopenharmony_ci ret = pvr2_ctrl_get_value( 36362306a36Sopenharmony_ci pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_FREQUENCY), 36462306a36Sopenharmony_ci &val); 36562306a36Sopenharmony_ci if (ret != 0) 36662306a36Sopenharmony_ci return ret; 36762306a36Sopenharmony_ci pvr2_ctrl_get_value( 36862306a36Sopenharmony_ci pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT), 36962306a36Sopenharmony_ci &cur_input); 37062306a36Sopenharmony_ci if (cur_input == PVR2_CVAL_INPUT_RADIO) 37162306a36Sopenharmony_ci vf->type = V4L2_TUNER_RADIO; 37262306a36Sopenharmony_ci else 37362306a36Sopenharmony_ci vf->type = V4L2_TUNER_ANALOG_TV; 37462306a36Sopenharmony_ci if (vt.capability & V4L2_TUNER_CAP_LOW) 37562306a36Sopenharmony_ci val = (val * 2) / 125; 37662306a36Sopenharmony_ci else 37762306a36Sopenharmony_ci val /= 62500; 37862306a36Sopenharmony_ci vf->frequency = val; 37962306a36Sopenharmony_ci return 0; 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cistatic int pvr2_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *fd) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci /* Only one format is supported: MPEG. */ 38562306a36Sopenharmony_ci if (fd->index) 38662306a36Sopenharmony_ci return -EINVAL; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci fd->pixelformat = V4L2_PIX_FMT_MPEG; 38962306a36Sopenharmony_ci return 0; 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic int pvr2_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *vf) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci struct pvr2_v4l2_fh *fh = file->private_data; 39562306a36Sopenharmony_ci struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; 39662306a36Sopenharmony_ci int val; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci memcpy(vf, &pvr_format[PVR_FORMAT_PIX], sizeof(struct v4l2_format)); 39962306a36Sopenharmony_ci val = 0; 40062306a36Sopenharmony_ci pvr2_ctrl_get_value( 40162306a36Sopenharmony_ci pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_HRES), 40262306a36Sopenharmony_ci &val); 40362306a36Sopenharmony_ci vf->fmt.pix.width = val; 40462306a36Sopenharmony_ci val = 0; 40562306a36Sopenharmony_ci pvr2_ctrl_get_value( 40662306a36Sopenharmony_ci pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_VRES), 40762306a36Sopenharmony_ci &val); 40862306a36Sopenharmony_ci vf->fmt.pix.height = val; 40962306a36Sopenharmony_ci return 0; 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cistatic int pvr2_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *vf) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci struct pvr2_v4l2_fh *fh = file->private_data; 41562306a36Sopenharmony_ci struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; 41662306a36Sopenharmony_ci int lmin, lmax, ldef; 41762306a36Sopenharmony_ci struct pvr2_ctrl *hcp, *vcp; 41862306a36Sopenharmony_ci int h = vf->fmt.pix.height; 41962306a36Sopenharmony_ci int w = vf->fmt.pix.width; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci hcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_HRES); 42262306a36Sopenharmony_ci vcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_VRES); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci lmin = pvr2_ctrl_get_min(hcp); 42562306a36Sopenharmony_ci lmax = pvr2_ctrl_get_max(hcp); 42662306a36Sopenharmony_ci pvr2_ctrl_get_def(hcp, &ldef); 42762306a36Sopenharmony_ci if (w == -1) 42862306a36Sopenharmony_ci w = ldef; 42962306a36Sopenharmony_ci else if (w < lmin) 43062306a36Sopenharmony_ci w = lmin; 43162306a36Sopenharmony_ci else if (w > lmax) 43262306a36Sopenharmony_ci w = lmax; 43362306a36Sopenharmony_ci lmin = pvr2_ctrl_get_min(vcp); 43462306a36Sopenharmony_ci lmax = pvr2_ctrl_get_max(vcp); 43562306a36Sopenharmony_ci pvr2_ctrl_get_def(vcp, &ldef); 43662306a36Sopenharmony_ci if (h == -1) 43762306a36Sopenharmony_ci h = ldef; 43862306a36Sopenharmony_ci else if (h < lmin) 43962306a36Sopenharmony_ci h = lmin; 44062306a36Sopenharmony_ci else if (h > lmax) 44162306a36Sopenharmony_ci h = lmax; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci memcpy(vf, &pvr_format[PVR_FORMAT_PIX], 44462306a36Sopenharmony_ci sizeof(struct v4l2_format)); 44562306a36Sopenharmony_ci vf->fmt.pix.width = w; 44662306a36Sopenharmony_ci vf->fmt.pix.height = h; 44762306a36Sopenharmony_ci return 0; 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_cistatic int pvr2_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *vf) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci struct pvr2_v4l2_fh *fh = file->private_data; 45362306a36Sopenharmony_ci struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; 45462306a36Sopenharmony_ci struct pvr2_ctrl *hcp, *vcp; 45562306a36Sopenharmony_ci int ret = pvr2_try_fmt_vid_cap(file, fh, vf); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci if (ret) 45862306a36Sopenharmony_ci return ret; 45962306a36Sopenharmony_ci hcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_HRES); 46062306a36Sopenharmony_ci vcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_VRES); 46162306a36Sopenharmony_ci pvr2_ctrl_set_value(hcp, vf->fmt.pix.width); 46262306a36Sopenharmony_ci pvr2_ctrl_set_value(vcp, vf->fmt.pix.height); 46362306a36Sopenharmony_ci pvr2_hdw_commit_ctl(hdw); 46462306a36Sopenharmony_ci return 0; 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_cistatic int pvr2_streamon(struct file *file, void *priv, enum v4l2_buf_type i) 46862306a36Sopenharmony_ci{ 46962306a36Sopenharmony_ci struct pvr2_v4l2_fh *fh = file->private_data; 47062306a36Sopenharmony_ci struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; 47162306a36Sopenharmony_ci struct pvr2_v4l2_dev *pdi = fh->pdi; 47262306a36Sopenharmony_ci int ret; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci if (!fh->pdi->stream) { 47562306a36Sopenharmony_ci /* No stream defined for this node. This means 47662306a36Sopenharmony_ci that we're not currently allowed to stream from 47762306a36Sopenharmony_ci this node. */ 47862306a36Sopenharmony_ci return -EPERM; 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci ret = pvr2_hdw_set_stream_type(hdw, pdi->config); 48162306a36Sopenharmony_ci if (ret < 0) 48262306a36Sopenharmony_ci return ret; 48362306a36Sopenharmony_ci return pvr2_hdw_set_streaming(hdw, !0); 48462306a36Sopenharmony_ci} 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_cistatic int pvr2_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) 48762306a36Sopenharmony_ci{ 48862306a36Sopenharmony_ci struct pvr2_v4l2_fh *fh = file->private_data; 48962306a36Sopenharmony_ci struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci if (!fh->pdi->stream) { 49262306a36Sopenharmony_ci /* No stream defined for this node. This means 49362306a36Sopenharmony_ci that we're not currently allowed to stream from 49462306a36Sopenharmony_ci this node. */ 49562306a36Sopenharmony_ci return -EPERM; 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci return pvr2_hdw_set_streaming(hdw, 0); 49862306a36Sopenharmony_ci} 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_cistatic int pvr2_queryctrl(struct file *file, void *priv, 50162306a36Sopenharmony_ci struct v4l2_queryctrl *vc) 50262306a36Sopenharmony_ci{ 50362306a36Sopenharmony_ci struct pvr2_v4l2_fh *fh = file->private_data; 50462306a36Sopenharmony_ci struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; 50562306a36Sopenharmony_ci struct pvr2_ctrl *cptr; 50662306a36Sopenharmony_ci int val; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci if (vc->id & V4L2_CTRL_FLAG_NEXT_CTRL) { 50962306a36Sopenharmony_ci cptr = pvr2_hdw_get_ctrl_nextv4l( 51062306a36Sopenharmony_ci hdw, (vc->id & ~V4L2_CTRL_FLAG_NEXT_CTRL)); 51162306a36Sopenharmony_ci if (cptr) 51262306a36Sopenharmony_ci vc->id = pvr2_ctrl_get_v4lid(cptr); 51362306a36Sopenharmony_ci } else { 51462306a36Sopenharmony_ci cptr = pvr2_hdw_get_ctrl_v4l(hdw, vc->id); 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci if (!cptr) { 51762306a36Sopenharmony_ci pvr2_trace(PVR2_TRACE_V4LIOCTL, 51862306a36Sopenharmony_ci "QUERYCTRL id=0x%x not implemented here", 51962306a36Sopenharmony_ci vc->id); 52062306a36Sopenharmony_ci return -EINVAL; 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci pvr2_trace(PVR2_TRACE_V4LIOCTL, 52462306a36Sopenharmony_ci "QUERYCTRL id=0x%x mapping name=%s (%s)", 52562306a36Sopenharmony_ci vc->id, pvr2_ctrl_get_name(cptr), 52662306a36Sopenharmony_ci pvr2_ctrl_get_desc(cptr)); 52762306a36Sopenharmony_ci strscpy(vc->name, pvr2_ctrl_get_desc(cptr), sizeof(vc->name)); 52862306a36Sopenharmony_ci vc->flags = pvr2_ctrl_get_v4lflags(cptr); 52962306a36Sopenharmony_ci pvr2_ctrl_get_def(cptr, &val); 53062306a36Sopenharmony_ci vc->default_value = val; 53162306a36Sopenharmony_ci switch (pvr2_ctrl_get_type(cptr)) { 53262306a36Sopenharmony_ci case pvr2_ctl_enum: 53362306a36Sopenharmony_ci vc->type = V4L2_CTRL_TYPE_MENU; 53462306a36Sopenharmony_ci vc->minimum = 0; 53562306a36Sopenharmony_ci vc->maximum = pvr2_ctrl_get_cnt(cptr) - 1; 53662306a36Sopenharmony_ci vc->step = 1; 53762306a36Sopenharmony_ci break; 53862306a36Sopenharmony_ci case pvr2_ctl_bool: 53962306a36Sopenharmony_ci vc->type = V4L2_CTRL_TYPE_BOOLEAN; 54062306a36Sopenharmony_ci vc->minimum = 0; 54162306a36Sopenharmony_ci vc->maximum = 1; 54262306a36Sopenharmony_ci vc->step = 1; 54362306a36Sopenharmony_ci break; 54462306a36Sopenharmony_ci case pvr2_ctl_int: 54562306a36Sopenharmony_ci vc->type = V4L2_CTRL_TYPE_INTEGER; 54662306a36Sopenharmony_ci vc->minimum = pvr2_ctrl_get_min(cptr); 54762306a36Sopenharmony_ci vc->maximum = pvr2_ctrl_get_max(cptr); 54862306a36Sopenharmony_ci vc->step = 1; 54962306a36Sopenharmony_ci break; 55062306a36Sopenharmony_ci default: 55162306a36Sopenharmony_ci pvr2_trace(PVR2_TRACE_V4LIOCTL, 55262306a36Sopenharmony_ci "QUERYCTRL id=0x%x name=%s not mappable", 55362306a36Sopenharmony_ci vc->id, pvr2_ctrl_get_name(cptr)); 55462306a36Sopenharmony_ci return -EINVAL; 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci return 0; 55762306a36Sopenharmony_ci} 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_cistatic int pvr2_querymenu(struct file *file, void *priv, struct v4l2_querymenu *vm) 56062306a36Sopenharmony_ci{ 56162306a36Sopenharmony_ci struct pvr2_v4l2_fh *fh = file->private_data; 56262306a36Sopenharmony_ci struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; 56362306a36Sopenharmony_ci unsigned int cnt = 0; 56462306a36Sopenharmony_ci int ret; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci ret = pvr2_ctrl_get_valname(pvr2_hdw_get_ctrl_v4l(hdw, vm->id), 56762306a36Sopenharmony_ci vm->index, 56862306a36Sopenharmony_ci vm->name, sizeof(vm->name) - 1, 56962306a36Sopenharmony_ci &cnt); 57062306a36Sopenharmony_ci vm->name[cnt] = 0; 57162306a36Sopenharmony_ci return ret; 57262306a36Sopenharmony_ci} 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_cistatic int pvr2_g_ctrl(struct file *file, void *priv, struct v4l2_control *vc) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci struct pvr2_v4l2_fh *fh = file->private_data; 57762306a36Sopenharmony_ci struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; 57862306a36Sopenharmony_ci int val = 0; 57962306a36Sopenharmony_ci int ret; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci ret = pvr2_ctrl_get_value(pvr2_hdw_get_ctrl_v4l(hdw, vc->id), 58262306a36Sopenharmony_ci &val); 58362306a36Sopenharmony_ci vc->value = val; 58462306a36Sopenharmony_ci return ret; 58562306a36Sopenharmony_ci} 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_cistatic int pvr2_s_ctrl(struct file *file, void *priv, struct v4l2_control *vc) 58862306a36Sopenharmony_ci{ 58962306a36Sopenharmony_ci struct pvr2_v4l2_fh *fh = file->private_data; 59062306a36Sopenharmony_ci struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; 59162306a36Sopenharmony_ci int ret; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci ret = pvr2_ctrl_set_value(pvr2_hdw_get_ctrl_v4l(hdw, vc->id), 59462306a36Sopenharmony_ci vc->value); 59562306a36Sopenharmony_ci pvr2_hdw_commit_ctl(hdw); 59662306a36Sopenharmony_ci return ret; 59762306a36Sopenharmony_ci} 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_cistatic int pvr2_g_ext_ctrls(struct file *file, void *priv, 60062306a36Sopenharmony_ci struct v4l2_ext_controls *ctls) 60162306a36Sopenharmony_ci{ 60262306a36Sopenharmony_ci struct pvr2_v4l2_fh *fh = file->private_data; 60362306a36Sopenharmony_ci struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; 60462306a36Sopenharmony_ci struct v4l2_ext_control *ctrl; 60562306a36Sopenharmony_ci struct pvr2_ctrl *cptr; 60662306a36Sopenharmony_ci unsigned int idx; 60762306a36Sopenharmony_ci int val; 60862306a36Sopenharmony_ci int ret; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci ret = 0; 61162306a36Sopenharmony_ci for (idx = 0; idx < ctls->count; idx++) { 61262306a36Sopenharmony_ci ctrl = ctls->controls + idx; 61362306a36Sopenharmony_ci cptr = pvr2_hdw_get_ctrl_v4l(hdw, ctrl->id); 61462306a36Sopenharmony_ci if (cptr) { 61562306a36Sopenharmony_ci if (ctls->which == V4L2_CTRL_WHICH_DEF_VAL) 61662306a36Sopenharmony_ci pvr2_ctrl_get_def(cptr, &val); 61762306a36Sopenharmony_ci else 61862306a36Sopenharmony_ci ret = pvr2_ctrl_get_value(cptr, &val); 61962306a36Sopenharmony_ci } else 62062306a36Sopenharmony_ci ret = -EINVAL; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci if (ret) { 62362306a36Sopenharmony_ci ctls->error_idx = idx; 62462306a36Sopenharmony_ci return ret; 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci /* Ensure that if read as a 64 bit value, the user 62762306a36Sopenharmony_ci will still get a hopefully sane value */ 62862306a36Sopenharmony_ci ctrl->value64 = 0; 62962306a36Sopenharmony_ci ctrl->value = val; 63062306a36Sopenharmony_ci } 63162306a36Sopenharmony_ci return 0; 63262306a36Sopenharmony_ci} 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_cistatic int pvr2_s_ext_ctrls(struct file *file, void *priv, 63562306a36Sopenharmony_ci struct v4l2_ext_controls *ctls) 63662306a36Sopenharmony_ci{ 63762306a36Sopenharmony_ci struct pvr2_v4l2_fh *fh = file->private_data; 63862306a36Sopenharmony_ci struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; 63962306a36Sopenharmony_ci struct v4l2_ext_control *ctrl; 64062306a36Sopenharmony_ci unsigned int idx; 64162306a36Sopenharmony_ci int ret; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci ret = 0; 64462306a36Sopenharmony_ci for (idx = 0; idx < ctls->count; idx++) { 64562306a36Sopenharmony_ci ctrl = ctls->controls + idx; 64662306a36Sopenharmony_ci ret = pvr2_ctrl_set_value( 64762306a36Sopenharmony_ci pvr2_hdw_get_ctrl_v4l(hdw, ctrl->id), 64862306a36Sopenharmony_ci ctrl->value); 64962306a36Sopenharmony_ci if (ret) { 65062306a36Sopenharmony_ci ctls->error_idx = idx; 65162306a36Sopenharmony_ci goto commit; 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci } 65462306a36Sopenharmony_cicommit: 65562306a36Sopenharmony_ci pvr2_hdw_commit_ctl(hdw); 65662306a36Sopenharmony_ci return ret; 65762306a36Sopenharmony_ci} 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_cistatic int pvr2_try_ext_ctrls(struct file *file, void *priv, 66062306a36Sopenharmony_ci struct v4l2_ext_controls *ctls) 66162306a36Sopenharmony_ci{ 66262306a36Sopenharmony_ci struct pvr2_v4l2_fh *fh = file->private_data; 66362306a36Sopenharmony_ci struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; 66462306a36Sopenharmony_ci struct v4l2_ext_control *ctrl; 66562306a36Sopenharmony_ci struct pvr2_ctrl *pctl; 66662306a36Sopenharmony_ci unsigned int idx; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci /* For the moment just validate that the requested control 66962306a36Sopenharmony_ci actually exists. */ 67062306a36Sopenharmony_ci for (idx = 0; idx < ctls->count; idx++) { 67162306a36Sopenharmony_ci ctrl = ctls->controls + idx; 67262306a36Sopenharmony_ci pctl = pvr2_hdw_get_ctrl_v4l(hdw, ctrl->id); 67362306a36Sopenharmony_ci if (!pctl) { 67462306a36Sopenharmony_ci ctls->error_idx = idx; 67562306a36Sopenharmony_ci return -EINVAL; 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci } 67862306a36Sopenharmony_ci return 0; 67962306a36Sopenharmony_ci} 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_cistatic int pvr2_g_pixelaspect(struct file *file, void *priv, 68262306a36Sopenharmony_ci int type, struct v4l2_fract *f) 68362306a36Sopenharmony_ci{ 68462306a36Sopenharmony_ci struct pvr2_v4l2_fh *fh = file->private_data; 68562306a36Sopenharmony_ci struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; 68662306a36Sopenharmony_ci struct v4l2_cropcap cap = { .type = type }; 68762306a36Sopenharmony_ci int ret; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 69062306a36Sopenharmony_ci return -EINVAL; 69162306a36Sopenharmony_ci ret = pvr2_hdw_get_cropcap(hdw, &cap); 69262306a36Sopenharmony_ci if (!ret) 69362306a36Sopenharmony_ci *f = cap.pixelaspect; 69462306a36Sopenharmony_ci return ret; 69562306a36Sopenharmony_ci} 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_cistatic int pvr2_g_selection(struct file *file, void *priv, 69862306a36Sopenharmony_ci struct v4l2_selection *sel) 69962306a36Sopenharmony_ci{ 70062306a36Sopenharmony_ci struct pvr2_v4l2_fh *fh = file->private_data; 70162306a36Sopenharmony_ci struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; 70262306a36Sopenharmony_ci struct v4l2_cropcap cap; 70362306a36Sopenharmony_ci int val = 0; 70462306a36Sopenharmony_ci int ret; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 70762306a36Sopenharmony_ci return -EINVAL; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci switch (sel->target) { 71262306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP: 71362306a36Sopenharmony_ci ret = pvr2_ctrl_get_value( 71462306a36Sopenharmony_ci pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL), &val); 71562306a36Sopenharmony_ci if (ret != 0) 71662306a36Sopenharmony_ci return -EINVAL; 71762306a36Sopenharmony_ci sel->r.left = val; 71862306a36Sopenharmony_ci ret = pvr2_ctrl_get_value( 71962306a36Sopenharmony_ci pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT), &val); 72062306a36Sopenharmony_ci if (ret != 0) 72162306a36Sopenharmony_ci return -EINVAL; 72262306a36Sopenharmony_ci sel->r.top = val; 72362306a36Sopenharmony_ci ret = pvr2_ctrl_get_value( 72462306a36Sopenharmony_ci pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW), &val); 72562306a36Sopenharmony_ci if (ret != 0) 72662306a36Sopenharmony_ci return -EINVAL; 72762306a36Sopenharmony_ci sel->r.width = val; 72862306a36Sopenharmony_ci ret = pvr2_ctrl_get_value( 72962306a36Sopenharmony_ci pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH), &val); 73062306a36Sopenharmony_ci if (ret != 0) 73162306a36Sopenharmony_ci return -EINVAL; 73262306a36Sopenharmony_ci sel->r.height = val; 73362306a36Sopenharmony_ci break; 73462306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP_DEFAULT: 73562306a36Sopenharmony_ci ret = pvr2_hdw_get_cropcap(hdw, &cap); 73662306a36Sopenharmony_ci sel->r = cap.defrect; 73762306a36Sopenharmony_ci break; 73862306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP_BOUNDS: 73962306a36Sopenharmony_ci ret = pvr2_hdw_get_cropcap(hdw, &cap); 74062306a36Sopenharmony_ci sel->r = cap.bounds; 74162306a36Sopenharmony_ci break; 74262306a36Sopenharmony_ci default: 74362306a36Sopenharmony_ci return -EINVAL; 74462306a36Sopenharmony_ci } 74562306a36Sopenharmony_ci return ret; 74662306a36Sopenharmony_ci} 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_cistatic int pvr2_s_selection(struct file *file, void *priv, 74962306a36Sopenharmony_ci struct v4l2_selection *sel) 75062306a36Sopenharmony_ci{ 75162306a36Sopenharmony_ci struct pvr2_v4l2_fh *fh = file->private_data; 75262306a36Sopenharmony_ci struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; 75362306a36Sopenharmony_ci int ret; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || 75662306a36Sopenharmony_ci sel->target != V4L2_SEL_TGT_CROP) 75762306a36Sopenharmony_ci return -EINVAL; 75862306a36Sopenharmony_ci ret = pvr2_ctrl_set_value( 75962306a36Sopenharmony_ci pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL), 76062306a36Sopenharmony_ci sel->r.left); 76162306a36Sopenharmony_ci if (ret != 0) 76262306a36Sopenharmony_ci goto commit; 76362306a36Sopenharmony_ci ret = pvr2_ctrl_set_value( 76462306a36Sopenharmony_ci pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT), 76562306a36Sopenharmony_ci sel->r.top); 76662306a36Sopenharmony_ci if (ret != 0) 76762306a36Sopenharmony_ci goto commit; 76862306a36Sopenharmony_ci ret = pvr2_ctrl_set_value( 76962306a36Sopenharmony_ci pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW), 77062306a36Sopenharmony_ci sel->r.width); 77162306a36Sopenharmony_ci if (ret != 0) 77262306a36Sopenharmony_ci goto commit; 77362306a36Sopenharmony_ci ret = pvr2_ctrl_set_value( 77462306a36Sopenharmony_ci pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH), 77562306a36Sopenharmony_ci sel->r.height); 77662306a36Sopenharmony_cicommit: 77762306a36Sopenharmony_ci pvr2_hdw_commit_ctl(hdw); 77862306a36Sopenharmony_ci return ret; 77962306a36Sopenharmony_ci} 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_cistatic int pvr2_log_status(struct file *file, void *priv) 78262306a36Sopenharmony_ci{ 78362306a36Sopenharmony_ci struct pvr2_v4l2_fh *fh = file->private_data; 78462306a36Sopenharmony_ci struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci pvr2_hdw_trigger_module_log(hdw); 78762306a36Sopenharmony_ci return 0; 78862306a36Sopenharmony_ci} 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops pvr2_ioctl_ops = { 79162306a36Sopenharmony_ci .vidioc_querycap = pvr2_querycap, 79262306a36Sopenharmony_ci .vidioc_s_audio = pvr2_s_audio, 79362306a36Sopenharmony_ci .vidioc_g_audio = pvr2_g_audio, 79462306a36Sopenharmony_ci .vidioc_enumaudio = pvr2_enumaudio, 79562306a36Sopenharmony_ci .vidioc_enum_input = pvr2_enum_input, 79662306a36Sopenharmony_ci .vidioc_g_pixelaspect = pvr2_g_pixelaspect, 79762306a36Sopenharmony_ci .vidioc_s_selection = pvr2_s_selection, 79862306a36Sopenharmony_ci .vidioc_g_selection = pvr2_g_selection, 79962306a36Sopenharmony_ci .vidioc_g_input = pvr2_g_input, 80062306a36Sopenharmony_ci .vidioc_s_input = pvr2_s_input, 80162306a36Sopenharmony_ci .vidioc_g_frequency = pvr2_g_frequency, 80262306a36Sopenharmony_ci .vidioc_s_frequency = pvr2_s_frequency, 80362306a36Sopenharmony_ci .vidioc_s_tuner = pvr2_s_tuner, 80462306a36Sopenharmony_ci .vidioc_g_tuner = pvr2_g_tuner, 80562306a36Sopenharmony_ci .vidioc_g_std = pvr2_g_std, 80662306a36Sopenharmony_ci .vidioc_s_std = pvr2_s_std, 80762306a36Sopenharmony_ci .vidioc_querystd = pvr2_querystd, 80862306a36Sopenharmony_ci .vidioc_log_status = pvr2_log_status, 80962306a36Sopenharmony_ci .vidioc_enum_fmt_vid_cap = pvr2_enum_fmt_vid_cap, 81062306a36Sopenharmony_ci .vidioc_g_fmt_vid_cap = pvr2_g_fmt_vid_cap, 81162306a36Sopenharmony_ci .vidioc_s_fmt_vid_cap = pvr2_s_fmt_vid_cap, 81262306a36Sopenharmony_ci .vidioc_try_fmt_vid_cap = pvr2_try_fmt_vid_cap, 81362306a36Sopenharmony_ci .vidioc_streamon = pvr2_streamon, 81462306a36Sopenharmony_ci .vidioc_streamoff = pvr2_streamoff, 81562306a36Sopenharmony_ci .vidioc_queryctrl = pvr2_queryctrl, 81662306a36Sopenharmony_ci .vidioc_querymenu = pvr2_querymenu, 81762306a36Sopenharmony_ci .vidioc_g_ctrl = pvr2_g_ctrl, 81862306a36Sopenharmony_ci .vidioc_s_ctrl = pvr2_s_ctrl, 81962306a36Sopenharmony_ci .vidioc_g_ext_ctrls = pvr2_g_ext_ctrls, 82062306a36Sopenharmony_ci .vidioc_s_ext_ctrls = pvr2_s_ext_ctrls, 82162306a36Sopenharmony_ci .vidioc_try_ext_ctrls = pvr2_try_ext_ctrls, 82262306a36Sopenharmony_ci}; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_cistatic void pvr2_v4l2_dev_destroy(struct pvr2_v4l2_dev *dip) 82562306a36Sopenharmony_ci{ 82662306a36Sopenharmony_ci struct pvr2_hdw *hdw = dip->v4lp->channel.mc_head->hdw; 82762306a36Sopenharmony_ci enum pvr2_config cfg = dip->config; 82862306a36Sopenharmony_ci char msg[80]; 82962306a36Sopenharmony_ci unsigned int mcnt; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci /* Construct the unregistration message *before* we actually 83262306a36Sopenharmony_ci perform the unregistration step. By doing it this way we don't 83362306a36Sopenharmony_ci have to worry about potentially touching deleted resources. */ 83462306a36Sopenharmony_ci mcnt = scnprintf(msg, sizeof(msg) - 1, 83562306a36Sopenharmony_ci "pvrusb2: unregistered device %s [%s]", 83662306a36Sopenharmony_ci video_device_node_name(&dip->devbase), 83762306a36Sopenharmony_ci pvr2_config_get_name(cfg)); 83862306a36Sopenharmony_ci msg[mcnt] = 0; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci pvr2_hdw_v4l_store_minor_number(hdw,dip->minor_type,-1); 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci /* Paranoia */ 84362306a36Sopenharmony_ci dip->v4lp = NULL; 84462306a36Sopenharmony_ci dip->stream = NULL; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci /* Actual deallocation happens later when all internal references 84762306a36Sopenharmony_ci are gone. */ 84862306a36Sopenharmony_ci video_unregister_device(&dip->devbase); 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci pr_info("%s\n", msg); 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci} 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_cistatic void pvr2_v4l2_dev_disassociate_parent(struct pvr2_v4l2_dev *dip) 85662306a36Sopenharmony_ci{ 85762306a36Sopenharmony_ci if (!dip) return; 85862306a36Sopenharmony_ci if (!dip->devbase.v4l2_dev->dev) return; 85962306a36Sopenharmony_ci dip->devbase.v4l2_dev->dev = NULL; 86062306a36Sopenharmony_ci device_move(&dip->devbase.dev, NULL, DPM_ORDER_NONE); 86162306a36Sopenharmony_ci} 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_cistatic void pvr2_v4l2_destroy_no_lock(struct pvr2_v4l2 *vp) 86562306a36Sopenharmony_ci{ 86662306a36Sopenharmony_ci if (vp->dev_video) { 86762306a36Sopenharmony_ci pvr2_v4l2_dev_destroy(vp->dev_video); 86862306a36Sopenharmony_ci vp->dev_video = NULL; 86962306a36Sopenharmony_ci } 87062306a36Sopenharmony_ci if (vp->dev_radio) { 87162306a36Sopenharmony_ci pvr2_v4l2_dev_destroy(vp->dev_radio); 87262306a36Sopenharmony_ci vp->dev_radio = NULL; 87362306a36Sopenharmony_ci } 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr2_v4l2 id=%p",vp); 87662306a36Sopenharmony_ci pvr2_channel_done(&vp->channel); 87762306a36Sopenharmony_ci kfree(vp); 87862306a36Sopenharmony_ci} 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_cistatic void pvr2_video_device_release(struct video_device *vdev) 88262306a36Sopenharmony_ci{ 88362306a36Sopenharmony_ci struct pvr2_v4l2_dev *dev; 88462306a36Sopenharmony_ci dev = container_of(vdev,struct pvr2_v4l2_dev,devbase); 88562306a36Sopenharmony_ci kfree(dev); 88662306a36Sopenharmony_ci} 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_cistatic void pvr2_v4l2_internal_check(struct pvr2_channel *chp) 89062306a36Sopenharmony_ci{ 89162306a36Sopenharmony_ci struct pvr2_v4l2 *vp; 89262306a36Sopenharmony_ci vp = container_of(chp,struct pvr2_v4l2,channel); 89362306a36Sopenharmony_ci if (!vp->channel.mc_head->disconnect_flag) return; 89462306a36Sopenharmony_ci pvr2_v4l2_dev_disassociate_parent(vp->dev_video); 89562306a36Sopenharmony_ci pvr2_v4l2_dev_disassociate_parent(vp->dev_radio); 89662306a36Sopenharmony_ci if (!list_empty(&vp->dev_video->devbase.fh_list) || 89762306a36Sopenharmony_ci (vp->dev_radio && 89862306a36Sopenharmony_ci !list_empty(&vp->dev_radio->devbase.fh_list))) { 89962306a36Sopenharmony_ci pvr2_trace(PVR2_TRACE_STRUCT, 90062306a36Sopenharmony_ci "pvr2_v4l2 internal_check exit-empty id=%p", vp); 90162306a36Sopenharmony_ci return; 90262306a36Sopenharmony_ci } 90362306a36Sopenharmony_ci pvr2_v4l2_destroy_no_lock(vp); 90462306a36Sopenharmony_ci} 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_cistatic int pvr2_v4l2_release(struct file *file) 90862306a36Sopenharmony_ci{ 90962306a36Sopenharmony_ci struct pvr2_v4l2_fh *fhp = file->private_data; 91062306a36Sopenharmony_ci struct pvr2_v4l2 *vp = fhp->pdi->v4lp; 91162306a36Sopenharmony_ci struct pvr2_hdw *hdw = fhp->channel.mc_head->hdw; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci pvr2_trace(PVR2_TRACE_OPEN_CLOSE,"pvr2_v4l2_release"); 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci if (fhp->rhp) { 91662306a36Sopenharmony_ci struct pvr2_stream *sp; 91762306a36Sopenharmony_ci pvr2_hdw_set_streaming(hdw,0); 91862306a36Sopenharmony_ci sp = pvr2_ioread_get_stream(fhp->rhp); 91962306a36Sopenharmony_ci if (sp) pvr2_stream_set_callback(sp,NULL,NULL); 92062306a36Sopenharmony_ci pvr2_ioread_destroy(fhp->rhp); 92162306a36Sopenharmony_ci fhp->rhp = NULL; 92262306a36Sopenharmony_ci } 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci v4l2_fh_del(&fhp->fh); 92562306a36Sopenharmony_ci v4l2_fh_exit(&fhp->fh); 92662306a36Sopenharmony_ci file->private_data = NULL; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci pvr2_channel_done(&fhp->channel); 92962306a36Sopenharmony_ci pvr2_trace(PVR2_TRACE_STRUCT, 93062306a36Sopenharmony_ci "Destroying pvr_v4l2_fh id=%p",fhp); 93162306a36Sopenharmony_ci if (fhp->input_map) { 93262306a36Sopenharmony_ci kfree(fhp->input_map); 93362306a36Sopenharmony_ci fhp->input_map = NULL; 93462306a36Sopenharmony_ci } 93562306a36Sopenharmony_ci kfree(fhp); 93662306a36Sopenharmony_ci if (vp->channel.mc_head->disconnect_flag && 93762306a36Sopenharmony_ci list_empty(&vp->dev_video->devbase.fh_list) && 93862306a36Sopenharmony_ci (!vp->dev_radio || 93962306a36Sopenharmony_ci list_empty(&vp->dev_radio->devbase.fh_list))) { 94062306a36Sopenharmony_ci pvr2_v4l2_destroy_no_lock(vp); 94162306a36Sopenharmony_ci } 94262306a36Sopenharmony_ci return 0; 94362306a36Sopenharmony_ci} 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_cistatic int pvr2_v4l2_open(struct file *file) 94762306a36Sopenharmony_ci{ 94862306a36Sopenharmony_ci struct pvr2_v4l2_dev *dip; /* Our own context pointer */ 94962306a36Sopenharmony_ci struct pvr2_v4l2_fh *fhp; 95062306a36Sopenharmony_ci struct pvr2_v4l2 *vp; 95162306a36Sopenharmony_ci struct pvr2_hdw *hdw; 95262306a36Sopenharmony_ci unsigned int input_mask = 0; 95362306a36Sopenharmony_ci unsigned int input_cnt,idx; 95462306a36Sopenharmony_ci int ret = 0; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci dip = container_of(video_devdata(file),struct pvr2_v4l2_dev,devbase); 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci vp = dip->v4lp; 95962306a36Sopenharmony_ci hdw = vp->channel.hdw; 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci pvr2_trace(PVR2_TRACE_OPEN_CLOSE,"pvr2_v4l2_open"); 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci if (!pvr2_hdw_dev_ok(hdw)) { 96462306a36Sopenharmony_ci pvr2_trace(PVR2_TRACE_OPEN_CLOSE, 96562306a36Sopenharmony_ci "pvr2_v4l2_open: hardware not ready"); 96662306a36Sopenharmony_ci return -EIO; 96762306a36Sopenharmony_ci } 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci fhp = kzalloc(sizeof(*fhp),GFP_KERNEL); 97062306a36Sopenharmony_ci if (!fhp) { 97162306a36Sopenharmony_ci return -ENOMEM; 97262306a36Sopenharmony_ci } 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci v4l2_fh_init(&fhp->fh, &dip->devbase); 97562306a36Sopenharmony_ci init_waitqueue_head(&fhp->wait_data); 97662306a36Sopenharmony_ci fhp->pdi = dip; 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr_v4l2_fh id=%p",fhp); 97962306a36Sopenharmony_ci pvr2_channel_init(&fhp->channel,vp->channel.mc_head); 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci if (dip->v4l_type == VFL_TYPE_RADIO) { 98262306a36Sopenharmony_ci /* Opening device as a radio, legal input selection subset 98362306a36Sopenharmony_ci is just the radio. */ 98462306a36Sopenharmony_ci input_mask = (1 << PVR2_CVAL_INPUT_RADIO); 98562306a36Sopenharmony_ci } else { 98662306a36Sopenharmony_ci /* Opening the main V4L device, legal input selection 98762306a36Sopenharmony_ci subset includes all analog inputs. */ 98862306a36Sopenharmony_ci input_mask = ((1 << PVR2_CVAL_INPUT_RADIO) | 98962306a36Sopenharmony_ci (1 << PVR2_CVAL_INPUT_TV) | 99062306a36Sopenharmony_ci (1 << PVR2_CVAL_INPUT_COMPOSITE) | 99162306a36Sopenharmony_ci (1 << PVR2_CVAL_INPUT_SVIDEO)); 99262306a36Sopenharmony_ci } 99362306a36Sopenharmony_ci ret = pvr2_channel_limit_inputs(&fhp->channel,input_mask); 99462306a36Sopenharmony_ci if (ret) { 99562306a36Sopenharmony_ci pvr2_channel_done(&fhp->channel); 99662306a36Sopenharmony_ci pvr2_trace(PVR2_TRACE_STRUCT, 99762306a36Sopenharmony_ci "Destroying pvr_v4l2_fh id=%p (input mask error)", 99862306a36Sopenharmony_ci fhp); 99962306a36Sopenharmony_ci v4l2_fh_exit(&fhp->fh); 100062306a36Sopenharmony_ci kfree(fhp); 100162306a36Sopenharmony_ci return ret; 100262306a36Sopenharmony_ci } 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci input_mask &= pvr2_hdw_get_input_available(hdw); 100562306a36Sopenharmony_ci input_cnt = 0; 100662306a36Sopenharmony_ci for (idx = 0; idx < (sizeof(input_mask) << 3); idx++) { 100762306a36Sopenharmony_ci if (input_mask & (1UL << idx)) input_cnt++; 100862306a36Sopenharmony_ci } 100962306a36Sopenharmony_ci fhp->input_cnt = input_cnt; 101062306a36Sopenharmony_ci fhp->input_map = kzalloc(input_cnt,GFP_KERNEL); 101162306a36Sopenharmony_ci if (!fhp->input_map) { 101262306a36Sopenharmony_ci pvr2_channel_done(&fhp->channel); 101362306a36Sopenharmony_ci pvr2_trace(PVR2_TRACE_STRUCT, 101462306a36Sopenharmony_ci "Destroying pvr_v4l2_fh id=%p (input map failure)", 101562306a36Sopenharmony_ci fhp); 101662306a36Sopenharmony_ci v4l2_fh_exit(&fhp->fh); 101762306a36Sopenharmony_ci kfree(fhp); 101862306a36Sopenharmony_ci return -ENOMEM; 101962306a36Sopenharmony_ci } 102062306a36Sopenharmony_ci input_cnt = 0; 102162306a36Sopenharmony_ci for (idx = 0; idx < (sizeof(input_mask) << 3); idx++) { 102262306a36Sopenharmony_ci if (!(input_mask & (1UL << idx))) continue; 102362306a36Sopenharmony_ci fhp->input_map[input_cnt++] = idx; 102462306a36Sopenharmony_ci } 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci fhp->file = file; 102762306a36Sopenharmony_ci file->private_data = fhp; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci fhp->fw_mode_flag = pvr2_hdw_cpufw_get_enabled(hdw); 103062306a36Sopenharmony_ci v4l2_fh_add(&fhp->fh); 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci return 0; 103362306a36Sopenharmony_ci} 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_cistatic void pvr2_v4l2_notify(void *ptr) 103762306a36Sopenharmony_ci{ 103862306a36Sopenharmony_ci struct pvr2_v4l2_fh *fhp = ptr; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci wake_up(&fhp->wait_data); 104162306a36Sopenharmony_ci} 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_cistatic int pvr2_v4l2_iosetup(struct pvr2_v4l2_fh *fh) 104462306a36Sopenharmony_ci{ 104562306a36Sopenharmony_ci int ret; 104662306a36Sopenharmony_ci struct pvr2_stream *sp; 104762306a36Sopenharmony_ci struct pvr2_hdw *hdw; 104862306a36Sopenharmony_ci if (fh->rhp) return 0; 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci if (!fh->pdi->stream) { 105162306a36Sopenharmony_ci /* No stream defined for this node. This means that we're 105262306a36Sopenharmony_ci not currently allowed to stream from this node. */ 105362306a36Sopenharmony_ci return -EPERM; 105462306a36Sopenharmony_ci } 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci /* First read() attempt. Try to claim the stream and start 105762306a36Sopenharmony_ci it... */ 105862306a36Sopenharmony_ci if ((ret = pvr2_channel_claim_stream(&fh->channel, 105962306a36Sopenharmony_ci fh->pdi->stream)) != 0) { 106062306a36Sopenharmony_ci /* Someone else must already have it */ 106162306a36Sopenharmony_ci return ret; 106262306a36Sopenharmony_ci } 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci fh->rhp = pvr2_channel_create_mpeg_stream(fh->pdi->stream); 106562306a36Sopenharmony_ci if (!fh->rhp) { 106662306a36Sopenharmony_ci pvr2_channel_claim_stream(&fh->channel,NULL); 106762306a36Sopenharmony_ci return -ENOMEM; 106862306a36Sopenharmony_ci } 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci hdw = fh->channel.mc_head->hdw; 107162306a36Sopenharmony_ci sp = fh->pdi->stream->stream; 107262306a36Sopenharmony_ci pvr2_stream_set_callback(sp, pvr2_v4l2_notify, fh); 107362306a36Sopenharmony_ci pvr2_hdw_set_stream_type(hdw,fh->pdi->config); 107462306a36Sopenharmony_ci if ((ret = pvr2_hdw_set_streaming(hdw,!0)) < 0) return ret; 107562306a36Sopenharmony_ci return pvr2_ioread_set_enabled(fh->rhp,!0); 107662306a36Sopenharmony_ci} 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_cistatic ssize_t pvr2_v4l2_read(struct file *file, 108062306a36Sopenharmony_ci char __user *buff, size_t count, loff_t *ppos) 108162306a36Sopenharmony_ci{ 108262306a36Sopenharmony_ci struct pvr2_v4l2_fh *fh = file->private_data; 108362306a36Sopenharmony_ci int ret; 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci if (fh->fw_mode_flag) { 108662306a36Sopenharmony_ci struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; 108762306a36Sopenharmony_ci char *tbuf; 108862306a36Sopenharmony_ci int c1,c2; 108962306a36Sopenharmony_ci int tcnt = 0; 109062306a36Sopenharmony_ci unsigned int offs = *ppos; 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci tbuf = kmalloc(PAGE_SIZE,GFP_KERNEL); 109362306a36Sopenharmony_ci if (!tbuf) return -ENOMEM; 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci while (count) { 109662306a36Sopenharmony_ci c1 = count; 109762306a36Sopenharmony_ci if (c1 > PAGE_SIZE) c1 = PAGE_SIZE; 109862306a36Sopenharmony_ci c2 = pvr2_hdw_cpufw_get(hdw,offs,tbuf,c1); 109962306a36Sopenharmony_ci if (c2 < 0) { 110062306a36Sopenharmony_ci tcnt = c2; 110162306a36Sopenharmony_ci break; 110262306a36Sopenharmony_ci } 110362306a36Sopenharmony_ci if (!c2) break; 110462306a36Sopenharmony_ci if (copy_to_user(buff,tbuf,c2)) { 110562306a36Sopenharmony_ci tcnt = -EFAULT; 110662306a36Sopenharmony_ci break; 110762306a36Sopenharmony_ci } 110862306a36Sopenharmony_ci offs += c2; 110962306a36Sopenharmony_ci tcnt += c2; 111062306a36Sopenharmony_ci buff += c2; 111162306a36Sopenharmony_ci count -= c2; 111262306a36Sopenharmony_ci *ppos += c2; 111362306a36Sopenharmony_ci } 111462306a36Sopenharmony_ci kfree(tbuf); 111562306a36Sopenharmony_ci return tcnt; 111662306a36Sopenharmony_ci } 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci if (!fh->rhp) { 111962306a36Sopenharmony_ci ret = pvr2_v4l2_iosetup(fh); 112062306a36Sopenharmony_ci if (ret) { 112162306a36Sopenharmony_ci return ret; 112262306a36Sopenharmony_ci } 112362306a36Sopenharmony_ci } 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci for (;;) { 112662306a36Sopenharmony_ci ret = pvr2_ioread_read(fh->rhp,buff,count); 112762306a36Sopenharmony_ci if (ret >= 0) break; 112862306a36Sopenharmony_ci if (ret != -EAGAIN) break; 112962306a36Sopenharmony_ci if (file->f_flags & O_NONBLOCK) break; 113062306a36Sopenharmony_ci /* Doing blocking I/O. Wait here. */ 113162306a36Sopenharmony_ci ret = wait_event_interruptible( 113262306a36Sopenharmony_ci fh->wait_data, 113362306a36Sopenharmony_ci pvr2_ioread_avail(fh->rhp) >= 0); 113462306a36Sopenharmony_ci if (ret < 0) break; 113562306a36Sopenharmony_ci } 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci return ret; 113862306a36Sopenharmony_ci} 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_cistatic __poll_t pvr2_v4l2_poll(struct file *file, poll_table *wait) 114262306a36Sopenharmony_ci{ 114362306a36Sopenharmony_ci __poll_t mask = 0; 114462306a36Sopenharmony_ci struct pvr2_v4l2_fh *fh = file->private_data; 114562306a36Sopenharmony_ci int ret; 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci if (fh->fw_mode_flag) { 114862306a36Sopenharmony_ci mask |= EPOLLIN | EPOLLRDNORM; 114962306a36Sopenharmony_ci return mask; 115062306a36Sopenharmony_ci } 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci if (!fh->rhp) { 115362306a36Sopenharmony_ci ret = pvr2_v4l2_iosetup(fh); 115462306a36Sopenharmony_ci if (ret) return EPOLLERR; 115562306a36Sopenharmony_ci } 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci poll_wait(file,&fh->wait_data,wait); 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci if (pvr2_ioread_avail(fh->rhp) >= 0) { 116062306a36Sopenharmony_ci mask |= EPOLLIN | EPOLLRDNORM; 116162306a36Sopenharmony_ci } 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci return mask; 116462306a36Sopenharmony_ci} 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_cistatic const struct v4l2_file_operations vdev_fops = { 116862306a36Sopenharmony_ci .owner = THIS_MODULE, 116962306a36Sopenharmony_ci .open = pvr2_v4l2_open, 117062306a36Sopenharmony_ci .release = pvr2_v4l2_release, 117162306a36Sopenharmony_ci .read = pvr2_v4l2_read, 117262306a36Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 117362306a36Sopenharmony_ci .poll = pvr2_v4l2_poll, 117462306a36Sopenharmony_ci}; 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_cistatic const struct video_device vdev_template = { 117862306a36Sopenharmony_ci .fops = &vdev_fops, 117962306a36Sopenharmony_ci}; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_cistatic void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, 118362306a36Sopenharmony_ci struct pvr2_v4l2 *vp, 118462306a36Sopenharmony_ci int v4l_type) 118562306a36Sopenharmony_ci{ 118662306a36Sopenharmony_ci int mindevnum; 118762306a36Sopenharmony_ci int unit_number; 118862306a36Sopenharmony_ci struct pvr2_hdw *hdw; 118962306a36Sopenharmony_ci int *nr_ptr = NULL; 119062306a36Sopenharmony_ci u32 caps = V4L2_CAP_TUNER | V4L2_CAP_READWRITE; 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci dip->v4lp = vp; 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci hdw = vp->channel.mc_head->hdw; 119562306a36Sopenharmony_ci dip->v4l_type = v4l_type; 119662306a36Sopenharmony_ci switch (v4l_type) { 119762306a36Sopenharmony_ci case VFL_TYPE_VIDEO: 119862306a36Sopenharmony_ci dip->stream = &vp->channel.mc_head->video_stream; 119962306a36Sopenharmony_ci dip->config = pvr2_config_mpeg; 120062306a36Sopenharmony_ci dip->minor_type = pvr2_v4l_type_video; 120162306a36Sopenharmony_ci nr_ptr = video_nr; 120262306a36Sopenharmony_ci caps |= V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_AUDIO; 120362306a36Sopenharmony_ci break; 120462306a36Sopenharmony_ci case VFL_TYPE_VBI: 120562306a36Sopenharmony_ci dip->config = pvr2_config_vbi; 120662306a36Sopenharmony_ci dip->minor_type = pvr2_v4l_type_vbi; 120762306a36Sopenharmony_ci nr_ptr = vbi_nr; 120862306a36Sopenharmony_ci caps |= V4L2_CAP_VBI_CAPTURE; 120962306a36Sopenharmony_ci break; 121062306a36Sopenharmony_ci case VFL_TYPE_RADIO: 121162306a36Sopenharmony_ci dip->stream = &vp->channel.mc_head->video_stream; 121262306a36Sopenharmony_ci dip->config = pvr2_config_mpeg; 121362306a36Sopenharmony_ci dip->minor_type = pvr2_v4l_type_radio; 121462306a36Sopenharmony_ci nr_ptr = radio_nr; 121562306a36Sopenharmony_ci caps |= V4L2_CAP_RADIO; 121662306a36Sopenharmony_ci break; 121762306a36Sopenharmony_ci default: 121862306a36Sopenharmony_ci /* Bail out (this should be impossible) */ 121962306a36Sopenharmony_ci pr_err(KBUILD_MODNAME ": Failed to set up pvrusb2 v4l dev due to unrecognized config\n"); 122062306a36Sopenharmony_ci return; 122162306a36Sopenharmony_ci } 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci dip->devbase = vdev_template; 122462306a36Sopenharmony_ci dip->devbase.release = pvr2_video_device_release; 122562306a36Sopenharmony_ci dip->devbase.ioctl_ops = &pvr2_ioctl_ops; 122662306a36Sopenharmony_ci dip->devbase.device_caps = caps; 122762306a36Sopenharmony_ci { 122862306a36Sopenharmony_ci int val; 122962306a36Sopenharmony_ci pvr2_ctrl_get_value( 123062306a36Sopenharmony_ci pvr2_hdw_get_ctrl_by_id(hdw, 123162306a36Sopenharmony_ci PVR2_CID_STDAVAIL), &val); 123262306a36Sopenharmony_ci dip->devbase.tvnorms = (v4l2_std_id)val; 123362306a36Sopenharmony_ci } 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci mindevnum = -1; 123662306a36Sopenharmony_ci unit_number = pvr2_hdw_get_unit_number(hdw); 123762306a36Sopenharmony_ci if (nr_ptr && (unit_number >= 0) && (unit_number < PVR_NUM)) { 123862306a36Sopenharmony_ci mindevnum = nr_ptr[unit_number]; 123962306a36Sopenharmony_ci } 124062306a36Sopenharmony_ci pvr2_hdw_set_v4l2_dev(hdw, &dip->devbase); 124162306a36Sopenharmony_ci if ((video_register_device(&dip->devbase, 124262306a36Sopenharmony_ci dip->v4l_type, mindevnum) < 0) && 124362306a36Sopenharmony_ci (video_register_device(&dip->devbase, 124462306a36Sopenharmony_ci dip->v4l_type, -1) < 0)) { 124562306a36Sopenharmony_ci pr_err(KBUILD_MODNAME 124662306a36Sopenharmony_ci ": Failed to register pvrusb2 v4l device\n"); 124762306a36Sopenharmony_ci } 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci pr_info("pvrusb2: registered device %s [%s]\n", 125062306a36Sopenharmony_ci video_device_node_name(&dip->devbase), 125162306a36Sopenharmony_ci pvr2_config_get_name(dip->config)); 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci pvr2_hdw_v4l_store_minor_number(hdw, 125462306a36Sopenharmony_ci dip->minor_type,dip->devbase.minor); 125562306a36Sopenharmony_ci} 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_cistruct pvr2_v4l2 *pvr2_v4l2_create(struct pvr2_context *mnp) 125962306a36Sopenharmony_ci{ 126062306a36Sopenharmony_ci struct pvr2_v4l2 *vp; 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci vp = kzalloc(sizeof(*vp),GFP_KERNEL); 126362306a36Sopenharmony_ci if (!vp) return vp; 126462306a36Sopenharmony_ci pvr2_channel_init(&vp->channel,mnp); 126562306a36Sopenharmony_ci pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr2_v4l2 id=%p",vp); 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci vp->channel.check_func = pvr2_v4l2_internal_check; 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci /* register streams */ 127062306a36Sopenharmony_ci vp->dev_video = kzalloc(sizeof(*vp->dev_video),GFP_KERNEL); 127162306a36Sopenharmony_ci if (!vp->dev_video) goto fail; 127262306a36Sopenharmony_ci pvr2_v4l2_dev_init(vp->dev_video,vp,VFL_TYPE_VIDEO); 127362306a36Sopenharmony_ci if (pvr2_hdw_get_input_available(vp->channel.mc_head->hdw) & 127462306a36Sopenharmony_ci (1 << PVR2_CVAL_INPUT_RADIO)) { 127562306a36Sopenharmony_ci vp->dev_radio = kzalloc(sizeof(*vp->dev_radio),GFP_KERNEL); 127662306a36Sopenharmony_ci if (!vp->dev_radio) goto fail; 127762306a36Sopenharmony_ci pvr2_v4l2_dev_init(vp->dev_radio,vp,VFL_TYPE_RADIO); 127862306a36Sopenharmony_ci } 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci return vp; 128162306a36Sopenharmony_ci fail: 128262306a36Sopenharmony_ci pvr2_trace(PVR2_TRACE_STRUCT,"Failure creating pvr2_v4l2 id=%p",vp); 128362306a36Sopenharmony_ci pvr2_v4l2_destroy_no_lock(vp); 128462306a36Sopenharmony_ci return NULL; 128562306a36Sopenharmony_ci} 1286