diff options
Diffstat (limited to 'drivers/media/video/omapgfx/gfx_init.c')
-rw-r--r-- | drivers/media/video/omapgfx/gfx_init.c | 297 |
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); |