aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/dream/camera/msm_v4l2.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/dream/camera/msm_v4l2.c')
-rw-r--r--drivers/staging/dream/camera/msm_v4l2.c797
1 files changed, 797 insertions, 0 deletions
diff --git a/drivers/staging/dream/camera/msm_v4l2.c b/drivers/staging/dream/camera/msm_v4l2.c
new file mode 100644
index 000000000000..6a7d46cf11eb
--- /dev/null
+++ b/drivers/staging/dream/camera/msm_v4l2.c
@@ -0,0 +1,797 @@
+/*
+ *
+ * Copyright (C) 2008-2009 QUALCOMM Incorporated.
+ *
+ */
+
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/ioctl.h>
+#include <linux/spinlock.h>
+#include <linux/videodev2.h>
+#include <linux/proc_fs.h>
+#include <media/v4l2-dev.h>
+#include <media/msm_camera.h>
+#include <mach/camera.h>
+#include <media/v4l2-ioctl.h>
+/*#include <linux/platform_device.h>*/
+
+#define MSM_V4L2_START_SNAPSHOT _IOWR('V', BASE_VIDIOC_PRIVATE+1, \
+ struct v4l2_buffer)
+
+#define MSM_V4L2_GET_PICTURE _IOWR('V', BASE_VIDIOC_PRIVATE+2, \
+ struct v4l2_buffer)
+
+#define MSM_V4L2_DEVICE_NAME "msm_v4l2"
+
+#define MSM_V4L2_PROC_NAME "msm_v4l2"
+
+#define MSM_V4L2_DEVNUM_MPEG2 0
+#define MSM_V4L2_DEVNUM_YUV 20
+
+/* HVGA-P (portrait) and HVGA-L (landscape) */
+#define MSM_V4L2_WIDTH 480
+#define MSM_V4L2_HEIGHT 320
+
+#if 1
+#define D(fmt, args...) printk(KERN_INFO "msm_v4l2: " fmt, ##args)
+#else
+#define D(fmt, args...) do {} while (0)
+#endif
+
+#define PREVIEW_FRAMES_NUM 4
+
+struct msm_v4l2_device {
+ struct list_head read_queue;
+ struct v4l2_format current_cap_format;
+ struct v4l2_format current_pix_format;
+ struct video_device *pvdev;
+ struct msm_v4l2_driver *drv;
+ uint8_t opencnt;
+
+ spinlock_t read_queue_lock;
+};
+
+static struct msm_v4l2_device *g_pmsm_v4l2_dev;
+
+
+static DEFINE_MUTEX(msm_v4l2_opencnt_lock);
+
+static int msm_v4l2_open(struct file *f)
+{
+ int rc = 0;
+ D("%s\n", __func__);
+ mutex_lock(&msm_v4l2_opencnt_lock);
+ if (!g_pmsm_v4l2_dev->opencnt) {
+ rc = g_pmsm_v4l2_dev->drv->open(
+ g_pmsm_v4l2_dev->drv->sync,
+ MSM_APPS_ID_V4L2);
+ }
+ g_pmsm_v4l2_dev->opencnt++;
+ mutex_unlock(&msm_v4l2_opencnt_lock);
+ return rc;
+}
+
+static int msm_v4l2_release(struct file *f)
+{
+ int rc = 0;
+ D("%s\n", __func__);
+ mutex_lock(&msm_v4l2_opencnt_lock);
+ if (!g_pmsm_v4l2_dev->opencnt) {
+ g_pmsm_v4l2_dev->opencnt--;
+ if (!g_pmsm_v4l2_dev->opencnt) {
+ rc = g_pmsm_v4l2_dev->drv->release(
+ g_pmsm_v4l2_dev->drv->sync);
+ }
+ }
+ mutex_unlock(&msm_v4l2_opencnt_lock);
+ return rc;
+}
+
+static unsigned int msm_v4l2_poll(struct file *f, struct poll_table_struct *w)
+{
+ return g_pmsm_v4l2_dev->drv->drv_poll(g_pmsm_v4l2_dev->drv->sync, f, w);
+}
+
+static long msm_v4l2_ioctl(struct file *filep,
+ unsigned int cmd, unsigned long arg)
+{
+ struct msm_ctrl_cmd *ctrlcmd;
+
+ D("msm_v4l2_ioctl, cmd = %d, %d\n", cmd, __LINE__);
+
+ switch (cmd) {
+ case MSM_V4L2_START_SNAPSHOT:
+
+ ctrlcmd = kmalloc(sizeof(struct msm_ctrl_cmd), GFP_ATOMIC);
+ if (!ctrlcmd) {
+ CDBG("msm_v4l2_ioctl: cannot allocate buffer\n");
+ return -ENOMEM;
+ }
+
+ ctrlcmd->length = 0;
+ ctrlcmd->value = NULL;
+ ctrlcmd->timeout_ms = 10000;
+
+ D("msm_v4l2_ioctl, MSM_V4L2_START_SNAPSHOT v4l2 ioctl %d\n",
+ cmd);
+ ctrlcmd->type = MSM_V4L2_SNAPSHOT;
+ return g_pmsm_v4l2_dev->drv->ctrl(g_pmsm_v4l2_dev->drv->sync,
+ ctrlcmd);
+
+ case MSM_V4L2_GET_PICTURE:
+ D("msm_v4l2_ioctl, MSM_V4L2_GET_PICTURE v4l2 ioctl %d\n", cmd);
+ ctrlcmd = (struct msm_ctrl_cmd *)arg;
+ return g_pmsm_v4l2_dev->drv->get_pict(
+ g_pmsm_v4l2_dev->drv->sync, ctrlcmd);
+
+ default:
+ D("msm_v4l2_ioctl, standard v4l2 ioctl %d\n", cmd);
+ return video_ioctl2(filep, cmd, arg);
+ }
+}
+
+static void msm_v4l2_release_dev(struct video_device *d)
+{
+ D("%s\n", __func__);
+}
+
+static int msm_v4l2_querycap(struct file *f,
+ void *pctx, struct v4l2_capability *pcaps)
+{
+ D("%s\n", __func__);
+ strncpy(pcaps->driver, MSM_APPS_ID_V4L2, sizeof(pcaps->driver));
+ strncpy(pcaps->card,
+ MSM_V4L2_DEVICE_NAME, sizeof(pcaps->card));
+ pcaps->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ return 0;
+}
+
+static int msm_v4l2_s_std(struct file *f, void *pctx, v4l2_std_id *pnorm)
+{
+ D("%s\n", __func__);
+ return 0;
+}
+
+static int msm_v4l2_queryctrl(struct file *f,
+ void *pctx, struct v4l2_queryctrl *pqctrl)
+{
+ int rc = 0;
+ struct msm_ctrl_cmd *ctrlcmd;
+
+ D("%s\n", __func__);
+
+ ctrlcmd = kmalloc(sizeof(struct msm_ctrl_cmd), GFP_ATOMIC);
+ if (!ctrlcmd) {
+ CDBG("msm_v4l2_queryctrl: cannot allocate buffer\n");
+ return -ENOMEM;
+ }
+
+ ctrlcmd->type = MSM_V4L2_QUERY_CTRL;
+ ctrlcmd->length = sizeof(struct v4l2_queryctrl);
+ ctrlcmd->value = pqctrl;
+ ctrlcmd->timeout_ms = 10000;
+
+ rc = g_pmsm_v4l2_dev->drv->ctrl(g_pmsm_v4l2_dev->drv->sync, ctrlcmd);
+ if (rc < 0)
+ return -1;
+
+ return ctrlcmd->status;
+}
+
+static int msm_v4l2_g_ctrl(struct file *f, void *pctx, struct v4l2_control *c)
+{
+ int rc = 0;
+ struct msm_ctrl_cmd *ctrlcmd;
+
+ D("%s\n", __func__);
+
+ ctrlcmd = kmalloc(sizeof(struct msm_ctrl_cmd), GFP_ATOMIC);
+ if (!ctrlcmd) {
+ CDBG("msm_v4l2_g_ctrl: cannot allocate buffer\n");
+ return -ENOMEM;
+ }
+
+ ctrlcmd->type = MSM_V4L2_GET_CTRL;
+ ctrlcmd->length = sizeof(struct v4l2_control);
+ ctrlcmd->value = c;
+ ctrlcmd->timeout_ms = 10000;
+
+ rc = g_pmsm_v4l2_dev->drv->ctrl(g_pmsm_v4l2_dev->drv->sync, ctrlcmd);
+ if (rc < 0)
+ return -1;
+
+ return ctrlcmd->status;
+}
+
+static int msm_v4l2_s_ctrl(struct file *f, void *pctx, struct v4l2_control *c)
+{
+ int rc = 0;
+ struct msm_ctrl_cmd *ctrlcmd;
+
+ ctrlcmd = kmalloc(sizeof(struct msm_ctrl_cmd), GFP_ATOMIC);
+ if (!ctrlcmd) {
+ CDBG("msm_v4l2_s_ctrl: cannot allocate buffer\n");
+ return -ENOMEM;
+ }
+
+ ctrlcmd->type = MSM_V4L2_SET_CTRL;
+ ctrlcmd->length = sizeof(struct v4l2_control);
+ ctrlcmd->value = c;
+ ctrlcmd->timeout_ms = 10000;
+
+ D("%s\n", __func__);
+
+ rc = g_pmsm_v4l2_dev->drv->ctrl(g_pmsm_v4l2_dev->drv->sync, ctrlcmd);
+ if (rc < 0)
+ return -1;
+
+ return ctrlcmd->status;
+}
+
+static int msm_v4l2_reqbufs(struct file *f,
+ void *pctx, struct v4l2_requestbuffers *b)
+{
+ D("%s\n", __func__);
+ return 0;
+}
+
+static int msm_v4l2_querybuf(struct file *f, void *pctx, struct v4l2_buffer *pb)
+{
+ struct msm_pmem_info pmem_buf;
+#if 0
+ __u32 width = 0;
+ __u32 height = 0;
+ __u32 y_size = 0;
+ __u32 y_pad = 0;
+
+ /* FIXME: g_pmsm_v4l2_dev->current_pix_format.fmt.pix.width; */
+ width = 640;
+ /* FIXME: g_pmsm_v4l2_dev->current_pix_format.fmt.pix.height; */
+ height = 480;
+
+ D("%s: width = %d, height = %d\n", __func__, width, height);
+
+ y_size = width * height;
+ y_pad = y_size % 4;
+#endif
+
+ __u32 y_pad = pb->bytesused % 4;
+
+ /* V4L2 videodev will do the copy_from_user. */
+
+ memset(&pmem_buf, 0, sizeof(struct msm_pmem_info));
+ pmem_buf.type = MSM_PMEM_OUTPUT2;
+ pmem_buf.vaddr = (void *)pb->m.userptr;
+ pmem_buf.y_off = 0;
+ pmem_buf.fd = (int)pb->reserved;
+ /* pmem_buf.cbcr_off = (y_size + y_pad); */
+ pmem_buf.cbcr_off = (pb->bytesused + y_pad);
+
+ g_pmsm_v4l2_dev->drv->reg_pmem(g_pmsm_v4l2_dev->drv->sync, &pmem_buf);
+
+ return 0;
+}
+
+static int msm_v4l2_qbuf(struct file *f, void *pctx, struct v4l2_buffer *pb)
+{
+ /*
+ __u32 y_size = 0;
+ __u32 y_pad = 0;
+ __u32 width = 0;
+ __u32 height = 0;
+ */
+
+ __u32 y_pad = 0;
+
+ struct msm_pmem_info meminfo;
+ struct msm_frame frame;
+ static int cnt;
+
+ if ((pb->flags >> 16) & 0x0001) {
+ /* this is for previwe */
+#if 0
+ width = 640;
+ height = 480;
+
+ /* V4L2 videodev will do the copy_from_user. */
+ D("%s: width = %d, height = %d\n", __func__, width, height);
+ y_size = width * height;
+ y_pad = y_size % 4;
+#endif
+
+ y_pad = pb->bytesused % 4;
+
+ if (pb->type == V4L2_BUF_TYPE_PRIVATE) {
+ /* this qbuf is actually for releasing */
+
+ frame.buffer = pb->m.userptr;
+ frame.y_off = 0;
+ /* frame.cbcr_off = (y_size + y_pad); */
+ frame.cbcr_off = (pb->bytesused + y_pad);
+ frame.fd = pb->reserved;
+
+ D("V4L2_BUF_TYPE_PRIVATE: pb->bytesused = %d \n",
+ pb->bytesused);
+
+ g_pmsm_v4l2_dev->drv->put_frame(
+ g_pmsm_v4l2_dev->drv->sync,
+ &frame);
+
+ return 0;
+ }
+
+ D("V4L2_BUF_TYPE_VIDEO_CAPTURE: pb->bytesused = %d \n",
+ pb->bytesused);
+
+ meminfo.type = MSM_PMEM_OUTPUT2;
+ meminfo.fd = (int)pb->reserved;
+ meminfo.vaddr = (void *)pb->m.userptr;
+ meminfo.y_off = 0;
+ /* meminfo.cbcr_off = (y_size + y_pad); */
+ meminfo.cbcr_off = (pb->bytesused + y_pad);
+ if (cnt == PREVIEW_FRAMES_NUM - 1)
+ meminfo.active = 0;
+ else
+ meminfo.active = 1;
+ cnt++;
+ g_pmsm_v4l2_dev->drv->reg_pmem(g_pmsm_v4l2_dev->drv->sync,
+ &meminfo);
+ } else if ((pb->flags) & 0x0001) {
+ /* this is for snapshot */
+
+ __u32 y_size = 0;
+
+ if ((pb->flags >> 8) & 0x01) {
+
+ y_size = pb->bytesused;
+
+ meminfo.type = MSM_PMEM_THUMBAIL;
+ } else if ((pb->flags >> 9) & 0x01) {
+
+ y_size = pb->bytesused;
+
+ meminfo.type = MSM_PMEM_MAINIMG;
+ }
+
+ y_pad = y_size % 4;
+
+ meminfo.fd = (int)pb->reserved;
+ meminfo.vaddr = (void *)pb->m.userptr;
+ meminfo.y_off = 0;
+ /* meminfo.cbcr_off = (y_size + y_pad); */
+ meminfo.cbcr_off = (y_size + y_pad);
+ meminfo.active = 1;
+ g_pmsm_v4l2_dev->drv->reg_pmem(g_pmsm_v4l2_dev->drv->sync,
+ &meminfo);
+ }
+
+ return 0;
+}
+
+static int msm_v4l2_dqbuf(struct file *f, void *pctx, struct v4l2_buffer *pb)
+{
+ struct msm_frame frame;
+ D("%s\n", __func__);
+
+ /* V4L2 videodev will do the copy_to_user. */
+ if (pb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+
+ D("%s, %d\n", __func__, __LINE__);
+
+ g_pmsm_v4l2_dev->drv->get_frame(
+ g_pmsm_v4l2_dev->drv->sync,
+ &frame);
+
+ pb->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ pb->m.userptr = (unsigned long)frame.buffer; /* FIXME */
+ pb->reserved = (int)frame.fd;
+ /* pb->length = (int)frame.cbcr_off; */
+
+ pb->bytesused = frame.cbcr_off;
+
+ } else if (pb->type == V4L2_BUF_TYPE_PRIVATE) {
+ __u32 y_pad = pb->bytesused % 4;
+
+ frame.buffer = pb->m.userptr;
+ frame.y_off = 0;
+ /* frame.cbcr_off = (y_size + y_pad); */
+ frame.cbcr_off = (pb->bytesused + y_pad);
+ frame.fd = pb->reserved;
+
+ g_pmsm_v4l2_dev->drv->put_frame(
+ g_pmsm_v4l2_dev->drv->sync,
+ &frame);
+ }
+
+ return 0;
+}
+
+static int msm_v4l2_streamon(struct file *f, void *pctx, enum v4l2_buf_type i)
+{
+ struct msm_ctrl_cmd *ctrlcmd;
+
+ ctrlcmd = kmalloc(sizeof(struct msm_ctrl_cmd), GFP_ATOMIC);
+ if (!ctrlcmd) {
+ CDBG("msm_v4l2_s_fmt_cap: cannot allocate buffer\n");
+ return -ENOMEM;
+ }
+
+ ctrlcmd->type = MSM_V4L2_STREAM_ON;
+ ctrlcmd->timeout_ms = 10000;
+ ctrlcmd->length = 0;
+ ctrlcmd->value = NULL;
+
+ D("%s\n", __func__);
+
+ g_pmsm_v4l2_dev->drv->ctrl(
+ g_pmsm_v4l2_dev->drv->sync,
+ ctrlcmd);
+
+ D("%s after drv->ctrl \n", __func__);
+
+ return 0;
+}
+
+static int msm_v4l2_streamoff(struct file *f, void *pctx, enum v4l2_buf_type i)
+{
+ struct msm_ctrl_cmd *ctrlcmd;
+
+ ctrlcmd = kmalloc(sizeof(struct msm_ctrl_cmd), GFP_ATOMIC);
+ if (!ctrlcmd) {
+ CDBG("msm_v4l2_s_fmt_cap: cannot allocate buffer\n");
+ return -ENOMEM;
+ }
+
+ ctrlcmd->type = MSM_V4L2_STREAM_OFF;
+ ctrlcmd->timeout_ms = 10000;
+ ctrlcmd->length = 0;
+ ctrlcmd->value = NULL;
+
+
+ D("%s\n", __func__);
+
+ g_pmsm_v4l2_dev->drv->ctrl(
+ g_pmsm_v4l2_dev->drv->sync,
+ ctrlcmd);
+
+ return 0;
+}
+
+static int msm_v4l2_enum_fmt_overlay(struct file *f,
+ void *pctx, struct v4l2_fmtdesc *pfmtdesc)
+{
+ D("%s\n", __func__);
+ return 0;
+}
+
+static int msm_v4l2_enum_fmt_cap(struct file *f,
+ void *pctx, struct v4l2_fmtdesc *pfmtdesc)
+{
+ D("%s\n", __func__);
+
+ switch (pfmtdesc->index) {
+ case 0:
+ pfmtdesc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ pfmtdesc->flags = 0;
+ strncpy(pfmtdesc->description, "YUV 4:2:0",
+ sizeof(pfmtdesc->description));
+ pfmtdesc->pixelformat = V4L2_PIX_FMT_YVU420;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int msm_v4l2_g_fmt_cap(struct file *f,
+ void *pctx, struct v4l2_format *pfmt)
+{
+ D("%s\n", __func__);
+ pfmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ pfmt->fmt.pix.width = MSM_V4L2_WIDTH;
+ pfmt->fmt.pix.height = MSM_V4L2_HEIGHT;
+ pfmt->fmt.pix.pixelformat = V4L2_PIX_FMT_YVU420;
+ pfmt->fmt.pix.field = V4L2_FIELD_ANY;
+ pfmt->fmt.pix.bytesperline = 0;
+ pfmt->fmt.pix.sizeimage = 0;
+ pfmt->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG;
+ pfmt->fmt.pix.priv = 0;
+ return 0;
+}
+
+static int msm_v4l2_s_fmt_cap(struct file *f,
+ void *pctx, struct v4l2_format *pfmt)
+{
+ struct msm_ctrl_cmd *ctrlcmd;
+
+ D("%s\n", __func__);
+
+ ctrlcmd = kmalloc(sizeof(struct msm_ctrl_cmd), GFP_ATOMIC);
+ if (!ctrlcmd) {
+ CDBG("msm_v4l2_s_fmt_cap: cannot allocate buffer\n");
+ return -ENOMEM;
+ }
+
+ ctrlcmd->type = MSM_V4L2_VID_CAP_TYPE;
+ ctrlcmd->length = sizeof(struct v4l2_format);
+ ctrlcmd->value = pfmt;
+ ctrlcmd->timeout_ms = 10000;
+
+ if (pfmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ kfree(ctrlcmd);
+ return -1;
+ }
+
+#if 0
+ /* FIXEME */
+ if (pfmt->fmt.pix.pixelformat != V4L2_PIX_FMT_YVU420) {
+ kfree(ctrlcmd);
+ return -EINVAL;
+ }
+#endif
+
+ /* Ok, but check other params, too. */
+
+#if 0
+ memcpy(&g_pmsm_v4l2_dev->current_pix_format.fmt.pix, pfmt,
+ sizeof(struct v4l2_format));
+#endif
+
+ g_pmsm_v4l2_dev->drv->ctrl(g_pmsm_v4l2_dev->drv->sync, ctrlcmd);
+
+ return 0;
+}
+
+static int msm_v4l2_g_fmt_overlay(struct file *f,
+ void *pctx, struct v4l2_format *pfmt)
+{
+ D("%s\n", __func__);
+ pfmt->type = V4L2_BUF_TYPE_VIDEO_OVERLAY;
+ pfmt->fmt.pix.width = MSM_V4L2_WIDTH;
+ pfmt->fmt.pix.height = MSM_V4L2_HEIGHT;
+ pfmt->fmt.pix.pixelformat = V4L2_PIX_FMT_YVU420;
+ pfmt->fmt.pix.field = V4L2_FIELD_ANY;
+ pfmt->fmt.pix.bytesperline = 0;
+ pfmt->fmt.pix.sizeimage = 0;
+ pfmt->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG;
+ pfmt->fmt.pix.priv = 0;
+ return 0;
+}
+
+static int msm_v4l2_s_fmt_overlay(struct file *f,
+ void *pctx, struct v4l2_format *pfmt)
+{
+ D("%s\n", __func__);
+ return 0;
+}
+
+static int msm_v4l2_overlay(struct file *f, void *pctx, unsigned int i)
+{
+ D("%s\n", __func__);
+ return 0;
+}
+
+static int msm_v4l2_g_jpegcomp(struct file *f,
+ void *pctx, struct v4l2_jpegcompression *pcomp)
+{
+ D("%s\n", __func__);
+ return 0;
+}
+
+static int msm_v4l2_s_jpegcomp(struct file *f,
+ void *pctx, struct v4l2_jpegcompression *pcomp)
+{
+ D("%s\n", __func__);
+ return 0;
+}
+
+#ifdef CONFIG_PROC_FS
+int msm_v4l2_read_proc(char *pbuf, char **start, off_t offset,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ len += snprintf(pbuf, strlen("stats\n") + 1, "stats\n");
+
+ if (g_pmsm_v4l2_dev) {
+ len += snprintf(pbuf, strlen("mode: ") + 1, "mode: ");
+
+ if (g_pmsm_v4l2_dev->current_cap_format.type
+ == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ len += snprintf(pbuf, strlen("capture\n") + 1,
+ "capture\n");
+ else
+ len += snprintf(pbuf, strlen("unknown\n") + 1,
+ "unknown\n");
+
+ len += snprintf(pbuf, 21, "resolution: %dx%d\n",
+ g_pmsm_v4l2_dev->current_cap_format.fmt.pix.
+ width,
+ g_pmsm_v4l2_dev->current_cap_format.fmt.pix.
+ height);
+
+ len += snprintf(pbuf,
+ strlen("pixel format: ") + 1, "pixel format: ");
+ if (g_pmsm_v4l2_dev->current_cap_format.fmt.pix.pixelformat
+ == V4L2_PIX_FMT_YVU420)
+ len += snprintf(pbuf, strlen("yvu420\n") + 1,
+ "yvu420\n");
+ else
+ len += snprintf(pbuf, strlen("unknown\n") + 1,
+ "unknown\n");
+
+ len += snprintf(pbuf, strlen("colorspace: ") + 1,
+ "colorspace: ");
+ if (g_pmsm_v4l2_dev->current_cap_format.fmt.pix.colorspace
+ == V4L2_COLORSPACE_JPEG)
+ len += snprintf(pbuf, strlen("jpeg\n") + 1, "jpeg\n");
+ else
+ len += snprintf(pbuf, strlen("unknown\n") + 1,
+ "unknown\n");
+ }
+
+ *eof = 1;
+ return len;
+}
+#endif
+
+static const struct v4l2_file_operations msm_v4l2_fops = {
+ .owner = THIS_MODULE,
+ .open = msm_v4l2_open,
+ .poll = msm_v4l2_poll,
+ .release = msm_v4l2_release,
+ .ioctl = msm_v4l2_ioctl,
+};
+
+static void msm_v4l2_dev_init(struct msm_v4l2_device *pmsm_v4l2_dev)
+{
+ pmsm_v4l2_dev->read_queue_lock =
+ __SPIN_LOCK_UNLOCKED(pmsm_v4l2_dev->read_queue_lock);
+ INIT_LIST_HEAD(&pmsm_v4l2_dev->read_queue);
+}
+
+static int msm_v4l2_try_fmt_cap(struct file *file,
+ void *fh, struct v4l2_format *f)
+{
+ /* FIXME */
+ return 0;
+}
+
+static int mm_v4l2_try_fmt_type_private(struct file *file,
+ void *fh, struct v4l2_format *f)
+{
+ /* FIXME */
+ return 0;
+}
+
+/*
+ * should the following structure be used instead of the code in the function?
+ * static const struct v4l2_ioctl_ops msm_v4l2_ioctl_ops = {
+ * .vidioc_querycap = ....
+ * }
+ */
+static const struct v4l2_ioctl_ops msm_ioctl_ops = {
+ .vidioc_querycap = msm_v4l2_querycap,
+ .vidioc_s_std = msm_v4l2_s_std,
+
+ .vidioc_queryctrl = msm_v4l2_queryctrl,
+ .vidioc_g_ctrl = msm_v4l2_g_ctrl,
+ .vidioc_s_ctrl = msm_v4l2_s_ctrl,
+
+ .vidioc_reqbufs = msm_v4l2_reqbufs,
+ .vidioc_querybuf = msm_v4l2_querybuf,
+ .vidioc_qbuf = msm_v4l2_qbuf,
+ .vidioc_dqbuf = msm_v4l2_dqbuf,
+
+ .vidioc_streamon = msm_v4l2_streamon,
+ .vidioc_streamoff = msm_v4l2_streamoff,
+
+ .vidioc_enum_fmt_vid_overlay = msm_v4l2_enum_fmt_overlay,
+ .vidioc_enum_fmt_vid_cap = msm_v4l2_enum_fmt_cap,
+
+ .vidioc_try_fmt_vid_cap = msm_v4l2_try_fmt_cap,
+ .vidioc_try_fmt_type_private = mm_v4l2_try_fmt_type_private,
+
+ .vidioc_g_fmt_vid_cap = msm_v4l2_g_fmt_cap,
+ .vidioc_s_fmt_vid_cap = msm_v4l2_s_fmt_cap,
+ .vidioc_g_fmt_vid_overlay = msm_v4l2_g_fmt_overlay,
+ .vidioc_s_fmt_vid_overlay = msm_v4l2_s_fmt_overlay,
+ .vidioc_overlay = msm_v4l2_overlay,
+
+ .vidioc_g_jpegcomp = msm_v4l2_g_jpegcomp,
+ .vidioc_s_jpegcomp = msm_v4l2_s_jpegcomp,
+};
+
+static int msm_v4l2_video_dev_init(struct video_device *pvd)
+{
+ strncpy(pvd->name, MSM_APPS_ID_V4L2, sizeof(pvd->name));
+ pvd->vfl_type = 1;
+ pvd->fops = &msm_v4l2_fops;
+ pvd->release = msm_v4l2_release_dev;
+ pvd->minor = -1;
+ pvd->ioctl_ops = &msm_ioctl_ops;
+ return msm_v4l2_register(g_pmsm_v4l2_dev->drv);
+}
+
+static int __init msm_v4l2_init(void)
+{
+ int rc = -ENOMEM;
+ struct video_device *pvdev = NULL;
+ struct msm_v4l2_device *pmsm_v4l2_dev = NULL;
+ D("%s\n", __func__);
+
+ pvdev = video_device_alloc();
+ if (pvdev == NULL)
+ return rc;
+
+ pmsm_v4l2_dev =
+ kzalloc(sizeof(struct msm_v4l2_device), GFP_KERNEL);
+ if (pmsm_v4l2_dev == NULL) {
+ video_device_release(pvdev);
+ return rc;
+ }
+
+ msm_v4l2_dev_init(pmsm_v4l2_dev);
+
+ g_pmsm_v4l2_dev = pmsm_v4l2_dev;
+ g_pmsm_v4l2_dev->pvdev = pvdev;
+
+ g_pmsm_v4l2_dev->drv =
+ kzalloc(sizeof(struct msm_v4l2_driver), GFP_KERNEL);
+ if (!g_pmsm_v4l2_dev->drv) {
+ video_device_release(pvdev);
+ kfree(pmsm_v4l2_dev);
+ return rc;
+ }
+
+ rc = msm_v4l2_video_dev_init(pvdev);
+ if (rc < 0) {
+ video_device_release(pvdev);
+ kfree(g_pmsm_v4l2_dev->drv);
+ kfree(pmsm_v4l2_dev);
+ return rc;
+ }
+
+ if (video_register_device(pvdev, VFL_TYPE_GRABBER,
+ MSM_V4L2_DEVNUM_YUV)) {
+ D("failed to register device\n");
+ video_device_release(pvdev);
+ kfree(g_pmsm_v4l2_dev);
+ g_pmsm_v4l2_dev = NULL;
+ return -ENOENT;
+ }
+#ifdef CONFIG_PROC_FS
+ create_proc_read_entry(MSM_V4L2_PROC_NAME,
+ 0, NULL, msm_v4l2_read_proc, NULL);
+#endif
+
+ return 0;
+}
+
+static void __exit msm_v4l2_exit(void)
+{
+ struct video_device *pvdev = g_pmsm_v4l2_dev->pvdev;
+ D("%s\n", __func__);
+#ifdef CONFIG_PROC_FS
+ remove_proc_entry(MSM_V4L2_PROC_NAME, NULL);
+#endif
+ video_unregister_device(pvdev);
+ video_device_release(pvdev);
+
+ msm_v4l2_unregister(g_pmsm_v4l2_dev->drv);
+
+ kfree(g_pmsm_v4l2_dev->drv);
+ g_pmsm_v4l2_dev->drv = NULL;
+
+ kfree(g_pmsm_v4l2_dev);
+ g_pmsm_v4l2_dev = NULL;
+}
+
+module_init(msm_v4l2_init);
+module_exit(msm_v4l2_exit);
+
+MODULE_DESCRIPTION("MSM V4L2 driver");
+MODULE_LICENSE("GPL v2");