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