162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * This is a V4L2 PCI Skeleton Driver. It gives an initial skeleton source 462306a36Sopenharmony_ci * for use with other PCI drivers. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * This skeleton PCI driver assumes that the card has an S-Video connector as 762306a36Sopenharmony_ci * input 0 and an HDMI connector as input 1. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/types.h> 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/init.h> 1662306a36Sopenharmony_ci#include <linux/kmod.h> 1762306a36Sopenharmony_ci#include <linux/mutex.h> 1862306a36Sopenharmony_ci#include <linux/pci.h> 1962306a36Sopenharmony_ci#include <linux/interrupt.h> 2062306a36Sopenharmony_ci#include <linux/videodev2.h> 2162306a36Sopenharmony_ci#include <linux/v4l2-dv-timings.h> 2262306a36Sopenharmony_ci#include <media/v4l2-device.h> 2362306a36Sopenharmony_ci#include <media/v4l2-dev.h> 2462306a36Sopenharmony_ci#include <media/v4l2-ioctl.h> 2562306a36Sopenharmony_ci#include <media/v4l2-dv-timings.h> 2662306a36Sopenharmony_ci#include <media/v4l2-ctrls.h> 2762306a36Sopenharmony_ci#include <media/v4l2-event.h> 2862306a36Sopenharmony_ci#include <media/videobuf2-v4l2.h> 2962306a36Sopenharmony_ci#include <media/videobuf2-dma-contig.h> 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ciMODULE_DESCRIPTION("V4L2 PCI Skeleton Driver"); 3262306a36Sopenharmony_ciMODULE_AUTHOR("Hans Verkuil"); 3362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/** 3662306a36Sopenharmony_ci * struct skeleton - All internal data for one instance of device 3762306a36Sopenharmony_ci * @pdev: PCI device 3862306a36Sopenharmony_ci * @v4l2_dev: top-level v4l2 device struct 3962306a36Sopenharmony_ci * @vdev: video node structure 4062306a36Sopenharmony_ci * @ctrl_handler: control handler structure 4162306a36Sopenharmony_ci * @lock: ioctl serialization mutex 4262306a36Sopenharmony_ci * @std: current SDTV standard 4362306a36Sopenharmony_ci * @timings: current HDTV timings 4462306a36Sopenharmony_ci * @format: current pix format 4562306a36Sopenharmony_ci * @input: current video input (0 = SDTV, 1 = HDTV) 4662306a36Sopenharmony_ci * @queue: vb2 video capture queue 4762306a36Sopenharmony_ci * @qlock: spinlock controlling access to buf_list and sequence 4862306a36Sopenharmony_ci * @buf_list: list of buffers queued for DMA 4962306a36Sopenharmony_ci * @field: the field (TOP/BOTTOM/other) of the current buffer 5062306a36Sopenharmony_ci * @sequence: frame sequence counter 5162306a36Sopenharmony_ci */ 5262306a36Sopenharmony_cistruct skeleton { 5362306a36Sopenharmony_ci struct pci_dev *pdev; 5462306a36Sopenharmony_ci struct v4l2_device v4l2_dev; 5562306a36Sopenharmony_ci struct video_device vdev; 5662306a36Sopenharmony_ci struct v4l2_ctrl_handler ctrl_handler; 5762306a36Sopenharmony_ci struct mutex lock; 5862306a36Sopenharmony_ci v4l2_std_id std; 5962306a36Sopenharmony_ci struct v4l2_dv_timings timings; 6062306a36Sopenharmony_ci struct v4l2_pix_format format; 6162306a36Sopenharmony_ci unsigned input; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci struct vb2_queue queue; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci spinlock_t qlock; 6662306a36Sopenharmony_ci struct list_head buf_list; 6762306a36Sopenharmony_ci unsigned field; 6862306a36Sopenharmony_ci unsigned sequence; 6962306a36Sopenharmony_ci}; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistruct skel_buffer { 7262306a36Sopenharmony_ci struct vb2_v4l2_buffer vb; 7362306a36Sopenharmony_ci struct list_head list; 7462306a36Sopenharmony_ci}; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic inline struct skel_buffer *to_skel_buffer(struct vb2_v4l2_buffer *vbuf) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci return container_of(vbuf, struct skel_buffer, vb); 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic const struct pci_device_id skeleton_pci_tbl[] = { 8262306a36Sopenharmony_ci /* { PCI_DEVICE(PCI_VENDOR_ID_, PCI_DEVICE_ID_) }, */ 8362306a36Sopenharmony_ci { 0, } 8462306a36Sopenharmony_ci}; 8562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, skeleton_pci_tbl); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci/* 8862306a36Sopenharmony_ci * HDTV: this structure has the capabilities of the HDTV receiver. 8962306a36Sopenharmony_ci * It is used to constrain the huge list of possible formats based 9062306a36Sopenharmony_ci * upon the hardware capabilities. 9162306a36Sopenharmony_ci */ 9262306a36Sopenharmony_cistatic const struct v4l2_dv_timings_cap skel_timings_cap = { 9362306a36Sopenharmony_ci .type = V4L2_DV_BT_656_1120, 9462306a36Sopenharmony_ci /* keep this initialization for compatibility with GCC < 4.4.6 */ 9562306a36Sopenharmony_ci .reserved = { 0 }, 9662306a36Sopenharmony_ci V4L2_INIT_BT_TIMINGS( 9762306a36Sopenharmony_ci 720, 1920, /* min/max width */ 9862306a36Sopenharmony_ci 480, 1080, /* min/max height */ 9962306a36Sopenharmony_ci 27000000, 74250000, /* min/max pixelclock*/ 10062306a36Sopenharmony_ci V4L2_DV_BT_STD_CEA861, /* Supported standards */ 10162306a36Sopenharmony_ci /* capabilities */ 10262306a36Sopenharmony_ci V4L2_DV_BT_CAP_INTERLACED | V4L2_DV_BT_CAP_PROGRESSIVE 10362306a36Sopenharmony_ci ) 10462306a36Sopenharmony_ci}; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci/* 10762306a36Sopenharmony_ci * Supported SDTV standards. This does the same job as skel_timings_cap, but 10862306a36Sopenharmony_ci * for standard TV formats. 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_ci#define SKEL_TVNORMS V4L2_STD_ALL 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci/* 11362306a36Sopenharmony_ci * Interrupt handler: typically interrupts happen after a new frame has been 11462306a36Sopenharmony_ci * captured. It is the job of the handler to remove the new frame from the 11562306a36Sopenharmony_ci * internal list and give it back to the vb2 framework, updating the sequence 11662306a36Sopenharmony_ci * counter, field and timestamp at the same time. 11762306a36Sopenharmony_ci */ 11862306a36Sopenharmony_cistatic irqreturn_t skeleton_irq(int irq, void *dev_id) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci#ifdef TODO 12162306a36Sopenharmony_ci struct skeleton *skel = dev_id; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci /* handle interrupt */ 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci /* Once a new frame has been captured, mark it as done like this: */ 12662306a36Sopenharmony_ci if (captured_new_frame) { 12762306a36Sopenharmony_ci ... 12862306a36Sopenharmony_ci spin_lock(&skel->qlock); 12962306a36Sopenharmony_ci list_del(&new_buf->list); 13062306a36Sopenharmony_ci spin_unlock(&skel->qlock); 13162306a36Sopenharmony_ci new_buf->vb.vb2_buf.timestamp = ktime_get_ns(); 13262306a36Sopenharmony_ci new_buf->vb.sequence = skel->sequence++; 13362306a36Sopenharmony_ci new_buf->vb.field = skel->field; 13462306a36Sopenharmony_ci if (skel->format.field == V4L2_FIELD_ALTERNATE) { 13562306a36Sopenharmony_ci if (skel->field == V4L2_FIELD_BOTTOM) 13662306a36Sopenharmony_ci skel->field = V4L2_FIELD_TOP; 13762306a36Sopenharmony_ci else if (skel->field == V4L2_FIELD_TOP) 13862306a36Sopenharmony_ci skel->field = V4L2_FIELD_BOTTOM; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci vb2_buffer_done(&new_buf->vb.vb2_buf, VB2_BUF_STATE_DONE); 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci#endif 14362306a36Sopenharmony_ci return IRQ_HANDLED; 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci/* 14762306a36Sopenharmony_ci * Setup the constraints of the queue: besides setting the number of planes 14862306a36Sopenharmony_ci * per buffer and the size and allocation context of each plane, it also 14962306a36Sopenharmony_ci * checks if sufficient buffers have been allocated. Usually 3 is a good 15062306a36Sopenharmony_ci * minimum number: many DMA engines need a minimum of 2 buffers in the 15162306a36Sopenharmony_ci * queue and you need to have another available for userspace processing. 15262306a36Sopenharmony_ci */ 15362306a36Sopenharmony_cistatic int queue_setup(struct vb2_queue *vq, 15462306a36Sopenharmony_ci unsigned int *nbuffers, unsigned int *nplanes, 15562306a36Sopenharmony_ci unsigned int sizes[], struct device *alloc_devs[]) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci struct skeleton *skel = vb2_get_drv_priv(vq); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci skel->field = skel->format.field; 16062306a36Sopenharmony_ci if (skel->field == V4L2_FIELD_ALTERNATE) { 16162306a36Sopenharmony_ci /* 16262306a36Sopenharmony_ci * You cannot use read() with FIELD_ALTERNATE since the field 16362306a36Sopenharmony_ci * information (TOP/BOTTOM) cannot be passed back to the user. 16462306a36Sopenharmony_ci */ 16562306a36Sopenharmony_ci if (vb2_fileio_is_active(vq)) 16662306a36Sopenharmony_ci return -EINVAL; 16762306a36Sopenharmony_ci skel->field = V4L2_FIELD_TOP; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci if (vq->num_buffers + *nbuffers < 3) 17162306a36Sopenharmony_ci *nbuffers = 3 - vq->num_buffers; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (*nplanes) 17462306a36Sopenharmony_ci return sizes[0] < skel->format.sizeimage ? -EINVAL : 0; 17562306a36Sopenharmony_ci *nplanes = 1; 17662306a36Sopenharmony_ci sizes[0] = skel->format.sizeimage; 17762306a36Sopenharmony_ci return 0; 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci/* 18162306a36Sopenharmony_ci * Prepare the buffer for queueing to the DMA engine: check and set the 18262306a36Sopenharmony_ci * payload size. 18362306a36Sopenharmony_ci */ 18462306a36Sopenharmony_cistatic int buffer_prepare(struct vb2_buffer *vb) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci struct skeleton *skel = vb2_get_drv_priv(vb->vb2_queue); 18762306a36Sopenharmony_ci unsigned long size = skel->format.sizeimage; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci if (vb2_plane_size(vb, 0) < size) { 19062306a36Sopenharmony_ci dev_err(&skel->pdev->dev, "buffer too small (%lu < %lu)\n", 19162306a36Sopenharmony_ci vb2_plane_size(vb, 0), size); 19262306a36Sopenharmony_ci return -EINVAL; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci vb2_set_plane_payload(vb, 0, size); 19662306a36Sopenharmony_ci return 0; 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci/* 20062306a36Sopenharmony_ci * Queue this buffer to the DMA engine. 20162306a36Sopenharmony_ci */ 20262306a36Sopenharmony_cistatic void buffer_queue(struct vb2_buffer *vb) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 20562306a36Sopenharmony_ci struct skeleton *skel = vb2_get_drv_priv(vb->vb2_queue); 20662306a36Sopenharmony_ci struct skel_buffer *buf = to_skel_buffer(vbuf); 20762306a36Sopenharmony_ci unsigned long flags; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci spin_lock_irqsave(&skel->qlock, flags); 21062306a36Sopenharmony_ci list_add_tail(&buf->list, &skel->buf_list); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci /* TODO: Update any DMA pointers if necessary */ 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci spin_unlock_irqrestore(&skel->qlock, flags); 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic void return_all_buffers(struct skeleton *skel, 21862306a36Sopenharmony_ci enum vb2_buffer_state state) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci struct skel_buffer *buf, *node; 22162306a36Sopenharmony_ci unsigned long flags; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci spin_lock_irqsave(&skel->qlock, flags); 22462306a36Sopenharmony_ci list_for_each_entry_safe(buf, node, &skel->buf_list, list) { 22562306a36Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, state); 22662306a36Sopenharmony_ci list_del(&buf->list); 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci spin_unlock_irqrestore(&skel->qlock, flags); 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci/* 23262306a36Sopenharmony_ci * Start streaming. First check if the minimum number of buffers have been 23362306a36Sopenharmony_ci * queued. If not, then return -ENOBUFS and the vb2 framework will call 23462306a36Sopenharmony_ci * this function again the next time a buffer has been queued until enough 23562306a36Sopenharmony_ci * buffers are available to actually start the DMA engine. 23662306a36Sopenharmony_ci */ 23762306a36Sopenharmony_cistatic int start_streaming(struct vb2_queue *vq, unsigned int count) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci struct skeleton *skel = vb2_get_drv_priv(vq); 24062306a36Sopenharmony_ci int ret = 0; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci skel->sequence = 0; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci /* TODO: start DMA */ 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if (ret) { 24762306a36Sopenharmony_ci /* 24862306a36Sopenharmony_ci * In case of an error, return all active buffers to the 24962306a36Sopenharmony_ci * QUEUED state 25062306a36Sopenharmony_ci */ 25162306a36Sopenharmony_ci return_all_buffers(skel, VB2_BUF_STATE_QUEUED); 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci return ret; 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci/* 25762306a36Sopenharmony_ci * Stop the DMA engine. Any remaining buffers in the DMA queue are dequeued 25862306a36Sopenharmony_ci * and passed on to the vb2 framework marked as STATE_ERROR. 25962306a36Sopenharmony_ci */ 26062306a36Sopenharmony_cistatic void stop_streaming(struct vb2_queue *vq) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci struct skeleton *skel = vb2_get_drv_priv(vq); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci /* TODO: stop DMA */ 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci /* Release all active buffers */ 26762306a36Sopenharmony_ci return_all_buffers(skel, VB2_BUF_STATE_ERROR); 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci/* 27162306a36Sopenharmony_ci * The vb2 queue ops. Note that since q->lock is set we can use the standard 27262306a36Sopenharmony_ci * vb2_ops_wait_prepare/finish helper functions. If q->lock would be NULL, 27362306a36Sopenharmony_ci * then this driver would have to provide these ops. 27462306a36Sopenharmony_ci */ 27562306a36Sopenharmony_cistatic const struct vb2_ops skel_qops = { 27662306a36Sopenharmony_ci .queue_setup = queue_setup, 27762306a36Sopenharmony_ci .buf_prepare = buffer_prepare, 27862306a36Sopenharmony_ci .buf_queue = buffer_queue, 27962306a36Sopenharmony_ci .start_streaming = start_streaming, 28062306a36Sopenharmony_ci .stop_streaming = stop_streaming, 28162306a36Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 28262306a36Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 28362306a36Sopenharmony_ci}; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci/* 28662306a36Sopenharmony_ci * Required ioctl querycap. Note that the version field is prefilled with 28762306a36Sopenharmony_ci * the version of the kernel. 28862306a36Sopenharmony_ci */ 28962306a36Sopenharmony_cistatic int skeleton_querycap(struct file *file, void *priv, 29062306a36Sopenharmony_ci struct v4l2_capability *cap) 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci struct skeleton *skel = video_drvdata(file); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); 29562306a36Sopenharmony_ci strlcpy(cap->card, "V4L2 PCI Skeleton", sizeof(cap->card)); 29662306a36Sopenharmony_ci snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s", 29762306a36Sopenharmony_ci pci_name(skel->pdev)); 29862306a36Sopenharmony_ci return 0; 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci/* 30262306a36Sopenharmony_ci * Helper function to check and correct struct v4l2_pix_format. It's used 30362306a36Sopenharmony_ci * not only in VIDIOC_TRY/S_FMT, but also elsewhere if changes to the SDTV 30462306a36Sopenharmony_ci * standard, HDTV timings or the video input would require updating the 30562306a36Sopenharmony_ci * current format. 30662306a36Sopenharmony_ci */ 30762306a36Sopenharmony_cistatic void skeleton_fill_pix_format(struct skeleton *skel, 30862306a36Sopenharmony_ci struct v4l2_pix_format *pix) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci pix->pixelformat = V4L2_PIX_FMT_YUYV; 31162306a36Sopenharmony_ci if (skel->input == 0) { 31262306a36Sopenharmony_ci /* S-Video input */ 31362306a36Sopenharmony_ci pix->width = 720; 31462306a36Sopenharmony_ci pix->height = (skel->std & V4L2_STD_525_60) ? 480 : 576; 31562306a36Sopenharmony_ci pix->field = V4L2_FIELD_INTERLACED; 31662306a36Sopenharmony_ci pix->colorspace = V4L2_COLORSPACE_SMPTE170M; 31762306a36Sopenharmony_ci } else { 31862306a36Sopenharmony_ci /* HDMI input */ 31962306a36Sopenharmony_ci pix->width = skel->timings.bt.width; 32062306a36Sopenharmony_ci pix->height = skel->timings.bt.height; 32162306a36Sopenharmony_ci if (skel->timings.bt.interlaced) { 32262306a36Sopenharmony_ci pix->field = V4L2_FIELD_ALTERNATE; 32362306a36Sopenharmony_ci pix->height /= 2; 32462306a36Sopenharmony_ci } else { 32562306a36Sopenharmony_ci pix->field = V4L2_FIELD_NONE; 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci pix->colorspace = V4L2_COLORSPACE_REC709; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci /* 33162306a36Sopenharmony_ci * The YUYV format is four bytes for every two pixels, so bytesperline 33262306a36Sopenharmony_ci * is width * 2. 33362306a36Sopenharmony_ci */ 33462306a36Sopenharmony_ci pix->bytesperline = pix->width * 2; 33562306a36Sopenharmony_ci pix->sizeimage = pix->bytesperline * pix->height; 33662306a36Sopenharmony_ci pix->priv = 0; 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_cistatic int skeleton_try_fmt_vid_cap(struct file *file, void *priv, 34062306a36Sopenharmony_ci struct v4l2_format *f) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci struct skeleton *skel = video_drvdata(file); 34362306a36Sopenharmony_ci struct v4l2_pix_format *pix = &f->fmt.pix; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci /* 34662306a36Sopenharmony_ci * Due to historical reasons providing try_fmt with an unsupported 34762306a36Sopenharmony_ci * pixelformat will return -EINVAL for video receivers. Webcam drivers, 34862306a36Sopenharmony_ci * however, will silently correct the pixelformat. Some video capture 34962306a36Sopenharmony_ci * applications rely on this behavior... 35062306a36Sopenharmony_ci */ 35162306a36Sopenharmony_ci if (pix->pixelformat != V4L2_PIX_FMT_YUYV) 35262306a36Sopenharmony_ci return -EINVAL; 35362306a36Sopenharmony_ci skeleton_fill_pix_format(skel, pix); 35462306a36Sopenharmony_ci return 0; 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cistatic int skeleton_s_fmt_vid_cap(struct file *file, void *priv, 35862306a36Sopenharmony_ci struct v4l2_format *f) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci struct skeleton *skel = video_drvdata(file); 36162306a36Sopenharmony_ci int ret; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci ret = skeleton_try_fmt_vid_cap(file, priv, f); 36462306a36Sopenharmony_ci if (ret) 36562306a36Sopenharmony_ci return ret; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci /* 36862306a36Sopenharmony_ci * It is not allowed to change the format while buffers for use with 36962306a36Sopenharmony_ci * streaming have already been allocated. 37062306a36Sopenharmony_ci */ 37162306a36Sopenharmony_ci if (vb2_is_busy(&skel->queue)) 37262306a36Sopenharmony_ci return -EBUSY; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci /* TODO: change format */ 37562306a36Sopenharmony_ci skel->format = f->fmt.pix; 37662306a36Sopenharmony_ci return 0; 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cistatic int skeleton_g_fmt_vid_cap(struct file *file, void *priv, 38062306a36Sopenharmony_ci struct v4l2_format *f) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci struct skeleton *skel = video_drvdata(file); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci f->fmt.pix = skel->format; 38562306a36Sopenharmony_ci return 0; 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_cistatic int skeleton_enum_fmt_vid_cap(struct file *file, void *priv, 38962306a36Sopenharmony_ci struct v4l2_fmtdesc *f) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci if (f->index != 0) 39262306a36Sopenharmony_ci return -EINVAL; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci f->pixelformat = V4L2_PIX_FMT_YUYV; 39562306a36Sopenharmony_ci return 0; 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_cistatic int skeleton_s_std(struct file *file, void *priv, v4l2_std_id std) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci struct skeleton *skel = video_drvdata(file); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci /* S_STD is not supported on the HDMI input */ 40362306a36Sopenharmony_ci if (skel->input) 40462306a36Sopenharmony_ci return -ENODATA; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci /* 40762306a36Sopenharmony_ci * No change, so just return. Some applications call S_STD again after 40862306a36Sopenharmony_ci * the buffers for streaming have been set up, so we have to allow for 40962306a36Sopenharmony_ci * this behavior. 41062306a36Sopenharmony_ci */ 41162306a36Sopenharmony_ci if (std == skel->std) 41262306a36Sopenharmony_ci return 0; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci /* 41562306a36Sopenharmony_ci * Changing the standard implies a format change, which is not allowed 41662306a36Sopenharmony_ci * while buffers for use with streaming have already been allocated. 41762306a36Sopenharmony_ci */ 41862306a36Sopenharmony_ci if (vb2_is_busy(&skel->queue)) 41962306a36Sopenharmony_ci return -EBUSY; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci /* TODO: handle changing std */ 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci skel->std = std; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci /* Update the internal format */ 42662306a36Sopenharmony_ci skeleton_fill_pix_format(skel, &skel->format); 42762306a36Sopenharmony_ci return 0; 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_cistatic int skeleton_g_std(struct file *file, void *priv, v4l2_std_id *std) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci struct skeleton *skel = video_drvdata(file); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci /* G_STD is not supported on the HDMI input */ 43562306a36Sopenharmony_ci if (skel->input) 43662306a36Sopenharmony_ci return -ENODATA; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci *std = skel->std; 43962306a36Sopenharmony_ci return 0; 44062306a36Sopenharmony_ci} 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci/* 44362306a36Sopenharmony_ci * Query the current standard as seen by the hardware. This function shall 44462306a36Sopenharmony_ci * never actually change the standard, it just detects and reports. 44562306a36Sopenharmony_ci * The framework will initially set *std to tvnorms (i.e. the set of 44662306a36Sopenharmony_ci * supported standards by this input), and this function should just AND 44762306a36Sopenharmony_ci * this value. If there is no signal, then *std should be set to 0. 44862306a36Sopenharmony_ci */ 44962306a36Sopenharmony_cistatic int skeleton_querystd(struct file *file, void *priv, v4l2_std_id *std) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci struct skeleton *skel = video_drvdata(file); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci /* QUERY_STD is not supported on the HDMI input */ 45462306a36Sopenharmony_ci if (skel->input) 45562306a36Sopenharmony_ci return -ENODATA; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci#ifdef TODO 45862306a36Sopenharmony_ci /* 45962306a36Sopenharmony_ci * Query currently seen standard. Initial value of *std is 46062306a36Sopenharmony_ci * V4L2_STD_ALL. This function should look something like this: 46162306a36Sopenharmony_ci */ 46262306a36Sopenharmony_ci get_signal_info(); 46362306a36Sopenharmony_ci if (no_signal) { 46462306a36Sopenharmony_ci *std = 0; 46562306a36Sopenharmony_ci return 0; 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci /* Use signal information to reduce the number of possible standards */ 46862306a36Sopenharmony_ci if (signal_has_525_lines) 46962306a36Sopenharmony_ci *std &= V4L2_STD_525_60; 47062306a36Sopenharmony_ci else 47162306a36Sopenharmony_ci *std &= V4L2_STD_625_50; 47262306a36Sopenharmony_ci#endif 47362306a36Sopenharmony_ci return 0; 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_cistatic int skeleton_s_dv_timings(struct file *file, void *_fh, 47762306a36Sopenharmony_ci struct v4l2_dv_timings *timings) 47862306a36Sopenharmony_ci{ 47962306a36Sopenharmony_ci struct skeleton *skel = video_drvdata(file); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci /* S_DV_TIMINGS is not supported on the S-Video input */ 48262306a36Sopenharmony_ci if (skel->input == 0) 48362306a36Sopenharmony_ci return -ENODATA; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci /* Quick sanity check */ 48662306a36Sopenharmony_ci if (!v4l2_valid_dv_timings(timings, &skel_timings_cap, NULL, NULL)) 48762306a36Sopenharmony_ci return -EINVAL; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci /* Check if the timings are part of the CEA-861 timings. */ 49062306a36Sopenharmony_ci if (!v4l2_find_dv_timings_cap(timings, &skel_timings_cap, 49162306a36Sopenharmony_ci 0, NULL, NULL)) 49262306a36Sopenharmony_ci return -EINVAL; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci /* Return 0 if the new timings are the same as the current timings. */ 49562306a36Sopenharmony_ci if (v4l2_match_dv_timings(timings, &skel->timings, 0, false)) 49662306a36Sopenharmony_ci return 0; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci /* 49962306a36Sopenharmony_ci * Changing the timings implies a format change, which is not allowed 50062306a36Sopenharmony_ci * while buffers for use with streaming have already been allocated. 50162306a36Sopenharmony_ci */ 50262306a36Sopenharmony_ci if (vb2_is_busy(&skel->queue)) 50362306a36Sopenharmony_ci return -EBUSY; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci /* TODO: Configure new timings */ 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci /* Save timings */ 50862306a36Sopenharmony_ci skel->timings = *timings; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci /* Update the internal format */ 51162306a36Sopenharmony_ci skeleton_fill_pix_format(skel, &skel->format); 51262306a36Sopenharmony_ci return 0; 51362306a36Sopenharmony_ci} 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_cistatic int skeleton_g_dv_timings(struct file *file, void *_fh, 51662306a36Sopenharmony_ci struct v4l2_dv_timings *timings) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci struct skeleton *skel = video_drvdata(file); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci /* G_DV_TIMINGS is not supported on the S-Video input */ 52162306a36Sopenharmony_ci if (skel->input == 0) 52262306a36Sopenharmony_ci return -ENODATA; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci *timings = skel->timings; 52562306a36Sopenharmony_ci return 0; 52662306a36Sopenharmony_ci} 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_cistatic int skeleton_enum_dv_timings(struct file *file, void *_fh, 52962306a36Sopenharmony_ci struct v4l2_enum_dv_timings *timings) 53062306a36Sopenharmony_ci{ 53162306a36Sopenharmony_ci struct skeleton *skel = video_drvdata(file); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci /* ENUM_DV_TIMINGS is not supported on the S-Video input */ 53462306a36Sopenharmony_ci if (skel->input == 0) 53562306a36Sopenharmony_ci return -ENODATA; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci return v4l2_enum_dv_timings_cap(timings, &skel_timings_cap, 53862306a36Sopenharmony_ci NULL, NULL); 53962306a36Sopenharmony_ci} 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci/* 54262306a36Sopenharmony_ci * Query the current timings as seen by the hardware. This function shall 54362306a36Sopenharmony_ci * never actually change the timings, it just detects and reports. 54462306a36Sopenharmony_ci * If no signal is detected, then return -ENOLINK. If the hardware cannot 54562306a36Sopenharmony_ci * lock to the signal, then return -ENOLCK. If the signal is out of range 54662306a36Sopenharmony_ci * of the capabilities of the system (e.g., it is possible that the receiver 54762306a36Sopenharmony_ci * can lock but that the DMA engine it is connected to cannot handle 54862306a36Sopenharmony_ci * pixelclocks above a certain frequency), then -ERANGE is returned. 54962306a36Sopenharmony_ci */ 55062306a36Sopenharmony_cistatic int skeleton_query_dv_timings(struct file *file, void *_fh, 55162306a36Sopenharmony_ci struct v4l2_dv_timings *timings) 55262306a36Sopenharmony_ci{ 55362306a36Sopenharmony_ci struct skeleton *skel = video_drvdata(file); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci /* QUERY_DV_TIMINGS is not supported on the S-Video input */ 55662306a36Sopenharmony_ci if (skel->input == 0) 55762306a36Sopenharmony_ci return -ENODATA; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci#ifdef TODO 56062306a36Sopenharmony_ci /* 56162306a36Sopenharmony_ci * Query currently seen timings. This function should look 56262306a36Sopenharmony_ci * something like this: 56362306a36Sopenharmony_ci */ 56462306a36Sopenharmony_ci detect_timings(); 56562306a36Sopenharmony_ci if (no_signal) 56662306a36Sopenharmony_ci return -ENOLINK; 56762306a36Sopenharmony_ci if (cannot_lock_to_signal) 56862306a36Sopenharmony_ci return -ENOLCK; 56962306a36Sopenharmony_ci if (signal_out_of_range_of_capabilities) 57062306a36Sopenharmony_ci return -ERANGE; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci /* Useful for debugging */ 57362306a36Sopenharmony_ci v4l2_print_dv_timings(skel->v4l2_dev.name, "query_dv_timings:", 57462306a36Sopenharmony_ci timings, true); 57562306a36Sopenharmony_ci#endif 57662306a36Sopenharmony_ci return 0; 57762306a36Sopenharmony_ci} 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_cistatic int skeleton_dv_timings_cap(struct file *file, void *fh, 58062306a36Sopenharmony_ci struct v4l2_dv_timings_cap *cap) 58162306a36Sopenharmony_ci{ 58262306a36Sopenharmony_ci struct skeleton *skel = video_drvdata(file); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci /* DV_TIMINGS_CAP is not supported on the S-Video input */ 58562306a36Sopenharmony_ci if (skel->input == 0) 58662306a36Sopenharmony_ci return -ENODATA; 58762306a36Sopenharmony_ci *cap = skel_timings_cap; 58862306a36Sopenharmony_ci return 0; 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_cistatic int skeleton_enum_input(struct file *file, void *priv, 59262306a36Sopenharmony_ci struct v4l2_input *i) 59362306a36Sopenharmony_ci{ 59462306a36Sopenharmony_ci if (i->index > 1) 59562306a36Sopenharmony_ci return -EINVAL; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci i->type = V4L2_INPUT_TYPE_CAMERA; 59862306a36Sopenharmony_ci if (i->index == 0) { 59962306a36Sopenharmony_ci i->std = SKEL_TVNORMS; 60062306a36Sopenharmony_ci strlcpy(i->name, "S-Video", sizeof(i->name)); 60162306a36Sopenharmony_ci i->capabilities = V4L2_IN_CAP_STD; 60262306a36Sopenharmony_ci } else { 60362306a36Sopenharmony_ci i->std = 0; 60462306a36Sopenharmony_ci strlcpy(i->name, "HDMI", sizeof(i->name)); 60562306a36Sopenharmony_ci i->capabilities = V4L2_IN_CAP_DV_TIMINGS; 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci return 0; 60862306a36Sopenharmony_ci} 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_cistatic int skeleton_s_input(struct file *file, void *priv, unsigned int i) 61162306a36Sopenharmony_ci{ 61262306a36Sopenharmony_ci struct skeleton *skel = video_drvdata(file); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci if (i > 1) 61562306a36Sopenharmony_ci return -EINVAL; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci /* 61862306a36Sopenharmony_ci * Changing the input implies a format change, which is not allowed 61962306a36Sopenharmony_ci * while buffers for use with streaming have already been allocated. 62062306a36Sopenharmony_ci */ 62162306a36Sopenharmony_ci if (vb2_is_busy(&skel->queue)) 62262306a36Sopenharmony_ci return -EBUSY; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci skel->input = i; 62562306a36Sopenharmony_ci /* 62662306a36Sopenharmony_ci * Update tvnorms. The tvnorms value is used by the core to implement 62762306a36Sopenharmony_ci * VIDIOC_ENUMSTD so it has to be correct. If tvnorms == 0, then 62862306a36Sopenharmony_ci * ENUMSTD will return -ENODATA. 62962306a36Sopenharmony_ci */ 63062306a36Sopenharmony_ci skel->vdev.tvnorms = i ? 0 : SKEL_TVNORMS; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci /* Update the internal format */ 63362306a36Sopenharmony_ci skeleton_fill_pix_format(skel, &skel->format); 63462306a36Sopenharmony_ci return 0; 63562306a36Sopenharmony_ci} 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_cistatic int skeleton_g_input(struct file *file, void *priv, unsigned int *i) 63862306a36Sopenharmony_ci{ 63962306a36Sopenharmony_ci struct skeleton *skel = video_drvdata(file); 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci *i = skel->input; 64262306a36Sopenharmony_ci return 0; 64362306a36Sopenharmony_ci} 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci/* The control handler. */ 64662306a36Sopenharmony_cistatic int skeleton_s_ctrl(struct v4l2_ctrl *ctrl) 64762306a36Sopenharmony_ci{ 64862306a36Sopenharmony_ci /*struct skeleton *skel = 64962306a36Sopenharmony_ci container_of(ctrl->handler, struct skeleton, ctrl_handler);*/ 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci switch (ctrl->id) { 65262306a36Sopenharmony_ci case V4L2_CID_BRIGHTNESS: 65362306a36Sopenharmony_ci /* TODO: set brightness to ctrl->val */ 65462306a36Sopenharmony_ci break; 65562306a36Sopenharmony_ci case V4L2_CID_CONTRAST: 65662306a36Sopenharmony_ci /* TODO: set contrast to ctrl->val */ 65762306a36Sopenharmony_ci break; 65862306a36Sopenharmony_ci case V4L2_CID_SATURATION: 65962306a36Sopenharmony_ci /* TODO: set saturation to ctrl->val */ 66062306a36Sopenharmony_ci break; 66162306a36Sopenharmony_ci case V4L2_CID_HUE: 66262306a36Sopenharmony_ci /* TODO: set hue to ctrl->val */ 66362306a36Sopenharmony_ci break; 66462306a36Sopenharmony_ci default: 66562306a36Sopenharmony_ci return -EINVAL; 66662306a36Sopenharmony_ci } 66762306a36Sopenharmony_ci return 0; 66862306a36Sopenharmony_ci} 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci/* ------------------------------------------------------------------ 67162306a36Sopenharmony_ci File operations for the device 67262306a36Sopenharmony_ci ------------------------------------------------------------------*/ 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops skel_ctrl_ops = { 67562306a36Sopenharmony_ci .s_ctrl = skeleton_s_ctrl, 67662306a36Sopenharmony_ci}; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci/* 67962306a36Sopenharmony_ci * The set of all supported ioctls. Note that all the streaming ioctls 68062306a36Sopenharmony_ci * use the vb2 helper functions that take care of all the locking and 68162306a36Sopenharmony_ci * that also do ownership tracking (i.e. only the filehandle that requested 68262306a36Sopenharmony_ci * the buffers can call the streaming ioctls, all other filehandles will 68362306a36Sopenharmony_ci * receive -EBUSY if they attempt to call the same streaming ioctls). 68462306a36Sopenharmony_ci * 68562306a36Sopenharmony_ci * The last three ioctls also use standard helper functions: these implement 68662306a36Sopenharmony_ci * standard behavior for drivers with controls. 68762306a36Sopenharmony_ci */ 68862306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops skel_ioctl_ops = { 68962306a36Sopenharmony_ci .vidioc_querycap = skeleton_querycap, 69062306a36Sopenharmony_ci .vidioc_try_fmt_vid_cap = skeleton_try_fmt_vid_cap, 69162306a36Sopenharmony_ci .vidioc_s_fmt_vid_cap = skeleton_s_fmt_vid_cap, 69262306a36Sopenharmony_ci .vidioc_g_fmt_vid_cap = skeleton_g_fmt_vid_cap, 69362306a36Sopenharmony_ci .vidioc_enum_fmt_vid_cap = skeleton_enum_fmt_vid_cap, 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci .vidioc_g_std = skeleton_g_std, 69662306a36Sopenharmony_ci .vidioc_s_std = skeleton_s_std, 69762306a36Sopenharmony_ci .vidioc_querystd = skeleton_querystd, 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci .vidioc_s_dv_timings = skeleton_s_dv_timings, 70062306a36Sopenharmony_ci .vidioc_g_dv_timings = skeleton_g_dv_timings, 70162306a36Sopenharmony_ci .vidioc_enum_dv_timings = skeleton_enum_dv_timings, 70262306a36Sopenharmony_ci .vidioc_query_dv_timings = skeleton_query_dv_timings, 70362306a36Sopenharmony_ci .vidioc_dv_timings_cap = skeleton_dv_timings_cap, 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci .vidioc_enum_input = skeleton_enum_input, 70662306a36Sopenharmony_ci .vidioc_g_input = skeleton_g_input, 70762306a36Sopenharmony_ci .vidioc_s_input = skeleton_s_input, 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci .vidioc_reqbufs = vb2_ioctl_reqbufs, 71062306a36Sopenharmony_ci .vidioc_create_bufs = vb2_ioctl_create_bufs, 71162306a36Sopenharmony_ci .vidioc_querybuf = vb2_ioctl_querybuf, 71262306a36Sopenharmony_ci .vidioc_qbuf = vb2_ioctl_qbuf, 71362306a36Sopenharmony_ci .vidioc_dqbuf = vb2_ioctl_dqbuf, 71462306a36Sopenharmony_ci .vidioc_expbuf = vb2_ioctl_expbuf, 71562306a36Sopenharmony_ci .vidioc_streamon = vb2_ioctl_streamon, 71662306a36Sopenharmony_ci .vidioc_streamoff = vb2_ioctl_streamoff, 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci .vidioc_log_status = v4l2_ctrl_log_status, 71962306a36Sopenharmony_ci .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 72062306a36Sopenharmony_ci .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 72162306a36Sopenharmony_ci}; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci/* 72462306a36Sopenharmony_ci * The set of file operations. Note that all these ops are standard core 72562306a36Sopenharmony_ci * helper functions. 72662306a36Sopenharmony_ci */ 72762306a36Sopenharmony_cistatic const struct v4l2_file_operations skel_fops = { 72862306a36Sopenharmony_ci .owner = THIS_MODULE, 72962306a36Sopenharmony_ci .open = v4l2_fh_open, 73062306a36Sopenharmony_ci .release = vb2_fop_release, 73162306a36Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 73262306a36Sopenharmony_ci .read = vb2_fop_read, 73362306a36Sopenharmony_ci .mmap = vb2_fop_mmap, 73462306a36Sopenharmony_ci .poll = vb2_fop_poll, 73562306a36Sopenharmony_ci}; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci/* 73862306a36Sopenharmony_ci * The initial setup of this device instance. Note that the initial state of 73962306a36Sopenharmony_ci * the driver should be complete. So the initial format, standard, timings 74062306a36Sopenharmony_ci * and video input should all be initialized to some reasonable value. 74162306a36Sopenharmony_ci */ 74262306a36Sopenharmony_cistatic int skeleton_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 74362306a36Sopenharmony_ci{ 74462306a36Sopenharmony_ci /* The initial timings are chosen to be 720p60. */ 74562306a36Sopenharmony_ci static const struct v4l2_dv_timings timings_def = 74662306a36Sopenharmony_ci V4L2_DV_BT_CEA_1280X720P60; 74762306a36Sopenharmony_ci struct skeleton *skel; 74862306a36Sopenharmony_ci struct video_device *vdev; 74962306a36Sopenharmony_ci struct v4l2_ctrl_handler *hdl; 75062306a36Sopenharmony_ci struct vb2_queue *q; 75162306a36Sopenharmony_ci int ret; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci /* Enable PCI */ 75462306a36Sopenharmony_ci ret = pci_enable_device(pdev); 75562306a36Sopenharmony_ci if (ret) 75662306a36Sopenharmony_ci return ret; 75762306a36Sopenharmony_ci ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); 75862306a36Sopenharmony_ci if (ret) { 75962306a36Sopenharmony_ci dev_err(&pdev->dev, "no suitable DMA available.\n"); 76062306a36Sopenharmony_ci goto disable_pci; 76162306a36Sopenharmony_ci } 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci /* Allocate a new instance */ 76462306a36Sopenharmony_ci skel = devm_kzalloc(&pdev->dev, sizeof(struct skeleton), GFP_KERNEL); 76562306a36Sopenharmony_ci if (!skel) { 76662306a36Sopenharmony_ci ret = -ENOMEM; 76762306a36Sopenharmony_ci goto disable_pci; 76862306a36Sopenharmony_ci } 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci /* Allocate the interrupt */ 77162306a36Sopenharmony_ci ret = devm_request_irq(&pdev->dev, pdev->irq, 77262306a36Sopenharmony_ci skeleton_irq, 0, KBUILD_MODNAME, skel); 77362306a36Sopenharmony_ci if (ret) { 77462306a36Sopenharmony_ci dev_err(&pdev->dev, "request_irq failed\n"); 77562306a36Sopenharmony_ci goto disable_pci; 77662306a36Sopenharmony_ci } 77762306a36Sopenharmony_ci skel->pdev = pdev; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci /* Fill in the initial format-related settings */ 78062306a36Sopenharmony_ci skel->timings = timings_def; 78162306a36Sopenharmony_ci skel->std = V4L2_STD_625_50; 78262306a36Sopenharmony_ci skeleton_fill_pix_format(skel, &skel->format); 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci /* Initialize the top-level structure */ 78562306a36Sopenharmony_ci ret = v4l2_device_register(&pdev->dev, &skel->v4l2_dev); 78662306a36Sopenharmony_ci if (ret) 78762306a36Sopenharmony_ci goto disable_pci; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci mutex_init(&skel->lock); 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci /* Add the controls */ 79262306a36Sopenharmony_ci hdl = &skel->ctrl_handler; 79362306a36Sopenharmony_ci v4l2_ctrl_handler_init(hdl, 4); 79462306a36Sopenharmony_ci v4l2_ctrl_new_std(hdl, &skel_ctrl_ops, 79562306a36Sopenharmony_ci V4L2_CID_BRIGHTNESS, 0, 255, 1, 127); 79662306a36Sopenharmony_ci v4l2_ctrl_new_std(hdl, &skel_ctrl_ops, 79762306a36Sopenharmony_ci V4L2_CID_CONTRAST, 0, 255, 1, 16); 79862306a36Sopenharmony_ci v4l2_ctrl_new_std(hdl, &skel_ctrl_ops, 79962306a36Sopenharmony_ci V4L2_CID_SATURATION, 0, 255, 1, 127); 80062306a36Sopenharmony_ci v4l2_ctrl_new_std(hdl, &skel_ctrl_ops, 80162306a36Sopenharmony_ci V4L2_CID_HUE, -128, 127, 1, 0); 80262306a36Sopenharmony_ci if (hdl->error) { 80362306a36Sopenharmony_ci ret = hdl->error; 80462306a36Sopenharmony_ci goto free_hdl; 80562306a36Sopenharmony_ci } 80662306a36Sopenharmony_ci skel->v4l2_dev.ctrl_handler = hdl; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci /* Initialize the vb2 queue */ 80962306a36Sopenharmony_ci q = &skel->queue; 81062306a36Sopenharmony_ci q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 81162306a36Sopenharmony_ci q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; 81262306a36Sopenharmony_ci q->dev = &pdev->dev; 81362306a36Sopenharmony_ci q->drv_priv = skel; 81462306a36Sopenharmony_ci q->buf_struct_size = sizeof(struct skel_buffer); 81562306a36Sopenharmony_ci q->ops = &skel_qops; 81662306a36Sopenharmony_ci q->mem_ops = &vb2_dma_contig_memops; 81762306a36Sopenharmony_ci q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 81862306a36Sopenharmony_ci /* 81962306a36Sopenharmony_ci * Assume that this DMA engine needs to have at least two buffers 82062306a36Sopenharmony_ci * available before it can be started. The start_streaming() op 82162306a36Sopenharmony_ci * won't be called until at least this many buffers are queued up. 82262306a36Sopenharmony_ci */ 82362306a36Sopenharmony_ci q->min_buffers_needed = 2; 82462306a36Sopenharmony_ci /* 82562306a36Sopenharmony_ci * The serialization lock for the streaming ioctls. This is the same 82662306a36Sopenharmony_ci * as the main serialization lock, but if some of the non-streaming 82762306a36Sopenharmony_ci * ioctls could take a long time to execute, then you might want to 82862306a36Sopenharmony_ci * have a different lock here to prevent VIDIOC_DQBUF from being 82962306a36Sopenharmony_ci * blocked while waiting for another action to finish. This is 83062306a36Sopenharmony_ci * generally not needed for PCI devices, but USB devices usually do 83162306a36Sopenharmony_ci * want a separate lock here. 83262306a36Sopenharmony_ci */ 83362306a36Sopenharmony_ci q->lock = &skel->lock; 83462306a36Sopenharmony_ci /* 83562306a36Sopenharmony_ci * Since this driver can only do 32-bit DMA we must make sure that 83662306a36Sopenharmony_ci * the vb2 core will allocate the buffers in 32-bit DMA memory. 83762306a36Sopenharmony_ci */ 83862306a36Sopenharmony_ci q->gfp_flags = GFP_DMA32; 83962306a36Sopenharmony_ci ret = vb2_queue_init(q); 84062306a36Sopenharmony_ci if (ret) 84162306a36Sopenharmony_ci goto free_hdl; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci INIT_LIST_HEAD(&skel->buf_list); 84462306a36Sopenharmony_ci spin_lock_init(&skel->qlock); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci /* Initialize the video_device structure */ 84762306a36Sopenharmony_ci vdev = &skel->vdev; 84862306a36Sopenharmony_ci strlcpy(vdev->name, KBUILD_MODNAME, sizeof(vdev->name)); 84962306a36Sopenharmony_ci /* 85062306a36Sopenharmony_ci * There is nothing to clean up, so release is set to an empty release 85162306a36Sopenharmony_ci * function. The release callback must be non-NULL. 85262306a36Sopenharmony_ci */ 85362306a36Sopenharmony_ci vdev->release = video_device_release_empty; 85462306a36Sopenharmony_ci vdev->fops = &skel_fops, 85562306a36Sopenharmony_ci vdev->ioctl_ops = &skel_ioctl_ops, 85662306a36Sopenharmony_ci vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | 85762306a36Sopenharmony_ci V4L2_CAP_STREAMING; 85862306a36Sopenharmony_ci /* 85962306a36Sopenharmony_ci * The main serialization lock. All ioctls are serialized by this 86062306a36Sopenharmony_ci * lock. Exception: if q->lock is set, then the streaming ioctls 86162306a36Sopenharmony_ci * are serialized by that separate lock. 86262306a36Sopenharmony_ci */ 86362306a36Sopenharmony_ci vdev->lock = &skel->lock; 86462306a36Sopenharmony_ci vdev->queue = q; 86562306a36Sopenharmony_ci vdev->v4l2_dev = &skel->v4l2_dev; 86662306a36Sopenharmony_ci /* Supported SDTV standards, if any */ 86762306a36Sopenharmony_ci vdev->tvnorms = SKEL_TVNORMS; 86862306a36Sopenharmony_ci video_set_drvdata(vdev, skel); 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); 87162306a36Sopenharmony_ci if (ret) 87262306a36Sopenharmony_ci goto free_hdl; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci dev_info(&pdev->dev, "V4L2 PCI Skeleton Driver loaded\n"); 87562306a36Sopenharmony_ci return 0; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_cifree_hdl: 87862306a36Sopenharmony_ci v4l2_ctrl_handler_free(&skel->ctrl_handler); 87962306a36Sopenharmony_ci v4l2_device_unregister(&skel->v4l2_dev); 88062306a36Sopenharmony_cidisable_pci: 88162306a36Sopenharmony_ci pci_disable_device(pdev); 88262306a36Sopenharmony_ci return ret; 88362306a36Sopenharmony_ci} 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_cistatic void skeleton_remove(struct pci_dev *pdev) 88662306a36Sopenharmony_ci{ 88762306a36Sopenharmony_ci struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev); 88862306a36Sopenharmony_ci struct skeleton *skel = container_of(v4l2_dev, struct skeleton, v4l2_dev); 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci video_unregister_device(&skel->vdev); 89162306a36Sopenharmony_ci v4l2_ctrl_handler_free(&skel->ctrl_handler); 89262306a36Sopenharmony_ci v4l2_device_unregister(&skel->v4l2_dev); 89362306a36Sopenharmony_ci pci_disable_device(skel->pdev); 89462306a36Sopenharmony_ci} 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_cistatic struct pci_driver skeleton_driver = { 89762306a36Sopenharmony_ci .name = KBUILD_MODNAME, 89862306a36Sopenharmony_ci .probe = skeleton_probe, 89962306a36Sopenharmony_ci .remove = skeleton_remove, 90062306a36Sopenharmony_ci .id_table = skeleton_pci_tbl, 90162306a36Sopenharmony_ci}; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_cimodule_pci_driver(skeleton_driver); 904