mirror of
https://github.com/patjak/facetimehd.git
synced 2026-04-09 11:02:31 +02:00
bcwc_pci: add video 4 linux skeleton
This commit is contained in:
2
Makefile
2
Makefile
@@ -1,4 +1,4 @@
|
||||
bcwc_pcie-objs := bcwc_ddr.o bcwc_hw.o bcwc_drv.o bcwc_ringbuf.o bcwc_isp.o
|
||||
bcwc_pcie-objs := bcwc_ddr.o bcwc_hw.o bcwc_drv.o bcwc_ringbuf.o bcwc_isp.o bcwc_v4l2.o
|
||||
obj-m := bcwc_pcie.o
|
||||
|
||||
KVERSION := $(shell uname -r)
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "bcwc_hw.h"
|
||||
#include "bcwc_isp.h"
|
||||
#include "bcwc_ringbuf.h"
|
||||
#include "bcwc_v4l2.h"
|
||||
|
||||
static int bcwc_pci_reserve_mem(struct bcwc_private *dev_priv)
|
||||
{
|
||||
|
||||
425
bcwc_v4l2.c
Normal file
425
bcwc_v4l2.c
Normal file
@@ -0,0 +1,425 @@
|
||||
|
||||
/*
|
||||
* Broadcom PCIe 1570 webcam driver
|
||||
*
|
||||
* Copyright (C) 2015 Sven Schnelle <svens@stackframe.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/videodev2.h>
|
||||
#include <media/v4l2-dev.h>
|
||||
#include <media/v4l2-ioctl.h>
|
||||
#include <media/videobuf-dma-sg.h>
|
||||
#include "bcwc_drv.h"
|
||||
#include "bcwc_hw.h"
|
||||
#include "bcwc_isp.h"
|
||||
#include "bcwc_ringbuf.h"
|
||||
#include "bcwc_buffer.h"
|
||||
|
||||
static int bcwc_vb_buf_setup(struct videobuf_queue *q,
|
||||
unsigned int *count, unsigned int *size)
|
||||
{
|
||||
struct bcwc_private *priv = q->priv_data;
|
||||
pr_debug("%s: %d\n", __FUNCTION__, priv->user_format.sizeimage);
|
||||
*size = priv->user_format.sizeimage;
|
||||
if (*count == 0 || *count > 6)
|
||||
*count = 6;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcwc_vb_buf_prepare(struct videobuf_queue *q,
|
||||
struct videobuf_buffer *vb, enum v4l2_field field)
|
||||
{
|
||||
struct bcwc_private *priv = q->priv_data;
|
||||
int ret;
|
||||
|
||||
pr_debug("%s\n", __FUNCTION__);
|
||||
|
||||
vb->size = priv->user_format.sizeimage;
|
||||
vb->width = priv->user_format.width;
|
||||
vb->height = priv->user_format.height;
|
||||
vb->field = field;
|
||||
|
||||
if (vb->state == VIDEOBUF_NEEDS_INIT) {
|
||||
ret = videobuf_iolock(q, vb, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
vb->state = VIDEOBUF_PREPARED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bcwc_vb_buf_queue(struct videobuf_queue *q,
|
||||
struct videobuf_buffer *vb)
|
||||
{
|
||||
struct bcwc_private *priv = q->priv_data;
|
||||
pr_debug("%s\n", __FUNCTION__);
|
||||
vb->state = VIDEOBUF_QUEUED;
|
||||
list_add_tail(&vb->queue, &priv->buffer_queue);
|
||||
}
|
||||
|
||||
static void bcwc_vb_buf_release(struct videobuf_queue *q,
|
||||
struct videobuf_buffer *vb)
|
||||
{
|
||||
struct bcwc_private *priv = q->priv_data;
|
||||
pr_debug("%s\n", __FUNCTION__);
|
||||
videobuf_dma_unmap(&priv->pdev->dev, videobuf_to_dma(vb));
|
||||
videobuf_dma_free(videobuf_to_dma(vb));
|
||||
vb->state = VIDEOBUF_NEEDS_INIT;
|
||||
}
|
||||
|
||||
static const struct videobuf_queue_ops bcwc_vb_ops = {
|
||||
.buf_setup = bcwc_vb_buf_setup,
|
||||
.buf_prepare = bcwc_vb_buf_prepare,
|
||||
.buf_queue = bcwc_vb_buf_queue,
|
||||
.buf_release = bcwc_vb_buf_release,
|
||||
};
|
||||
|
||||
static int bcwc_v4l2_open(struct file *filp)
|
||||
{
|
||||
struct bcwc_private *priv = video_drvdata(filp);
|
||||
pr_info("%s: %p\n", __FUNCTION__, priv);
|
||||
|
||||
mutex_lock(&priv->ioctl_lock);
|
||||
videobuf_queue_sg_init(&priv->vb_queue, &bcwc_vb_ops,
|
||||
&priv->pdev->dev, &priv->vb_queue_lock,
|
||||
V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
|
||||
sizeof(struct videobuf_buffer), priv, NULL);
|
||||
|
||||
if (!priv->users++) {
|
||||
bcwc_isp_cmd_channel_start(priv);
|
||||
}
|
||||
|
||||
mutex_unlock(&priv->ioctl_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcwc_v4l2_release(struct file *filp)
|
||||
{
|
||||
struct bcwc_private *priv = video_drvdata(filp);
|
||||
pr_info("%s\n", __FUNCTION__);
|
||||
mutex_lock(&priv->ioctl_lock);
|
||||
if (!--priv->users) {
|
||||
bcwc_isp_cmd_channel_stop(priv);
|
||||
}
|
||||
|
||||
videobuf_stop(&priv->vb_queue);
|
||||
videobuf_mmap_free(&priv->vb_queue);
|
||||
mutex_unlock(&priv->ioctl_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t bcwc_v4l2_read(struct file *filp, char __user *buffer,
|
||||
size_t len, loff_t *pos)
|
||||
{
|
||||
struct bcwc_private *priv = video_drvdata(filp);
|
||||
pr_info("%s: buf %p, size %d, pos %p\n", __FUNCTION__, buffer, (int)len, pos);
|
||||
return videobuf_read_stream(&priv->vb_queue, buffer, len, pos, 0,
|
||||
filp->f_flags & O_NONBLOCK);
|
||||
}
|
||||
|
||||
static unsigned int bcwc_v4l2_poll(struct file *filp, struct poll_table_struct *pt)
|
||||
{
|
||||
struct bcwc_private *priv = video_drvdata(filp);
|
||||
pr_debug("%s\n", __FUNCTION__);
|
||||
return videobuf_poll_stream(filp, &priv->vb_queue, pt);
|
||||
}
|
||||
|
||||
static int bcwc_v4l2_mmap(struct file *filp, struct vm_area_struct *vma)
|
||||
{
|
||||
struct bcwc_private *priv = video_drvdata(filp);
|
||||
pr_debug("%s\n", __FUNCTION__);
|
||||
return videobuf_mmap_mapper(&priv->vb_queue, vma);
|
||||
}
|
||||
|
||||
static struct v4l2_file_operations bcwc_vdev_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = bcwc_v4l2_open,
|
||||
.release = bcwc_v4l2_release,
|
||||
.read = bcwc_v4l2_read,
|
||||
.poll = bcwc_v4l2_poll,
|
||||
.mmap = bcwc_v4l2_mmap,
|
||||
.unlocked_ioctl = video_ioctl2
|
||||
};
|
||||
|
||||
static int bcwc_v4l2_ioctl_enum_input(struct file *filp, void *priv,
|
||||
struct v4l2_input *input)
|
||||
{
|
||||
if (input->index != 0)
|
||||
return -EINVAL;
|
||||
|
||||
input->type = V4L2_INPUT_TYPE_CAMERA;
|
||||
input->std = V4L2_STD_ALL;
|
||||
strcpy(input->name, "Apple Facetime");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcwc_v4l2_ioctl_g_input(struct file *filp, void *priv, unsigned int *i)
|
||||
{
|
||||
*i = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcwc_v4l2_ioctl_s_input(struct file *filp, void *priv, unsigned int i)
|
||||
{
|
||||
if (i != 0)
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcwc_v4l2_ioctl_s_std(struct file *filp, void *priv, v4l2_std_id std)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcwc_v4l2_ioctl_g_std(struct file *filp, void *priv, v4l2_std_id *std)
|
||||
{
|
||||
*std = V4L2_STD_NTSC_M;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcwc_v4l2_ioctl_querycap(struct file *filp, void *priv,
|
||||
struct v4l2_capability *cap)
|
||||
{
|
||||
strcpy(cap->driver, "bcwc");
|
||||
strcpy(cap->card, "facetimehd");
|
||||
cap->device_caps = V4L2_CAP_VIDEO_CAPTURE |
|
||||
V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
|
||||
cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct bcwc_format {
|
||||
__u8 *desc;
|
||||
__u32 pixelformat;
|
||||
int bpp; /* Bytes per pixel */
|
||||
u32 mbus_code;
|
||||
} bcwc_formats[] = {
|
||||
{
|
||||
.desc = "YUYV 4:2:2",
|
||||
.pixelformat = V4L2_PIX_FMT_YUYV,
|
||||
.mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
|
||||
.bpp = 2,
|
||||
},
|
||||
};
|
||||
#if 0
|
||||
static struct bcwc_format *bcwc_find_format(u32 pixelformat)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(bcwc_formats); i++)
|
||||
if (bcwc_formats[i].pixelformat == pixelformat)
|
||||
return bcwc_formats + i;
|
||||
/* Not found? Then return the first format. */
|
||||
return bcwc_formats;
|
||||
}
|
||||
#endif
|
||||
static const struct v4l2_pix_format bcwc_def_pix_format = {
|
||||
.width = 1280,
|
||||
.height = 720,
|
||||
.pixelformat = V4L2_PIX_FMT_YUYV,
|
||||
.field = V4L2_FIELD_NONE,
|
||||
.bytesperline = 1280 * 2,
|
||||
.sizeimage = 1280 * 720 * 2,
|
||||
};
|
||||
|
||||
static const u32 bcwc_def_mbus_code = MEDIA_BUS_FMT_YUYV8_2X8;
|
||||
|
||||
static int bcwc_v4l2_ioctl_enum_fmt_vid_cap(struct file *filp, void *priv,
|
||||
struct v4l2_fmtdesc *fmt)
|
||||
{
|
||||
if (fmt->index >= ARRAY_SIZE(bcwc_formats))
|
||||
return -EINVAL;
|
||||
strlcpy(fmt->description, bcwc_formats[fmt->index].desc,
|
||||
sizeof(fmt->description));
|
||||
fmt->pixelformat = bcwc_formats[fmt->index].pixelformat;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcwc_v4l2_ioctl_try_fmt_vid_cap(struct file *filp, void *_priv,
|
||||
struct v4l2_format *fmt)
|
||||
{
|
||||
struct bcwc_private *priv = video_drvdata(filp);
|
||||
pr_info("%s: %dx%d\n", __FUNCTION__, fmt->fmt.pix.width, fmt->fmt.pix.height);
|
||||
|
||||
if (fmt->fmt.pix.height != 720 ||
|
||||
fmt->fmt.pix.width != 1280 ||
|
||||
fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV)
|
||||
return -EINVAL;
|
||||
|
||||
priv->user_format.height = fmt->fmt.pix.height;
|
||||
priv->user_format.width = fmt->fmt.pix.width;
|
||||
priv->user_format.sizeimage = priv->user_format.height * priv->user_format.width * 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcwc_v4l2_ioctl_g_fmt_vid_cap(struct file *filp, void *priv,
|
||||
struct v4l2_format *fmt)
|
||||
{
|
||||
fmt->fmt.pix.height = 720;
|
||||
fmt->fmt.pix.width = 1280;
|
||||
fmt->fmt.pix.sizeimage = 1280 * 720;
|
||||
fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcwc_v4l2_ioctl_s_fmt_vid_cap(struct file *filp, void *priv,
|
||||
struct v4l2_format *fmt)
|
||||
{
|
||||
pr_info("%s: %dx%d\n", __FUNCTION__, fmt->fmt.pix.width, fmt->fmt.pix.height);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcwc_v4l2_ioctl_reqbufs(struct file *filp, void *_priv,
|
||||
struct v4l2_requestbuffers *rb)
|
||||
{
|
||||
struct bcwc_private *priv = video_drvdata(filp);
|
||||
return videobuf_reqbufs(&priv->vb_queue, rb);
|
||||
}
|
||||
|
||||
static int bcwc_v4l2_ioctl_querybuf(struct file *filp, void *_priv,
|
||||
struct v4l2_buffer *buf)
|
||||
{
|
||||
struct bcwc_private *priv = video_drvdata(filp);
|
||||
return videobuf_querybuf(&priv->vb_queue, buf);
|
||||
}
|
||||
|
||||
static int bcwc_v4l2_ioctl_qbuf(struct file *filp, void *_priv,
|
||||
struct v4l2_buffer *buf)
|
||||
{
|
||||
struct bcwc_private *priv = video_drvdata(filp);
|
||||
return videobuf_qbuf(&priv->vb_queue, buf);
|
||||
}
|
||||
|
||||
static int bcwc_v4l2_ioctl_dqbuf(struct file *filp, void *_priv,
|
||||
struct v4l2_buffer *buf)
|
||||
{
|
||||
struct bcwc_private *priv = video_drvdata(filp);
|
||||
return videobuf_dqbuf(&priv->vb_queue, buf, filp->f_flags & O_NONBLOCK);
|
||||
}
|
||||
|
||||
static int bcwc_v4l2_ioctl_streamon(struct file *filp, void *priv, enum v4l2_buf_type t)
|
||||
{
|
||||
pr_debug("%s\n", __FUNCTION__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int bcwc_v4l2_ioctl_streamoff(struct file *filp, void *priv, enum v4l2_buf_type t)
|
||||
{
|
||||
pr_debug("%s\n", __FUNCTION__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int bcwc_v4l2_ioctl_g_parm(struct file *filp, void *priv,
|
||||
struct v4l2_streamparm *parm)
|
||||
{
|
||||
pr_debug("%s\n", __FUNCTION__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int bcwc_v4l2_ioctl_s_parm(struct file *filp, void *priv,
|
||||
struct v4l2_streamparm *parm)
|
||||
{
|
||||
pr_debug("%s\n", __FUNCTION__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int bcwc_v4l2_ioctl_enum_framesizes(struct file *filp, void *priv,
|
||||
struct v4l2_frmsizeenum *sizes)
|
||||
{
|
||||
pr_debug("%s\n", __FUNCTION__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int bcwc_v4l2_ioctl_enum_frameintervals(struct file *filp, void *priv,
|
||||
struct v4l2_frmivalenum *interval)
|
||||
{
|
||||
pr_debug("%s\n", __FUNCTION__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static struct v4l2_ioctl_ops bcwc_ioctl_ops = {
|
||||
.vidioc_enum_input = bcwc_v4l2_ioctl_enum_input,
|
||||
.vidioc_g_input = bcwc_v4l2_ioctl_g_input,
|
||||
.vidioc_s_input = bcwc_v4l2_ioctl_s_input,
|
||||
.vidioc_s_std = bcwc_v4l2_ioctl_s_std,
|
||||
.vidioc_g_std = bcwc_v4l2_ioctl_g_std,
|
||||
.vidioc_enum_fmt_vid_cap = bcwc_v4l2_ioctl_enum_fmt_vid_cap,
|
||||
.vidioc_try_fmt_vid_cap = bcwc_v4l2_ioctl_try_fmt_vid_cap,
|
||||
|
||||
.vidioc_g_fmt_vid_cap = bcwc_v4l2_ioctl_g_fmt_vid_cap,
|
||||
.vidioc_s_fmt_vid_cap = bcwc_v4l2_ioctl_s_fmt_vid_cap,
|
||||
.vidioc_querycap = bcwc_v4l2_ioctl_querycap,
|
||||
.vidioc_reqbufs = bcwc_v4l2_ioctl_reqbufs,
|
||||
|
||||
.vidioc_querybuf = bcwc_v4l2_ioctl_querybuf,
|
||||
.vidioc_qbuf = bcwc_v4l2_ioctl_qbuf,
|
||||
.vidioc_dqbuf = bcwc_v4l2_ioctl_dqbuf,
|
||||
|
||||
.vidioc_streamon = bcwc_v4l2_ioctl_streamon,
|
||||
.vidioc_streamoff = bcwc_v4l2_ioctl_streamoff,
|
||||
.vidioc_g_parm = bcwc_v4l2_ioctl_g_parm,
|
||||
.vidioc_s_parm = bcwc_v4l2_ioctl_s_parm,
|
||||
.vidioc_enum_framesizes = bcwc_v4l2_ioctl_enum_framesizes,
|
||||
.vidioc_enum_frameintervals = bcwc_v4l2_ioctl_enum_frameintervals,
|
||||
};
|
||||
|
||||
int bcwc_v4l2_register(struct bcwc_private *dev_priv)
|
||||
{
|
||||
struct v4l2_device *v4l2_dev = &dev_priv->v4l2_dev;
|
||||
struct video_device *vdev;
|
||||
int ret;
|
||||
|
||||
ret = v4l2_device_register(&dev_priv->pdev->dev, v4l2_dev);
|
||||
if (ret) {
|
||||
pr_err("v4l2_device_register: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
vdev = video_device_alloc();
|
||||
if (!vdev) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
dev_priv->videodev = vdev;
|
||||
|
||||
vdev->v4l2_dev = v4l2_dev;
|
||||
strcpy(vdev->name, "Apple Facetime HD"); // XXX: Length?
|
||||
vdev->vfl_dir = VFL_DIR_RX;
|
||||
vdev->fops = &bcwc_vdev_fops;
|
||||
vdev->ioctl_ops = &bcwc_ioctl_ops;
|
||||
vdev->release = video_device_release;
|
||||
pr_info("%s: dev_priv %p\n", __FUNCTION__, dev_priv);
|
||||
video_set_drvdata(vdev, dev_priv);
|
||||
ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
|
||||
if (ret) {
|
||||
video_device_release(vdev);
|
||||
goto fail;
|
||||
}
|
||||
return 0;
|
||||
fail:
|
||||
v4l2_device_unregister(&dev_priv->v4l2_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void bcwc_v4l2_unregister(struct bcwc_private *dev_priv)
|
||||
{
|
||||
video_unregister_device(dev_priv->videodev);
|
||||
v4l2_device_unregister(&dev_priv->v4l2_dev);
|
||||
}
|
||||
34
bcwc_v4l2.h
Normal file
34
bcwc_v4l2.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Broadcom PCIe 1570 webcam driver
|
||||
*
|
||||
* Copyright (C) 2015 Sven Schnelle <svens@stackframe.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _BCWC_PCIE_H
|
||||
#define _PCWC_PCIE_H
|
||||
|
||||
#include <linux/pci.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <media/videobuf-dma-sg.h>
|
||||
#include <media/v4l2-device.h>
|
||||
|
||||
struct bcwc_private;
|
||||
extern int bcwc_v4l2_register(struct bcwc_private *dev_priv);
|
||||
extern void bcwc_v4l2_unregister(struct bcwc_private *dev_priv);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user