From 9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00 Mon Sep 17 00:00:00 2001 From: Yunhong Jiang Date: Tue, 4 Aug 2015 12:17:53 -0700 Subject: Add the rt linux 4.1.3-rt3 as base Import the rt linux 4.1.3-rt3 as OPNFV kvm base. It's from git://git.kernel.org/pub/scm/linux/kernel/git/rt/linux-rt-devel.git linux-4.1.y-rt and the base is: commit 0917f823c59692d751951bf5ea699a2d1e2f26a2 Author: Sebastian Andrzej Siewior Date: Sat Jul 25 12:13:34 2015 +0200 Prepare v4.1.3-rt3 Signed-off-by: Sebastian Andrzej Siewior We lose all the git history this way and it's not good. We should apply another opnfv project repo in future. Change-Id: I87543d81c9df70d99c5001fbdf646b202c19f423 Signed-off-by: Yunhong Jiang --- kernel/drivers/media/platform/vivid/vivid-core.c | 1419 ++++++++++++++++++++++ 1 file changed, 1419 insertions(+) create mode 100644 kernel/drivers/media/platform/vivid/vivid-core.c (limited to 'kernel/drivers/media/platform/vivid/vivid-core.c') diff --git a/kernel/drivers/media/platform/vivid/vivid-core.c b/kernel/drivers/media/platform/vivid/vivid-core.c new file mode 100644 index 000000000..d33f16495 --- /dev/null +++ b/kernel/drivers/media/platform/vivid/vivid-core.c @@ -0,0 +1,1419 @@ +/* + * vivid-core.c - A Virtual Video Test Driver, core initialization + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vivid-core.h" +#include "vivid-vid-common.h" +#include "vivid-vid-cap.h" +#include "vivid-vid-out.h" +#include "vivid-radio-common.h" +#include "vivid-radio-rx.h" +#include "vivid-radio-tx.h" +#include "vivid-sdr-cap.h" +#include "vivid-vbi-cap.h" +#include "vivid-vbi-out.h" +#include "vivid-osd.h" +#include "vivid-ctrls.h" + +#define VIVID_MODULE_NAME "vivid" + +/* The maximum number of vivid devices */ +#define VIVID_MAX_DEVS 64 + +MODULE_DESCRIPTION("Virtual Video Test Driver"); +MODULE_AUTHOR("Hans Verkuil"); +MODULE_LICENSE("GPL"); + +static unsigned n_devs = 1; +module_param(n_devs, uint, 0444); +MODULE_PARM_DESC(n_devs, " number of driver instances to create"); + +static int vid_cap_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 }; +module_param_array(vid_cap_nr, int, NULL, 0444); +MODULE_PARM_DESC(vid_cap_nr, " videoX start number, -1 is autodetect"); + +static int vid_out_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 }; +module_param_array(vid_out_nr, int, NULL, 0444); +MODULE_PARM_DESC(vid_out_nr, " videoX start number, -1 is autodetect"); + +static int vbi_cap_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 }; +module_param_array(vbi_cap_nr, int, NULL, 0444); +MODULE_PARM_DESC(vbi_cap_nr, " vbiX start number, -1 is autodetect"); + +static int vbi_out_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 }; +module_param_array(vbi_out_nr, int, NULL, 0444); +MODULE_PARM_DESC(vbi_out_nr, " vbiX start number, -1 is autodetect"); + +static int sdr_cap_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 }; +module_param_array(sdr_cap_nr, int, NULL, 0444); +MODULE_PARM_DESC(sdr_cap_nr, " swradioX start number, -1 is autodetect"); + +static int radio_rx_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 }; +module_param_array(radio_rx_nr, int, NULL, 0444); +MODULE_PARM_DESC(radio_rx_nr, " radioX start number, -1 is autodetect"); + +static int radio_tx_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 }; +module_param_array(radio_tx_nr, int, NULL, 0444); +MODULE_PARM_DESC(radio_tx_nr, " radioX start number, -1 is autodetect"); + +static int ccs_cap_mode[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 }; +module_param_array(ccs_cap_mode, int, NULL, 0444); +MODULE_PARM_DESC(ccs_cap_mode, " capture crop/compose/scale mode:\n" + "\t\t bit 0=crop, 1=compose, 2=scale,\n" + "\t\t -1=user-controlled (default)"); + +static int ccs_out_mode[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 }; +module_param_array(ccs_out_mode, int, NULL, 0444); +MODULE_PARM_DESC(ccs_out_mode, " output crop/compose/scale mode:\n" + "\t\t bit 0=crop, 1=compose, 2=scale,\n" + "\t\t -1=user-controlled (default)"); + +static unsigned multiplanar[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 1 }; +module_param_array(multiplanar, uint, NULL, 0444); +MODULE_PARM_DESC(multiplanar, " 1 (default) creates a single planar device, 2 creates a multiplanar device."); + +/* Default: video + vbi-cap (raw and sliced) + radio rx + radio tx + sdr + vbi-out + vid-out */ +static unsigned node_types[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 0x1d3d }; +module_param_array(node_types, uint, NULL, 0444); +MODULE_PARM_DESC(node_types, " node types, default is 0x1d3d. Bitmask with the following meaning:\n" + "\t\t bit 0: Video Capture node\n" + "\t\t bit 2-3: VBI Capture node: 0 = none, 1 = raw vbi, 2 = sliced vbi, 3 = both\n" + "\t\t bit 4: Radio Receiver node\n" + "\t\t bit 5: Software Defined Radio Receiver node\n" + "\t\t bit 8: Video Output node\n" + "\t\t bit 10-11: VBI Output node: 0 = none, 1 = raw vbi, 2 = sliced vbi, 3 = both\n" + "\t\t bit 12: Radio Transmitter node\n" + "\t\t bit 16: Framebuffer for testing overlays"); + +/* Default: 4 inputs */ +static unsigned num_inputs[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 4 }; +module_param_array(num_inputs, uint, NULL, 0444); +MODULE_PARM_DESC(num_inputs, " number of inputs, default is 4"); + +/* Default: input 0 = WEBCAM, 1 = TV, 2 = SVID, 3 = HDMI */ +static unsigned input_types[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 0xe4 }; +module_param_array(input_types, uint, NULL, 0444); +MODULE_PARM_DESC(input_types, " input types, default is 0xe4. Two bits per input,\n" + "\t\t bits 0-1 == input 0, bits 31-30 == input 15.\n" + "\t\t Type 0 == webcam, 1 == TV, 2 == S-Video, 3 == HDMI"); + +/* Default: 2 outputs */ +static unsigned num_outputs[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 2 }; +module_param_array(num_outputs, uint, NULL, 0444); +MODULE_PARM_DESC(num_outputs, " number of outputs, default is 2"); + +/* Default: output 0 = SVID, 1 = HDMI */ +static unsigned output_types[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 2 }; +module_param_array(output_types, uint, NULL, 0444); +MODULE_PARM_DESC(output_types, " output types, default is 0x02. One bit per output,\n" + "\t\t bit 0 == output 0, bit 15 == output 15.\n" + "\t\t Type 0 == S-Video, 1 == HDMI"); + +unsigned vivid_debug; +module_param(vivid_debug, uint, 0644); +MODULE_PARM_DESC(vivid_debug, " activates debug info"); + +static bool no_error_inj; +module_param(no_error_inj, bool, 0444); +MODULE_PARM_DESC(no_error_inj, " if set disable the error injecting controls"); + +static struct vivid_dev *vivid_devs[VIVID_MAX_DEVS]; + +const struct v4l2_rect vivid_min_rect = { + 0, 0, MIN_WIDTH, MIN_HEIGHT +}; + +const struct v4l2_rect vivid_max_rect = { + 0, 0, MAX_WIDTH * MAX_ZOOM, MAX_HEIGHT * MAX_ZOOM +}; + +static const u8 vivid_hdmi_edid[256] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x63, 0x3a, 0xaa, 0x55, 0x00, 0x00, 0x00, 0x00, + 0x0a, 0x18, 0x01, 0x03, 0x80, 0x10, 0x09, 0x78, + 0x0e, 0x00, 0xb2, 0xa0, 0x57, 0x49, 0x9b, 0x26, + 0x10, 0x48, 0x4f, 0x2f, 0xcf, 0x00, 0x31, 0x59, + 0x45, 0x59, 0x81, 0x80, 0x81, 0x40, 0x90, 0x40, + 0x95, 0x00, 0xa9, 0x40, 0xb3, 0x00, 0x02, 0x3a, + 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c, + 0x46, 0x00, 0x10, 0x09, 0x00, 0x00, 0x00, 0x1e, + 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18, 0x55, 0x18, + 0x5e, 0x11, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 0x00, 'v', + '4', 'l', '2', '-', 'h', 'd', 'm', 'i', + 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf0, + + 0x02, 0x03, 0x1a, 0xc0, 0x48, 0xa2, 0x10, 0x04, + 0x02, 0x01, 0x21, 0x14, 0x13, 0x23, 0x09, 0x07, + 0x07, 0x65, 0x03, 0x0c, 0x00, 0x10, 0x00, 0xe2, + 0x00, 0x2a, 0x01, 0x1d, 0x00, 0x80, 0x51, 0xd0, + 0x1c, 0x20, 0x40, 0x80, 0x35, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1e, 0x8c, 0x0a, 0xd0, 0x8a, + 0x20, 0xe0, 0x2d, 0x10, 0x10, 0x3e, 0x96, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd7 +}; + +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct vivid_dev *dev = video_drvdata(file); + struct video_device *vdev = video_devdata(file); + + strcpy(cap->driver, "vivid"); + strcpy(cap->card, "vivid"); + snprintf(cap->bus_info, sizeof(cap->bus_info), + "platform:%s", dev->v4l2_dev.name); + + if (vdev->vfl_type == VFL_TYPE_GRABBER && vdev->vfl_dir == VFL_DIR_RX) + cap->device_caps = dev->vid_cap_caps; + if (vdev->vfl_type == VFL_TYPE_GRABBER && vdev->vfl_dir == VFL_DIR_TX) + cap->device_caps = dev->vid_out_caps; + else if (vdev->vfl_type == VFL_TYPE_VBI && vdev->vfl_dir == VFL_DIR_RX) + cap->device_caps = dev->vbi_cap_caps; + else if (vdev->vfl_type == VFL_TYPE_VBI && vdev->vfl_dir == VFL_DIR_TX) + cap->device_caps = dev->vbi_out_caps; + else if (vdev->vfl_type == VFL_TYPE_SDR) + cap->device_caps = dev->sdr_cap_caps; + else if (vdev->vfl_type == VFL_TYPE_RADIO && vdev->vfl_dir == VFL_DIR_RX) + cap->device_caps = dev->radio_rx_caps; + else if (vdev->vfl_type == VFL_TYPE_RADIO && vdev->vfl_dir == VFL_DIR_TX) + cap->device_caps = dev->radio_tx_caps; + cap->capabilities = dev->vid_cap_caps | dev->vid_out_caps | + dev->vbi_cap_caps | dev->vbi_out_caps | + dev->radio_rx_caps | dev->radio_tx_caps | + dev->sdr_cap_caps | V4L2_CAP_DEVICE_CAPS; + return 0; +} + +static int vidioc_s_hw_freq_seek(struct file *file, void *fh, const struct v4l2_hw_freq_seek *a) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_type == VFL_TYPE_RADIO) + return vivid_radio_rx_s_hw_freq_seek(file, fh, a); + return -ENOTTY; +} + +static int vidioc_enum_freq_bands(struct file *file, void *fh, struct v4l2_frequency_band *band) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_type == VFL_TYPE_RADIO) + return vivid_radio_rx_enum_freq_bands(file, fh, band); + if (vdev->vfl_type == VFL_TYPE_SDR) + return vivid_sdr_enum_freq_bands(file, fh, band); + return -ENOTTY; +} + +static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_type == VFL_TYPE_RADIO) + return vivid_radio_rx_g_tuner(file, fh, vt); + if (vdev->vfl_type == VFL_TYPE_SDR) + return vivid_sdr_g_tuner(file, fh, vt); + return vivid_video_g_tuner(file, fh, vt); +} + +static int vidioc_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *vt) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_type == VFL_TYPE_RADIO) + return vivid_radio_rx_s_tuner(file, fh, vt); + if (vdev->vfl_type == VFL_TYPE_SDR) + return vivid_sdr_s_tuner(file, fh, vt); + return vivid_video_s_tuner(file, fh, vt); +} + +static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency *vf) +{ + struct vivid_dev *dev = video_drvdata(file); + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_type == VFL_TYPE_RADIO) + return vivid_radio_g_frequency(file, + vdev->vfl_dir == VFL_DIR_RX ? + &dev->radio_rx_freq : &dev->radio_tx_freq, vf); + if (vdev->vfl_type == VFL_TYPE_SDR) + return vivid_sdr_g_frequency(file, fh, vf); + return vivid_video_g_frequency(file, fh, vf); +} + +static int vidioc_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *vf) +{ + struct vivid_dev *dev = video_drvdata(file); + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_type == VFL_TYPE_RADIO) + return vivid_radio_s_frequency(file, + vdev->vfl_dir == VFL_DIR_RX ? + &dev->radio_rx_freq : &dev->radio_tx_freq, vf); + if (vdev->vfl_type == VFL_TYPE_SDR) + return vivid_sdr_s_frequency(file, fh, vf); + return vivid_video_s_frequency(file, fh, vf); +} + +static int vidioc_overlay(struct file *file, void *fh, unsigned i) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_dir == VFL_DIR_RX) + return vivid_vid_cap_overlay(file, fh, i); + return vivid_vid_out_overlay(file, fh, i); +} + +static int vidioc_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *a) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_dir == VFL_DIR_RX) + return vivid_vid_cap_g_fbuf(file, fh, a); + return vivid_vid_out_g_fbuf(file, fh, a); +} + +static int vidioc_s_fbuf(struct file *file, void *fh, const struct v4l2_framebuffer *a) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_dir == VFL_DIR_RX) + return vivid_vid_cap_s_fbuf(file, fh, a); + return vivid_vid_out_s_fbuf(file, fh, a); +} + +static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id id) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_dir == VFL_DIR_RX) + return vivid_vid_cap_s_std(file, fh, id); + return vivid_vid_out_s_std(file, fh, id); +} + +static int vidioc_s_dv_timings(struct file *file, void *fh, struct v4l2_dv_timings *timings) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_dir == VFL_DIR_RX) + return vivid_vid_cap_s_dv_timings(file, fh, timings); + return vivid_vid_out_s_dv_timings(file, fh, timings); +} + +static int vidioc_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cc) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_dir == VFL_DIR_RX) + return vivid_vid_cap_cropcap(file, fh, cc); + return vivid_vid_out_cropcap(file, fh, cc); +} + +static int vidioc_g_selection(struct file *file, void *fh, + struct v4l2_selection *sel) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_dir == VFL_DIR_RX) + return vivid_vid_cap_g_selection(file, fh, sel); + return vivid_vid_out_g_selection(file, fh, sel); +} + +static int vidioc_s_selection(struct file *file, void *fh, + struct v4l2_selection *sel) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_dir == VFL_DIR_RX) + return vivid_vid_cap_s_selection(file, fh, sel); + return vivid_vid_out_s_selection(file, fh, sel); +} + +static int vidioc_g_parm(struct file *file, void *fh, + struct v4l2_streamparm *parm) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_dir == VFL_DIR_RX) + return vivid_vid_cap_g_parm(file, fh, parm); + return vivid_vid_out_g_parm(file, fh, parm); +} + +static int vidioc_s_parm(struct file *file, void *fh, + struct v4l2_streamparm *parm) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_dir == VFL_DIR_RX) + return vivid_vid_cap_s_parm(file, fh, parm); + return vivid_vid_out_g_parm(file, fh, parm); +} + +static ssize_t vivid_radio_read(struct file *file, char __user *buf, + size_t size, loff_t *offset) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_dir == VFL_DIR_TX) + return -EINVAL; + return vivid_radio_rx_read(file, buf, size, offset); +} + +static ssize_t vivid_radio_write(struct file *file, const char __user *buf, + size_t size, loff_t *offset) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_dir == VFL_DIR_RX) + return -EINVAL; + return vivid_radio_tx_write(file, buf, size, offset); +} + +static unsigned int vivid_radio_poll(struct file *file, struct poll_table_struct *wait) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_dir == VFL_DIR_RX) + return vivid_radio_rx_poll(file, wait); + return vivid_radio_tx_poll(file, wait); +} + +static bool vivid_is_in_use(struct video_device *vdev) +{ + unsigned long flags; + bool res; + + spin_lock_irqsave(&vdev->fh_lock, flags); + res = !list_empty(&vdev->fh_list); + spin_unlock_irqrestore(&vdev->fh_lock, flags); + return res; +} + +static bool vivid_is_last_user(struct vivid_dev *dev) +{ + unsigned uses = vivid_is_in_use(&dev->vid_cap_dev) + + vivid_is_in_use(&dev->vid_out_dev) + + vivid_is_in_use(&dev->vbi_cap_dev) + + vivid_is_in_use(&dev->vbi_out_dev) + + vivid_is_in_use(&dev->sdr_cap_dev) + + vivid_is_in_use(&dev->radio_rx_dev) + + vivid_is_in_use(&dev->radio_tx_dev); + + return uses == 1; +} + +static int vivid_fop_release(struct file *file) +{ + struct vivid_dev *dev = video_drvdata(file); + struct video_device *vdev = video_devdata(file); + + mutex_lock(&dev->mutex); + if (!no_error_inj && v4l2_fh_is_singular_file(file) && + !video_is_registered(vdev) && vivid_is_last_user(dev)) { + /* + * I am the last user of this driver, and a disconnect + * was forced (since this video_device is unregistered), + * so re-register all video_device's again. + */ + v4l2_info(&dev->v4l2_dev, "reconnect\n"); + set_bit(V4L2_FL_REGISTERED, &dev->vid_cap_dev.flags); + set_bit(V4L2_FL_REGISTERED, &dev->vid_out_dev.flags); + set_bit(V4L2_FL_REGISTERED, &dev->vbi_cap_dev.flags); + set_bit(V4L2_FL_REGISTERED, &dev->vbi_out_dev.flags); + set_bit(V4L2_FL_REGISTERED, &dev->sdr_cap_dev.flags); + set_bit(V4L2_FL_REGISTERED, &dev->radio_rx_dev.flags); + set_bit(V4L2_FL_REGISTERED, &dev->radio_tx_dev.flags); + } + mutex_unlock(&dev->mutex); + if (file->private_data == dev->overlay_cap_owner) + dev->overlay_cap_owner = NULL; + if (file->private_data == dev->radio_rx_rds_owner) { + dev->radio_rx_rds_last_block = 0; + dev->radio_rx_rds_owner = NULL; + } + if (file->private_data == dev->radio_tx_rds_owner) { + dev->radio_tx_rds_last_block = 0; + dev->radio_tx_rds_owner = NULL; + } + if (vdev->queue) + return vb2_fop_release(file); + return v4l2_fh_release(file); +} + +static const struct v4l2_file_operations vivid_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vivid_fop_release, + .read = vb2_fop_read, + .write = vb2_fop_write, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, +}; + +static const struct v4l2_file_operations vivid_radio_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vivid_fop_release, + .read = vivid_radio_read, + .write = vivid_radio_write, + .poll = vivid_radio_poll, + .unlocked_ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops vivid_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_mplane, + .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt_vid_cap_mplane, + .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt_vid_cap_mplane, + .vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt_vid_cap_mplane, + + .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid, + .vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out, + .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out, + .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out, + .vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_mplane, + .vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt_vid_out_mplane, + .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt_vid_out_mplane, + .vidioc_s_fmt_vid_out_mplane = vidioc_s_fmt_vid_out_mplane, + + .vidioc_g_selection = vidioc_g_selection, + .vidioc_s_selection = vidioc_s_selection, + .vidioc_cropcap = vidioc_cropcap, + + .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, + .vidioc_try_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, + .vidioc_s_fmt_vbi_cap = vidioc_s_fmt_vbi_cap, + + .vidioc_g_fmt_sliced_vbi_cap = vidioc_g_fmt_sliced_vbi_cap, + .vidioc_try_fmt_sliced_vbi_cap = vidioc_try_fmt_sliced_vbi_cap, + .vidioc_s_fmt_sliced_vbi_cap = vidioc_s_fmt_sliced_vbi_cap, + .vidioc_g_sliced_vbi_cap = vidioc_g_sliced_vbi_cap, + + .vidioc_g_fmt_vbi_out = vidioc_g_fmt_vbi_out, + .vidioc_try_fmt_vbi_out = vidioc_g_fmt_vbi_out, + .vidioc_s_fmt_vbi_out = vidioc_s_fmt_vbi_out, + + .vidioc_g_fmt_sliced_vbi_out = vidioc_g_fmt_sliced_vbi_out, + .vidioc_try_fmt_sliced_vbi_out = vidioc_try_fmt_sliced_vbi_out, + .vidioc_s_fmt_sliced_vbi_out = vidioc_s_fmt_sliced_vbi_out, + + .vidioc_enum_fmt_sdr_cap = vidioc_enum_fmt_sdr_cap, + .vidioc_g_fmt_sdr_cap = vidioc_g_fmt_sdr_cap, + .vidioc_try_fmt_sdr_cap = vidioc_g_fmt_sdr_cap, + .vidioc_s_fmt_sdr_cap = vidioc_g_fmt_sdr_cap, + + .vidioc_overlay = vidioc_overlay, + .vidioc_enum_framesizes = vidioc_enum_framesizes, + .vidioc_enum_frameintervals = vidioc_enum_frameintervals, + .vidioc_g_parm = vidioc_g_parm, + .vidioc_s_parm = vidioc_s_parm, + + .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt_vid_overlay, + .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay, + .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay, + .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay, + .vidioc_g_fmt_vid_out_overlay = vidioc_g_fmt_vid_out_overlay, + .vidioc_try_fmt_vid_out_overlay = vidioc_try_fmt_vid_out_overlay, + .vidioc_s_fmt_vid_out_overlay = vidioc_s_fmt_vid_out_overlay, + .vidioc_g_fbuf = vidioc_g_fbuf, + .vidioc_s_fbuf = vidioc_s_fbuf, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_enumaudio = vidioc_enumaudio, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_modulator = vidioc_s_modulator, + .vidioc_g_modulator = vidioc_g_modulator, + .vidioc_s_hw_freq_seek = vidioc_s_hw_freq_seek, + .vidioc_enum_freq_bands = vidioc_enum_freq_bands, + + .vidioc_enum_output = vidioc_enum_output, + .vidioc_g_output = vidioc_g_output, + .vidioc_s_output = vidioc_s_output, + .vidioc_s_audout = vidioc_s_audout, + .vidioc_g_audout = vidioc_g_audout, + .vidioc_enumaudout = vidioc_enumaudout, + + .vidioc_querystd = vidioc_querystd, + .vidioc_g_std = vidioc_g_std, + .vidioc_s_std = vidioc_s_std, + .vidioc_s_dv_timings = vidioc_s_dv_timings, + .vidioc_g_dv_timings = vidioc_g_dv_timings, + .vidioc_query_dv_timings = vidioc_query_dv_timings, + .vidioc_enum_dv_timings = vidioc_enum_dv_timings, + .vidioc_dv_timings_cap = vidioc_dv_timings_cap, + .vidioc_g_edid = vidioc_g_edid, + .vidioc_s_edid = vidioc_s_edid, + + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = vidioc_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +/* ----------------------------------------------------------------- + Initialization and module stuff + ------------------------------------------------------------------*/ + +static void vivid_dev_release(struct v4l2_device *v4l2_dev) +{ + struct vivid_dev *dev = container_of(v4l2_dev, struct vivid_dev, v4l2_dev); + + vivid_free_controls(dev); + v4l2_device_unregister(&dev->v4l2_dev); + vfree(dev->scaled_line); + vfree(dev->blended_line); + vfree(dev->edid); + vfree(dev->bitmap_cap); + vfree(dev->bitmap_out); + tpg_free(&dev->tpg); + kfree(dev->query_dv_timings_qmenu); + kfree(dev); +} + +static int vivid_create_instance(struct platform_device *pdev, int inst) +{ + static const struct v4l2_dv_timings def_dv_timings = + V4L2_DV_BT_CEA_1280X720P60; + unsigned in_type_counter[4] = { 0, 0, 0, 0 }; + unsigned out_type_counter[4] = { 0, 0, 0, 0 }; + int ccs_cap = ccs_cap_mode[inst]; + int ccs_out = ccs_out_mode[inst]; + bool has_tuner; + bool has_modulator; + struct vivid_dev *dev; + struct video_device *vfd; + struct vb2_queue *q; + unsigned node_type = node_types[inst]; + v4l2_std_id tvnorms_cap = 0, tvnorms_out = 0; + int ret; + int i; + + /* allocate main vivid state structure */ + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dev->inst = inst; + + /* register v4l2_device */ + snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name), + "%s-%03d", VIVID_MODULE_NAME, inst); + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); + if (ret) { + kfree(dev); + return ret; + } + dev->v4l2_dev.release = vivid_dev_release; + + /* start detecting feature set */ + + /* do we use single- or multi-planar? */ + dev->multiplanar = multiplanar[inst] > 1; + v4l2_info(&dev->v4l2_dev, "using %splanar format API\n", + dev->multiplanar ? "multi" : "single "); + + /* how many inputs do we have and of what type? */ + dev->num_inputs = num_inputs[inst]; + if (dev->num_inputs < 1) + dev->num_inputs = 1; + if (dev->num_inputs >= MAX_INPUTS) + dev->num_inputs = MAX_INPUTS; + for (i = 0; i < dev->num_inputs; i++) { + dev->input_type[i] = (input_types[inst] >> (i * 2)) & 0x3; + dev->input_name_counter[i] = in_type_counter[dev->input_type[i]]++; + } + dev->has_audio_inputs = in_type_counter[TV] && in_type_counter[SVID]; + + /* how many outputs do we have and of what type? */ + dev->num_outputs = num_outputs[inst]; + if (dev->num_outputs < 1) + dev->num_outputs = 1; + if (dev->num_outputs >= MAX_OUTPUTS) + dev->num_outputs = MAX_OUTPUTS; + for (i = 0; i < dev->num_outputs; i++) { + dev->output_type[i] = ((output_types[inst] >> i) & 1) ? HDMI : SVID; + dev->output_name_counter[i] = out_type_counter[dev->output_type[i]]++; + } + dev->has_audio_outputs = out_type_counter[SVID]; + + /* do we create a video capture device? */ + dev->has_vid_cap = node_type & 0x0001; + + /* do we create a vbi capture device? */ + if (in_type_counter[TV] || in_type_counter[SVID]) { + dev->has_raw_vbi_cap = node_type & 0x0004; + dev->has_sliced_vbi_cap = node_type & 0x0008; + dev->has_vbi_cap = dev->has_raw_vbi_cap | dev->has_sliced_vbi_cap; + } + + /* do we create a video output device? */ + dev->has_vid_out = node_type & 0x0100; + + /* do we create a vbi output device? */ + if (out_type_counter[SVID]) { + dev->has_raw_vbi_out = node_type & 0x0400; + dev->has_sliced_vbi_out = node_type & 0x0800; + dev->has_vbi_out = dev->has_raw_vbi_out | dev->has_sliced_vbi_out; + } + + /* do we create a radio receiver device? */ + dev->has_radio_rx = node_type & 0x0010; + + /* do we create a radio transmitter device? */ + dev->has_radio_tx = node_type & 0x1000; + + /* do we create a software defined radio capture device? */ + dev->has_sdr_cap = node_type & 0x0020; + + /* do we have a tuner? */ + has_tuner = ((dev->has_vid_cap || dev->has_vbi_cap) && in_type_counter[TV]) || + dev->has_radio_rx || dev->has_sdr_cap; + + /* do we have a modulator? */ + has_modulator = dev->has_radio_tx; + + if (dev->has_vid_cap) + /* do we have a framebuffer for overlay testing? */ + dev->has_fb = node_type & 0x10000; + + /* can we do crop/compose/scaling while capturing? */ + if (no_error_inj && ccs_cap == -1) + ccs_cap = 7; + + /* if ccs_cap == -1, then the use can select it using controls */ + if (ccs_cap != -1) { + dev->has_crop_cap = ccs_cap & 1; + dev->has_compose_cap = ccs_cap & 2; + dev->has_scaler_cap = ccs_cap & 4; + v4l2_info(&dev->v4l2_dev, "Capture Crop: %c Compose: %c Scaler: %c\n", + dev->has_crop_cap ? 'Y' : 'N', + dev->has_compose_cap ? 'Y' : 'N', + dev->has_scaler_cap ? 'Y' : 'N'); + } + + /* can we do crop/compose/scaling with video output? */ + if (no_error_inj && ccs_out == -1) + ccs_out = 7; + + /* if ccs_out == -1, then the use can select it using controls */ + if (ccs_out != -1) { + dev->has_crop_out = ccs_out & 1; + dev->has_compose_out = ccs_out & 2; + dev->has_scaler_out = ccs_out & 4; + v4l2_info(&dev->v4l2_dev, "Output Crop: %c Compose: %c Scaler: %c\n", + dev->has_crop_out ? 'Y' : 'N', + dev->has_compose_out ? 'Y' : 'N', + dev->has_scaler_out ? 'Y' : 'N'); + } + + /* end detecting feature set */ + + if (dev->has_vid_cap) { + /* set up the capabilities of the video capture device */ + dev->vid_cap_caps = dev->multiplanar ? + V4L2_CAP_VIDEO_CAPTURE_MPLANE : + V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY; + dev->vid_cap_caps |= V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; + if (dev->has_audio_inputs) + dev->vid_cap_caps |= V4L2_CAP_AUDIO; + if (in_type_counter[TV]) + dev->vid_cap_caps |= V4L2_CAP_TUNER; + } + if (dev->has_vid_out) { + /* set up the capabilities of the video output device */ + dev->vid_out_caps = dev->multiplanar ? + V4L2_CAP_VIDEO_OUTPUT_MPLANE : + V4L2_CAP_VIDEO_OUTPUT; + if (dev->has_fb) + dev->vid_out_caps |= V4L2_CAP_VIDEO_OUTPUT_OVERLAY; + dev->vid_out_caps |= V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; + if (dev->has_audio_outputs) + dev->vid_out_caps |= V4L2_CAP_AUDIO; + } + if (dev->has_vbi_cap) { + /* set up the capabilities of the vbi capture device */ + dev->vbi_cap_caps = (dev->has_raw_vbi_cap ? V4L2_CAP_VBI_CAPTURE : 0) | + (dev->has_sliced_vbi_cap ? V4L2_CAP_SLICED_VBI_CAPTURE : 0); + dev->vbi_cap_caps |= V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; + if (dev->has_audio_inputs) + dev->vbi_cap_caps |= V4L2_CAP_AUDIO; + if (in_type_counter[TV]) + dev->vbi_cap_caps |= V4L2_CAP_TUNER; + } + if (dev->has_vbi_out) { + /* set up the capabilities of the vbi output device */ + dev->vbi_out_caps = (dev->has_raw_vbi_out ? V4L2_CAP_VBI_OUTPUT : 0) | + (dev->has_sliced_vbi_out ? V4L2_CAP_SLICED_VBI_OUTPUT : 0); + dev->vbi_out_caps |= V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; + if (dev->has_audio_outputs) + dev->vbi_out_caps |= V4L2_CAP_AUDIO; + } + if (dev->has_sdr_cap) { + /* set up the capabilities of the sdr capture device */ + dev->sdr_cap_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_TUNER; + dev->sdr_cap_caps |= V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; + } + /* set up the capabilities of the radio receiver device */ + if (dev->has_radio_rx) + dev->radio_rx_caps = V4L2_CAP_RADIO | V4L2_CAP_RDS_CAPTURE | + V4L2_CAP_HW_FREQ_SEEK | V4L2_CAP_TUNER | + V4L2_CAP_READWRITE; + /* set up the capabilities of the radio transmitter device */ + if (dev->has_radio_tx) + dev->radio_tx_caps = V4L2_CAP_RDS_OUTPUT | V4L2_CAP_MODULATOR | + V4L2_CAP_READWRITE; + + /* initialize the test pattern generator */ + tpg_init(&dev->tpg, 640, 360); + if (tpg_alloc(&dev->tpg, MAX_ZOOM * MAX_WIDTH)) + goto free_dev; + dev->scaled_line = vzalloc(MAX_ZOOM * MAX_WIDTH); + if (!dev->scaled_line) + goto free_dev; + dev->blended_line = vzalloc(MAX_ZOOM * MAX_WIDTH); + if (!dev->blended_line) + goto free_dev; + + /* load the edid */ + dev->edid = vmalloc(256 * 128); + if (!dev->edid) + goto free_dev; + + /* create a string array containing the names of all the preset timings */ + while (v4l2_dv_timings_presets[dev->query_dv_timings_size].bt.width) + dev->query_dv_timings_size++; + dev->query_dv_timings_qmenu = kmalloc(dev->query_dv_timings_size * + (sizeof(void *) + 32), GFP_KERNEL); + if (dev->query_dv_timings_qmenu == NULL) + goto free_dev; + for (i = 0; i < dev->query_dv_timings_size; i++) { + const struct v4l2_bt_timings *bt = &v4l2_dv_timings_presets[i].bt; + char *p = (char *)&dev->query_dv_timings_qmenu[dev->query_dv_timings_size]; + u32 htot, vtot; + + p += i * 32; + dev->query_dv_timings_qmenu[i] = p; + + htot = V4L2_DV_BT_FRAME_WIDTH(bt); + vtot = V4L2_DV_BT_FRAME_HEIGHT(bt); + snprintf(p, 32, "%ux%u%s%u", + bt->width, bt->height, bt->interlaced ? "i" : "p", + (u32)bt->pixelclock / (htot * vtot)); + } + + /* disable invalid ioctls based on the feature set */ + if (!dev->has_audio_inputs) { + v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_AUDIO); + v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_G_AUDIO); + v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_ENUMAUDIO); + v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_S_AUDIO); + v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_G_AUDIO); + v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_ENUMAUDIO); + } + if (!dev->has_audio_outputs) { + v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_S_AUDOUT); + v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_G_AUDOUT); + v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_ENUMAUDOUT); + v4l2_disable_ioctl(&dev->vbi_out_dev, VIDIOC_S_AUDOUT); + v4l2_disable_ioctl(&dev->vbi_out_dev, VIDIOC_G_AUDOUT); + v4l2_disable_ioctl(&dev->vbi_out_dev, VIDIOC_ENUMAUDOUT); + } + if (!in_type_counter[TV] && !in_type_counter[SVID]) { + v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_STD); + v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_G_STD); + v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_ENUMSTD); + v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_QUERYSTD); + } + if (!out_type_counter[SVID]) { + v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_S_STD); + v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_G_STD); + v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_ENUMSTD); + } + if (!has_tuner && !has_modulator) { + v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_FREQUENCY); + v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_G_FREQUENCY); + v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_S_FREQUENCY); + v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_G_FREQUENCY); + } + if (!has_tuner) { + v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_TUNER); + v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_G_TUNER); + v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_S_TUNER); + v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_G_TUNER); + } + if (in_type_counter[HDMI] == 0) { + v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_EDID); + v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_G_EDID); + v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_DV_TIMINGS_CAP); + v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_G_DV_TIMINGS); + v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_DV_TIMINGS); + v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_ENUM_DV_TIMINGS); + v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_QUERY_DV_TIMINGS); + } + if (out_type_counter[HDMI] == 0) { + v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_G_EDID); + v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_DV_TIMINGS_CAP); + v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_G_DV_TIMINGS); + v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_S_DV_TIMINGS); + v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_ENUM_DV_TIMINGS); + } + if (!dev->has_fb) { + v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_G_FBUF); + v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_S_FBUF); + v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_OVERLAY); + } + v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_HW_FREQ_SEEK); + v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_S_HW_FREQ_SEEK); + v4l2_disable_ioctl(&dev->sdr_cap_dev, VIDIOC_S_HW_FREQ_SEEK); + v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_S_FREQUENCY); + v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_G_FREQUENCY); + v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_ENUM_FRAMESIZES); + v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_ENUM_FRAMEINTERVALS); + v4l2_disable_ioctl(&dev->vbi_out_dev, VIDIOC_S_FREQUENCY); + v4l2_disable_ioctl(&dev->vbi_out_dev, VIDIOC_G_FREQUENCY); + + /* configure internal data */ + dev->fmt_cap = &vivid_formats[0]; + dev->fmt_out = &vivid_formats[0]; + if (!dev->multiplanar) + vivid_formats[0].data_offset[0] = 0; + dev->webcam_size_idx = 1; + dev->webcam_ival_idx = 3; + tpg_s_fourcc(&dev->tpg, dev->fmt_cap->fourcc); + dev->std_cap = V4L2_STD_PAL; + dev->std_out = V4L2_STD_PAL; + if (dev->input_type[0] == TV || dev->input_type[0] == SVID) + tvnorms_cap = V4L2_STD_ALL; + if (dev->output_type[0] == SVID) + tvnorms_out = V4L2_STD_ALL; + dev->dv_timings_cap = def_dv_timings; + dev->dv_timings_out = def_dv_timings; + dev->tv_freq = 2804 /* 175.25 * 16 */; + dev->tv_audmode = V4L2_TUNER_MODE_STEREO; + dev->tv_field_cap = V4L2_FIELD_INTERLACED; + dev->tv_field_out = V4L2_FIELD_INTERLACED; + dev->radio_rx_freq = 95000 * 16; + dev->radio_rx_audmode = V4L2_TUNER_MODE_STEREO; + if (dev->has_radio_tx) { + dev->radio_tx_freq = 95500 * 16; + dev->radio_rds_loop = false; + } + dev->radio_tx_subchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_RDS; + dev->sdr_adc_freq = 300000; + dev->sdr_fm_freq = 50000000; + dev->edid_max_blocks = dev->edid_blocks = 2; + memcpy(dev->edid, vivid_hdmi_edid, sizeof(vivid_hdmi_edid)); + ktime_get_ts(&dev->radio_rds_init_ts); + + /* create all controls */ + ret = vivid_create_controls(dev, ccs_cap == -1, ccs_out == -1, no_error_inj, + in_type_counter[TV] || in_type_counter[SVID] || + out_type_counter[SVID], + in_type_counter[HDMI] || out_type_counter[HDMI]); + if (ret) + goto unreg_dev; + + /* + * update the capture and output formats to do a proper initial + * configuration. + */ + vivid_update_format_cap(dev, false); + vivid_update_format_out(dev); + + v4l2_ctrl_handler_setup(&dev->ctrl_hdl_vid_cap); + v4l2_ctrl_handler_setup(&dev->ctrl_hdl_vid_out); + v4l2_ctrl_handler_setup(&dev->ctrl_hdl_vbi_cap); + v4l2_ctrl_handler_setup(&dev->ctrl_hdl_vbi_out); + v4l2_ctrl_handler_setup(&dev->ctrl_hdl_radio_rx); + v4l2_ctrl_handler_setup(&dev->ctrl_hdl_radio_tx); + v4l2_ctrl_handler_setup(&dev->ctrl_hdl_sdr_cap); + + /* initialize overlay */ + dev->fb_cap.fmt.width = dev->src_rect.width; + dev->fb_cap.fmt.height = dev->src_rect.height; + dev->fb_cap.fmt.pixelformat = dev->fmt_cap->fourcc; + dev->fb_cap.fmt.bytesperline = dev->src_rect.width * tpg_g_twopixelsize(&dev->tpg, 0) / 2; + dev->fb_cap.fmt.sizeimage = dev->src_rect.height * dev->fb_cap.fmt.bytesperline; + + /* initialize locks */ + spin_lock_init(&dev->slock); + mutex_init(&dev->mutex); + + /* init dma queues */ + INIT_LIST_HEAD(&dev->vid_cap_active); + INIT_LIST_HEAD(&dev->vid_out_active); + INIT_LIST_HEAD(&dev->vbi_cap_active); + INIT_LIST_HEAD(&dev->vbi_out_active); + INIT_LIST_HEAD(&dev->sdr_cap_active); + + /* start creating the vb2 queues */ + if (dev->has_vid_cap) { + /* initialize vid_cap queue */ + q = &dev->vb_vid_cap_q; + q->type = dev->multiplanar ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE : + V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; + q->drv_priv = dev; + q->buf_struct_size = sizeof(struct vivid_buffer); + q->ops = &vivid_vid_cap_qops; + q->mem_ops = &vb2_vmalloc_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->min_buffers_needed = 2; + q->lock = &dev->mutex; + + ret = vb2_queue_init(q); + if (ret) + goto unreg_dev; + } + + if (dev->has_vid_out) { + /* initialize vid_out queue */ + q = &dev->vb_vid_out_q; + q->type = dev->multiplanar ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE : + V4L2_BUF_TYPE_VIDEO_OUTPUT; + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_WRITE; + q->drv_priv = dev; + q->buf_struct_size = sizeof(struct vivid_buffer); + q->ops = &vivid_vid_out_qops; + q->mem_ops = &vb2_vmalloc_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->min_buffers_needed = 2; + q->lock = &dev->mutex; + + ret = vb2_queue_init(q); + if (ret) + goto unreg_dev; + } + + if (dev->has_vbi_cap) { + /* initialize vbi_cap queue */ + q = &dev->vb_vbi_cap_q; + q->type = dev->has_raw_vbi_cap ? V4L2_BUF_TYPE_VBI_CAPTURE : + V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; + q->drv_priv = dev; + q->buf_struct_size = sizeof(struct vivid_buffer); + q->ops = &vivid_vbi_cap_qops; + q->mem_ops = &vb2_vmalloc_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->min_buffers_needed = 2; + q->lock = &dev->mutex; + + ret = vb2_queue_init(q); + if (ret) + goto unreg_dev; + } + + if (dev->has_vbi_out) { + /* initialize vbi_out queue */ + q = &dev->vb_vbi_out_q; + q->type = dev->has_raw_vbi_out ? V4L2_BUF_TYPE_VBI_OUTPUT : + V4L2_BUF_TYPE_SLICED_VBI_OUTPUT; + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_WRITE; + q->drv_priv = dev; + q->buf_struct_size = sizeof(struct vivid_buffer); + q->ops = &vivid_vbi_out_qops; + q->mem_ops = &vb2_vmalloc_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->min_buffers_needed = 2; + q->lock = &dev->mutex; + + ret = vb2_queue_init(q); + if (ret) + goto unreg_dev; + } + + if (dev->has_sdr_cap) { + /* initialize sdr_cap queue */ + q = &dev->vb_sdr_cap_q; + q->type = V4L2_BUF_TYPE_SDR_CAPTURE; + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; + q->drv_priv = dev; + q->buf_struct_size = sizeof(struct vivid_buffer); + q->ops = &vivid_sdr_cap_qops; + q->mem_ops = &vb2_vmalloc_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->min_buffers_needed = 8; + q->lock = &dev->mutex; + + ret = vb2_queue_init(q); + if (ret) + goto unreg_dev; + } + + if (dev->has_fb) { + /* Create framebuffer for testing capture/output overlay */ + ret = vivid_fb_init(dev); + if (ret) + goto unreg_dev; + v4l2_info(&dev->v4l2_dev, "Framebuffer device registered as fb%d\n", + dev->fb_info.node); + } + + /* finally start creating the device nodes */ + if (dev->has_vid_cap) { + vfd = &dev->vid_cap_dev; + strlcpy(vfd->name, "vivid-vid-cap", sizeof(vfd->name)); + vfd->fops = &vivid_fops; + vfd->ioctl_ops = &vivid_ioctl_ops; + vfd->release = video_device_release_empty; + vfd->v4l2_dev = &dev->v4l2_dev; + vfd->queue = &dev->vb_vid_cap_q; + vfd->tvnorms = tvnorms_cap; + + /* + * Provide a mutex to v4l2 core. It will be used to protect + * all fops and v4l2 ioctls. + */ + vfd->lock = &dev->mutex; + video_set_drvdata(vfd, dev); + + ret = video_register_device(vfd, VFL_TYPE_GRABBER, vid_cap_nr[inst]); + if (ret < 0) + goto unreg_dev; + v4l2_info(&dev->v4l2_dev, "V4L2 capture device registered as %s\n", + video_device_node_name(vfd)); + } + + if (dev->has_vid_out) { + vfd = &dev->vid_out_dev; + strlcpy(vfd->name, "vivid-vid-out", sizeof(vfd->name)); + vfd->vfl_dir = VFL_DIR_TX; + vfd->fops = &vivid_fops; + vfd->ioctl_ops = &vivid_ioctl_ops; + vfd->release = video_device_release_empty; + vfd->v4l2_dev = &dev->v4l2_dev; + vfd->queue = &dev->vb_vid_out_q; + vfd->tvnorms = tvnorms_out; + + /* + * Provide a mutex to v4l2 core. It will be used to protect + * all fops and v4l2 ioctls. + */ + vfd->lock = &dev->mutex; + video_set_drvdata(vfd, dev); + + ret = video_register_device(vfd, VFL_TYPE_GRABBER, vid_out_nr[inst]); + if (ret < 0) + goto unreg_dev; + v4l2_info(&dev->v4l2_dev, "V4L2 output device registered as %s\n", + video_device_node_name(vfd)); + } + + if (dev->has_vbi_cap) { + vfd = &dev->vbi_cap_dev; + strlcpy(vfd->name, "vivid-vbi-cap", sizeof(vfd->name)); + vfd->fops = &vivid_fops; + vfd->ioctl_ops = &vivid_ioctl_ops; + vfd->release = video_device_release_empty; + vfd->v4l2_dev = &dev->v4l2_dev; + vfd->queue = &dev->vb_vbi_cap_q; + vfd->lock = &dev->mutex; + vfd->tvnorms = tvnorms_cap; + video_set_drvdata(vfd, dev); + + ret = video_register_device(vfd, VFL_TYPE_VBI, vbi_cap_nr[inst]); + if (ret < 0) + goto unreg_dev; + v4l2_info(&dev->v4l2_dev, "V4L2 capture device registered as %s, supports %s VBI\n", + video_device_node_name(vfd), + (dev->has_raw_vbi_cap && dev->has_sliced_vbi_cap) ? + "raw and sliced" : + (dev->has_raw_vbi_cap ? "raw" : "sliced")); + } + + if (dev->has_vbi_out) { + vfd = &dev->vbi_out_dev; + strlcpy(vfd->name, "vivid-vbi-out", sizeof(vfd->name)); + vfd->vfl_dir = VFL_DIR_TX; + vfd->fops = &vivid_fops; + vfd->ioctl_ops = &vivid_ioctl_ops; + vfd->release = video_device_release_empty; + vfd->v4l2_dev = &dev->v4l2_dev; + vfd->queue = &dev->vb_vbi_out_q; + vfd->lock = &dev->mutex; + vfd->tvnorms = tvnorms_out; + video_set_drvdata(vfd, dev); + + ret = video_register_device(vfd, VFL_TYPE_VBI, vbi_out_nr[inst]); + if (ret < 0) + goto unreg_dev; + v4l2_info(&dev->v4l2_dev, "V4L2 output device registered as %s, supports %s VBI\n", + video_device_node_name(vfd), + (dev->has_raw_vbi_out && dev->has_sliced_vbi_out) ? + "raw and sliced" : + (dev->has_raw_vbi_out ? "raw" : "sliced")); + } + + if (dev->has_sdr_cap) { + vfd = &dev->sdr_cap_dev; + strlcpy(vfd->name, "vivid-sdr-cap", sizeof(vfd->name)); + vfd->fops = &vivid_fops; + vfd->ioctl_ops = &vivid_ioctl_ops; + vfd->release = video_device_release_empty; + vfd->v4l2_dev = &dev->v4l2_dev; + vfd->queue = &dev->vb_sdr_cap_q; + vfd->lock = &dev->mutex; + video_set_drvdata(vfd, dev); + + ret = video_register_device(vfd, VFL_TYPE_SDR, sdr_cap_nr[inst]); + if (ret < 0) + goto unreg_dev; + v4l2_info(&dev->v4l2_dev, "V4L2 capture device registered as %s\n", + video_device_node_name(vfd)); + } + + if (dev->has_radio_rx) { + vfd = &dev->radio_rx_dev; + strlcpy(vfd->name, "vivid-rad-rx", sizeof(vfd->name)); + vfd->fops = &vivid_radio_fops; + vfd->ioctl_ops = &vivid_ioctl_ops; + vfd->release = video_device_release_empty; + vfd->v4l2_dev = &dev->v4l2_dev; + vfd->lock = &dev->mutex; + video_set_drvdata(vfd, dev); + + ret = video_register_device(vfd, VFL_TYPE_RADIO, radio_rx_nr[inst]); + if (ret < 0) + goto unreg_dev; + v4l2_info(&dev->v4l2_dev, "V4L2 receiver device registered as %s\n", + video_device_node_name(vfd)); + } + + if (dev->has_radio_tx) { + vfd = &dev->radio_tx_dev; + strlcpy(vfd->name, "vivid-rad-tx", sizeof(vfd->name)); + vfd->vfl_dir = VFL_DIR_TX; + vfd->fops = &vivid_radio_fops; + vfd->ioctl_ops = &vivid_ioctl_ops; + vfd->release = video_device_release_empty; + vfd->v4l2_dev = &dev->v4l2_dev; + vfd->lock = &dev->mutex; + video_set_drvdata(vfd, dev); + + ret = video_register_device(vfd, VFL_TYPE_RADIO, radio_tx_nr[inst]); + if (ret < 0) + goto unreg_dev; + v4l2_info(&dev->v4l2_dev, "V4L2 transmitter device registered as %s\n", + video_device_node_name(vfd)); + } + + /* Now that everything is fine, let's add it to device list */ + vivid_devs[inst] = dev; + + return 0; + +unreg_dev: + video_unregister_device(&dev->radio_tx_dev); + video_unregister_device(&dev->radio_rx_dev); + video_unregister_device(&dev->sdr_cap_dev); + video_unregister_device(&dev->vbi_out_dev); + video_unregister_device(&dev->vbi_cap_dev); + video_unregister_device(&dev->vid_out_dev); + video_unregister_device(&dev->vid_cap_dev); +free_dev: + v4l2_device_put(&dev->v4l2_dev); + return ret; +} + +/* This routine allocates from 1 to n_devs virtual drivers. + + The real maximum number of virtual drivers will depend on how many drivers + will succeed. This is limited to the maximum number of devices that + videodev supports, which is equal to VIDEO_NUM_DEVICES. + */ +static int vivid_probe(struct platform_device *pdev) +{ + const struct font_desc *font = find_font("VGA8x16"); + int ret = 0, i; + + if (font == NULL) { + pr_err("vivid: could not find font\n"); + return -ENODEV; + } + + tpg_set_font(font->data); + + n_devs = clamp_t(unsigned, n_devs, 1, VIVID_MAX_DEVS); + + for (i = 0; i < n_devs; i++) { + ret = vivid_create_instance(pdev, i); + if (ret) { + /* If some instantiations succeeded, keep driver */ + if (i) + ret = 0; + break; + } + } + + if (ret < 0) { + pr_err("vivid: error %d while loading driver\n", ret); + return ret; + } + + /* n_devs will reflect the actual number of allocated devices */ + n_devs = i; + + return ret; +} + +static int vivid_remove(struct platform_device *pdev) +{ + struct vivid_dev *dev; + unsigned i; + + for (i = 0; vivid_devs[i]; i++) { + dev = vivid_devs[i]; + + if (dev->has_vid_cap) { + v4l2_info(&dev->v4l2_dev, "unregistering %s\n", + video_device_node_name(&dev->vid_cap_dev)); + video_unregister_device(&dev->vid_cap_dev); + } + if (dev->has_vid_out) { + v4l2_info(&dev->v4l2_dev, "unregistering %s\n", + video_device_node_name(&dev->vid_out_dev)); + video_unregister_device(&dev->vid_out_dev); + } + if (dev->has_vbi_cap) { + v4l2_info(&dev->v4l2_dev, "unregistering %s\n", + video_device_node_name(&dev->vbi_cap_dev)); + video_unregister_device(&dev->vbi_cap_dev); + } + if (dev->has_vbi_out) { + v4l2_info(&dev->v4l2_dev, "unregistering %s\n", + video_device_node_name(&dev->vbi_out_dev)); + video_unregister_device(&dev->vbi_out_dev); + } + if (dev->has_sdr_cap) { + v4l2_info(&dev->v4l2_dev, "unregistering %s\n", + video_device_node_name(&dev->sdr_cap_dev)); + video_unregister_device(&dev->sdr_cap_dev); + } + if (dev->has_radio_rx) { + v4l2_info(&dev->v4l2_dev, "unregistering %s\n", + video_device_node_name(&dev->radio_rx_dev)); + video_unregister_device(&dev->radio_rx_dev); + } + if (dev->has_radio_tx) { + v4l2_info(&dev->v4l2_dev, "unregistering %s\n", + video_device_node_name(&dev->radio_tx_dev)); + video_unregister_device(&dev->radio_tx_dev); + } + if (dev->has_fb) { + v4l2_info(&dev->v4l2_dev, "unregistering fb%d\n", + dev->fb_info.node); + unregister_framebuffer(&dev->fb_info); + vivid_fb_release_buffers(dev); + } + v4l2_device_put(&dev->v4l2_dev); + vivid_devs[i] = NULL; + } + return 0; +} + +static void vivid_pdev_release(struct device *dev) +{ +} + +static struct platform_device vivid_pdev = { + .name = "vivid", + .dev.release = vivid_pdev_release, +}; + +static struct platform_driver vivid_pdrv = { + .probe = vivid_probe, + .remove = vivid_remove, + .driver = { + .name = "vivid", + }, +}; + +static int __init vivid_init(void) +{ + int ret; + + ret = platform_device_register(&vivid_pdev); + if (ret) + return ret; + + ret = platform_driver_register(&vivid_pdrv); + if (ret) + platform_device_unregister(&vivid_pdev); + + return ret; +} + +static void __exit vivid_exit(void) +{ + platform_driver_unregister(&vivid_pdrv); + platform_device_unregister(&vivid_pdev); +} + +module_init(vivid_init); +module_exit(vivid_exit); -- cgit 1.2.3-korg