aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/omapgfx/gfx_init.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/video/omapgfx/gfx_init.c')
-rw-r--r--drivers/media/video/omapgfx/gfx_init.c297
1 files changed, 297 insertions, 0 deletions
diff --git a/drivers/media/video/omapgfx/gfx_init.c b/drivers/media/video/omapgfx/gfx_init.c
new file mode 100644
index 0000000..14ee80f
--- /dev/null
+++ b/drivers/media/video/omapgfx/gfx_init.c
@@ -0,0 +1,297 @@
+/*
+ * drivers/media/video/omap/v4gfx.c
+ *
+ * Copyright (C) 2010 Texas Instruments.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/version.h>
+
+#include <linux/omap_v4l2_gfx.h> /* private ioctls */
+
+#include <media/v4l2-ioctl.h>
+
+#include "v4gfx.h"
+#include "gfx_bc.h"
+
+MODULE_AUTHOR("Texas Instruments.");
+MODULE_DESCRIPTION("OMAP V4L2 GFX driver");
+MODULE_LICENSE("GPL");
+
+/*
+ * Device node will be: /dev/video<VOUT_DEVICENODE_SUFFIX>
+ * See also /sys/devices/virtual/video4linux/<node>/name which will be
+ * whatever the value of VOUT_NAME is
+ */
+#define VOUT_DEVICENODE_SUFFIX 100
+
+static struct gbl_v4gfx *gbl_dev;
+
+int debug; /* is used outside this compilation unit too */
+module_param(debug, int, 0644);
+
+/*
+ * If bypass is set then buffer streaming operations will be bypassed. This
+ * enables us to check what the raw performance of stack above the V4L2
+ * driver is
+ */
+static int bypass;
+module_param(bypass, int, 0644);
+
+
+static int bypass_vidioc_qbuf(
+ struct file *file, void *fh, struct v4l2_buffer *buf) { return 0; }
+
+static int bypass_vidioc_dqbuf(
+ struct file *file, void *fh, struct v4l2_buffer *buf) { return 0; }
+
+static int bypass_vidioc_streamon(
+ struct file *file, void *fh, enum v4l2_buf_type i) { return 0; }
+
+static int bypass_vidioc_streamoff(
+ struct file *file, void *fh, enum v4l2_buf_type i) { return 0; }
+
+static long bypass_vidioc_default(
+ struct file *file, void *fh, int cmd, void *arg)
+{
+ struct v4l2_gfx_buf_params *parms = (struct v4l2_gfx_buf_params *)arg;
+ int rv = 0;
+
+ switch (cmd) {
+ case V4L2_GFX_IOC_CONSUMER:
+ break;
+ case V4L2_GFX_IOC_ACQ:
+ /* In bypass mode default the first buffer */
+ parms->bufid = 0;
+ break;
+ case V4L2_GFX_IOC_REL:
+ break;
+ default:
+ rv = -EINVAL;
+ }
+ return rv;
+}
+
+/*
+ * If the module is put in bypass mode the following ioctls
+ * are effectively nops
+ */
+static void v4gfx_enable_bypass(void)
+{
+ v4gfx_ioctl_ops.vidioc_qbuf = bypass_vidioc_qbuf;
+ v4gfx_ioctl_ops.vidioc_dqbuf = bypass_vidioc_dqbuf;
+ v4gfx_ioctl_ops.vidioc_streamon = bypass_vidioc_streamon;
+ v4gfx_ioctl_ops.vidioc_streamoff = bypass_vidioc_streamoff;
+ v4gfx_ioctl_ops.vidioc_default = bypass_vidioc_default;
+}
+
+static void v4gfx_cleanup_device(struct v4gfx_device *vout)
+{
+ struct video_device *vfd;
+
+ if (!vout)
+ return;
+ vfd = vout->vfd;
+
+ if (vfd) {
+ if (vfd->minor == -1) {
+ /*
+ * The device was never registered, so release the
+ * video_device struct directly.
+ */
+ video_device_release(vfd);
+ } else {
+ /*
+ * The unregister function will release the video_device
+ * struct as well as unregistering it.
+ */
+ video_unregister_device(vfd);
+ }
+ }
+
+ v4gfx_tiler_buffer_free(vout, vout->buffer_allocated, 0);
+ kfree(vout);
+}
+
+static int driver_remove(struct platform_device *pdev)
+{
+ struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
+ struct gbl_v4gfx *dev = container_of(v4l2_dev, struct
+ gbl_v4gfx, v4l2_dev);
+ int k;
+
+ v4l2_device_unregister(v4l2_dev);
+ for (k = 0; k < pdev->num_resources; k++)
+ v4gfx_cleanup_device(dev->vouts[k]);
+
+ kfree(gbl_dev);
+ return 0;
+}
+
+static int driver_probe(struct platform_device *pdev)
+{
+ printk(KERN_INFO "Probing: " VOUT_NAME);
+ return 0;
+}
+
+static int v4gfx_create_instance(struct v4gfx_device **voutp)
+{
+ int r = 0;
+ struct v4gfx_device *vout = NULL;
+ struct video_device *vfd = NULL;
+
+ vout = kzalloc(sizeof(struct v4gfx_device), GFP_KERNEL);
+ if (vout == NULL) {
+ r = -ENOMEM;
+ goto end;
+ }
+ mutex_init(&vout->lock);
+ spin_lock_init(&vout->vbq_lock);
+ /* TODO set this to an invalid value, need to change unit test though */
+ vout->bpp = RGB565_BPP;
+ vout->gbl_dev = gbl_dev;
+ vout->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+
+ init_timer(&vout->acquire_timer);
+ vout->acquire_timer.function = v4gfx_acquire_timer;
+ vout->acquire_timer.data = (unsigned long)vout;
+
+ init_waitqueue_head(&vout->sync_done);
+ init_waitqueue_head(&vout->consumer_wait);
+
+ vfd = vout->vfd = video_device_alloc();
+ if (!vfd)
+ goto end;
+
+ strlcpy(vfd->name, VOUT_NAME, sizeof(vfd->name));
+ vfd->vfl_type = VFL_TYPE_GRABBER;
+ vfd->release = video_device_release;
+ vfd->ioctl_ops = &v4gfx_ioctl_ops;
+ vfd->fops = &v4gfx_fops;
+ vfd->minor = -1;
+ vfd->debug = debug;
+
+ r = video_register_device(vfd, VFL_TYPE_GRABBER,
+ VOUT_DEVICENODE_SUFFIX);
+ if (r < 0)
+ goto end;
+
+ video_set_drvdata(vfd, vout);
+
+ *voutp = vout;
+ printk(KERN_INFO VOUT_NAME ":video device registered\n");
+ return 0;
+end:
+
+ if (vfd)
+ video_device_release(vfd);
+
+ kfree(vout); /* safe with null vout */
+
+ return r;
+}
+
+static void v4gfx_delete_instance(
+ struct v4l2_device *v4l2_dev, struct v4gfx_device *vout)
+{
+ v4l2_info(v4l2_dev, "unregistering /dev/video%d\n", vout->vfd->num);
+ video_unregister_device(vout->vfd);
+ v4gfx_buffer_array_free(vout, vout->buffer_allocated);
+ kfree(vout);
+ return;
+}
+
+static struct platform_driver v4gfx_driver = {
+ .driver = {
+ .name = VOUT_NAME,
+ },
+ .probe = driver_probe,
+ .remove = driver_remove,
+};
+
+static int module_init_v4gfx(void)
+{
+ int rv;
+ bool v4l2_dev_registered = false;
+ bool bc_dev_registered = false;
+
+ if (bypass) {
+ printk(KERN_INFO VOUT_NAME ":Enable bypass mode\n");
+ v4gfx_enable_bypass();
+ }
+
+ rv = platform_driver_register(&v4gfx_driver);
+ if (rv != 0) {
+ printk(KERN_ERR VOUT_NAME ":platform_driver_register failed\n");
+ goto end;
+ }
+
+ gbl_dev = kzalloc(sizeof(struct gbl_v4gfx), GFP_KERNEL);
+ if (gbl_dev == NULL) {
+ rv = -ENOMEM;
+ goto end;
+ }
+
+ snprintf(gbl_dev->v4l2_dev.name, sizeof(gbl_dev->v4l2_dev.name),
+ "%s-%03d", VOUT_NAME, VOUT_DEVICENODE_SUFFIX);
+
+ rv = v4l2_device_register(NULL, &gbl_dev->v4l2_dev);
+ if (rv != 0) {
+ printk(KERN_ERR VOUT_NAME ":v4l2_device_register failed\n");
+ goto end;
+ }
+ v4l2_dev_registered = true;
+
+ rv = v4gfx_create_instance(&gbl_dev->vouts[0]);
+ if (rv != 0)
+ goto end;
+
+ rv = bc_init();
+ if (rv != 0)
+ goto end;
+
+ bc_dev_registered = true;
+
+ printk(KERN_INFO VOUT_NAME ":OMAP V4L2 GFX driver loaded ok\n");
+ return rv;
+end:
+ printk(KERN_INFO VOUT_NAME ":Error %d loading OMAP V4L2 GFX driver\n",
+ rv);
+
+ if (bc_dev_registered)
+ bc_cleanup();
+
+ if (v4l2_dev_registered)
+ v4l2_device_unregister(&gbl_dev->v4l2_dev);
+
+ kfree(gbl_dev); /* gbl_dev can be null */
+
+ return rv;
+}
+
+static void module_exit_v4gfx(void)
+{
+ bc_cleanup();
+
+ v4gfx_delete_instance(&gbl_dev->v4l2_dev, gbl_dev->vouts[0]);
+
+ v4l2_device_unregister(&gbl_dev->v4l2_dev);
+
+ kfree(gbl_dev);
+
+ platform_driver_unregister(&v4gfx_driver);
+}
+
+module_init(module_init_v4gfx);
+module_exit(module_exit_v4gfx);